てくなべ (tekunabe)

ansible / network / automation

[Ansible] 利用する Python をタスクごとに切り替える方法(ansible_python_interpreter をタスク変数で指定)

■ はじめに

Ansible では、ターゲットノード(SSH接続先)で利用する Pythonansible_python_interpreter という変数で明示的に指定できます。

この変数は、インベントリ変数や host_vars の他にも、タスク変数としても指定できます。そのため、タスクごとにターゲットノードで利用する Python を切り替えられます。

この記事では、かんたんなサンプルでご紹介します。

  • 動作確認環境
    • Ansible 2.8.4
    • CentosOS 7.6 (コントロールノード、ターゲットノード共通)
    • Python 3 未インストール(ターゲットノード)
      • /usr/bin/pyrhon は Python 2


■ 検証サンプル

ここでは、 yum モジュールが Python 2 のみの対応していること利用して検証します。

(いい方法が思いつきませんでした・・・)

Playbook

- hosts: testsv
  gather_facts: no
  become: yes
 
  tasks:
    # デフォルのまま Python2 で実行
    - name: task1 
      yum:
        list: curl  

    # Python3 を使おうとしてエラー(そもそも未インストールのため)
    - name: task2 
      yum:
        list: curl
      ignore_errors: yes # エラーになるが検証を続けるため無視
      vars:
        ansible_python_interpreter: /usr/bin/python3

    ###########################
    # python3 のインストール
    - name: task3
      yum:
        name: python3

    # Python3 をインストールしたが、まだ Python2
    - name: task4
      yum:
        list: curl

    # Python3 を使う(インストール済み、指定済みなので今度はOK])
    - name: task5
      yum:
        list: curl
      vars:
        ansible_python_interpreter: /usr/bin/python3
      ignore_errors: yes # エラーになるが検証を続けるため無視

実行

$ ansible-playbook -i inventory.ini test.yml -v
Using /vagrant/ansible.cfg as config file

PLAY [testsv] *********************************************************************************

TASK [task1] **********************************************************************************
ok: [testsv] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "results": [{"arch": "x86_64", "envra": "0:curl-7.29.0-51.el7.x86_64", "epoch": "0", "name": "curl", "release": "51.el7", "repo": "installed", "version": "7.29.0", "yumstate": "installed"}, {"arch": "x86_64", "envra": "0:curl-7.29.0-54.el7.x86_64", "epoch": "0", "name": "curl", "release": "54.el7", "repo": "base", "version": "7.29.0", "yumstate": "available"}]}

TASK [task2] **********************************************************************************
fatal: [testsv]: FAILED! => {"changed": false, "msg": ["Could not detect which major revision of yum is in use, which is required to determine module backend.", "You can manually specify use_backend to tell the module whether to use the yum (yum3) or dnf (yum4) backend})"]}
...ignoring

