[備忘録]自作アプリへの理解を深める

### 注意）この記事について
この記事は、私自身が作成したWebアプリについて自身で振り返り、理解を深めるためのものです。基本的には個人的な備忘録として執筆したものですので、ご了承ください。

作成したwebアプリ

https://topic-board.jp/

# 備忘録 Docker#2
### docker-compose.yml


```yml
version: '3'
services:
  db:
    container_name: postgresql_db
    image: postgres:16
    restart: always
    environment:
      TZ: Asia/Tokyo
      POSTGRES_PASSWORD: password
    volumes:
      - postgresql_data:/var/lib/postgresql
    ports:
      - 5432:5432
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d myapp_development -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
  web:
    container_name: rails_app
    build:
      context: .
      dockerfile: Dockerfile.prod
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    volumes:
      - .:/myapp
      - bundle_data:/usr/local/bundle:cached
      - node_modules:/myapp/node_modules
    environment:
      TZ: Asia/Tokyo
      SELENIUM_DRIVER_URL: http://chrome:4444/wd/hub
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy
  chrome:
    image: seleniarm/standalone-chromium:latest
    ports:
      - 4444:4444
  redis:
    image: "redis:7.0-alpine"
    volumes:
      - redis_volume:/data
    command: redis-server --appendonly yes
    ports:
      - 6379:6379

volumes:
  redis_volume:
  bundle_data:
  postgresql_data:
  node_modules:
```

###解説

`version: '3'`
- Composeファイルのバージョンを指定している。バージョンによって使用できるオプションが異なる。
- なお、`docker-compose.yml`ファイルの拡張子は`yml`,`yaml`のどちらも利用できる。

https://docs.docker.jp/compose/compose-file/compose-versioning.html#:~:text=Edit%20on%20GitHub-,Compose%20%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%A8%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89,-%E7%9B%AE%E6%AC%A1

`services`
・作成するサービスを定義する。サービスとはコンテナの集まりのこと。
・ここでは`db`,`web`,`chrome`,`redis`という４つのサービスを定義する。

> services トップレベル要素

https://docs.docker.jp/compose/compose-file/index.html#services:~:text=I%27m%20running%20%24%7BCOMPOSE_PROJECT_NAME%7D%22-,services%20%E3%83%88%E3%83%83%E3%83%97%E3%83%AC%E3%83%99%E3%83%AB%E8%A6%81%E7%B4%A0,-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9service%20%E3%81%A8

```yml
db:
  container_name: postgresql_db
  image: postgres:16
  restart: always
  environment:
    TZ: Asia/Tokyo
    POSTGRES_PASSWORD: password
  volumes:
    - postgresql_data:/var/lib/postgresql
  ports:
    - 5432:5432
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -d myapp_development -U postgres"]
    interval: 10s
    timeout: 5s
    retries: 5
```
`db`
- データベースの定義をする。

`container_name: postgresql_db`
- デフォルトで生成される名前ではなく、任意のコンテナ名を指定する。
- Composeファイルでcontainer_nameを指定している場合、Compose実装は、コンテナ1つよりも多くにサービスをスケールさせてはいけない。container_name は一意でなければならないため、2つのコンテナで同じ名前を使えない。スケールが必要な場合はcontainer_nameは指定しない。
#### スケール

https://7me.nobiki.com/2019/06/26/docker-compose-scale-container/#:~:text=docker%2Dcompose%E3%81%A7%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%E3%81%95%E3%81%9B%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AE%E3%83%A1%E3%83%A2

- 正規表現に従った命名にしなければならない。
- 名前を指定すると`docker ps`コマンドなどでコンテナを確認する場合にわかりやすい。

`image`
- Docker公式のイメージを指定する。

`restart: always`
- コンテナが終了した際の動作を指定する。
- `always`により、コンテナを削除するまで、常に再起動する。

https://docs.docker.jp/compose/compose-file/index.html#restart:~:text=restart%20%E3%81%AF%E3%80%81%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%B5%82%E4%BA%86%E6%99%82%E3%81%AB%E3%80%81%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%8C%E9%81%A9%E7%94%A8%E3%81%99%E3%82%8B%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

- なお、手動でコンテナを停止した場合は自動で再起動はしないらしい。
>Always restart the container if it stops. If it's manually stopped, it's restarted only when Docker daemon restarts or the container itself is manually restarted. 

https://docs.docker.com/engine/containers/start-containers-automatically/#use-a-restart-policy

```
environment:
  TZ: Asia/Tokyo
  RAILS_ENV: production
  POSTGRES_PASSWORD: <%= Rails.application.credentials.dig(:db, :password) %>
```
`environment`
- コンテナ内での環境変数を定義する。

 https://qiita.com/kujirahand/items/3b96b99c9d91ad0a6882

https://docs.docker.jp/compose/compose-file/index.html#environment:~:text=environment%20%E3%81%AF%E3%80%81%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E5%86%85%E3%81%A7%E3%81%AE%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

`TZ: Asia/Tokyo`
- タイムゾーンを指定する。ここではアジア/東京を指定する。
- TZを設定しない場合、基本的にはUTCとなる。（ベースイメージによって異なるらしい）

`POSTGRES_PASSWORD`
- PostgreSQLインスタンスのパスワードを定義する。
- postgresをdbとして使用する場合、パスワードの設定が最低限必要になる。なお、本番環境では`credentials`等を利用して機密にすること。

