LoginSignup
214
174

More than 3 years have passed since last update.

Docker in Docker のベタープラクティス

Last updated at Posted at 2016-07-07

Docker in Docker したいケース

Jenkins などの CI ツールを Docker コンテナ上で動かしたいことがあります。
プロダクト別に Jenkins を分けたい、とか、本番環境と同じ OS 上でテストを実行したい、などの理由で。

Jenkins 上のテストで DB を使用したい場合、テスト用のデータが入った DB の Docker image を作っておいて、テストジョブを走らせる時に Docker コンテナを作成してテストコードから参照できたらいいですよね。

このような場合には Docker コンテナ(Jenkins 稼動)上で Docker コンテナ(テスト用DB 稼動)を動かすことになります。これを Docker in Docker といいます。

Docker in Docker をするには以下に挙げる二つの方法があるようです(参考)。どちらがベターでしょうか。

方法① docker:dind イメージを使用する

Jenkins コンテナのベースイメージとして docker:dind を使用すると、コンテナ上から docker を実行できるようになります。docker:dind についての詳細は参考を参照してください。

やってみる

ホストマシン上に以下のイメージがあります。

[root@localhost vagrant]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              05188b417f30        4 days ago          196.8 MB
hello-world         latest              c54a2cc56cbb        4 days ago          1.848 kB
docker              latest              b7b7422b4d51        12 days ago         75.76 MB

docker:dind イメージを privileged オプションを付けて起動します。

[root@localhost ~]# docker run --privileged -e HTTPS_PROXY=プロキシ --name dind -d docker:dind

docker:dind は Data Volume を作成して、コンテナ内の /var/lib/docker にマウントします。そのため、ホストマシンに Data Volume が作成されます。

[root@localhost ~]# ls /var/lib/docker/volumes/
634df4e69a7ea66ff117b775c6ea573775a1775aa00b197947dc4f5dad769a65  metadata.db

コンテナにログインして hello-world コンテナを起動します。

[root@localhost ~]# docker exec -it dind sh
/ # docker run hello-world
Unable to find image 'hello-world:latest' locally
... (hello-world イメージがダウンロードされます) ...

/ # docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS
cce166f99e54        hello-world         "/hello"            4 seconds ago       Exited (0) 3 seconds ago

コンテナを起動できました。
ホストマシン上で動いている Docker daemon は hello-world イメージを持っているのですが、docker:dind の Docker daemon は同じイメージを新たにダウンロードします。

さて、ホストに戻って docker:dind コンテナを削除します。

[root@localhost ~]# docker stop dind
[root@localhost ~]# docker rm dind

再度 docker:dind コンテナを起動して、ホストマシンの /var/lib/docker/volumes を参照すると、ディレクトリが増えていることが分かります。Data Volume なので、一つ目のコンテナのデータが残ったままになるのです。これは手動で消す必要があります。

良い点

ホストマシンの Docker daemon と docker:dind コンテナ上の Docker daemon が別になっているので、ホストマシンからコンテナ上のコンテナは見えませんし、その逆もまた見えません。つまりコンテナが階層構造になっているので、管理しやすいかなと思います。

悪い点

docker コンテナのいいところの一つに、コンテナ上ではっちゃけてもホストマシンに影響がない、というのがありますが、privileged オプションを付けると台無しかなと思います。

また、暗黙的に Data Volume が使われるので、コンテナの再生成を繰り返すとゴミが溜まっていきます。つまり、Jenkins コンテナを破棄しても、ホストマシン上にデータが残ったままになります。これを理解せずに使用していると、ディスクスペースを不要に消費してしまいます。

方法② ホストマシン上の Docker daemon を共有する

Docker コンテナ上からホストマシンの Docker daemon を利用します。起動したコンテナは起動元のコンテナと同じ階層になるので、Docker in Docker というとちょっと語弊があるかもしれません。

やってみる

ホストマシン上に以下のイメージがあります。

[root@localhost vagrant]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              latest              05188b417f30        4 days ago          196.8 MB
hello-world         latest              c54a2cc56cbb        4 days ago          1.848 kB
docker              latest              b7b7422b4d51        12 days ago         75.76 MB

普通のコンテナを上げます。

[root@localhost vagrant]# docker run hello-world

...

[root@localhost vagrant]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
4d68c3c4acbb        hello-world         "/hello"            7 seconds ago       Exited (0) 6 seconds ago                       evil_varahamihira

ホストマシンの Docker daemon を参照できるコンテナを上げて(Docker daemon の socket をマウントするのみ)、コンテナにログインします。
Docker バイナリが必要(※Docker daemon の起動は不要)なので、公式の docker イメージを使用します。

[root@localhost vagrant]# docker run -v /var/run/docker.sock:/var/run/docker.sock -ti docker sh

Docker daemon を利用できることを確かめます。
先ほど作成した hello-world のコンテナの存在を確認します。

/ # docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
03436f286399        docker              "docker-entrypoint.sh"   19 seconds ago      Up 18 seconds                                  sharp_sammet
4d68c3c4acbb        hello-world         "/hello"                 4 minutes ago       Exited (0) 4 minutes ago                       evil_varahamihira
/ #

ホストマシンのコンテナを参照できました。

削除も確認します。

/ # docker rm 4d68c3c4acbb
4d68c3c4acbb
/ # docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
03436f286399        docker              "docker-entrypoint.sh"   About a minute ago   Up About a minute                       sharp_sammet

このコンテナ内から centos コンテナを起動してログインしてみます。

/ # docker run -ti centos bash
[root@3af05e3e57cd /]#

centos コンテナを起動できました。
イメージの取得処理も走っていません。ホストマシンの docker daemon が管理しているイメージを使用できています。

さて、この状態でホストマシン上で Docker コンテナを一覧するとどうなるでしょうか。

[root@localhost ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS
3af05e3e57cd        centos              "bash"                   3 minutes ago       Up 3 minutes
03436f286399        docker              "docker-entrypoint.sh"   7 minutes ago       Up 7 minutes

公式docker イメージのコンテナと、そのコンテナ上で起動した centos コンテナが、並列に見えますね。親子関係は見えません。

良い点

ホストマシンの socket をマウントするだけなので分かりやすいですし、Docker イメージを重複して持たないのでディスクスペースを節約できす。

悪い点

ホストマシンで動いてる他の Docker コンテナも見えちゃうので、例えば Jenkins コンテナ上で他の Jenkins コンテナを停止できてしまいます。

また、ホストマシン上からは Jenkins コンテナと、Jenkins コンテナ上で動いているコンテナが並列に見える(親子関係が分からない)ので、管理しにくい気がします。

結論

docker:dind を使用するよりはホストマシンの Docker daemon を共有する方がベターかなと思います。

いずれにせよ、プロダクト環境ではあまりやりたくないですねー。

参考

http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
https://github.com/jpetazzo/dind

214
174
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
214
174