結論から言うと「inventoryでconnection=local
指定してローカル接続してinterpreter_python
指定でPython2で動かす」しかないかもしれない。
- 環境
- Pythonバージョンとyum/dnf
- AnsibleのPythonバージョンとパッケージマネージャ
- Python2を使用する設定
- interpreter指定はplaybookの"hosts: localhost"指定時に無視される
環境
以下全部、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でローカルのサーバー構築しようとしたらyum
もdnf
も使えず困った」というもの。
Pythonバージョンとyum/dnf
- yumはPython2で動作。Python3では動かない
- dnfはPython3で動作。Python2では動かない
CentOS7の標準のPythonはver2なので、この時点でdnfは動作しない。
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種類
- inventory変数の
ansible_python_interpreter
で指定 Interpreter Discovery - ansible.cfgの
[defaults]
セクションのinterpreter_python
で指定 INTERPRETER_PYTHON - 環境変数
ANSIBLE_PYTHON_INTERPRETER
で指定 ANSIBLE_PYTHON_INTERPRETER
このどれかで、/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'
昨夜遅い時間まですぎむらさんにヒアリングしていただいて感謝です!
おおなるほど再現しました。
— すぎむら (@sugitk) 2020年8月24日
インベントリとして ansible-playbook -i localhost, を渡せば SSH になりますが、渡さない場合は LOCAL CONNECTION になりました。
これはおそらく仕様だと思いますが、必要ならば明日確認します。
$ ansible-config dump --only-changed
— すぎむら (@sugitk) 2020年8月24日
INTERPRETER_PYTHON(/home/sugimura/zaki/ansible.cfg) = /usr/bin/perl
$ ansible-playbook playbook.yml -i localhost, -k -K -vvv
これならエラーになりますね。playbook は同じものを使っています。
発端
Ansible 2.9、playbookのターゲットホストに"localhost"指定すると、sshとかなしにローカル実行になるけど、ansible.cfgのinterpreter_pythonの設定が無視しされる?
— z a k i (@zaki_hmkc) 2020年8月24日
細かいログはGistにも書いてるので気になる方はどぞ
pythonのパスについてはこちらも参考