>The PostgreSQL image uses several environment variables which are easy to miss. The only variable required is POSTGRES_PASSWORD, the rest are optional.

https://github.com/docker-library/docs/blob/master/postgres/README.md#:~:text=have%20found%20useful.-,Environment%20Variables,-The%20PostgreSQL%20image

`volumes`
- ボリュームについては後述する。

`port`
- 起動したコンテナのポートとホストのポートにポートフォワーディングする。
#### ポートフォワーディング

https://qiita.com/tatsuo-iriyama/items/e4bf2404411343116e3e#:~:text=Docker%E3%81%A7%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AF%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E8%A7%A3%E8%AA%AC

- スケールして複数のコンテナを立てる場合、ホスト側の同じポートを指定するとエラーになるため、コンテナ側のポートのみ指定する。

`healthcheck`
- このサービスが正常かどうかを確認する。
- 後述するが、依存する側のサービスに`depends_on`で依存先のサービスの状態を指定することで、サービスの起動順序を指定できる。依存先のサービスが正常に起動できていない場合、以下のエラーが発生する。
`dependency failed to start: container dbコンテナ名 is unhealthy`

https://docs.docker.jp/compose/compose-file/index.html#id106

https://zenn.dev/sun_asterisk/articles/b4b17681d08018

```yml
  web:
    container_name: rails_app
    build:
      context: .
      dockerfile: Dockerfile.prod
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    volumes:
      - .:/myapp
      - bundle_data:/usr/local/bundle:cached
      - node_modules:/myapp/node_modules
    environment:
      TZ: Asia/Tokyo
      RAILS_ENV: production
      SELENIUM_DRIVER_URL: http://chrome:4444/wd/hub # Capybaraによるテストのための設定
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy

```
`build`
- `docker compose build`実行時の設定。
- `context: .`でbuildするディレクトリを`docker-compose.yml`と同じカレントディレクトリに指定する。以下のdockerfileのディレクトリは、ここで設定したディレクトリをルートディレクトリとする。
- `dockerfile: Dockerfile.prod`で、buildに使用するDockerfileを`Dockerfile.prod`に指定する。

https://qiita.com/sam8helloworld/items/e7fffa9afc82aea68a7a

`tty: true`
- 仮想端末を用意してコンテナを持続させるための設定。

`studin_open`
- サービスコンテナに標準入力を割り当てて実行するよう設定する。

https://zenn.dev/hohner/articles/43a0da20181d34#:~:text=%E3%82%89%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82-,TTY%E3%81%A3%E3%81%A6%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%82%E3%82%8B%E3%81%AE%EF%BC%9F,-tty%3Atrue%E8%A8%AD%E5%AE%9A

https://utouto97.hatenablog.com/entry/2022/03/15/092924

https://docs.docker.jp/compose/compose-file/index.html#id146

`volumes`
- ボリュームを設定することで、コンテナを削除後もデータを保存できる。（永続化）
- コンテナ内部にファイル等のデータを保存しても、コンテナを破棄するとデータごと消えてしまう。なので、データを永続化したい場合には、コンテナの外に保存する必要がある。その場所のことをボリュームと呼ぶ。

https://qiita.com/Hirochon/items/121f172fbbae8c8ad749#:~:text=%E8%B6%B3%E3%81%8C%E3%81%8B%E3%82%8A%E3%81%AB%E3%81%AA%E3%82%8B-,%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%B0%B8%E7%B6%9A%E5%8C%96%E3%81%99%E3%82%8B%E3%81%A8%E3%81%AF,-%E7%AA%81%E7%84%B6%E3%81%A7%E3%81%99%E3%81%8C%E3%80%81%E8%B2%B4%E9%87%8D

- `docker compose up`を初めて実行した際にボリュームが作成され、続く実行でも同じボリュームが再利用される。

https://docs.docker.jp/storage/volumes.html#id25

#### データ永続化の手法について
ボリュームマウント
- 名前付きボリュームと無名ボリュームがある。
- 無名ボリュームはコンテナ作成と同時に自動生成されるが、名前がないので判別が困難で、再利用性は低い。

```yml
# docker-compose.ymlでの記述例
services:
  web:
    volumes:
      - node_modules:/myapp/node_modules # 外で定義したボリューム名:コンテナの絶対Path

volumes:
  node_modules: # ボリューム名を定義
```
バインドマウント
- ホストマシン上のファイルやディレクトリがコンテナ内にマウントされる。
- コンテナ側で予めファイルやディレクトリを準備する必要がないため、コンテナ削除後もローカルにデータがあれば、同様に使用できる。
- ローカルでのファイルの変更がコンテナに反映される。
```yml
# docker-compose.ymlでの記述例
services:
  web:
    volumes:
      - .:/myapp # ホスト側の(docker-compose.ymlからの)相対Path:コンテナの絶対Path
```

https://qiita.com/mtgto/items/aabc212a1d3f99878828

https://docs.docker.jp/storage/index.html

https://zenn.dev/ajapa/articles/1369a3c0e8085d

`depends_on`
・`db（サービス名）`と`condition: service_healthy`を指定することで、`db`というサービスが正常（healty）に起動した場合のみ、このサービスを起動する。これによりサービスの起動順序を指定できる。