Dockerコンテナ内からDockerを使うことについて
April 30, 2019
Docker内からDockerを使う目的
CI用途で使う人が多いのではないでしょうか? CIでビルドなりテストなりやるとき、毎回同じ環境で実行したいと思う サーバ上で直接ジョブ動かしちゃうと、ゴミが残る可能性があったり サーバに直接ジョブ実行に必要なjavaなりrubyなりを突っ込む必要が出てくるので、どんどん汚れていく
Dockerでビルドやテストを実行すれば毎回終わったら消せばいいので、クリーンにできるよねーって話 CIサーバを直接yumなりでインストールして、そこからDocker使うのであれば特に問題ないけど 最近はCIサーバ自体をDockerコンテナで立ち上げて、さらに中でDockerを呼ぶというのがよく使われている気がする その時のやり方の話です。
なぜDockerコンテナ内からDocker使えないか
前提としてDockerの仮想化の仕組みは今までのHypervisorとかと少し違って ベースとなるイメージや、ホストのカーネルなど多くの部分を共有して、コンテナごとに少ない書き込み領域を用意することで、高速起動, 軽量などを実現しています.
そしてセキュリティのため、コンテナ内からいくつかの操作は制限されます Dockerの操作は、Dockerデーモンに対して、クライアントを利用して接続しています. Dockerデーモンはホストに対して下の制約に引っかかることをやっているので、コンテナ内からは起動できないです
DockerコンテナからDockerを使うには?
2種類の方法があります
- 権限を付与して、コンテナ内からホストのリソースを自由に扱える権利を持たせる(–privilegedオプションをつける) – Docker in Docker(DinD)
- ホストのDockerデーモンのソケットファイルをvolume接続(マウント)して、そことやりとりするようにする – Docker outside of Docker (DooD)
イメージとしては下のような感じ
簡単な違い
わかりやすい違いとしては、DinDではホストと、コンテナで完全にデーモンが分かれるので イメージ・コンテナ管理なども別々になります, これは管理しやすいという利点でもありますが、 ディスク容量を大量に消費する要因にもなります
DooDではデーモンを共有するため、ホストとコンテナ内で共通のイメージ・コンテナ管理を行います。 利点は↑と逆ですね
シェルで確認してみましょう
DinD
$ sudo docker run --privileged --name dind -d docker:dind
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cba616b2250c docker:dind "dockerd-entrypoint.…" 8 seconds ago Up 7 seconds 2375/tcp dind
// dockerコンテナに入ってみる
$ docker exec -it dind sh
// dockerコンテナ内でイメージ起動
/ # docker run -d -p 80:80 --name webserver nginx
// nginxのコンテナしか見えない
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
99e66f209820 nginx "nginx -g 'daemon of…" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp webserver
// ホストに戻って確認
/ # exit
// dindコンテナしか見れない
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cba616b2250c docker:dind "dockerd-entrypoint.…" 4 minutes ago Up 4 minutes 2375/tcp dind
// 消しておく
$ docker stop cba616b2250c && docker rm cba616b2250c
DooD
// doodコンテナ起動
$ sudo docker run -v /var/run/docker.sock:/var/run/docker.sock -it --name dood -d docker
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4cfa187ac191 docker "docker-entrypoint.s…" 3 seconds ago Up 1 second dood
// コンテナに入る
akasetnoMacBook-puro% docker exec -it dood sh
// コンテナ内からホストで動いているコンテナ観れる
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4cfa187ac191 docker "docker-entrypoint.s…" 18 seconds ago Up 16 seconds dood
// コンテナ起動
/ # docker run -d -p 80:80 --name webserver nginx
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be56745b1af4 nginx "nginx -g 'daemon of…" 7 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp webserver
4cfa187ac191 docker "docker-entrypoint.s…" 52 seconds ago Up 51 seconds dood
// ホストに戻る
/ # exit
// ホストからもコンテナ内で起動したコンテナが見れる
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be56745b1af4 nginx "nginx -g 'daemon of…" 14 seconds ago Up 12 seconds 0.0.0.0:80->80/tcp webserver
4cfa187ac191 docker "docker-entrypoint.s…" 59 seconds ago Up 57 seconds dood
どっちを使えばいいの?
CI用途に関してはDooDを使うのが好ましいと思います. DinDの開発者自身がブログでDinDのCI利用について述べています https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
ざっと要点
- そもそものDinDの用途はDockerの開発プロセス高速化のためだった
- DinDは次の問題がある
- SELinuxとかをホストとコンテナで別設定にしていると、クラッシュする可能性がある
- ホストとコンテナで別々のファイルシステムを使っているとクラッシュする可能性がある
- /var/lib/dockerはdockerデーモンの専用領域みたいなものだから、別デーモン作って触らせると何が起きても知らないよ
- CIをやりたないならDooDでいいんじゃない?
- ホストとDockerデーモンを共有することで、ビルドごとにイメージのキャッシュが消えたりがなくなると思うよ
- 上の問題点も解決すると思うよ