zaki work log

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

[Ansible / Docker] Dockerコンテナ版Ansible(2.8.13)を作ってみる (練習)

CI/CDや、Ansibleのインストールが難しいオフライン環境とかで使えるかな、と思って、試しに作ってみた。

Ansibleバージョン

各メジャーバージョン毎の、最新マイナーバージョンは、各バージョン毎のリリースノートで確認できる。

docs.ansible.com

2.8であればここから。
(なぜ2.8なのかは、deprecatedとなって2.9で消えたモジュールとかあるので、とりあえず2.8にした感じ)
(ちなみに試した結果、Python3だとpysphereは動作しない)

github.com

※ 2020.08.10時点で2.8.13だったが、2020.08.11時点で2.8.14にアップデートしてる。記事内では2.8.13で進めてる。

ベースイメージ

pipでインストールするので、Pythonイメージをベースとした。

hub.docker.com

2020.08.10時点でpython:3.8.5-slim-busterあたりかな。

とりあえずpullする。

[zaki@cloud-dev ~]$ docker pull python:3.8.5-slim-buster
3.8.5-slim-buster: Pulling from library/python
bf5952930446: Pull complete
385bb58d08e6: Pull complete
ab14b629693d: Pull complete
7a5d07f2fd13: Pull complete
56745e40505a: Pull complete
Digest: sha256:f7edd1bb431a224e7f4f3e23cbb22738e82f4895a6d28f86294ce006177360c3
Status: Downloaded newer image for python:3.8.5-slim-buster
docker.io/library/python:3.8.5-slim-buster

Dockerfile作成のためのネタ作り用にbash起動。

[zaki@cloud-dev ~]$ docker run -it python:3.8.5-slim-buster bash
root@6b6492e770bb:/#
root@6b6492e770bb:/# python --version
Python 3.8.5
root@6b6492e770bb:/# pip
pip     pip3    pip3.8
root@6b6492e770bb:/# pip --version
pip 20.2.1 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
root@6b6492e770bb:/#

Ansible 2.8インストール

root@6b6492e770bb:/# pip install ansible==2.8.13
Collecting ansible==2.8.13
  Downloading ansible-2.8.13.tar.gz (12.7 MB)
     |████████████████████████████████| 12.7 MB 3.2 MB/s
Collecting jinja2
  Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
     |████████████████████████████████| 125 kB 6.5 MB/s
Collecting PyYAML
  Downloading PyYAML-5.3.1.tar.gz (269 kB)
     |████████████████████████████████| 269 kB 3.2 MB/s
Collecting cryptography
  Downloading cryptography-3.0-cp35-abi3-manylinux2010_x86_64.whl (2.7 MB)
     |████████████████████████████████| 2.7 MB 11.3 MB/s
Collecting MarkupSafe>=0.23
  Downloading MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl (32 kB)
Collecting cffi!=1.11.3,>=1.8
  Downloading cffi-1.14.1-cp38-cp38-manylinux1_x86_64.whl (409 kB)
     |████████████████████████████████| 409 kB 18.9 MB/s
Collecting six>=1.4.1
  Downloading six-1.15.0-py2.py3-none-any.whl (10 kB)
Collecting pycparser
  Downloading pycparser-2.20-py2.py3-none-any.whl (112 kB)
     |████████████████████████████████| 112 kB 18.5 MB/s
Building wheels for collected packages: ansible, PyYAML
  Building wheel for ansible (setup.py) ... done
  Created wheel for ansible: filename=ansible-2.8.13-py3-none-any.whl size=12654620 sha256=f768271c4c958b04f4dfddde1e73dd6fcce16ae61b59e53b0812838b8c823884
  Stored in directory: /root/.cache/pip/wheels/c2/75/3a/4707639b7733d0689b966c190b6828ddc98c8f8c9a90dcef07
  Building wheel for PyYAML (setup.py) ... done
  Created wheel for PyYAML: filename=PyYAML-5.3.1-cp38-cp38-linux_x86_64.whl size=44617 sha256=1e4c20c21b93f8b820447ffdcf4c60f331feb3c808cedf3286461e48daa4e334
  Stored in directory: /root/.cache/pip/wheels/13/90/db/290ab3a34f2ef0b5a0f89235dc2d40fea83e77de84ed2dc05c
Successfully built ansible PyYAML
Installing collected packages: MarkupSafe, jinja2, PyYAML, pycparser, cffi, six, cryptography, ansible
Successfully installed MarkupSafe-1.1.1 PyYAML-5.3.1 ansible-2.8.13 cffi-1.14.1 cryptography-3.0 jinja2-2.11.2 pycparser-2.20 six-1.15.0

