dind(docker-in-docker)とdood(docker-outside-of-docker)でコンテナを料理する

# 1. はじめに

コンテナの中にコンテナを立てたい、コンテナ内からホストマシンのコンテナを操作したい、そんなことを考えた方も多いのではないでしょうか。
こうした要求にこたえられる手法が`dind (docker-in-docker)`と`dood (docker-outside-of-docker)`です。

- コンテナの中にコンテナを立てたい >> dind (docker-in-docker) を使います
- コンテナ内からホストマシンのコンテナを操作したい >> dood (docker-outside-of-docker) を使います

本記事では実際にdindとdoodを用いてコンテナを起動するまでを実施します。

**想定読者**
- コンテナの起動停止、コンテナに入っての作業など基本的な操作ができる方
- コンテナに関する基礎的な用語を理解している方
- dind、doodの概要を知りたい方
- dind、doodを使ってコンテナを利用したい方


# 2. dindとdoodについて

## 2.1 Dockerデーモンとtcp通信/Unixドメインソケット通信
コンテナの起動停止などを行う際にはdockerコマンドを用いてdockerデーモンに対して命令を送ります。
dockerコマンドを実行すると`①TCP通信`または`②Unixドメインソケット通信`のいずれかによりdockerデーモンへ命令が送られます。
従って、操作対象としたいマシンのdockerデーモンへ命令を送ることが出来れば、どのような環境からも対象マシン上のコンテナを操作することが出来ます。

