Docker Composeについてざっくり理解する【概要 / ymlファイル書き方 / コマンド操作】


# はじめに
最近、山浦清透さんが運営されているプログラミング学習サービス「[独学エンジニア](https://dokugaku-engineer.com/)」にてDockerについて学び始め、最後のセクションである`Docker Compose`について学び終わりましたので、備忘録も兼ねて自分なりにまとめていきたいと思います:muscle:
今回の内容はDockerの基本操作を理解した上での記事となりますので、「Docker使えたら便利らしいけど、よく分からん！」って方は以前投稿した記事、[「Docker基本操作Part1」](https://qiita.com/gon0821/items/2ab56df440e057f9d1b7)[「Docker基本操作Part2」](https://qiita.com/gon0821/items/8765a32a89d56335c10a)[「Dockerfileの書き方」](https://qiita.com/gon0821/items/f9e3bcbb6cb01d4ef7fa)を軽くご一読ください:bow_tone2:

![docker-compose-tako.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3117662/90a96cd1-d7ba-78b4-dcf3-f998bf2f8be9.png)

（タコが複数のコンテナを持って考えてる・・・・・・:eyes:）
（まさかお前、、、複数のコンテナをまとめて操作できるっていうのか！？:neutral_face:）

タコ:rage:「そのとおーり」

（なんか喋った笑）


# １. Docker Composeとは
> * 複数のコンテナを定義し実行する Docker アプリケーションのためのツール
> * YAMLファイルを使い、アプリケーションのサービスを設定し、コマンドを１つ実行するだけで、設定内容に基づいた全てのサービスを生成・起動

<sub>[「Docker公式ドキュメント」](https://docs.docker.jp/compose/index.html#compose-features)より</sub>

つまり要約すると、これまでコンテナ一つごとにコマンドを打って操作していたものを、`Docker Compose`を用いることにより、**複数のコンテナで構成されるアプリケーションについて、Dockerイメージのビルドや各コンテナの起動・停止などをより簡単に行えるようにするツール**と言えます。

おそらく実際の開発現場では、コンテナ１つだけで開発するというのはほとんどなく、複数のコンテナを用いて開発するでしょうから、Docker Composeは必須といえるでしょうね。


# ２． DockerとDocker Composeの違い

|  | Docker(Docker Engine) | Docker Compose |
|:-:|:-:|:-:|
| 役割  | Dockerコンテナを運用するための**プラットフォーム**  | 複数のDockerコンテナを一度にできる<font color=red>**ツール**</font>  |
|  コンテナ操作 | １度に**１つずつ**しかDockerコンテナを操作できない  | １度に<font color=red>**複数のDockerコンテナ**</font>を操作可能  |

# ３． コンテナを起動するまでの流れ

1. <font color=blue>**DockerFileの作成**</font>
   docker-compose.ymlファイルで直接イメージを指定すれば、Docker Composeでコンテナを起動するための`Dockerfile`は必要ないですが、
   既存のイメージに対して追加の設定やカスタマイズを行いたい場合は、`Dockerfile`を作成し、それをdocker-compose.ymlから参照します。


2. <font color=blue>**docker-compose.ymlの作成**</font>
   `docker-compose.yml`とはDocker Composeの設定ファイルであり、これから作成するコンテナの初期状態を「ports:」「volumes:」などYAML形式を用いて定義されています。
    また、このファイルは通常、プロジェクトのルートディレクトリに配置されるのが一般的です。
    
   ここで初登場した`docker-compose.yml`君を使用することで、複数のコンテで構成されるアプリケーションの全体像を一目で把握でき、さらに一つのコマンドで全てのコンテナを起動したり、停止したりすることができちゃいます。 

3. <font color=blue>**docker-composeコマンドでコンテナ起動**</font>
    docker-compose.ymlファイルが存在するディレクトリで`docker compose up`コマンドを実行すると、docker-compose.ymlファイルに記述された全てのサービス（コンテナ）をビルドし、起動することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3117662/b21e4093-30e9-deef-4037-f05175a29d1b.png)
<sub>参考：[「【初心者向け・図解】Docker Composeとは？Dockerとの違いを現役エンジニアがわかりやすく解説」](https://o2mamiblog.com/docker-beginner-2/)より</sub>



# ４． docker-compose.ymlファイルの書き方
まずはdocker-compose.ymlファイルのサンプル（Railsの環境構築）を見て、よく使うものをピックアップしてそれぞれ理解していきましょう！
```yaml:Sample
services: # （１）
  db:
    image: mysql:5.7 # （２）
    volumes: # （４）
      - db_data:/var/lib/mysql 
    restart: always
    environment: # （６）
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: your_database
      MYSQL_USER: user_name
      MYSQL_PASSWORD: user_password

  web:
    build: ./rails_project # （３）
    command: bundle exec rails s -p 3000 -b '0.0.0.0' # （９）
    volumes: # （４）
      - .:/myapp
    ports: # （７）
      - "3000:3000"
    depends_on: # （８）
      - db
      
volumes: # （５）
    db_data:
```
## （１）services
ここでは、作成したいサービス（コンテナ）を列挙します。
サンプルでは「web」と「db」という名前で２つのコンテナについて、それぞれ設定が記述されています。
```yaml:services
services: 
  db: #サービス名
    
  web: #サービス名

```

:::note info
**「web」や「db」以外の名前を付けるのはあり？**
サービス名は任意のものなので、違う名前を付けるのもありです。
しかし、以下２点には気をつけましょう。
* そのサービスが何を示しているか理解しやすい名前を付けること
* サービス名を他のサービスから参照している場合があるので、変更する場合は参照されているサービス名も変更すること

※環境構築の際にチームメンバーへ共有したりすることを考えると、基本的には「web」や「db」など一般的に使われているサービス名にするのが無難でしょう。
:::

## （２）image
既存のイメージに対して追加の設定やカスタマイズを行う必要がない場合、`image:`に続いてDockerイメージを指定することで、そのイメージよりビルドします。
```yaml:image
  image: mysql:5.7 # イメージ名:タグ
```

## （３）build
`image:`よりビルドするのではなく、用意した**Dockerfile**よりビルドする場合、`build:`へymlファイルが置いてあるディレクトリ（カレントディレクトリ）から**Dockerfile**が置いてあるディレクトリへの相対パスを記述します。
```yaml:build
  build: ./rails_project #　Dockerfileがあるディレクトリへの相対パス
```

## （４）volumes【サービス内にある方】
`volumes`はデータをコンテナの外に保存するための設定項目であり、ホストマシンにもデータを共有することでデータの永続性を可能にしています。これがないと、コンテナが削除されたときにそのコンテナ内にあった全てのデータが消えてしまい大変です:scream:

```yaml:volumes
  # (dbコンテナ)
  volumes:
      - db_data:/var/lib/mysql　 #　ホストディレクトリ : コンテナ内ディレクトリ
  # (webコンテナ)
  volumes: 
      - .:/myapp  #　ホストディレクトリ : コンテナ内ディレクトリ  
```
`volumes`はホスト側のディレクトリとコンテナ内のディレクトリをマウント、要は結びつけをするために使われ、ホスト上でコードを変更を行うと、その変更がリアルタイムでコンテナ内に反映されます:ok_hand:

## （５）volumes【トップレベルで指定されている方】
ここでの`volumes`はDocker Composeにボリュームを作成するように指示します。
そしてここで定義されたボリュームはサービス内の`volumes`で使用することができ、サンプルにおいてもdbコンテナにおいて使用されています。
```yaml:volumes
   volumes: 
      db_data:
```
ボリュームを使うとデータはDockerコンテナの起動・停止のサイクルから独立して保存・管理できるため、コンテナが削除されてもボリュームに保存されたデータは保持されているので消したくないデータ等はボリュームに入れておくと、安心ですね:blush:

:::note warn 
<details><summary><strong>なぜdbコンテナ内でマウント先をホストマシンではなく、Dockerボリュームにしているの?<strong></summary>

dbサービスでは、MySQLのデータが保存される/var/lib/mysqlディレクトリをDockerボリュームにマウントしています。これには主に以下の理由があります。

* **データの永続化**
  上記で述べている通り、コンテナの処理サイクルに関わらずデータを永続化することができます。

* **パフォーマンス面**
   Dockerボリュームはパフォーマンスが最適化されており、特にデータベースのようなI/O（インプットとアウトプットの繰り返し）が頻繁に発生するアプリケーションにとっては重要なってきます。

* **データの独立性**
    データベースのデータはアプリケーションコードとは異なり、開発者が直接操作するものではありません。
そのため、それらをホストマシンの特定のディレクトリにマウントする必要性は低く、またホスト側にマウントするとホストマシンのディレクトリ構造を複雑にする可能性があります。

* **バックアップ及び復元**
    Dockerボリュームはバックアップと復元が容易で、重要なデータベースデータの管理に適しています。

以上のような理由から、データベースデータの永続化にはDockerボリュームが一般的に推奨されます。
しかし、特定のケースや要件によってはホストのディレクトリを使用することもあります。
</details>

:::

ボリュームについて「ん〜、よく分からん！」って方は以下の記事が参考になったので貼っておきますね

https://qiita.com/aki_55p/items/63c47214cab7bcb027e0

## （６）environment
`environment`は、Dockerのコンテナ内で使用される環境変数を設定するために使用します。
Dockerコンテナはホストマシンの環境と完全に分離されて動作するため、コンテナ内部で環境変数を設定する必要があります。
```yaml:environment
  environment: 
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: your_database
      MYSQL_USER: user_name
      MYSQL_PASSWORD: user_password
```

## （７）ports
`ports`の設定では、ホストマシンとDockerコンテナ間でのネットワークポートのマッピングを指定します。
指定する形式は`ホストマシンのポート番号:コンテナのポート番号`となるので逆にならないように注意しましょう:ok_hand:
```yaml:ports
    ports: 
      - "3000:3000" # ホストマシンのポート番号:コンテナのポート番号
```
`ports`の設定はDockerコンテナのアプリケーションがネットワークを通じて外部と通信するために必要な設定ですので、ネットワークを通じた外部とのやり取りがある場合は忘れずに設定しておきましょう:wink:

## （８）depends_on
`depends_on`はサービス間の依存関係を定義するために使用し、Docker Composeに指定したサービスが他の１つ以上のサービスに依存していることを伝えることができます。
その結果、Docker Composeは、そのサービスを開始する前に依存している全てのサービスが先に開始されていることを確認してくれます。

```yaml:depends_on
   # (webコンテナ)
   depends_on: 
     - db
```
上記の内容では、webコンテナがdbコンテナに依存しているので、dbコンテナが先に開始されることをDocker Composeは確認します。

## （９）command
`command`はサービスコンテナが起動したときに実行するコマンドを指定します。
```yaml:command
    command: bundle exec rails s -p 3000 -b '0.0.0.0' 
```

設定項目はこの他にも多数ありますが、よく使うものはこの辺りで抑えられていると思いますので、知らない項目が出てきたらその都度調べるスタイルでもいいんじゃないかなと思います:point_up:

:::note info
**ymlファイルに「version」が書かれているのよく見るけど、あれって書かなあかん？**
結論、<font color=red>書かなくて大丈夫</font>です。
Docker Composeの使用を定めている[「compose-spec」](https://github.com/compose-spec/compose-spec/blob/master/spec.md#compose-file)より
>The Compose file is a YAML file defining version (DEPRECATED), services (REQUIRED), networks, volumes, configs and secrets. 
【日本語訳】
Composeファイルは、バージョン（非推奨）、サービス（必須）、ネットワーク、ボリューム、設定、シークレットを定義するYAMLファイルです。

との記載があるためです！！
「version」がなくても問題なく動作しますので、安心してくださいね(^^)
:::

# ５． Docker Composeコマンドで操作する
docker-compose.ymlファイルが作成できたら、Docker Composeコマンドを用いて操作していきましょう:raised_hands:

## （１）up 【コンテナ作成・起動】
```
$ docker compose up  # イメージが存在しないときはビルドする
```
オプションでは以下の２つがよく使われます。
* `--build`　イメージをビルドした上でコンテナを作成・起動する
* `-d` コンテナをバッググラウンドで起動させる（detach）
```
$ docker compose up --build 
$ docker compose up -d
```
Dockerfileやdocker-compose.ymlファイルを修正し、改めてコンテナを立ち上げたい場合はイメージのビルドからしたいので`--build`オプションが使われます:point_up_tone2:

## （２）ps 【コンテナ一覧表示】
停止中を含めたコンテナの一覧を表示させたい場合は、`-a`オプションを付けます。
```
$ docker compose ps    　　# コンテナの一覧を表示
$ docker compose ps -a 　 #停止中を含めたコンテナの一覧を表示
```

## （３）logs 【ログを表示】
`logs`コマンドの後に、サービス名を指定することでログを表示させます。
```
$ docker compose logs web  　# サービス名を指定
```
## （４）run 【コンテナ作成後、コマンド実行】
`run`コマンドの後に、サービス名とコマンドを指定することで、コンテナを作成して１度だけコマンドを実行します。
```
$ docker compose run web ruby app.rb  　# サービスとコマンドを指定
```

## （５）exec 【コマンド実行】
起動中のコンテナで`exec`コマンドの後に、サービス名とコマンドを指定することでコマンドを実行します。
```
$ docker compose exec web /bin/sh  # サービスとコマンドを指定
```

## （６）down 【コンテナを停止・削除】
`down`コマンドを付けるだけで`up`コマンドで作成されたコンテナを停止・削除します。
```
$ docker compose down
```

# おわりに
最後まで見ていただき、ありがとうございました！
少し長くなりましたが、これで`Docker Compose`についてはある程度抑えられたのではないかなと思います:writing_hand:
あとは実践あるのみですね:basketball_player_tone1:
私はこれまでに学んだ知識を活かして、RailsをDocker上で環境構築できるように頑張っていきたいと思います:laughing:

間違っている点などございましたら、遠慮なくご指摘いただければと思います:bow_tone2:


# 参考文献

https://o2mamiblog.com/docker-beginner-2/

https://knowledge.sakura.ad.jp/16862/

https://dokugaku-engineer.com/course/serverside

https://redj.hatenablog.com/entry/2022/09/19/220110