docker上でansible-containerを実行する時の5つのポイント

## はじめに

みなさん、dockerは使っているでしょうか？
僕はdockerを使いすぎてローカルのMacにbrewすら入っていない状態です。
dockerを使えば、あらゆる言語をローカル環境を汚さずに使用できたり、バージョンの切り替えに悩まされることはありません。

そんな中、ansible-containerで開発環境を構築しようとした時に詰まったところをメモとして残そうと思います。

## ansible-containerとは

[ansible-container](https://github.com/ansible/ansible-container)は、ansibleを使ったdocker imageのビルドとコンテナオーケストレーションをしてくれるツールです。

簡単に書くとdocker-composeを拡張したようなものです。

docker-composeで複数のコンテナを自動で構築してくれるところに、[ansible](https://github.com/ansible/ansible)を使ってdocker imageも作ってくれる優れものって感じです。
作成したコンテナはansible-containerコマンドを使ってデプロイすることも可能です。

既にansibleを使ってサーバ構成管理をしている状況であれば、それを利用してdocker環境を簡単に構築できるということで使ってみました。

## ansible-containerの仕組み

ansible-containerはansibleを流してdocker imageをビルドするところから少々独特な処理をします。

1. conductor imageを作成
2. conductorコンテナを作成
3. `2.`で作成したconductor imageからコンテナを複製
4. `2.` で作成したconductorコンテナから、`3.` で作ったコンテナにansibleを流す
5. `3.`で作成したコンテナを停止し、docker commitでdocker imageを作成
6. `3.` ~ `5.` を全サービス分行う

conductorというものが、ansible-containerの特徴になるのかと思います。
ansibleはssh接続した先のサーバに対してRoleと呼ばれるタスク群を実行します。

conductorというのは、ansibleでいうsshの接続元になるコンテナです。
conductorという名前が付いている程なので、buildやrun、deployコマンドを実行する際の指揮的な役割をするコンテナになります。

今回は、ansible-containerについての説明がメインではないので省きますが、こちらの[demo](https://ansible.github.io/ansible-container-demo/)を見ていただければ大体わかるかと思います。

前置きが長くなりましたが、ansible-containerを走らせる時にはdockerが必要になります。
つまり、ansible-containerをdockerで実行するには、必然的にdocker in dockerの構成になります。

これは実際にやってみてわかったことですが、docker in dockerでansible-containerを実行するには気をつけなければならないハマりポイントが幾つかあります。

## docker in docker + ansible-containerのハマりポイント

### ポイント1: dindにpythonを入れるのは意外と面倒

ハマりポイントって程ではないですが、ansible-container実行環境を作る時にこれを知っとくと時間がかからなくて良いよっていう話です。

docker in docker自体はdockerhubで公式ライブラリが出ているのでそれを使えば良いと思います。
https://hub.docker.com/_/docker/

ただ、公式が出しているdocker imageはpythonが入っていないため、それを入れるのは結構しんどいです。

ansible-containerを実行するには周辺ライブラリが必要だったりするので、alphine linuxでそれらを入れるのは結構面倒でした。

そこで、公式のpython imageにdocker-ceをインストールして使う方法をとります。こちらの方が数段楽です。
Dockerfileはこんな感じ。

```
FROM python
RUN apt-get update -y && apt-get install -y \
     apt-transport-https \
     ca-certificates \
     curl \
     gnupg2 \
     software-properties-common
RUN curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | apt-key add -
RUN apt-key fingerprint 0EBFCD88
RUN add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
   $(lsb_release -cs) \
   stable"
RUN apt-get update -y && apt-get install -y docker-ce
VOLUME /var/lib/docker
CMD ["/etc/init.d/docker", "start"]
```

### ポイント2: privilegedオプションを付けないと動かない

docker in dockerの実行方法は2つあります。
1. `docker run` 時にホスト側のdocker.sockをマウントする方法
2. `docker run` 時に--privilegedオプションを付ける方法

ansible-containerを使う場合には、 `2.` の方が良いです。

conductorでansibleを実行する際にrolesをconductorコンテナにマウントさせるのですが、 `1.` のやり方だとホスト側のpathをマウントしようとするため、空ディレクトリがマウントされてしまいます。
ちなみに、先程のデモだと以下のようなエラが発生します。

```
ansible.errors.AnsibleError: the role 'django-gunicorn' was not found in ./roles:/src/roles:/etc/ansible/roles:.
```

`2.` のやり方だと、コンテナ側にdocker deamonが立ち上がるので、コンテナのパスをマウントしてくれます。

ちなみに、privilegedオプションをつけないで実行する場合、docker deamonが立ち上がらないので注意すること(コンテナ側からdocker deamonが立ち上げれない)。

### ポイント3: Volumeが無いと動かない

先程のDockerfileにも記述がありますが、必ずDockerfile内にVolume節を書くようにしてください。
原因は正直はっきりわかっていないんですが、なんかエラります。
多分、ポイント2で書いたようにroleをマウントする際にVolume設定していないとマウントする先がなくてエラーになると思われます(当てずっぽう)。

### ポイント4: docker-pyが新しくなって動かない

ansible-containerではdocker-pyを使ってdockerの操作をしてるのですが、そのdocker-pyが数日前にversion3.0.1になっていました。
その影響でansible-containerが動かなくなってしまっている様子。
ansible-containerがアップデートされれば問題なく動くので時間の問題ですが、今すぐ動かしたい場合はdocker-pyのバージョンを下げることで対応できます。

```bash
$ pip uninstall docker
$ pip install docker==2.7.0
```

### ポイント5: ansible-container buildがタイムアウトになる

先程のデモでもそうなのですが、`ansible-container build`の実行時間が長いとタイムアウトになってしまいます。
そういう場合は以下を設定してやるとうまくいきます。

```bash
$ export DOCKER_CLIENT_TIMEOUT=300
```

## まとめ

後半はdocker in dockerはあまり関係ありませんでしたが、docker上でansible-containerを実行させるのには色々と気をつけないといけないハマりポイントがあります。
dockerは便利でとても良いものですが、使いこなせないと予期せぬエラーで時間を浪費してしまうので注意が必要です(僕はこれで一日溶かしました...)。

## 参考

https://qiita.com/sugiyasu-qr/items/85a1bedb6458d4573407
https://docs.ansible.com/ansible-container/conductor.html