これだけか?
(だめ。sshがない。後述)

root@6b6492e770bb:/# which ansible
/usr/local/bin/ansible
root@6b6492e770bb:/# ansible --version
ansible 2.8.13
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.8/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.8.5 (default, Aug  4 2020, 16:24:08) [GCC 8.3.0]

失敗作

Dockerfile

FROM python:3.8.5-slim-buster

RUN pip install ansible==2.8.13
CMD ["/usr/local/bin/ansible", "--version"]

これだけでいいかな。
(だめ。sshがない。後述)

build

[zaki@cloud-dev ansible]$ ls -F
2.8.13/
[zaki@cloud-dev ansible]$ ls -l 2.8.13/
合計 4
-rw-rw-r--. 1 zaki zaki 107  8月 10 17:45 Dockerfile
[zaki@cloud-dev ansible]$ cat 2.8.13/Dockerfile 
FROM python:3.8.5-slim-buster

RUN pip install ansible==2.8.13
CMD ["/usr/local/bin/ansible", "--version"]
[zaki@cloud-dev ansible]$ docker build -t ansible:2.8.13 .
unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /home/zaki/src/docker-images/ansible/Dockerfile: no such file or directory
[zaki@cloud-dev ansible]$ docker build -t ansible:2.8.13 2.8.13/
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM python:3.8.5-slim-buster
 ---> 07ea617545cd
Step 2/3 : RUN pip install ansible==2.8.13
 ---> Running in 32b8a3c22ee2
Collecting ansible==2.8.13
  Downloading ansible-2.8.13.tar.gz (12.7 MB)
Collecting jinja2
  Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
Collecting PyYAML
  Downloading PyYAML-5.3.1.tar.gz (269 kB)
Collecting cryptography
  Downloading cryptography-3.0-cp35-abi3-manylinux2010_x86_64.whl (2.7 MB)
Collecting MarkupSafe>=0.23
  Downloading MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl (32 kB)
Collecting six>=1.4.1
  Downloading six-1.15.0-py2.py3-none-any.whl (10 kB)
Collecting cffi!=1.11.3,>=1.8
  Downloading cffi-1.14.1-cp38-cp38-manylinux1_x86_64.whl (409 kB)
Collecting pycparser
  Downloading pycparser-2.20-py2.py3-none-any.whl (112 kB)
Building wheels for collected packages: ansible, PyYAML
  Building wheel for ansible (setup.py): started
  Building wheel for ansible (setup.py): finished with status 'done'
  Created wheel for ansible: filename=ansible-2.8.13-py3-none-any.whl size=12654620 sha256=d6580d8b3a5eb988efd8fbf52f5012c47d8eea979356146134e4ac40ec6b3e82
  Stored in directory: /root/.cache/pip/wheels/c2/75/3a/4707639b7733d0689b966c190b6828ddc98c8f8c9a90dcef07
  Building wheel for PyYAML (setup.py): started
  Building wheel for PyYAML (setup.py): finished with status 'done'
  Created wheel for PyYAML: filename=PyYAML-5.3.1-cp38-cp38-linux_x86_64.whl size=44617 sha256=38be490d110d8b5f68c43993b572f7d7d4a89bd33fc4dfa302a1e55ecf253c1a
  Stored in directory: /root/.cache/pip/wheels/13/90/db/290ab3a34f2ef0b5a0f89235dc2d40fea83e77de84ed2dc05c
Successfully built ansible PyYAML
Installing collected packages: MarkupSafe, jinja2, PyYAML, six, pycparser, cffi, cryptography, ansible
Successfully installed MarkupSafe-1.1.1 PyYAML-5.3.1 ansible-2.8.13 cffi-1.14.1 cryptography-3.0 jinja2-2.11.2 pycparser-2.20 six-1.15.0
Removing intermediate container 32b8a3c22ee2
 ---> c723d156486c
Step 3/3 : CMD ["/usr/local/bin/ansible", "--version"]
 ---> Running in 902e6e0c9f94
Removing intermediate container 902e6e0c9f94
 ---> 39a7fa69321b
Successfully built 39a7fa69321b
Successfully tagged ansible:2.8.13
[zaki@cloud-dev ansible]$
[zaki@cloud-dev ansible]$ sudo docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
ansible                      2.8.13              39a7fa69321b        22 seconds ago      243MB
python                       3.8.5-slim-buster   07ea617545cd        5 days ago          113MB
:
:

