zaki work log

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

[Ansible] Connectionプラグインを使ってDockerコンテナを対象に接続・処理する

2021.05.23、docker_container_execモジュールについて追記


Ansibleの処理対象としてDockerコンテナを指定する。 sshを使用しないので、接続先(=コンテナ内)でsshサーバーは不要だけど、コンテナ内にPythonは必要。

お試しということで、Python:3イメージを使う。

対象コンテナ起動

[zaki@cloud-dev ~]$ docker run -d --name=sample-py-container python:3 tail -f /dev/null
5ed36892e53e70e9945883e662d76a9e93f1734924b5ef3fd7197203cfb8bcc0

これでPythonインタプリタ入りの何もしないコンテナが実行される。
ansible_hostで指定するのでコンテナ名も指定しておく (指定しない場合でも、コンテナID(↑だと5ed3とか)でも指定可能)。

[zaki@cloud-dev ~]$ docker exec -it sample-py-container bash
root@5ed36892e53e:/# python --version
Python 3.8.5

playbook

例なのでディレクトリ作ってlsの結果を出力してみる。

- hosts: docker
  gather_facts: no

  tasks:
  - name: create directory
    file:
      path: /zzz
      state: directory
      mode: '0755'

  - name: command sample
    shell: hostname; ls -C /
    register: result_ls

  - name: print result
    debug:
      msg: "{{ result_ls }}"

hosts: dockerと書いてるけど、これは次のinventoryで定義する。

inventory

[docker]
localhost  ansible_connection=docker ansible_host=sample-py-container

localhostって書いてはいるけど、後ろにansible_host=で接続先を明示的に指定してるので、このホスト名は特に意味はない(極端な話、名前解決できないzzzzとかでも動く)。要は後続の設定でAnsible実行ホスト上で動いてるDockerのコンテナに接続に行く。

重要なのはansible_connection=dockerで、接続プラグインdockerを指定すること。
それから、対象コンテナをansible_hostでコンテナ名またはコンテナIDを指定する。

sample-py-containerは、コンテナ起動時に--nameで指定したコンテナ名。
再掲だけど、コンテナ名を指定しない場合はコンテナIDでもOK。
要はdocker start <container-name or container-id>とかで指定するのと同じようにコンテナを一意に指定できる識別子。

実行

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

PLAY [docker] ****************************************************************************************

TASK [create directory] ******************************************************************************
[WARNING]: Platform linux on host localhost is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
changed: [localhost]

TASK [command sample] ********************************************************************************

changed: [localhost]

TASK [print result] **********************************************************************************

ok: [localhost] =>
  msg:
    changed: true
    cmd: hostname; ls -C /
    delta: '0:00:00.409172'
    end: '2020-09-20 12:23:46.157786'
    failed: false
    rc: 0
    start: '2020-09-20 12:23:45.748614'
    stderr: ''
    stderr_lines: []
    stdout: |-
      5ed36892e53e
      bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
      boot  etc  lib   media  opt  root  sbin  sys  usr  zzz
    stdout_lines:
    - 5ed36892e53e
    - "bin   dev  home  lib64\tmnt  proc  run\t srv  tmp  var"
    - "boot  etc  lib\t media\topt  root  sbin  sys  usr  zzz"

PLAY RECAP *******************************************************************************************

localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

ちゃんと動いた。

接続情報

ちなみに、実行時に-vvvくらいまで付けると

<sample-py-container> EXEC ['/usr/bin/docker', b'exec', b'-i', 'sample-py-container', '/bin/sh', '-c', '/bin/sh -c \'echo PLATFORM; uname; echo FOUND; command -v \'"\'"\'/usr/bin/python\'"\'"\'; command -v \'"\'"\'python3.7\'"\'"\'; command -v \'"\'"\'python3.6\'"\'"\'; command -v \'"\'"\'python3.5\'"\'"\'; command -v \'"\'"\'python2.7\'"\'"\'; command -v \'"\'"\'python2.6\'"\'"\'; command -v \'"\'"\'/usr/libexec/platform-python\'"\'"\'; command -v \'"\'"\'/usr/bin/python3\'"\'"\'; command -v \'"\'"\'python\'"\'"\'; echo ENDFOUND && sleep 0\'']

