Docker in Docker (dind) でプロジェクト毎にSandbox化すると便利だよ


motivation

近頃はほとんどのプロジェクトでDockerないしKubernetesといったコンテナで開発します
特にフリーランスだと複数の仕事が同じDockerホスト上のコンテナとしてぶら下がります
あまり大きな問題が出たことはありませんが

  • プロジェクト終了時に全てのコンテナを削除したい
  • コンテナ、イメージ名がかぶる可能性
  • 他のプロジェクトのイメージが
  • お客さんの前あるいは配信時に、他の案件のコンテナリストが表示される

等の問題が生じる可能性があります

まず、シェルなどをまとめたものを下記においておきます
https://github.com/YukiMiyatake/util/tree/master/environment/docker_sandbox

$ docker images

REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
docker                    dind                  3c34f16aeaa0        19 hours ago        505MB
<none>                    <none>                31f248f7f9b6        19 hours ago        658MB
kgb_secret_project        latest                bc7c96ecde80        3 days ago          814MB
cia_secret_project        latest                4b7b8690eb05        6 days ago          2.89GB
docker                    dind-alpine           a4b37e66ffc7        7 days ago          230MB
rothschild_report         latest                10 days ago         3.09GB
golang                    latest                54e71dcafb7c        2 weeks ago         803MB
ubuntu                    16.04                 5f2bf26e3524        3 weeks ago         123MB
ubuntu                    18.04                 775349758637        3 weeks ago         64.2MB
buildpack-deps            stretch               81af74c34933        5 weeks ago         835MB
golang                    1.12.7                be63d15101cb        4 months ago        814MB
docker                    1.13.0-dind           7d6978320b24        2 years ago         99MB

こんな情報がバレてしまうと、命が危険だ!!

もちろん、案件ごとにPCを換える、マルチブートにする、ユーザーを切り替える、案件ごとにVMを作りそこでDockerする
等の方法で、回避は可能だが
例えばWindowsでHyper-Vを有効にしている場合にはVMを作れない
そのため、現実的な回避手段として
dindを思いついた

dindとは

DockerはOSの殆どの操作はできますが、一部の操作は出来なくなっています
通常Dockerコンテナ内からDockerを起動できないようになっています

$ docker run -it ubuntu:18.04 bash

root@d3da654e2a5f:/# docker
bash: docker: command not found

コンテナ内からDockerを使う方法は、dindとdoodの2種類の方法があります

通常のDocker

ホストPCのDocker上で全てのイメージを起動します
全てのプロジェクトのコンテナは同じDocker上に存在します(皆兄弟)

不便ですよね?

dind

プロジェクト毎に dind Dockerを立ち上げます( privileged フラグが必須!! )
プロジェクト毎のDockerにログインし、そこで Dockerイメージを作成します
Dockerイメージは親子関係で作成されます

$ docker run -it --privileged -v $('pwd'):/volume -w /volume  -d docker:dind

root@e394f7876063:/volume# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

root@e394f7876063:/volume# docker run -it busybox
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
0f8c40e1270f: Pull complete 
Digest: sha256:1303dbf110c57f3edf68d9f5a16c082ec06c4cf7604831669faf2c712260b5a0
Status: Downloaded newer image for busybox:latest
/ # exit

root@e394f7876063:/volume# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@e394f7876063:/volume# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
busybox             latest              020584afccce        3 weeks ago         1.22MB

別プロジェクトのコンテナは見えないし影響を与えない
完全にプロジェクト毎のSandbox環境に成功しました!

dood

dindに近い考え方として Docker Out of Dockerがあります

dindなDockerを立ち上げます。
このとき privilegidフラグはたてず、docker.socファイルを Volumeでマウントします
プロジェクトのDockerコンテナにログインし、dockerコマンドを使うと ホスト上のdockerホストに繋がります
ここで作ったコンテナはホスト上に作成されます=兄弟関係

$ docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker:dind bash