TASK [task3] **********************************************************************************
changed: [testsv] => {"ansible_facts": {"pkg_mgr": "yum"}, "changed": true, "changes": {"installed": ["python3"]}, "msg": "", "rc": 0, "results": ["Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: ty1.mirror.newmediaexpress.com\n * extras: ty1.mirror.newmediaexpress.com\n * updates: ty1.mirror.newmediaexpress.com\nResolving Dependencies\n--> Running transaction check\n---> Package python3.x86_64 0:3.6.8-10.el7 will be installed\n--> Processing Dependency: python3-libs(x86-64) = 3.6.8-10.el7 for package: python3-3.6.8-10.el7.x86_64\n--> Processing Dependency: python3-setuptools for package: python3-3.6.8-10.el7.x86_64\n--> Processing Dependency: python3-pip for package: python3-3.6.8-10.el7.x86_64\n--> Processing Dependency: libpython3.6m.so.1.0()(64bit) for package: python3-3.6.8-10.el7.x86_64\n--> Running transaction check\n---> Package python3-libs.x86_64 0:3.6.8-10.el7 will be installed\n---> Package python3-pip.noarch 0:9.0.3-5.el7 will be installed\n---> Package python3-setuptools.noarch 0:39.2.0-10.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package                   Arch          Version              Repository   Size\n================================================================================\nInstalling:\n python3                   x86_64        3.6.8-10.el7         base         69 k\nInstalling for dependencies:\n python3-libs              x86_64        3.6.8-10.el7         base        7.0 M\n python3-pip               noarch        9.0.3-5.el7          base        1.8 M\n python3-setuptools        noarch        39.2.0-10.el7        base        629 k\n\nTransaction Summary\n================================================================================\nInstall  1 Package (+3 Dependent packages)\n\nTotal download size: 9.4 M\nInstalled size: 48 M\nDownloading packages:\n--------------------------------------------------------------------------------\nTotal                                               30 kB/s | 9.4 MB  05:20     \nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Installing : python3-libs-3.6.8-10.el7.x86_64                             1/4 \n  Installing : python3-3.6.8-10.el7.x86_64                                  2/4 \n  Installing : python3-setuptools-39.2.0-10.el7.noarch                      3/4 \n  Installing : python3-pip-9.0.3-5.el7.noarch                               4/4 \n  Verifying  : python3-setuptools-39.2.0-10.el7.noarch                      1/4 \n  Verifying  : python3-libs-3.6.8-10.el7.x86_64                             2/4 \n  Verifying  : python3-3.6.8-10.el7.x86_64                                  3/4 \n  Verifying  : python3-pip-9.0.3-5.el7.noarch                               4/4 \n\nInstalled:\n  python3.x86_64 0:3.6.8-10.el7                                                 \n\nDependency Installed:\n  python3-libs.x86_64 0:3.6.8-10.el7          python3-pip.noarch 0:9.0.3-5.el7  \n  python3-setuptools.noarch 0:39.2.0-10.el7  \n\nComplete!\n"]}

TASK [task4] **********************************************************************************
ok: [testsv] => {"changed": false, "results": [{"arch": "x86_64", "envra": "0:curl-7.29.0-51.el7.x86_64", "epoch": "0", "name": "curl", "release": "51.el7", "repo": "installed", "version": "7.29.0", "yumstate": "installed"}, {"arch": "x86_64", "envra": "0:curl-7.29.0-54.el7.x86_64", "epoch": "0", "name": "curl", "release": "54.el7", "repo": "base", "version": "7.29.0", "yumstate": "available"}]}

TASK [task5] **********************************************************************************
fatal: [testsv]: 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."}
...ignoring

PLAY RECAP ************************************************************************************
testsv                     : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=2   

各タスクの説明

  • task1
    • 利用する Python を指定していないため、デフォルトの /usr/bin/python で実行。そのため Python 2。結果、yum モジュールは正常実行。
    • yum モジュールが使用できるかどうか確認するのが目的のため、システムに変更を及ばさない適当な内容にしている。以下同じ
  • task2
    • ansible_python_interpreter/usr/bin/python3Python 3 を利用しようとしているが、未インストールのためエラー。タスクを続行させるために ignore_errors: yes
    • CentOS 7 系の python 3 インストールに関する記事はこちら
  • task3
    • ターゲットノードに python3 をインストールする。このタスク自体は Python 2 で実行。結果、yum モジュールは正常実行。
  • task4

    • Python 3 をインストールしたが、ansible_python_interpreter を指定していないため、まだ Python 2 で実行。結果、yum モジュールは正常実行。
  • task5

    • ansible_python_interpreter/usr/bin/python3 を指定し、Python 3 もインストール済みなので、Python 3 で実行。結果、 yum モジュールは Python 3 に対応していないためエラー。


■ まとめ

タスク変数で ansible_python_interpreter を指定することにより、タスクごとにターゲットノードで利用する Python を切り替えらることをご紹介しました。

今回は、基本的には Python 2 で実行し、例外的に Python 3 で実行するサンプルでしたが、もちろん逆もできます。ただし、例外をあまり乱発すると混乱のもとになるので、特別な事情がある場合のみの利用にとどめたほうが良さそうです。