tk_ch’s blog

インフラエンジニアのブログ

Ansibleの実行環境をDockerコンテナにする

Ansibleを使う際に、以下のような環境面での悩みが出てくるケースがある。

  • 複数の構成管理サーバにAnsibleをインストールするのが手間。
  • Ansibleの実行環境にはPythonが必要だが、Ansibleのために特定バージョンのPythonをインストールして環境が汚れるのが嫌。
  • 複数のバージョンのAnsibleを同じサーバで実行させたい場合、共存させるのが面倒(というか共存できない?)。

→これらは、Ansibleの実行環境をDockerコンテナにすれば解決できる。
 という訳で、Ansible実行用のDockerコンテナを用意してみる。

環境

  • DockerホストのOS:RockyLinux8.6
  • 管理対象サーバ(Ansibleの実行先)のOS:RockyLinux8.6
  • Docker:20.10.18
  • ansible:7.3.0
  • ansible-core:2.14.3

実施内容

Ansibleの最新バージョンを確認

公式ドキュメントのAnsible Roadmapによると、2023年3月現在の最新安定版のバージョンは7系。
pipを使って、インストールできる最新版を確認する。

# docker run --rm -it rockylinux:8.6 /bin/bash

# dnf module -y install python39

# python3 -m pip install ansible==
WARNING: Running pip install with root privileges is generally not a good idea. Try `python3 -m pip install --   user` instead.
ERROR: Could not find a version that satisfies the requirement ansible== (from versions: 1.0, 1.1, 1.2, 1.2.1,    1.2.2, 1.2.3, 1.3.0, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.5, 1.5.1, 1.5.2, 1   .5.3, 1.5.4, 1.5.5, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.7, 1.7.1, 1.   7.2, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.9.0.1, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.9.5, 1.9.6, 2.0.0.0, 2.0.0.1, 2.0   .0.2, 2.0.1.0, 2.0.2.0, 2.1.0.0, 2.1.1.0, 2.1.2.0, 2.1.3.0, 2.1.4.0, 2.1.5.0, 2.1.6.0, 2.2.0.0, 2.2.1.0, 2.2.2   .0, 2.2.3.0, 2.3.0.0, 2.3.1.0, 2.3.2.0, 2.3.3.0, 2.4.0.0, 2.4.1.0, 2.4.2.0, 2.4.3.0, 2.4.4.0, 2.4.5.0, 2.4.6.0   , 2.5.0a1, 2.5.0b1, 2.5.0b2, 2.5.0rc1, 2.5.0rc2, 2.5.0rc3, 2.5.0, 2.5.1, 2.5.2, 2.5.3, 2.5.4, 2.5.5, 2.5.6, 2.   5.7, 2.5.8, 2.5.9, 2.5.10, 2.5.11, 2.5.12, 2.5.13, 2.5.14, 2.5.15, 2.6.0a1, 2.6.0a2, 2.6.0rc1, 2.6.0rc2, 2.6.0   rc3, 2.6.0rc4, 2.6.0rc5, 2.6.0, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6, 2.6.7, 2.6.8, 2.6.9, 2.6.10, 2.6.11,    2.6.12, 2.6.13, 2.6.14, 2.6.15, 2.6.16, 2.6.17, 2.6.18, 2.6.19, 2.6.20, 2.7.0.dev0, 2.7.0a1, 2.7.0b1, 2.7.0rc   1, 2.7.0rc2, 2.7.0rc3, 2.7.0rc4, 2.7.0, 2.7.1, 2.7.2, 2.7.3, 2.7.4, 2.7.5, 2.7.6, 2.7.7, 2.7.8, 2.7.9, 2.7.10,    2.7.11, 2.7.12, 2.7.13, 2.7.14, 2.7.15, 2.7.16, 2.7.17, 2.7.18, 2.8.0a1, 2.8.0b1, 2.8.0rc1, 2.8.0rc2, 2.8.0rc   3, 2.8.0, 2.8.1, 2.8.2, 2.8.3, 2.8.4, 2.8.5, 2.8.6, 2.8.7, 2.8.8, 2.8.9, 2.8.10, 2.8.11, 2.8.12, 2.8.13, 2.8.1   4, 2.8.15, 2.8.16rc1, 2.8.16, 2.8.17rc1, 2.8.17, 2.8.18rc1, 2.8.18, 2.8.19rc1, 2.8.19, 2.8.20rc1, 2.8.20, 2.9.   0b1, 2.9.0rc1, 2.9.0rc2, 2.9.0rc3, 2.9.0rc4, 2.9.0rc5, 2.9.0, 2.9.1, 2.9.2, 2.9.3, 2.9.4, 2.9.5, 2.9.6, 2.9.7,    2.9.8, 2.9.9, 2.9.10, 2.9.11, 2.9.12, 2.9.13, 2.9.14rc1, 2.9.14, 2.9.15rc1, 2.9.15, 2.9.16rc1, 2.9.16, 2.9.17   rc1, 2.9.17, 2.9.18rc1, 2.9.18, 2.9.19rc1, 2.9.19, 2.9.20rc1, 2.9.20, 2.9.21rc1, 2.9.21, 2.9.22rc1, 2.9.22, 2.   9.23rc1, 2.9.23, 2.9.24rc1, 2.9.24, 2.9.25rc1, 2.9.25, 2.9.26rc1, 2.9.26, 2.9.27rc1, 2.9.27, 2.10.0a1, 2.10.0a   2, 2.10.0a3, 2.10.0a4, 2.10.0a5, 2.10.0a6, 2.10.0a7, 2.10.0a8, 2.10.0a9, 2.10.0b1, 2.10.0b2, 2.10.0rc1, 2.10.0   , 2.10.1, 2.10.2, 2.10.3, 2.10.4, 2.10.5, 2.10.6, 2.10.7, 3.0.0b1, 3.0.0rc1, 3.0.0, 3.1.0, 3.2.0, 3.3.0, 3.4.0   , 4.0.0a1, 4.0.0a2, 4.0.0a3, 4.0.0a4, 4.0.0b1, 4.0.0b2, 4.0.0rc1, 4.0.0, 4.1.0, 4.2.0, 4.3.0, 4.4.0, 4.5.0, 4.   6.0, 4.7.0, 4.8.0, 4.9.0, 4.10.0, 5.0.0a1, 5.0.0a2, 5.0.0a3, 5.0.0b1, 5.0.0b2, 5.0.0rc1, 5.0.1, 5.1.0, 5.2.0,    5.3.0, 5.4.0, 5.5.0, 5.6.0, 5.7.0, 5.7.1, 5.8.0, 5.9.0, 5.10.0, 6.0.0a1, 6.0.0a2, 6.0.0a3, 6.0.0b1, 6.0.0b2, 6   .0.0rc1, 6.0.0, 6.1.0, 6.2.0, 6.3.0, 7.3.0, 6.5.0, 6.6.0, 6.7.0, 7.0.0a1, 7.0.0a2, 7.0.0b1, 7.0.0rc1, 7.0.0, 7   .1.0, 7.2.0, 7.3.0)
ERROR: No matching distribution found for ansible==

