雰囲気でコンテナを立ち上げないために。あらためて学ぶDocker Composeの基礎



# はじめに:qiitan:
この記事は、`Docker-compose.yml`を書いたり、`docker-compose`コマンドを使ってアプリケーションを開発したり運用したことがあるが、

*——全然わからない、俺は雰囲気で複数コンテナを立ち上げているーー*

という方（主に自分）に向けて、**あらためてDocker Composeがどういうものかを、簡単に説明した記事**です。

## この記事のゴール🏁
- なぜ**複数コンテナの管理ツールが必要になるのかを把握**する
- 複数コンテナ実行管理ツールである**Docker Composeの基礎を理解**する


# 複数コンテナ管理の基礎を学ぶ🐳

## 1. なぜ複数コンテナの管理ツールが必要になるのか

### 1.1. 複数コンテナを運用するパターン
現在の一般的な構成である三層アーキテクチャの**WebアプリケーションをDockerを使って開発・運用すること**を例として挙げます。

![Screenshot 2020-09-13 18.30.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/18493da1-f0a5-bbd0-94ef-76acd47ad659.png)

プロセスとしては、**Web**・**アプリケーション**・**データベース**の３つのプロセスが考えられます。これを1コンテナで実現するのは、（できるにはできるみたいですが）あまり良い構成とは言えません。
スケール・管理がしにくく、プロセスが一つでも落ちるとコンテナが落ちてしまうなど、リスクが高いためです。

![Screenshot 2020-09-13 19.17.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/c45cd467-8fd4-7dfc-b462-9caf677eb9bb.png)

なので、現在は１コンテナ＝１プロセスとして、**それぞれの役割ごとにコンテナを立てる**のが一般的になっています。必要に応じてコンテナを追加したり差し替えができるため、スケールや変更が容易になります。
![Screenshot 2020-09-13 19.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/989d2351-dc3b-1ca7-8556-bdd3f0f54c0a.png)

### 1.2. 管理ツールを使わずに複数コンテナを動かすと...?
ここで、コンテナの起動・管理方法について考えます。１コンテナ＝１プロセスなのですから、例えば今回の環境を立ち上げる際には、計3回`docker build` & `docker run`をすることになります。これは、**環境の構成コンテナが増えるごとにコマンドを打つ回数が増えていく**ことを意味します。また、ポートマッピングの設定など、それぞれのコンテナを起動する際にオプションでいちいち指定しなければなりません。シェルスクリプト化するにしても、コンテナの立ち上げ順序等もあるため、環境の管理は面倒でしょう。

```Terminal:例えばのイメージ
$ docker run --name mysql #この後にvolumesなどのいろいろなオプションがつく
$ docker run --name nginx
$ docker run --name rails
$ #...コンテナの数だけ回数は増える
```
こうなると、環境を構築するのが面倒になりますし、手動でそれぞれのコンテナを運用することになります。単純に管理・維持が大変になり、**"環境構築の手軽さ"というDockerの特徴を潰す**ことになります。

## 2. Docker Compose とは🐙
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/0939e3d8-639a-0d64-092f-9a2268f79fe0.png)
ここで、**Docker Compose**の登場です。
Docker Compose とは、**複数コンテナで構成されるプロジェクトの起動・停止などの手順を、yamlファイルに定義した設定内容に基づいて自動的にしてくれるツール**です。

```yaml:docker-compose.yml(適当に作った例)
version: '3'
services:
  db:
    image: postgres:alpine
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    networks:
      - sampleapp

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - ./web/app/
    ports:
      - '8080:8080'
    depends_on:
      - db
    networks:
      - sampleapp

networks:
  sampleapp:
    external: true
```
各プロセスのDockerfileを用意した上で、上記のように`docker-compose.yml`にプロジェクトの設定内容を記述しておき、

```terminal
$ docker-compose up
```

とすると、簡単に環境を立ち上げることができます。