この辺りは[Docker を他のホスト・ポートや Unix ソケットに接続](https://docs.docker.jp/engine/userguide/basics.html#docker-unix)に記載があります。

dindやdoodではこの仕組みを利用し、命令の送り先として操作対象のマシンを明示的に指定することで実現されます。
なお、本記事では`②Unixドメインソケット通信`を用いることとします。

https://docs.docker.jp/engine/userguide/basics.html#docker-unix

## 2.2 dind (docker-in-docker)

dind (docker-in-docker) はコンテナ内にホストマシンとは完全に独立したdocker環境を構築する手法です。
下図では、ホストマシン上にdocker環境があり、これとは別にコンテナA内部にもdocker環境が構築されています。

:::note info
ホストマシンとコンテナAとで異なる`docker.sock`ファイルを保持している点に注目してください。
`docker.sock`は、一般的にソケットファイルと呼ばれるものでUnixドメインソケット通信に利用されます。
これはホストマシンのdockerとコンテナAのdockerが異なるdockerデーモンを操作対象としていることを意味しています。
:::

従って、ホストマシン上でdockerコマンドを実行した場合はホストマシンのdockerデーモンに対して命令が送られます。
一方でコンテナA内部でdockerコマンドを実行した場合はコンテナAのdockerデーモンに対して命令が送られます。

ホストマシン上で起動したコンテナ（図中のコンテナA/B/C）の存在はホストマシン上からしか確認できず、コンテナA上で起動したコンテナ（図中のコンテナX/Y）はコンテナAからしか存在を確認できません。
また、当然のことながらコンテナAを停止するとコンテナX/Yも同時に停止されます。

![dind.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/d57c11c6-5b25-3701-f4ba-df30bdb05472.png)

## 2.3 dood (docker-outside-of-docker)

dood (docker-outside-of-docker) はコンテナ内からホストマシンのdockerを操作する手法です。
下図では、ホストマシン上にdocker環境があり、コンテナAはホストマシンのdocker環境に接続しています。

:::note info
コンテナAにはホストマシンの`docker.sock`ファイルがマウントされている点に注目してください。
`docker.sock`は、一般的にソケットファイルと呼ばれるものでUnixドメインソケット通信に利用されます。
コンテナAのdockerがホストマシンのdockerデーモンを操作対象としていることを意味しています。
:::


従って、ホストマシン上でdockerコマンドを実行した場合も、コンテナA上でdockerコマンドを実行した場合もホストマシンのdockerデーモンに対して命令が送られます。
ホストマシン上からはホストマシン上で起動したコンテナ（図中のコンテナA/B/C）とコンテナA上で起動したコンテナ（図中のコンテナX/Y）の両方の存在を確認できます。
これは、逆にコンテナAからもすべてのコンテナ（図中のコンテナA/B/C/X/Y）の存在が確認できることを意味しています。
また、コンテナAを停止したとしてもホストマシン上で起動されているコンテナX/Yは停止されません。

![dood.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/705474/f7bbe3f9-b984-e182-490c-8c672ee0ae49.png)

# 3. dind (docker-in-docker) を試す

## 3.1 docker:dindイメージを利用したdind

まずは一般的によく使われている`docker:dind`のイメージを利用したdindの方法です。

以下のコマンドでコンテナを起動します。

```shell
docker run -it --rm --privileged --name dind -d docker:20-dind
```

起動したコンテナ内に入って`docker ps`コマンドを実行すると何も表示されません。
これにより、コンテナ内にホストマシンとは異なる独立したコンテナ環境が構築できていることを確認できます。

```console
C:\Users\user>docker exec -it dind sh
/ # docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
```

## 3.2 CentOSイメージを利用したdind

`docker:dind`のイメージを使うと簡単にdindが実現できることが分かりましたが、CentOSなどのイメージでdindを行いたい時があります。
この場合はコンテナ内で通常と同じ手順でdockerをインストールすると実現できます。

以下でcentosのコンテナを起動します。
コンテナ内で`systemctl`が使用できるように`--privileged`オプションを使用しています。

```shell
C:\>docker run -d --privileged --name dind centos:7 /sbin/init
C:\>docker exec -it dind bash
```

起動したコンテナに入り、以下の手順に従ってdockerをインストールします。

https://docs.docker.com/engine/install/centos/

```
[root@00587f333c1e /]# yum install -y yum-utils
[root@00587f333c1e /]# yum-config-manager \
>     --add-repo \
>     https://download.docker.com/linux/centos/docker-ce.repo
[root@00587f333c1e /]# yum install docker-ce docker-ce-cli containerd.io
[root@00587f333c1e /]# systemctl enable --now docker
```

インストール完了後にdocker psコマンドを実行するとコンテナが1つも起動していないことが確認できます。
これによりホストマシンとは異なるdocker環境がコンテナ内に構築できていることが確認できました。

```
[root@00587f333c1e /]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
```


# 4. dood (docker-outside-of-docker) を試す

## 4.1 dockerイメージを利用したdood

doodを実現するために、下記のコマンドでdockerイメージを起動します。
`-v /var/run/docker.sock:/var/run/docker.sock`オプションによりホストマシンのソケットファイルをコンテナ内へマウントさせています。

```shell
docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock --name dood docker:20
```

起動したコンテナ内から`docker ps`コマンドを実行してみるとホストマシン上で稼働しているコンテナ（この場合だと自分自身のコンテナ）が確認できます。

```console:コンテナ内から実行中のコンテナ一覧を取得
/ # docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS     NAMES
af159e03e643   docker:20   "docker-entrypoint.s…"   3 seconds ago   Up 2 seconds             dood
```

## 4.2 CentOSイメージを利用したdood

dindと同じく、centosなどを用いてもdoodは実現できます。
先ほどと同じく、ソケットファイルをマウントしてコンテナを起動します。

```shell
docker run -d -v /var/run/docker.sock:/var/run/docker.sock --name dood centos:7 /sbin/init
```

コンテナ内へ入り、`docker-cli`をインストールします。
doodではコンテナ内でdockerデーモンを起動するわけではないのでインストールするのはCLIのみで大丈夫です。

```
[root@86a4aff52d05 /]# yum install -y yum-utils
[root@86a4aff52d05 /]# yum-config-manager \
>     --add-repo \
>     https://download.docker.com/linux/centos/docker-ce.repo
[root@86a4aff52d05 /]# yum install docker-ce-cli
```

インストール完了後にdockerコマンドを実行すると、ホストマシン上で起動しているコンテナを確認することができました。

```
[root@86a4aff52d05 /]# docker ps
CONTAINER ID   IMAGE      COMMAND        CREATED          STATUS          PORTS     NAMES
86a4aff52d05   centos:7   "/sbin/init"   16 minutes ago   Up 16 minutes             dood
00587f333c1e   centos:7   "/sbin/init"   24 minutes ago   Up 24 minutes             dind
```