という感じで内部ではdocker execを実行しているのが確認できるので、ssh接続のときのような認証情報を必要としていない。

警告は何?

Python:3使ってるのになんで?と思ったけど、デフォルトで使用する/usr/bin/pythonが2.7だったw

(dev) [zaki@cloud-dev connection-docker]$ docker exec -it sample-py-container bash
root@5ed36892e53e:/# /usr/bin/python --version
Python 2.7.16
root@5ed36892e53e:/# /usr/bin/python3 --version
Python 3.7.3

これは、ansible.cfgに以下を追加すれば、接続先(=コンテナ内)のインタプリタ/usr/bin/python3を使うようになるので警告も消える。

[defaults]
stdout_callback = yaml
interpreter_python = /usr/bin/python3

最初にpython --versionでバージョン確認したはずなんだけどなー。。。

root@5ed36892e53e:/# which python
/usr/local/bin/python

なるほどねー。。。

確認

ちゃんとコンテナの中でPlaybookの内容が処理されました。

(dev) [zaki@cloud-dev connection-docker]$ ls -F /
bin@   dev/  home/  lib64@  mnt/  proc/  run/   srv/  tmp/  var/
boot/  etc/  lib@   media/  opt/  root/  sbin@  sys/  usr/
(dev) [zaki@cloud-dev connection-docker]$ docker exec -it sample-py-container ls -F /
bin/   dev/  home/  lib64/  mnt/  proc/  run/   srv/  tmp/  var/
boot/  etc/  lib/   media/  opt/  root/  sbin/  sys/  usr/  zzz/

環境

(dev) [zaki@cloud-dev connection-docker]$ ansible-playbook --version
ansible-playbook 2.9.10
  config file = /home/zaki/src/ansible-sample/connection-docker/ansible.cfg
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/.local/lib/python3.6/site-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 3.6.8 (default, Apr  2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
(dev) [zaki@cloud-dev connection-docker]$ ansible-config dump --only-changed
DEFAULT_STDOUT_CALLBACK(/home/zaki/src/ansible-sample/connection-docker/ansible.cfg) = yaml

(参考)

Connection Plugins

docs.ansible.com

サンプルplaybook

github.com

コンテナ内にPythonが無い場合

コネクションプラグインにデフォルトのsshでなくdockerを指定することで、Ansibleの基本要件である「SSHサーバーが動いていてPythonインタプリタが入ってればターゲットノードとして処理可能」のうち、「SSH接続」のかわりに「DockerのAPI接続」を使うようにしている。
つまり、対象のターゲットノードにPythonが必要という要件はそのまま残っているので、コンテナ内にPythonインタプリタが無い場合は処理できない。

raw

例ではpythonイメージを使ったので普通にAnsible実行できるが、Pythonインタプリタが入ってないイメージ(AlpineやDebianベースのイメージは基本入ってないっぽい)も当然あり、その場合は通常のAnsibleモジュールは使用できない。
そのようなコンテナに対する操作をAnsibleを使った処理にどうしても組み込みたい場合は、rawモジュールを使うなどする。
rawモジュールはPythonインタプリタが無くても使うことができ、dockerコネクションを使って接続した上でdocker execでコンテナ内のシェル起動してから実行できるコマンド類をAnsibleを使って実行することが可能。

docs.ansible.com

docker_container_exec

community.dockerコレクションのver 1.5.0以降では、docker_container_execモジュールが新しく使えるようになっている。
これはコネクションプラグインを使った接続でなく、(基本的には)ターゲットノード上で動作しているDockerエンジンを対象に、このモジュールで指定したコンテナ内で指定のコマンドを実行することができる。

docs.ansible.com

docker exec相当のコンテナ内でコマンドを実行するだけでよく、新しいバージョンのコレクションを使用できるのであれば、このモジュールを使うのが一番楽かもしれない。