root@b49f798ae8ca:/# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
b49f798ae8ca        docker:dind         "bash"              6 seconds ago       Up 5 seconds                            crazy_goodall
root@b49f798ae8ca:/# docker images
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
docker                    dind                  3c34f16aeaa0        24 hours ago        505MB
<none>                    <none>                31f248f7f9b6        24 hours ago        658MB
ubuntu                    16.04                 5f2bf26e3524        3 weeks ago         123MB
ubuntu                    18.04                 775349758637        3 weeks ago         64.2MB
buildpack-deps            stretch               81af74c34933        5 weeks ago         835MB
golang                    1.12.7                be63d15101cb        4 months ago        814MB
docker                    1.13.0-dind           7d6978320b24        2 years ago         99MB

(ホストのDockerイメージが見えている)

root@b49f798ae8ca:/# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
0f8c40e1270f: Pull complete 
Digest: sha256:1303dbf110c57f3edf68d9f5a16c082ec06c4cf7604831669faf2c712260b5a0
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest

root@b49f798ae8ca:/# docker images
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
docker                    dind                  3c34f16aeaa0        24 hours ago        505MB
<none>                    <none>                31f248f7f9b6        24 hours ago        658MB
ubuntu                    16.04                 5f2bf26e3524        3 weeks ago         123MB
ubuntu                    18.04                 775349758637        3 weeks ago         64.2MB
busybox                   latest                020584afccce        3 weeks ago         1.22MB
buildpack-deps            stretch               81af74c34933        5 weeks ago         835MB
golang                    1.12.7                be63d15101cb        4 months ago        814MB
docker                    1.13.0-dind           7d6978320b24        2 years ago         99MB

(ホストのDockerイメージに追加された)

通常のDocker起動と違うのは、Dockerコンテナから兄弟関係のコンテナを作成&利用出来ることです
プロジェクトごとにDockerイメージは別れないので今回の目的には合致しません

CIや、状況に応じてコンテナを生成破壊をするサービスには DooDが向いています(たいていは DinD使わなくてもよい)

dindを使いこなす

まず、Docker in Doockerの開発者によると
dindを使うときは本当にdindが必要なのか再度考えてみろ
大抵のことはdoodでできる
dindはDockerの開発用途だ

ということで、おそらく私の使ってるdindも、開発者いわく、使うなと言うことだろうけど
面白いんで使ってみます

実際の使い方を

上記にスクリプトなどを追加して、より良い環境を構築中です

dindイメージの作成

プロジェクト毎に、dindのDockerイメージを作成します
dindの公式イメージはalpineですが、開発用のホストとして使うのは辛いので
ubuntuなりarchなり好きなイメージを作りましょう
上記の Dockerfileとwrapdockerで、好きなOSのイメージを作れるようにしています

コンテナの作成

コンテナ名を環境変数で指定するために direnvを使ってます

docker run -it --privileged --name $PROJECT_GROUP -v $('pwd'):/volume -w /volume  -d docker:dind

docer_run.shに、コンテナの作成例をかいています
ディレクトリをマウントすると大抵の場合便利です
スクリプトでは dindイメージをそのまま使っていますが、プロジェクトで使うツールや言語などをDokerfileで書いておくと便利です

コンテナの実行

docker start $PROJECT_GROUP
docker exec -it $PROJECT_GROUP bash

コンテナの中に入ります
普段どおりにコンテナを作って開発を行います

その他

プロジェクトが終了した際などは、親のコンテナを削除すれば全部キレイになくなります!

ただし dindはvolumeを自動でつくるため、volumeも削除しましょう(volumuとコンテナを全部Cleanにするスクリプト作りたい)

あと、時々 commitしてコンテナをイメージに焼いて置くと安心

メリット

プロジェクトごとにディレクトリもコンテナも分かれるので、もしもの時の心配がない
他のプロジェクトの修正の影響を万が一にもうけない
フォルダもプロジェクトごとにSandbox化できるので、変なことが起きにくい
スクリーンショット見せたときに、他のサービスのコンテナリストが出ない
マイクロサービス等のコンテナが複数あるサービスで、全てのコンテナを消すのが簡単

デメリット

Sandbox毎にOSやDBのイメージがPullされるのでストレージ容量を使う
Privilegeフラグが必要