データコンテナを作ってみるDockerミニハンズオン

「Dockerが何かは知っている・動かしてみたことはある」ぐらいの方以上を想定して

- ```Dockerfile```でimageを自作
- ```docker-compose```で複数コンテナを立ち上げ
- サイドカーパターン

をつまみ食いしながらまとめていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/297653/9ce7b25e-83c5-a73f-c380-ce4ffb08961a.png)
__サイドカーパターンの図__を[こちらのブログ](https://kakakakakku.hatenablog.com/entry/2018/09/24/003723)より転載させていただきました。
冒頭記載の対象読者層を超えてしまいますが、良い論文紹介記事です！

データを他のコンテナが共用で使っている様子が、まるでサイドカーみたいですね(棒)
今回はこの共用データを持つ役割もコンテナに担当してもらいます(データコンテナ)。

# いきなり余談(？)

この記事を書くに至った経緯ですので飛ばしてもらっても構いません。

Composeファイルのフォーマットには2020年1月現在```1.0```、```2.x```、```3.x```と複数のバージョンがあります。
```docker-compose.yaml```ファイルの最初に

```yaml
version: '3'
```

みたいに指定します。
インターンでデータコンテナを含む設計を触ることになったのでDockerの勉強から始め、docker-composeを```3.x```系で何の疑いもなく作ろうとしました。

しかし他のコンテナ名を指定して特定のボリュームを参照できる```volumes_from```が、```3.x```では無くなっていました。
ここで悩んだ挙句に代替案が分からなかったので、一旦のまとめとしてこの記事を書いています。

2つ以上のコンテナでデータを共用したい場合

- データ用のコンテナを1つ作り、特定のディレクトリを他のコンテナからも覗けるようにする
- ホスト側にデータを保存しみんなで参照する永続化ボリュームを確保する

のいずれかの構成にすることが多いかと思います。

業務ではKubernetesでサイドカーパターンのPodをスケーリングさせる予定だったので、2つめの永続化ボリュームを使う設計では新しいボリュームがどんどこ作られてつらい事態に陥りそうと考え1つめのデザインパターンを試しました。

なおKubernetesでは各Podに専用の空ディレクトリを作成すればよさそうなので、結局はDockerのおべんきょうができただけでした。

```yaml:sample.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-pod-1
spec:
  containers:
  - image: centos:latest
    name: test1
    volumeMounts:
    - mountPath: /test
      name: test-volume
    command: ["tail",  "-f", "/dev/null"]
  - image: centos:latest
    name: test2
    volumeMounts:
      - mountPath: /test
        name: test-volume
    command: ["tail",  "-f", "/dev/null"]
  volumes:
  - name: sample-volume
    emptyDir: {}
```

# ハンズオン！

それでは

- ```centos```コンテナ
- ```Dockerfile```でつくってみるオリジナル```centos-volume```コンテナ
- ```docker-compose```でまとめて立ち上げ
- 確認・お片づけ

の順でさくさくと試していきましょう。


## DockerfileでオリジナルのDocker imageを作る

ざっっっくり言うと、Dockerコンテナを作る型抜きの原型がDocker imageです。
Docker Hubに公開されているものだけでなく、元となるファイル(名前を```Dockerfile```にしておく)を自分で記述し新しいimageを作ることもできます。

今回は共有したいデータを持つ方のデータコンテナを自作で定義してみます。

```Dockerfile:Dockerfile
FROM centos

WORKDIR /mount-test
RUN echo "volume mount test" > mount_test.txt
```

なんとなくこういうルールなのかな、みたいなのは察していただけるかと思いますが
```FROM```：CentOSのimageである```centos```を元にして
```WORKDIR```：作業ディレクトリを移って
```RUN```：指定したコマンドを実行
のように順番に操作を書くのが```Dockerfile```です。
既存のimageをベースに敷き、各コマンドの層を積み重ねていく感じで作られていきます。
今回はデータコンテナとしての役割を確かめるため、```/mount-test```ディレクトリとその中に```mount_test.txt```ファイルを作成しています。

この```FROM```や```RUN```などの命令は全部で20種類くらいあります。詳しくは公式のドキュメントなどをまた適宜ご覧ください。

__参考__
[Dockerfile リファレンス]( http://docs.docker.jp/engine/reference/builder.html)

こうして書いたDockerfileを```build```することで、Docker imageとして使えるようになります。

```console
$ docker build ./ -t centos-volume
```

ここで```./```は```Dockerfile```が今のディレクトリ階層にあり、```-t```は```build```するimageに名前をつけることをそれぞれ指示しています。
以下のように表示されていれば成功です。

```sh
Sending build context to Docker daemon   42.5kB
Step 1/3 : FROM centos
 ---> <適当なハッシュ値1>
Step 2/3 : WORKDIR /mount-test
 ---> Running in <適当なハッシュ値2>
Removing intermediate container <適当なハッシュ値2>
 ---> <適当なハッシュ値3>
Step 3/3 : RUN echo "volume mount test" > mount_test.txt
 ---> Running in <適当なハッシュ値4>
Removing intermediate container <適当なハッシュ値4>
 ---> <適当なハッシュ値5>
Successfully built <適当なハッシュ値5>
Successfully tagged centos-volume:latest
```

これが最新バージョンだよ、と最後には```latest```タグまでつけてimage化してくれていますね。
タグとは例えば

```console
$ docker pull centos:centos7
```

のように、Docker imageをバージョンなどの情報つきで指定する時に併記するために使います。
数字は中のミドルウェア・ソフトウェアのバージョンに対応してることが多いと思います。

ここでタグを指定しない時には、```latest```タグがついたimageを表します。
```latest```タグはそのimageを使いたい人みんなにとっていい感じの塩梅になるようなバージョンを選んでつけてくれている(？)とは思いますが、特定のバージョンを指定する必要がある場合も多いかと思います。

また今回は行いませんが、```build```したオリジナルのimageは```$ docker push```を行うことでDocker Hubに登録することでオープンに共有ができます。
逆に自分だけが使えるようにしておきたい、あるいは業務などで特定の人にだけ限定公開したい場合は

- Docker Hubにプライベートリポジトリを作成(2つ目以降は有料プラン)
- クラウドのマネージドサービス(AWSならECR、GCPならGCRなど)を利用

のどちらかを選びます。

そして上記の```build```プロセスを見てお気づきかもしれませんが、```Dockerfile```から「コマンドを一行ずつ実行してはimage化」を繰り返して行われます。
そのため同じ命令がたくさんある場合には、中間生成されるimageが少なくて済むように```\```や```&&```で繋いでひとまとめの命令にしておきましょう。
以下の例のように```RUN```命令などが特に当てはまります。

```Dockerfile
RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
```

__参考・参照__
[Dockerfileを書くベストプラクティス](http://docs.docker.jp/engine/userguide/eng-image/dockerfile_best-practice.html)


## Docker Composeで複数コンテナをまとめて立ち上げる

Docker Composeは複数のコンテナをまとめて扱いたい時に便利です。
ファイル名を```docker-compose.yaml```としてComposeファイルのバージョンと各コンテナの定義を書いていきます。

```yaml:docker-compose.yaml
version: '2'
services:

  data-container:
    image: centos-volume
    container_name: data-container
    tty: true
    volumes:
      - /mount-test

  centos1:
    image: centos
    container_name: centos1
    tty: true
    volumes_from:
      - data-container:ro
```

なんとなく```image```や```container_name```は分かりやすいかと思います。
```version```はDockerではなくComposeファイルのバージョンです。
また```tty: true```でポート待ち受けなど特に何もしないコンテナも立ち上げ後に起動させ続けることができます。
これが無いとコンテナがすぐに終了してしまいます。
そして```volumes_from```ではマウント元となるボリュームを読み取り専用でマウントするため、後ろに```:ro```(read only)とつけています。
マウント元のコンテナ名だけを書くか```:rw```と付けると読み書きできる権限が付与されます。

なおこのComposeファイルで指定できる項目はめちゃくちゃたくさんあるので、またドキュメントなどを適宜参照してください。

__参考__
[Composeファイル バージョン2リファレンス](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/compose-file-v2/)
[Composeファイル バージョン3リファレンス](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/)

あとは```docker-compose.yaml```があるディレクトリで

```console
$ docker-compose up -d
```

とすれば、```docker-compose.yaml```と名前がつけられたファイルを探して読み込んでコンテナを立ち上げてくれます。
```-d```はバックグラウンド実行を指定しています。

```sh
Creating data-container ... done
Creating centos1        ... done
```

と表示されていればコンテナ作成は成功。

```sh
$ docker ps
```

で2つとも表示されていればちゃんと動いています。

続いて各コンテナが定義通り作られているかを確認してみましょう。
まずはデータコンテナの方に入り、```Dockerfile```内で作ったはずの```mount_test.txt```があるかを見てみます。

```console
$ docker exec -it data-container bash

[root@<皆さんのコンテナID> mount-test]# cat mount_test.txt 
volume mount test
```

ファイルはちゃんと作成されており、作業(カレント)ディレクトリが```Dockerfile```の通り```/mount-test```になっていますね。
またこのディレクトリではvimなどで```mount_test.txt```を編集したり、新しいファイルを作ったりといった読み書き作業もできるかと思います。

次に```exit```コマンドでデータコンテナから抜け、データを覗く側のコンテナに入ります。

```console
$ docker exec -it centos1 bash

[root@<皆さんのコンテナID> /]# cat mount-test/mount_test.txt 
volume mount test
```

データコンテナ(```data-container```)のディレクトリとその中身がちゃんと見えていますね。
今回は```docker-compose.yaml```にてread onlyを指定しているので、マウントされたこの```/mount-test```ディレクトリの中では既存ファイルの編集や新規ファイルの作成をしようとするとちゃんと怒られます。

最後に立ち上げたコンテナたちを停止、削除しましょう。
片付けるまでがハンズオンです。

```sh
$ docker-compose stop
```

で```docker-compose up```したコンテナ群をまとめて停止、

```
$ docker-compose rm
```

で削除できます。便利...。

# さいごに

まだDocker始めて2週間ぐらいしか触ってないので、修正・追記すべき箇所などあるかと思います。
Compose```3.x```系ではこうすればいいよなどのアドバイスも含めありましたらよろしくお願いします＾＾