zaki work log

作業ログやら生活ログやらなんやら

[Ansible] Python3 on CentOS7のローカル実行でパッケージ操作する際のエラーと回避策

結論から言うと「inventoryでconnection=local指定してローカル接続してinterpreter_python指定でPython2で動かす」しかないかもしれない。

環境

以下全部、OS標準のPythonがver2であるCentOS 7の話。
(推測だけどRHEL7も同じかも)

(2.9) [zaki@cloud-dev ansible]$ cat /etc/redhat-release
CentOS Linux release 7.8.2003 (Core)
(2.9) [zaki@cloud-dev ansible]$ ansible --version
ansible 2.9.10
  config file = /home/zaki/tmp/ansible/ansible.cfg
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/ansible/2.9/lib/python3.6/site-packages/ansible
  executable location = /home/zaki/ansible/2.9/bin/ansible
  python version = 3.6.8 (default, Apr  2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
(2.9) [zaki@cloud-dev ansible]$ ansible-config dump --only-changed
DEFAULT_STDOUT_CALLBACK(/home/zaki/tmp/ansible/ansible.cfg) = yaml

これに加えて、connection=localによるローカル実行をしたときの動作について整理。

やろうとしてプチハマりしたのは「CentOS7にpip3でインストールしたPython3ベースのAnsibleでローカルのサーバー構築しようとしたらyumdnfも使えず困った」というもの。

Pythonバージョンとyum/dnf

  • yumはPython2で動作。Python3では動かない
  • dnfはPython3で動作。Python2では動かない
    • 加えて、OS標準のPython(/usr/bin/python)で動くため、これがver2だとdnfは使用できない

CentOS7の標準のPythonはver2なので、この時点でdnfは動作しない。

github.com

AnsibleのPythonバージョンとパッケージマネージャ

Ansibleのパッケージマネージャモジュール(yum/dnf)についても、AnsibleのPythonバージョンによって上記の仕様に当てはまる。

  • Python2で動くAnsible(yumで入れたansibleなど)はyumモジュールを使用
  • Python3で動くAnsible(pip3で入れたansibleなど)は、yumモジュールはPython2じゃないため使用できず、dnfモジュールはOS標準のPythonがver2のため使用できない

※ Ansibleの実行ノードがPython3のAnsible on CentOS7でも、ターゲットノードがリモートでそれがCentOS7でPython3がインストールされていても、yum自体は標準のPython2で動くので問題無い。
また、ターゲットノードが物理的にはローカルでも、ローカルへSSH接続してのAnsible実行であれば、やはり問題ない。

Python2を使用する設定

設定方法は以下の3種類

このどれかで、/usr/bin/python2などを指定すれば実行時にPython ver2が使用される。

…なのですが……

interpreter指定はplaybookの"hosts: localhost"指定時に無視される

上記のインタプリタのパス設定、connection=local実行時にこの設定が無視される場合がある。

(うまくいかない)playbookのみでlocalhost指定

inventoryファイルを使用せずにplaybookでターゲットホストにlocalhostと書けばローカル接続するけど、この動作だとなぜかansible.cfgや環境変数で指定したinterpreter_pythonで指定したPythonパスを参照してくれない。
例えば/usr/bin/falseを指定して「絶対に処理失敗」するような設定をしても、普通に実行される。(そしてPython3を使用したyumで失敗する)

- hosts: localhost
  become: true
  tasks:
    - name: install sl
      yum:
        name: sl
        state: present

このplaybookに対して以下のansible.cfgを用意

[defaults]
stdout_callback = yaml
interpreter_python=/usr/bin/false
(2.9) [zaki@cloud-dev ansible]$ ansible-config dump --only-changed
DEFAULT_STDOUT_CALLBACK(/home/zaki/tmp/ansible/ansible.cfg) = yaml
INTERPRETER_PYTHON(/home/zaki/tmp/ansible/ansible.cfg) = /usr/bin/false

この設定で、ansible-playbook実行

(2.9) [zaki@cloud-dev ansible]$ ansible-playbook playbook.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] *********************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [localhost]

