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
サンプルplaybook
コンテナ内に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を使って実行することが可能。
docker_container_exec
community.docker
コレクションのver 1.5.0以降では、docker_container_execモジュールが新しく使えるようになっている。
これはコネクションプラグインを使った接続でなく、(基本的には)ターゲットノード上で動作しているDockerエンジンを対象に、このモジュールで指定したコンテナ内で指定のコマンドを実行することができる。
docker exec
相当のコンテナ内でコマンドを実行するだけでよく、新しいバージョンのコレクションを使用できるのであれば、このモジュールを使うのが一番楽かもしれない。