### 2.1. docker-compose.yml で定義する内容
さて、`docker-compose.yml`では、どのようなことが設定できるのでしょうか？
[Compose ファイル・リファレンス](https://docs.docker.jp/compose/compose-file.html)によると、
> Compose ファイルは YAML ファイルであり、 サービス（services） 、 ネットワーク（networks） 、 ボリューム（volumes） を定義します。

とあります。
うーん、なんとなく今まで雰囲気で`docker-compose.yml`に書いてきたから、よくわかっていない...（心の声）
ということで、それぞれについてあらためて確認していきたいと思います。

#### 2.1.1. services
![Screenshot 2020-09-17 19.39.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/fc40b545-f4fe-4a48-38bf-72e0b3e3846f.png)

servicesには、プロジェクトを構成する各コンテナの情報を定義します。ここには、各コンテナの`docker run`時に指定していたオプションを記述することができます。ポートマッピングの設定や、後述するvolumesやnetworksの設定などもここに記述することで、`docker-compose up`でのコンテナ一括起動時に反映してくれるようになります。

#### 2.1.2. volumes
![Screenshot 2020-09-24 17.44.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/34daf924-40c9-4aaa-e29c-ec6f4a9f3edd.png)

続いて、`volumes`に記述する内容です。
起動したコンテナで扱うデータは、コンテナが削除されると一緒に消えてしまいます。また、コンテナを複数起動した際、そのままではお互いのファイルシステムを直接参照できません。

ですが、コンテナには**ボリューム(volume)**と呼ばれる、ディレクトリやファイルをマウントできる領域があり（実体はホスト上に自動生成されるディレクトリ `/var/lib/docker/volumes`）、異なるコンテナでも、`docker`コマンドのオプションで同じvolumeを指定することで、データの共有が可能になります。

Docker Compose では、その設定も各コンテナ起動時にいちいちオプションをつける代わりに、`docker-compose.yml`の`volumes`に定義することで、`docker-compose up`でのコンテナ一括起動時に反映してくれるようになります。

#### 2.1.3. networks
![Screenshot 2020-09-24 17.45.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261575/fb63a293-2197-38b1-0cc6-6b01a8da65b2.png)

最後に`networks`に定義する内容についてです。
今回の例では、アプリケーションを構成するWeb・アプリケーション・DBの各コンテナを通信させる必要がありました。
ここで、Dockerには、３つの標準ネットワークモデルがあり、設定によってホスト側のネットワークに直接接続したり、コンテナ間を通信させることができます([わかりやすい参考記事](https://qiita.com/etaroid/items/88ec3a0e2d80d7cdf87a#%E7%8B%AC%E8%87%AA%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF))。

特に何も設定せずにコンテナを複数起動させている場合、**各コンテナはIPアドレスを指定して通信することは可能ですが、コンテナ名を指定して通信することはできません**。コンテナ名で通信させるにはユーザー独自の`Dockerネットワーク`を設定する必要があります。

その設定も、本来なら`docker network`コマンドでいちいち指定する必要がありますが、Docker Composeでは`docker-compose.yml`の`networks`に定義することで、`docker-compose up`でのコンテナ一括起動時に反映してくれるようになります。

# まとめ:stock:
- 管理ツールを使わずに、複数コンテナ構成プロジェクトを運用しようとすると、各コンテナ実行時にいちいちコマンドを走らせる&オプションで設定する必要があるため、**環境構築や環境の維持・再現が大変**

- Docker Composeを使うと、`docker-compose.yml`に各コンテナの情報をまとめて定義しておくことができ、**1コマンドで環境が構築できる**

- Docker Composeによる管理は、特に`network`や`volume`が複雑な場合に真価を発揮する

# 引用・参考記事

- [Docker Compose 徹底解説](https://www.slideshare.net/zembutsu/docker-compose-guidebook)
- [docker-composeを使うと複数コンテナの管理が便利に](https://qiita.com/y_hokkey/items/d51e69c6ff4015e85fce)
- [【図解】Dockerの全体像を理解する -中編-](https://qiita.com/etaroid/items/88ec3a0e2d80d7cdf87a#%E7%8B%AC%E8%87%AA%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)
- [Compose ファイル・リファレンス](https://docs.docker.jp/compose/compose-file.html)