TASK [install sl] ********************************************************************************************************************
fatal: [localhost]: FAILED! => changed=false 
  msg: The Python 2 bindings for rpm are needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.. The Python 2 yum module is needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.

PLAY RECAP ***************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

/usr/bin/falseの設定が無視されている。
-vvvを付与すると

<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: zaki

[...]

<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/zaki/.ansible/tmp/ansible-tmp-1598313835.7301395-27630-190728513076291/ /home/zaki/.ansible/tmp/ansible-tmp-1598313835.7301395-27630-190728513076291/AnsiballZ_yum.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'sudo -H -S -n  -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-xwpqogmpfnulfmjdzsjcpszwuqtkxjia ; /home/zaki/ansible/2.9/bin/python /home/zaki/.ansible/tmp/ansible-tmp-1598313835.7301395-27630-190728513076291/AnsiballZ_yum.py'"'"' && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /home/zaki/.ansible/tmp/ansible-tmp-1598313835.7301395-27630-190728513076291/ > /dev/null 2>&1 && sleep 0'

こんな感じで/home/zaki/ansible/2.9/bin/pythonが使用されている。

(2.9) [zaki@cloud-dev ansible]$ /home/zaki/ansible/2.9/bin/python --version
Python 3.6.8

(回避策)inventoryでconnection=localを使用する

playbookのターゲットホストにlocalhost指定だとダメだったけど、inventoryファイルを用意してここでローカル接続するように設定すればinterpreter_python設定を参照するようになった。

playbook

- hosts: hosts
  become: true
  tasks:
    - name: install sl
      yum:
        name: sl
        state: present

inventory (ポイントはansible_connection=local)

[hosts]
localhost ansible_connection=local

ansible.cfg (変更なし)

[defaults]
stdout_callback = yaml
interpreter_python=/usr/bin/false
(2.9) [zaki@cloud-dev ansible]$ ansible-config dump --only-changed
DEFAULT_STDOUT_CALLBACK(/home/zaki/tmp/ansible/ansible.cfg) = yaml
INTERPRETER_PYTHON(/home/zaki/tmp/ansible/ansible.cfg) = /usr/bin/false

これで実行すると、/usr/bin/falseを参照するようになった。

(2.9) [zaki@cloud-dev ansible]$ ansible-playbook playbook.yml -i inventory.ini 

PLAY [hosts] *************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
fatal: [localhost]: FAILED! => changed=false 
  ansible_facts: {}
  failed_modules:
    setup:
      failed: true
      module_stderr: ''
      module_stdout: ''
      msg: |-
        MODULE FAILURE
        See stdout/stderr for the exact error
      rc: 1
  msg: |-
    The following modules failed to execute: setup

PLAY RECAP ***************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

-vvvを付与するとこんな感じ(抜粋)
/usr/bin/falseになっている。

<localhost> ESTABLISH LOCAL CONNECTION FOR USER: zaki
<localhost> EXEC /bin/sh -c 'echo ~zaki && sleep 0'

[...]

<localhost> EXEC /bin/sh -c 'chmod u+x /home/zaki/.ansible/tmp/ansible-tmp-1598314180.3785675-32269-274279378447909/ /home/zaki/.ansible/tmp/ansible-tmp-1598314180.3785675-32269-274279378447909/AnsiballZ_setup.py && sleep 0'
<localhost> EXEC /bin/sh -c 'sudo -H -S -n  -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-txciufnwuqpjhscuqvxxuicdqwfblxxe ; /usr/bin/false /home/zaki/.ansible/tmp/ansible-tmp-1598314180.3785675-32269-274279378447909/AnsiballZ_setup.py'"'"' && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /home/zaki/.ansible/tmp/ansible-tmp-1598314180.3785675-32269-274279378447909/ > /dev/null 2>&1 && sleep 0'

昨夜遅い時間まですぎむらさんにヒアリングしていただいて感謝です!

発端


細かいログはGistにも書いてるので気になる方はどぞ


pythonのパスについてはこちらも参考

zaki-hmkc.hatenablog.com