docker-composeのコンテナ稼働状態をホストとコンテナ間で共有する

# はじめに
　dockerコンテナ内からコンテナを制御する方法の1つにDooD(Docker outside of Docker)がありますが、コンテナを制御するときにホストとコンテナ両方ともにdocker-composeを使用できるか検証してみました。
　検証では、CentOS 7.6にdocker-ce(19.03)とdocker-compose(1.25.2)をインストールして検証しました。構成としては、下のような構成で、Ubuntuからアクセスして設定しています。

![test-environment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624259/90294a84-fc8e-563b-2989-428863ddf931.png)


# 準備
　CentOS 7.6 のminimalインストールの状態からdockerとdocker-composeをインストールし、ついでにdoodコンテナイメージを自作します。

## dockerのインストール
[参考：http://docs.docker.jp/engine/installation/linux/docker-ce/centos.html](http://docs.docker.jp/engine/installation/linux/docker-ce/centos.html)

* CentOS 7のバージョンを7.6の固定してupdateする。
* yum-utils、device-mapper-persistent-data、lvm2をインストールする。
* docker-ceをインストールする。

```
# echo 7.6.1810 > /etc/yum/vars/releasever
# sed -i".org" -e "s/^mirror/#mirror/" -e "s/^#baseurl/baseurl/g" -e "s/mirror.centos/vault.centos/g" /etc/yum.repos.d/CentOS-Base.repo
# yum update
# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum install docker-ce-19.03.8-3.el7
# yum clean all
```

## dockerdの設定変更
＊この設定は半分くらい趣味です。dockerのimageやvolume情報等は、デフォルトでは/var/lib/docker以下に格納されますが、そのディレクトリを/home/docker/system以下に変更します。

* docker.serviceが起動している場合は、停止させる。
* /usr/lib/systemd/system/docker.service　をviで以下のように修正する。
* /var/lib/docker/以下のファイルをすべて削除する。
* daemon reload後、docker.serviceを再起動する。

```
# systemctl stop docker
# mkdir /home/docker; mkdir /home/docker/system; mkdir /home/docker/container
# vi /usr/lib/systemd/system/docker.service
--------------------------------------------------------------------------------
(修正前)
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

(修正後)
ExecStart=/usr/bin/dockerd -g /home/docker/system --storage-driver overlay2 -H fd:// --containerd=/run/containerd/containerd.sock
--------------------------------------------------------------------------------
# rm -rf /var/lib/docker/
# systemctl daemon-reload
# systemctl restart docker
```

## docker起動確認
　公式のhttpdイメージを使って動作確認します。

* docker hubからhttpdコンテナイメージをpullする。
* port 58080をfirewallの許可ポートに設定する。(port 58080からアクセスさせるため)
* httpdコンテナをport 58080 -> 80に転送するようにオプションを設定して起動する。
* Ubuntuのブラウザから`http://192.168.1.10:58080`にアクセスし、「It works!」が表示されることを確認する。

```
# docker image pull httpd
# firewall-cmd --add-port=58080/tcp --zone=public --permanent
# docker container run -d --rm --name web -p 58080:80 httpd:latest
(動作確認後)
# docker container stop web
```

## docker-composeのインストール
　githubのdocker-composeプロジェクトの[release](https://github.com/docker/compose/releases)からdocker-composeを取得する。(下のコマンドはreleaseの手順より)

```
# curl -L https://github.com/docker/compose/releases/download/v1.25.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
```

## DooDコンテナイメージ作成
　DooDを実施するだけならば、dockerhub公式の[dockerイメージ](https://hub.docker.com/_/docker)を活用すると良いです。今回はDooDコンテナ内に入ってデバッグ等することを考慮して、準備作業で作ったCentOS7.6をまるごとコンテナ化してDooDコンテナイメージを作成します。

* /root/workspace/container_base を作成し、この中に/ 以下の必要なディレクトリをコピーしたり、空ディレクトリを作成したりする。
* docker image importを使用して、centos7-dood:0.0としてローカルリポジトリに登録する。

```
# mkdir /root/workspace/container_base
# cd /root/workspace/container_base
# cp -r /etc /run /srv /usr /var .
# mkdir boot dev home media opt proc root sys tmp 
# chmod 777 tmp
# ln -s usr/bin; ln -s usr/sbin; ln -s usr/lib; ln -s usr/lib64
# cd /root/workspace/container_base/root;cp /root/.* .
# cd /root/workspace/container_base/home;cp -r /home/guest .; chown -R guest:guest guest; chmod 755 guest
# cd /root/workspace/container_base; tar cvf centos7-dood.tar *
# cat centos7-dood.tar | docker import - centos7-dood:0.0
```

## 確認
　現時点で、centos7.6にはhttpdイメージとcentos7-doodイメージがローカルリポジトリ登録されたことになります。
(centos7-doodコンテナは自作なのでサイズがデカイです。多言語の言語ファイルやfirmware等のゴミを削除すればもう少し小さくなります。)

```
# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos7-dood        0.0                 e5fb14d9d88a        XX hours ago        1.48GB
httpd               latest              b2c2ab6dcf2e        YY hours ago         166MB
```

# 検証
## docker-compose.ymlの構成
　/home/docker/container以下にdocker-compose.ymlを作成し、以下のようにdirectorサービスとwebサービスを定義します。directorがDooDを実施するコンテナであり、コンテナ内でwebサービスの起動を実施します。DooDをするため、ホストのdocker.sockをコンテナ内にマウントします。

```yaml
# docker-compose.yml

version: '3'
services:
  director:
    image: centos7-dood:0.0
    privileged: true
    tty: true
    stdin_open: true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /home/docker/container/docker-compose.yml:/home/docker/container/docker-compose.yml:ro
    command: /bin/bash

  web:
    image: httpd:latest
    ports:
      - "58080:80"
```

## ホスト側での動作確認
 docker-compose up -dで実行し、コンテナが起動することを確認します。また、httpdが立ち上がったことを確認するため、Ubuntuのブラウザから`http://192.168.1.10:58080`にアクセスし、「It works!」が表示されることを確認します。

```
# cd /home/docker/container/
# docker-compose up -d
Creating network "container_default" with the default driver
Creating container_web_1      ... done
Creating container_director_1 ... done
(動作確認後)
# docker-compose down
```

　止める前に、docker inspectにてcontainer_director_1の中身を確認してみます。

```
# docker container inspect container_director_1
                    (中略)
        "Labels": {
                "com.docker.compose.config-hash": "a93046・・・・・・"
                    (後略)
```
　docker-composeのソースコードを確認していないのでこれは推測となりますが、このconfig-hash生成の一部にdocker-compose.ymlのパス情報が含まれます。(そのため、-fオプションでymlファイルを指定して、yumファイルごとにcompose制御できます。)
　そのため、ホストとコンテナでdocker-composeの情報を共有するならば、`ホストと同じディレクトリにホストと同じdocker-compose.ymlを配置`すればいい、とわかります。上記のdocker-compose.ymlで`/home/docker/container/docker-compose.yml:/home/docker/container/docker-compose.yml:ro`としているのは、そのためです。

＊2020/5/3(日) 一部訂正：ホストとコンテナで同じディレクトリにdocker-compose.ymlを配置する必要はなかったです。
　追調査したところ、docker-compose.ymlが格納されるディレクトリ名称が同じならば大丈夫でした。例えば、
「/home/docker/`container`/docker-compose.yml:/home/`container`/docker-compose.yml:ro」
とcontainerが揃っていればOKです。

## DooDの実施
　ホストにてdirectorサービスを立ち上げた後、directorコンテナ内に入り、docker-composeでwebサービスを立ち上げます。確認は上記と同様にUbuntuのブラウザから`http://192.168.1.10:58080`にアクセスし、「It works!」が表示されることを確認します。
(区別がつくように、＠centos1 と表示されているときはホスト、＠a8784c960fe6 と表示されているときはコンテナの操作となります。)

```
[root@centos1 ~]# cd /home/docker/container/

[root@centos1 container]# docker-compose up -d director
Creating network "container_default" with the default driver
Creating container_director_1 ... done

[root@centos1 container]# docker-compose ps
        Name            Command    State   Ports
------------------------------------------------
container_director_1   /bin/bash   Up           

[root@centos1 container]# docker-compose exec director /bin/bash

[root@a8784c960fe6 /]# docker-compose -f /home/docker/container/docker-compose.yml ps
        Name            Command    State   Ports
------------------------------------------------
container_director_1   /bin/bash   Up           

[root@a8784c960fe6 /]# docker-compose -f /home/docker/container/docker-compose.yml up -d web
Creating container_web_1 ... done

[root@a8784c960fe6 /]# docker-compose -f /home/docker/container/docker-compose.yml ps
        Name               Command        State           Ports        
-----------------------------------------------------------------------
container_director_1   /bin/bash          Up                           
container_web_1        httpd-foreground   Up      0.0.0.0:58080->80/tcp
```

# まとめ
　DooDするときに、docker-composeの情報をホストとコンテナで共有できます。これ単独では旨味が少ないですが、Docker Remote APIと組み合わせて、外部サーバからDooDコンテナにコンテナ起動指示を出すことで、複数コンテナの同時制御ができるようになります。