[備忘録] [初心者] Docker Compose / Rails(公式doc.)について自分用補足#2 (docker-compose.yml)

# はじめに

前回の投稿、
[[備忘録] [初心者] Docker Compose / Rails(公式doc.)について自分用補足#1 (Dockerfile, entrypoint.sh)](https://qiita.com/RiSE_blackbird/items/e16c84cc18d4639d5d4f)
に続き、[Docker + Raislの公式チュートリアル](https://matsuand.github.io/docs.docker.jp.onthefly/compose/rails/)の内容を丁寧に追って理解を試みる取り組みのアウトプットです。

あくまで学習過程の私的なアウトプットなので、信頼性の高い情報は、各リンク先を参照していただければと思います。

# 本編

[クィックスタート: Compose と Rails | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/rails/)
上記のチュートリアルで扱われる、``docker-compose.yml``の理解のためのメモです。
一部、深掘りのために、``PostgreSQL``関連や、公式イメージの``Dockerfile``の内容も扱います。


#### ``docker-compose.yml``の概観理解に役立ったリンク例

- [Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/)
- [Docker Compose の概要 | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/#common-use-cases)
- [さわって理解するDocker入門 第4回 | オブジェクトの広場](https://www.ogis-ri.co.jp/otc/hiroba/technical/docker/part4.html)
- [Compose file version3のリファレンス - Qiita](https://qiita.com/goforbroke/items/b4a071af71f457960e59)

## docker-compose.yml

~~~yml:docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
~~~

### version:
使用するDocker Composeのバージョン

### services:
[#サービス設定リファレンス | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#service-configuration-reference)
アプリケーションを構成する各サービスのコンテナを設定します。
今回扱うチュートリアルでは、２つのサービス``db``, ``web``によってサービス全体を構成しています。

~~~yml
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
~~~
~~~yml
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
~~~

#### db:
任意のサービスの名称として"db"が付けられています。
公式のサンプルでは、下位に``image``, ``volumes``, ``environment``の設定項目が続きます。

~~~yml
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
~~~

##### image:
~~~yml
image: postgres
~~~
[#image | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#image)

コンテナを起動させるイメージの設定とあります。
今回は、[PostgreSQLのDocker公式イメージ](https://hub.docker.com/_/postgres)を用います。
デフォルトのコンテナOSは、Debian:stretch、オプションでAlpineも選択できるようです。


##### volumes:

~~~yml
volumes:
  - ./tmp/db:/var/lib/postgresql/data
~~~
[#volumes | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#volumes)
> マウントホストパスや名前つきボリュームを、サービスに対するサブオプションとして指定します。

まず、``マウント``という言葉もピンときていなかったのですが、下記の質疑がとても分かりやすかったです。
[filesystems - understanding "mount" as a concept in the OS - Unix & Linux Stack Exchange](https://unix.stackexchange.com/questions/3247/understanding-mount-as-a-concept-in-the-os)
[What is a mount point in Linux/Unix? - The Linux Juggernaut](https://www.linuxnix.com/what-is-a-mount-point-in-linuxunix/)
特に、耕地の管理とディスクドライブの管理とのアナロジーを示した例え話はとても面白いと感じました。
各パーティション内のデータにアクセスするための"門"を設けることがマウントであるということに納得しました。

また、ボリュームの概念が分かっていないので調べました。
　[ボリュームの利用 | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/storage/volumes/)
> ボリュームとは、Docker コンテナーにおいて生成され利用されるデータを、永続的に保持する目的で利用される仕組みです。

このように、システムを終了してもデータが消失しないような仕組みを設けることを**永続化**と呼ぶようです。(不揮発性メモリへの保存、回復可能にしておくなど)
　[永続性 - Wikipedia](https://ja.wikipedia.org/wiki/%E6%B0%B8%E7%B6%9A%E6%80%A7)
　[What Is Persistent Data? - DZone Database](https://dzone.com/articles/what-is-persistent-data)

では、volumeに設定されている``./tmp/db:/var/lib/postgresql/data``とはどういうディレクトリなのでしょうか。
[ボリュームの利用 | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/storage/volumes/)には、設定の書式として、``[SOURCE:]TARGET[:MODE]``という書式が適用されることが説明されています。
この書式にあてはめると、下記表の対応となります。

| 書式   | 記述例  | 意味 |
|:------|:-------|:----|
| [SOURCE:] | ./tmp/db: | ホストのパスあるいはボリューム名 |
| TARGET | /var/lib/postgresql/data | ボリュームがマウントされているコンテナのパス |
| [:MODE] | none | 読み込み専用``ro``, 読み書き可能``rw``. デフォルトは後者 |

つまり、コンテナから見たとき、``/var/lib/postgresql/data``の中身として、ホストOS上の``./tmp/db``を見ている状態になります。

これはこういうものだと、割り切ってしまうのもひとつかもしれません。
ただ、もう少し自分の中で具体的根拠を伴った意味付けが欲しいと思いました。

###### ``/var/lib/postgresql/data``とは何か

このディレクトリについては、PostgreSQLのリファレンスに説明がありました。

[データベースの物理的な格納 - PostgreSQL 8.0.4 文書](https://www.postgresql.jp/document/8.0/html/storage.html)
> ***データベースクラスタ***で必要となる全てのデータは、クラスタのデータディレクトリ内に格納され、通常 **PGDATA** として参照されます。 (**そのディレクトリを定義するために使用できる環境変数名です。**) 通常の PGDATA の位置は /var/lib/pgsql/data です。

データベースクラスタはPostgreSQL独自の用語らしいです。
　[第18回　データベースクラスタ - OSS-DB道場](https://oss-db.jp/dojo/dojo_18)
　[sql - What's a PostgreSQL "Cluster" and how do I create one? - Stack Overflow](https://stackoverflow.com/questions/12908205/whats-a-postgresql-cluster-and-how-do-i-create-one#:~:text=1%20Answer&text=A%20PostgreSQL%20database%20%22cluster%22%20is,contains%20one%20or%20more%20databases.&text=A%20cluster%20is%20normally%20created,a%20new%20cluster%20for%20you.)

PostgreSQLでは、1つのサーバインスタンス上に複数のデータベースを構成することができ、PostgreSQLで言う"クラスタ"とは、そうした1つ以上のデータベースの集合体を含む共有ディレクトリを管理する仕組みであると解釈しました。
そして、それらに関わるデータが格納される場所が``PGDATA``の環境変数で参照され、その保存先が通常では``/var/lib/pgsql/data``に指定してあると。

###### ``/var/lib/``という階層の意味 : ファイルシステム階層標準 (FHS)

他でもよく見る``/var/lib``についても意味を把握しておこうと思いました。
LinuxやUnix系のOSのディレクトリの階層は、ファイルシステム階層標準によって定められていることを知りました。
[Filesystem Hierarchy Standard - Wikipedia(JP)](https://ja.wikipedia.org/wiki/Filesystem_Hierarchy_Standard)
> Filesystem Hierarchy Standard（ファイルシステム・ヒエラルキー・スタンダード、FHS、ファイルシステム階層標準）は、Linuxを含むUNIX系オペレーティングシステムでの主なディレクトリとその内容を定めたものである。

2020-07現在の最新版はFHS 3.0で、下記リンクからHTMLやPDFなどの形式で閲覧することが出来ます。

- [FHS Referenced Specifications](https://refspecs.linuxfoundation.org/fhs.shtml)
 - [Chapter 5. The /var Hierarchy](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05.html)
     - [5.8. /var/lib : Variable state information](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s08.html)

``/var ``は、動的に変化する変数データファイルを格納するもので、、ネットワーク上で他のコンピュータと共有されないもの。
``/var/lib``は、アプリケーションやシステムに関連する状態情報を保持。

FHSの解説では、アプリケーションは``/var/lib``のサブディレクトリを使用しなければならない、とあるので、``/var/lib/pgsql``はその規則に則ったPostgreSQL用のデータ格納先であることが伺えます。

###### PostgreSQLの公式イメージにおける``/var/lib/postgresql/data``の扱い

[PostgreSQLの公式Dockerイメージ](https://github.com/docker-library/postgres)の``Dockerfile``を見てみます。
下記の２行に``/var/lib/postgresql/data``の記述を見つけました。([ディレクトリの生成](https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L21)も含めれば3行)
登場するDockerの``ENV``と``VOLUME``の命令は、この親投稿では触れなかったものなので、調べてみます。

https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L181

~~~Dockerfile:postgres/Dockerfile-debian.template#L181
ENV PGDATA /var/lib/postgresql/data
~~~

[#ENV | Dockerfile リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#env)
> 環境変数 ``<key>`` に ``<value>`` という値を設定します。

つまり環境変数``PGDATA``に``/var/lib/postgresql/data``を設定しています。
[PostgreSQLの公式リファレンス](https://www.postgresql.jp/document/8.0/html/storage.html)では、``pgsql``というデフォルトのディレクトリ名が、Dockerの公式イメージでは　``postgresql``となっています。

https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L184

~~~Dockerfile:postgres/Dockerfile-debian.template#L184
VOLUME /var/lib/postgresql/data
~~~

[#VOLUME | Dockerfile リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/#volume)
> VOLUME 命令は指定された名前を使ってマウントポイントを生成します。 そして自ホストまたは他のコンテナーからマウントされたボリュームとして、そのマウントポイントを扱います。

荒い解釈ではありますが、``/var/lib/postgresql/data``をめぐる流れとしては、下記のように整理できると思いました。
　1. ホストOSのローカルに``/var/lib/postgresql/data``を生成
　    [(postgres/Dockerfile-debian.template#L21)](https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L21)
　2. PostgreSQLがサービスのデータを参照するための環境変数``PGDATA``に``/var/lib/postgresql/data``を設定
　    [(postgres/Dockerfile-debian.template#L181)](https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L181)
　3. Composeの起動時にPostgreSQLのサービスが``/var/lib/postgresql/data``をマウントできるようにマウントポイントを生成
　    [(postgres/Dockerfile-debian.template#L184)](https://github.com/docker-library/postgres/blob/master/Dockerfile-debian.template#L184)
　4. ``$ docker compose run ~``でコンテナを起動したらマウントポイントの``/var/lib/postgresql/data``をマウントしてサービス側から使用できるようにする
　    (チュートリアルのdocker-compose.ymlのdb: volume:設定行)

##### environment:

~~~yml
environment:
  POSTGRES_PASSWORD: password
~~~

[#environment | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#environment)
環境変数を追加します。

#### web:
任意のサービスの名称として"db"が付けられています。
公式のサンプルでは、下位に``build``, ``command``, ``volumes``, ``ports``, ``depends_on``の設定項目が続きます。

~~~yml
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
~~~

##### build:

~~~yml
build: .
~~~

[#build | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#build)
> build の指定方法の 1 つは、**ビルドコンテキスト**へのパスを表わす文字列を指定します

ビルドコンテキスト？
[Docker Tips: All About the Build Context - Better Programming - Medium](https://medium.com/better-programming/docker-tips-about-the-build-context-dbc76505e178)
> The build context is the set of files located at the specified PATH or URL. Those files are sent to the Docker daemon during the build so it can use them in the filesystem of the image.

指定されたURLやPATHにあるファイル一式。これらのファイルはDockerのデーモンに送られビルド中にイメージのファイルシステムで使用できるようになる。
今回では、``web``サービスのイメージで``build``のPATHに``.``を指定しているので、 ``/var``, ``/myapp``などの全ての階層とファイルが``web``サービスイメージのファイルシステムで使用できるようになっています。

##### command:

~~~yml
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
~~~

[#command | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#command)
コンテナ起動時にデフォルトで実行されるコマンド。

###### bash -c

[bash(1): GNU Bourne-Again SHell - Linux man page](https://linux.die.net/man/1/bash)
``-c``は後に続く文字列をコマンドとして実行するので、``bash -c "<command>"``はBashシェルで<command>を実行する意味になります。

##### volumes:

~~~yml
volumes:
  - .:/myapp
~~~

再掲：[#volumes | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#volumes)
ホストOS上のパスである``.``を、コンテナ上の``/myapp``へ専用のボリュームとしてマウントします。

##### ports:

~~~yml
ports:
  - "3000:3000"
~~~

[#ports | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#ports)
公開するポートを``HOST:CONTAINER``で指定します。

##### depends_on:

~~~yml
depends_on:
  - db
~~~

[#depends_on | Compose ファイル バージョン 3 リファレンス | Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#depends_on)
サービスの依存関係を指定します。複数ある場合は順番に起動。
ただ、依存サービスの「準備」状態を待たずに当サービスの起動を実行するため、ちゃんと依存関係にあるサービスの「準備」を待ちたいなら、別途コマンドにより待機させる工夫が必要です。

---

## 後書き

[Docker + Raislの公式チュートリアル](https://matsuand.github.io/docs.docker.jp.onthefly/compose/rails/)については今回で一旦終了です。
DeepL頼みなことが多いですが、海外の記事やリファレンスを抵抗なく触れられるようになったことは、自身として少しは進歩しているのかなと感じます。

理解が荒い部分も多いので、このような調査とアウトプットを継続して、正しい理解に近づくように取り組んでいきます。

また、英語だと教育資料や初心者向けの教材なども簡単にヒットするので、様々な情報源から学びを得られるように独力で英文を読む能力も鍛えていきたいと思いました。

次は、CircleCIの設定ファイルについて学習します。