できました。

[zaki@cloud-dev ~]$ docker run --rm ansible:2.8.13 
ansible 2.8.13
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.8/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.8.5 (default, Aug  4 2020, 16:24:08) [GCC 8.3.0]
[zaki@cloud-dev ~]$

push

Docker Hubに置いておく。

Docker Hub

f:id:zaki-hmkc:20200810181135p:plain

Docker Hubにログインし、「Create Repository」押下。
リポジトリ名やDescriptionを適度に入力して、ページ下部の「Create」押下する。
Build Settingsのオプションはひとまず設定なし。

f:id:zaki-hmkc:20200810181459p:plain

(今表示を見て気付いたんだけど、Docker Hubのプライベートのリポジトリって1個しか作れなかったのね…)

f:id:zaki-hmkc:20200810181646p:plain

Docker Hubにリポジトリができました。

イメージのpush

ビルド済みイメージはこの通り。

[zaki@cloud-dev ~]$ docker images ansible
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ansible             2.8.13              39a7fa69321b        30 minutes ago      243MB

これを、作成したzakihmkc/ansiblepushするために、docker tagを使って別名を作成する。

[zaki@cloud-dev ~]$ docker images | grep ansible
ansible                      2.8.13              39a7fa69321b        30 minutes ago      243MB
zakihmkc/ansible             2.8.13              39a7fa69321b        30 minutes ago      243MB
[zaki@cloud-dev ~]$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: zakihmkc
Password:
WARNING! Your password will be stored unencrypted in /home/zaki/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[zaki@cloud-dev ~]$
[zaki@cloud-dev ~]$ docker push zakihmkc/ansible:2.8.13 
The push refers to repository [docker.io/zakihmkc/ansible]
0cdb117a6909: Pushed
71c24c079ec4: Mounted from library/python
1e441fe06d90: Mounted from library/python
98ff2784e9f5: Mounted from library/python
2b99e2403063: Mounted from library/python
d0f104dc0a1f: Mounted from library/python
2.8.13: digest: sha256:f6388d0d606eeee5c2d06ded24cf683b814b6f6b48d5d01d6d4999a03580068d size: 1582
[zaki@cloud-dev ~]$

Docker Hubだとこうなる。

f:id:zaki-hmkc:20200810182625p:plain

一般ユーザーから見るとこんな感じ

f:id:zaki-hmkc:20200810182854p:plain

hub.docker.com

※ ちなみにこれ、push直後でpull 4になってるので、くれぐれも機密情報込みのイメージをうっかりpushしないように。。

お試し実行

Dockerは動いているが、Ansibleは未インストールのホストでお試し。

[zaki@example ~]$ which ansible
/usr/bin/which: no ansible in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zaki/.local/bin:/home/zaki/bin)
[zaki@example ~]$ which docker
/usr/bin/docker
[zaki@example ~]$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:46:54 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:45:28 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
[zaki@example ~]$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

実行 (初回はpull分の時間がかかる)

[zaki@example ~]$ sudo docker run --rm zakihmkc/ansible:2.8.13
Unable to find image 'zakihmkc/ansible:2.8.13' locally
2.8.13: Pulling from zakihmkc/ansible
bf5952930446: Pull complete
385bb58d08e6: Pull complete
ab14b629693d: Pull complete
7a5d07f2fd13: Pull complete
56745e40505a: Pull complete
a651563c3173: Pull complete
Digest: sha256:f6388d0d606eeee5c2d06ded24cf683b814b6f6b48d5d01d6d4999a03580068d
Status: Downloaded newer image for zakihmkc/ansible:2.8.13
ansible 2.8.13
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.8/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.8.5 (default, Aug  4 2020, 16:24:08) [GCC 8.3.0]
[zaki@example ~]$ 

うむ。

ansible pingモジュール実行

Dockerコンテナ実行時に、用意したインベントリとプレイブックを参照できるようにvolume設定すればOK