→7.3.0が最新なようなので、これを使うことにする。

Ansible実行用のDockerイメージをビルドする

Dockerfileを作成する。 Dockerfile_ansible

# ベースイメージ
FROM rockylinux:8.6

# 変数定義
ARG ANSIBLE_VERSION

# 必要なパッケージのインストール
RUN dnf module -y install python39 && \
    dnf -y install openssh-clients \
    sshpass && \
    dnf clean all && \
    rm -rf /var/cache/dnf/*

# Ansibleのインストール
RUN python3 -m pip install --no-cache-dir ansible==$ANSIBLE_VERSION

# ロケールを日本語に設定
RUN dnf -y install glibc-locale-source glibc-langpack-en && \
    dnf clean all && \
    rm -rf /var/cache/dnf/*
RUN localedef -f UTF-8 -i ja_JP ja_JP.utf8
RUN echo 'LANG="ja_JP.UTF-8"' >  /etc/locale.conf

# タイムゾーンをJSTに設定
RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock
RUN rm -f /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# コンテナログイン時のカレントディレクトリを設定
WORKDIR /work/ansible

→Dockerfileの解説

  • ベースイメージがRockyLinux8.6なのは使い慣れているからで、特に意味は無い。他のOSで作っても良い。
  • Ansibleのインストールコマンドは、Ansible公式ドキュメントのここを参考にしている。
  • openssh-clientsは、Ansibleから管理対象サーバへ接続するために必要なのでインストールした。
  • sshpassは、管理対象サーバへ接続する際にパスワード認証を使う場合は必要なため、インストールした。
  • Ansibleで使うモジュールによっては他にもパッケージが必要になるので、適宜追加すること。
  • インストールするAnsibleのバージョンを制御できるよう、ARGにて変数ANSIBLE_VERSIONを定義し、Ansibleインストール時に指定している。 変数に設定する値は、後ほど「docker build」実行時に--build-argオプションで指定する。
    詳細はDockerの公式ドキュメントのここを参照。
  • dnf実行時の書き方については、RedHatの「How to build tiny container images」を参考にした。
  • ロケールタイムゾーンの設定は必須ではないが、自前のコンテナイメージ作成時はいつもやっているので入れている。

Ansibleバージョンを7.3.0と指定し、コンテナイメージをビルドする。

インストールしたいAnsibleのバージョンを環境変数に格納
# export ANSIBLE_VERSION=7.3.0

Dockerイメージをビルド
# docker build --no-cache=true -f Dockerfile_ansible -t ansible:$ANSIBLE_VERSION --build-arg ANSIBLE_VERSION=$ANSIBLE_VERSION .

ビルドされたイメージを確認
# docker images | grep ansible | grep $ANSIBLE_VERSION
ansible               7.3.0     ac001dbb28c2   2 minutes ago   657MB

ビルドされたイメージを起動し、想定通りのAnsibleバージョンがインストールされたことを確認
# docker run --rm -it ansible:$ANSIBLE_VERSION python3 -m pip freeze | grep ansible
ansible==7.3.0
ansible-core==2.13.4

ビルドしたイメージを使ってansible-playbookを実行する

Dockerホスト(上記イメージを起動するサーバ)にansibleで使うファイルを準備する。
今回は、Dockerホストの/root/ansibleディレクリに以下のように配置した。
ディレクトリ構成は、Ansible公式ドキュメントのBest Practiceを参考にしている。

# cd /root
# tree --charset=c ansible
ansible
|-- ansible.cfg
|-- inventories
|   `-- production
|       |-- group_vars
|       |   `-- testservers.yml
|       `-- hosts
|-- roles
|   `-- test
|       |-- files
|       |   `-- test.txt
|       `-- tasks
|           `-- main.yml
|-- site.yml
`-- testservers.yml

ansible/ansible.cfg
ansible実行時の設定。Ansibleが管理対象サーバへSSHする際に、ホスト鍵のチェックをしないようにしている。

[defaults]
host_key_checking = False

ansible/inventories/production/group_vars/testservers.yml
管理対象サーバへのアクセス情報を記載。

---
ansible_ssh_user: root
ansible_ssh_pass: password

ansible/inventories/production/hosts
管理対象サーバの接続先を記載。

[testservers]
192.168.10.123

ansible/roles/test/tasks/main.yml
test.txtというファイルを管理対象サーバの/tmpディレクトリに転送するだけの処理。

---
- name: copy test file
  copy:
    src: "test.txt"
    dest: "/tmp"

ansible/roles/test/files/test.txt

test

ansible/site.yml
実行するplaybook。個別のplaybookをimportして使う。

---
- import_playbook: testservers.yml

ansible/testservers.yml
個別のplaybook。

---
- hosts: testservers
  roles:
    - test

先ほどビルドしたコンテナイメージを指定し、コンテナを起動する。 DockerfileでWORKDIRとして指定したコンテナ内のディレクトリ(今回は「/work/ansible」)に、Dockerホストのansible用ファイル置き場(今回は「/root/ansible」)をマウントする。

# export ANSIBLE_VERSION=7.3.0
# docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ls -l
total 12
-rw-r--r-- 1 root root 37 Oct 10 23:02 ansible.cfg
drwxr-xr-x 3 root root 24 Oct 10 20:27 inventories
drwxr-xr-x 3 root root 18 Oct 10 21:47 roles
-rw-r--r-- 1 root root 39 Oct 10 20:38 site.yml
-rw-r--r-- 1 root root 45 Oct 10 21:42 testservers.yml

→「docker run」した際のカレントディレクトリから、Dockerホストのansible用ファイルにアクセスできることが確認できる。

ansible-playbookを実行する。

# export ANSIBLE_VERSION=7.3.0
# docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ansible-playbook -i inventories/production/hosts site.yml

# docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ansible-playbook -i inventories/production/hosts site.yml

PLAY [testservers] *******************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [192.168.10.123]

TASK [test : copy test file] *********************************************************************************
changed: [192.168.10.123]

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

→無事に実行された。

管理対象サーバにSSHして、/tmpディレクトリにtest.txtファイルが転送されていることが確認できる。

# ssh root@192.168.10.123
# cat /tmp/test.txt
test

毎回「docker run ~」を入力するのは手間なので、エイリアスを使って簡単に実行できるようにする。
Dockerホストの「~/.bashrc」に以下の2行を追記する。

export ANSIBLE_VERSION=7.3.0
alias ansible-playbook='docker run --rm -it -v /root/.ssh/:/root/.ssh/ -v /root/ansible:/work/ansible ansible:$ANSIBLE_VERSION ansible-playbook'

試してみる。

変更を反映
# source ~/.bashrc

エイリアスを指定してplaybookを実行する。
# ansible-playbook -i inventories/production/hosts site.yml

PLAY [testservers] *******************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [192.168.10.123]

TASK [test : copy test file] *********************************************************************************
ok: [192.168.10.123]

PLAY RECAP ***************************************************************************************************
192.168.10.123               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

→「ansible-playbook」を実行するだけでansibleコンテナを使ってansible-playbookを実行できるようになった。
別バージョンのAnsibleも実行したい場合は、該当バージョンのAnsibleがインストールされたDockerイメージを用意し、環境変数「ANSIBLE_VERSION」の値を変えることで実行するバージョンを制御できる。

まとめ

  • Ansibleの実行環境をDockerコンテナにすることができた。Dockerさえインストールされていれば、簡単にAnsibleの実行環境を整備できる。

参考文献