ハトネコエ Web がくしゅうちょう

プログラミングやサーバー関連、チームマネジメントなど得た技術のまとめ

Docker で Ansible の実行環境を作る(Ubuntu 18.04)

Ansible を何度も本番環境に流すのは、インスタンスの作り直しがその度に発生し大変です。
そこで Docker を利用し、簡単に使い捨てが出来る、Ansible の開発用実行環境にしてみようと思いました。

なお、本番環境は DigitalOceanUbuntu 18.04 環境を利用しようと思っているので、
Ubuntu 18.04 の Docker イメージを使用します。

Ansible with Docker
Ansible with Docker

1. Python がない!

最初は ubuntu:18.04 イメージをそのまま使って Ansible を流せると思ったんですけど、

fatal: [ansible-test01]: FAILED! => {"changed": false, "module_stderr": "/bin/sh: 1: /usr/bin/python: not found\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 127}

のエラーが……。
わかりやすく太文字にしましたが、そうです、 pythonubuntu:18.04 イメージには入っていなかったのです!

これでは Ansible を実行できません。というわけで、しぶしぶ Dockerfile を書くことにしたのでした。

2. Dockerfile はこんな感じ

FROM ubuntu:18.04

RUN \
  apt-get update && \
  apt-get install -y \
    sudo \
    python \
    python3

ENTRYPOINT ["/bin/bash"]

シンプルにこんな感じにしました。
python3 を入れているのは、DigitalOcean の Ubuntu イメージが python3 しか入っていないことを考慮してです。

Ansible では sudo を付けてコマンドが実行されることが多いので、
sudo パッケージも入れています。

3. Makefile で楽をしよう

Dockerfile が出来ましたので、これをビルドして Docker イメージを作りましょう。
ただ、修正を加えるたびにコマンドを書くのも大変なので、Makefile で定義しておきます。

CONTAINER_NAME := ansible-test01

.PHONY: build
build:
  docker build ./ -t ubuntu18_ansible:latest

.PHONY: run
run:
  docker run -itd --rm \
      --name $(CONTAINER_NAME) \
      --cap-add NET_ADMIN \
      ubuntu18_ansible:latest

.PHONY: login
login:
  docker exec -it $(CONTAINER_NAME) /bin/bash

.PHONY: stop
stop:
  docker stop $(CONTAINER_NAME)

Dockerイメージ内でDockerを呼び出す場合は、
docker run のコマンドに -v /var/run/docker.sock:/var/run/docker.sock のオプションを付けておいてください。
ただし、セキュリティ上のリスクが大きいので、このオプションを付けるのは必ず開発用Dockerに留めてください*1

この Makefile で、

  • make build で Docker イメージの作成
  • make run で Docker コンテナの開始
  • make login で Docker コンテナ内部の確認
  • make stop で Docker コンテナの終了と廃棄

という一連のことが出来るようになります。

docker run コマンドに d オプション(デタッチド・モード)を使いバックグラウンドで動作させているのと、
rm オプションを使い、コンテナ停止時にコンテナが削除されるように設定しているのがポイントですね。

--cap-add NET_ADMIN オプションを付けているのは、
Docker内で iptables を実行可能にするため です。(参考: https://github.com/moby/moby/issues/18230

これがないと

fatal: [ansible-test01]: FAILED! => {"changed": false, "msg": "ERROR: initcaps\n[Errno 2] iptables v1.6.1: can't initialize iptables table `filter': Permission denied (you must be root)\nPerhaps iptables or your kernel needs to be upgraded.\n\n"}

とエラーが出て、 ufw などの設定がおこなえません。(ここめっちゃハマりました)

--privileged オプションを使うのもアリですが、セキュリティ上のリスクはあります。

CONTAINER_NAME を変数にしているので、

make run CONTAINER_NAME=test02

のように、別名のコンテナを建てることも出来ます。

4. hosts ファイルにコンテナ名

Ansible の実行時に指定する i オプション(インベントリ)で指定する hosts ファイルには、
コンテナの名前を記載します。

今回は Makefile で ansible-test01 という名前をデフォルトにしているので、例えば以下のように書きます。

[ubuntu]
ansible-test01

[ubuntu:vars]
ansible_python_interpreter=/usr/bin/python3

今回は DigitalOceanUbuntu 18.04 環境に python3 しか入っていないことを考慮し、
ansible_python_interpreter を指定し、開発環境でも python3 を使用するようにしています。

5. connection オプションが大事

Docker コンテナに Ansible を流すには、Ansible の connection オプション を使います。

https://docs.ansible.com/ansible/latest/plugins/connection.html#using-connection

playbook 内に connection: docker と書く方法もありますが、
本番環境と同じ playbook を使う場合には、Ansible 実行時に、例えば以下のように指定することになります。
(3. で扱った hosts ファイルの場所や、playbook 名に合わせて、適宜変更してください)

ansible-playbook -i hosts/development -c docker -e ansible_user=root site.yml

c オプションがポイントですね!
--connection docker と指定することももちろん可能です。

また、Docker の場合は root ログインになるでしょうから、
-e ansible_user=root を指定しています。(デフォルト値なので、ansible_user の値をどこかで設定していない場合は不要です)

6. 留意点

DigitalOcean, ConoHa, AWS などの VPS サービスから提供されるOSは扱いやすいように多少カスタマイズされているのですが、
Docker イメージはそのようなカスタマイズがありません。

ゆえに、/etc/ssh/sshd_config の設定をしようとするもファイルが存在しなく、
openssh-server のインストールが先に必要だったりと、インストールしないといけないものが色々あります。
少し面倒ですが、しっかり書いていくことで、環境依存が少ないしっかりした playbook になるでしょう。