[zaki@example ansible-work]$ ls
inventory.ini  playbook.yml
[zaki@example ansible-work]$ cat inventory.ini 
[hosts]
client1     ansible_host=192.168.0.18
client2     ansible_host=192.168.0.19
client3     ansible_host=192.168.0.21
[zaki@example ansible-work]$ sudo docker run -v "$PWD":/tmp -it --rm zakihmkc/ansible:2.8.13 ansible all -i /tmp/inventory.ini -m ping -k
SSH password: 
client1 | FAILED! => {
    "msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}
client3 | FAILED! => {
    "msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}
client2 | FAILED! => {
    "msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}

sshが無い。なるほど。。


修正版

Dockerfile

FROM python:3.8.5-slim-buster

RUN pip install ansible==2.8.13 \
    && apt update \
    && apt install -y sshpass \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

CMD ["/usr/local/bin/ansible", "--version"]

build

push用のtagつけてビルド。(上書き)

[zaki@cloud-dev ansible]$ ls 2.8.13/
Dockerfile
[zaki@cloud-dev ansible]$ docker build -t zakihmkc/ansible:2.8.13 2.8.13/
[zaki@cloud-dev ansible]$ docker images zakihmkc/ansible
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
zakihmkc/ansible    2.8.13              1a6ff41f0e9b        44 minutes ago      253MB
zakihmkc/ansible    <none>              39a7fa69321b        28 hours ago        243MB

前日(28時間前)の残骸が見える

push

[zaki@cloud-dev ansible]$ docker push zakihmkc/ansible:2.8.13

お試し実行

[zaki@example ansible-work]$ ls
ansible.cfg  inventory.ini  playbook.yml

ファイル説明:

~/.ssh/known_hostsなどは無いし書き込んでも無意味なので、ansible.cfgに無視する設定を記述。

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null

Ansible実行時にコンテナ内のユーザーIDは(デフォルトではrootだが)基本的に固定されてない前提で、inventory.iniにansible_userで設定。

[hosts]
client1     ansible_host=192.168.0.18
client2     ansible_host=192.168.0.19
client3     ansible_host=192.168.0.21

[all:vars]
ansible_user=zaki

パスワード認証

-k付与して実行。
前述のansible.cfg/inventoryをコンテナ内で使用するため、-v $PWD:/mntでコンテナ内/mntに送り込み、-w /mntで実行時のワーキングディレクトリを/mntに設定(ansible実行時のカレントディレクトリを/mntにすることで、-vで設定したansible.cfgをカレントディレクトリで参照させる)
-itでttyとstdinを実行時シェルと繋げて-kによるパスワード認証などコンソールが使えるようにする。

この辺りもどうぞ。

qiita.com

あと、コンテナイメージ名指定の後のansible all -i /mnt/inventory.ini -m ping -kは通常利用の通り。

[zaki@example ansible-work]$ sudo docker run -v "$PWD":/mnt -w /mnt -it --rm zakihmkc/ansible:2.8.13 ansible all -i /mnt/inventory.ini -m ping -k
SSH password: 
client2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
client3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
client1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

公開鍵認証

公開鍵設定は↑を組み合わせるなどして手動で一度仕込んでおこう。

[zaki@example ansible-work]$ sudo docker run -v "$PWD":/mnt -w /mnt -v ~/.ssh:/.ssh  -it --rm zakihmkc/ansible:2.8.13 ansible all -i inventory.ini --private-key /.ssh/id_rsa_nopass -m ping
client3 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
client2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
client1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

無事に公開鍵認証で(対話的にパスワード入力無しに)Ansible実行できました。
(rootで実行してるのでコンテナ実行中は鍵の参照権限的に危ないんだけど。。)

ansible-playbook

同じディレクトリにPlaybookファイルも置いてコンテナから参照できるようにすればこの通り。

[zaki@example ansible-work]$ sudo docker run -v "$PWD":/mnt -w /mnt -v ~/.ssh:/.ssh  -it --rm zakihmkc/ansible:2.8.13 ansible-playbook -i inventory.ini --private-key /.ssh/id_rsa_nopass playbook.yml

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [client2]
ok: [client3]
ok: [client1]

TASK [run container] ***********************************************************
ok: [client1] => {
    "msg": "cloud-dev"
}
ok: [client2] => {
    "msg": "manager"
}
ok: [client3] => {
    "msg": "registry"
}

PLAY RECAP *********************************************************************
client1                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
client2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
client3                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[zaki@example ansible-work]$ 

動いてる。


イメージとソース(Dockerfile)はこちら。

hub.docker.com

github.com


Docker HubにdescribeとかDockerfileソースへのリンクとか設定しないとなぁ。。


Docker Hub、describeにusageとかちょっと書いてみた。Markdownで書けたのね。

Ansible Runner

公式でAnsibleのイメージあったらしい!

github.com

ansible-runner.readthedocs.io

hub.docker.com