DockerコンテナでAnsibleをテストする¶
Ansible 2.0になり、Docker connection pluginが標準で入りました。これにより、Docker内にsshdを立てることなくAnsibleを直接実行できるようになりました。
すでに導入されている方も多く、かなり今更ではありますが、Dockerコンテナに対してAnsibleを実行してテストする方法についてここに記します。
Dockerに対する場合の制限¶
まず最初にAnsibleをDockerコンテナに対して実行する際の制限についてです。
基本的にはすべての機能が使えます。ただ、以下の制限があります。
/etc/hosts, /etc/resolv.conf, /etc/hostnameは書き換えできない
これらのファイルはDockerがbind mountしており、書き換えられるが、置き換えることは出来ないため。 /etc/hostnameが変更できないため、hostnameモジュールでの変更もできない。
また、実行するイメージによっては少なくとも以下の問題があります。他にもいろいろあるかもしれません。このあたりはDocker自体に関する問題で、Ansible特有の問題ではないので、なんとか解決して下さい。
systemdのserviceが起動できない
D-busがないため、 Failed to connect to bus: No such file or directory と言われる。upstartやrc initは起動できる。 CAP_SYS_ADMIN capabilityが必要
sudoがない場合がある
付け加えるならば、まっさらなイメージからテストをしていくとダウンロードなどに時間がかかってしまいますので、適宜設定を施したイメージを事前に用意しておくとテストの時間が減ると思います。
Inventory¶
さて、本題です。Ansibleの docker connection pluginを使うには
web ansible_connection=docker ansible_host=<コンテナID>
というように、 ansible_connection=docker とするだけですぐに使えます。しかし、ansible_hostにはコンテナIDを指定しなければいけません。DockerのコンテナIDは本当に一時的なものなのでここに書くのはデバッグ時だけです。
これを回避するためには docker moduleを使ってコンテナを立ち上げ、 add_host でグループを生成することも可能ですが、playbookをテスト用に編集する必要が出てきます。それでもいい場合もありますが、せっかくですからDocker dynamic inventoryを使いましょう。
Docker dynamic inventory¶
GitHubのansibleのリポジトリ から docker.py を取得し実行権限を付与しておきます。 docker.yml はなくても構いません。
# docker containerを立ち上げる
$ docker run --name <hostsでの指定対象> ubuntu:16.04 /bin/sleep 3600
# 立ち上げたdocker containerに対してansibleを実行する
$ ansible-playbook -i docker.py something.yml
で現在起動しているコンテナのnameからコンテナIDを取得して、使ってくれます。docker.pyで取得できる情報の一部を以下に示します。name以外にもimageやコンテナIDでグループが作成されていることが分かります。しかし、今回はテストなので、通常のグループと同じ名前を使いたいため name を使います。
"web": [
"web"
],
"image_ubuntu:16.04": [
"web"
],
"zzzzzd5bed36e033fac72d52ae115a2da12f3e08b995325182398aae2a95a": [
"web"
],
"zzzzz114d5bed": [
"web"
],
"running": [
"web"
],
"docker_hosts": [
"unix://var/run/docker.sock"
],
"unix://var/run/docker.sock": [
"web"
],
Inventory Directory¶
dynamic inventoryをつかうと、inventoryファイルで指定してあるgroup_varsが使えなくなってしまうのではないか、と思うかもしれません。
その場合ディレクトリを分けてdocker.pyと静的なファイルを入れておきます。そうしておいてinventoryファイルとして ディレクトリ を指定すると、静的なファイルとdynamic inventoryの両方から情報をとってくれます。CIでのみ使う場合はCI用のInventoryとして、ディレクトリを分けておくと扱いやすくなると思います。
CircleCI¶
では、CIを通してみましょう。CircleCIを試します。circle.ymlはこんな感じです。
machine:
services:
- docker
environment:
DOCKER_API_VERSION: "1.20"
dependencies:
pre:
- docker pull ubuntu:16.04
- sudo pip install ansible ansible-lint docker-py
test:
override:
- docker run -d --name web ubuntu:16.04 /bin/sleep 3600
- ansible-playbook -i inventory_docker web.yml test.yml --syntax-check
- ansible-lint test.yml
- ansible-playbook -i inventory_docker web.yml test.yml -l running
--syntax-check やansible-lintもついでに行っています。 DOCKER_API_VERSION はCircleCIのdockerが古いために設定しています。 また、docker runで --name web としています。これは、通常使うplaybookではweb グループに対して実行しており、そのplaybookを変えたくないからです。
これでpushすると、
fatal: [web]: FAILED! => {"changed": false, "failed": true, "rc": 1, "stderr": "Error response from daemon: Unsupported: Exec is not supported by the lxc driver\n", "stdout": "", "stdout_lines": []}
と怒られてしまいました。そうです。CircleCIはlxc driverを使っており、Docker connection pluginが使う docker exec が使えないのです。
ということで諦めました。
他のCIサービスは wercker とか drone.io ありますが、これらはそもそもCIでDockerを使っており、Docker in Dockerになってしまいいろいろ大変です。
別解: 自前Dockerホストを用意する¶
あるいは、circle.ymlの environment で DOCKER_HOST を設定することで、CircleCI外に立てたDockerホストに対して実行することもできます。次に説明するGitLabを使うよりも手軽かもしれませんが、セキュリティ設定をしっかりする点は特に気をつけて下さい。
GitLab¶
GitLab が最近流行りですね。最近CIも付いたのでこれを使う場合も紹介します。
gitlabやgitlab CI runner自体のインストールは省略します。Docker内で実行するCI Runnerもありますが、それではDocker in Dockerになってしまいますので、今回の用途ではshell runnerにすることを忘れないで下さい。
結論からいうと、runnerの設定がしっかりしてあれば、このような .gitlab-ci.yml で動きます。ほとんどCircleCIと変わらず、 after_script によるコンテナの削除が入っているぐらいです。
before_script:
- pip install ansible ansible-lint docker-py
stages:
- build
build_job:
stage: build
script:
- docker run -d --name web ubuntu:16.04 /bin/sleep 3600
- ansible-playbook -i inventory_docker web.yml test.yml --syntax-check
- ansible-lint test.yml
- ansible-playbook -i inventory_docker web.yml test.yml -l running
after_script:
- docker kill `docker ps -aq`
- docker rm `docker ps -aq`
runnerの設定としては sudo gpasswd -a $USER docker をしてsudoをしなくてもdockerを使えるようにしておくとよいと思います。
追記: Travis CI¶
@auchida さんから Travis CI ならば使える、ということをお聞きしました。 auchidaさんのリポジトリ を参考にしました。
ポイントは sudo: required を入れることのようです。
しかし、たぶんvirtualenvとsystemとがなにかおかしいようで、docker dynamic inventoryを実行時に以下のエラーが出ました。そのうち直したいと思います。
class AnsibleDockerClient(Client):
NameError: name 'Client' is not defined
ありがとうございました。
まとめ¶
この記事では、Dockerコンテナを利用してAnsibleのテストを行う方法についてご紹介しました。
Ansible2.0からDockerコンテナに対して直接ansibleを実行できる
一部の制限はあるが、Docker上でも問題なく動く
CircleCIでは動かないので以下の三つの方法を紹介
自前でDockerホストを用意する
GitLabなどを立てる
Travis CIを使う
おまけ: ansible-lintのルール¶
最近弊社内でPlaybookの書き方を統一するためにansible-lintのルールの制定を始めました。 ansible-lint-rules にて公開しています。
Long descriptionがないなど、まだ途中ではありますが、使ってみてIssueやPRをいただけると大変ありがたく思います。
Comments
comments powered by Disqus