はじめに
プログラミング学習コミュニティ「progaku」で行っているチーム開発で取り組んだ内容になります。
コンテナの実行順序の制御が必要になった場面
Dockerで環境構築を行い、初めてDB作成のコマンド実行する際に以下のようなエラーが発生
docker compose run backend rails db:create
エラー内容
[+] Building 0.0s (0/0) docker:desktop-linux
[+] Creating 3/0
✔ Network [ネットワーク名] Created 0.0s
✔ Volume [ボリューム名] Created 0.0s
✔ Container pa_database Created 0.0s
[+] Running 1/1
✔ Container pa_database Started 0.1s
[+] Building 0.0s (0/0) docker:desktop-linux
Can't connect to server on 'db' (115)
Couldn't create 'app_development' database. Please check your configuration.
rails aborted!
ActiveRecord::ConnectionNotEstablished: Can't connect to server on 'db' (115)
Caused by:
Mysql2::Error::ConnectionError: Can't connect to server on 'db' (115)
Tasks: TOP => db:create
(See full trace by running task with --trace)
原因
[+] Running 1/1 ✔ Container pa_database Started
ここでデータベースは起動されているけど
Mysql2::Error::ConnectionError: Can't connect to server on 'db' (115)
データベースに接続ができなかったと言われている
docker-compose.ymlは以下の通り
version: "3"
services:
backend:
container_name: pa_backend
build:
context: .
dockerfile: ./infra/backend/Dockerfile
volumes:
- ./backend:/usr/src/app
stdin_open: true
tty: true
env_file:
- ./infra/env/backend.env
ports:
- 3000:3000
command: sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
depends_on:
- db
db:
image: mysql:8.0
platform: linux/amd64
container_name: pa_database
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
volumes:
db-data:
driver: local
調べるとデータベースは起動してから設定ファイルの読み込み等行うため、実際に接続完了となるまでに少し時間がかかるようです。
depends_onの仕様を理解する
ただ、現在すでに
「depends_on」オプションを指定してサービス間の起動順序を指定するようになっています。
depends_on:
- db
depends_onはサービス間の依存関係を設定するものです。
上記の例ではbakendのコンテナはdbのコンテナに依存しています。
なので
- db
- backend
の順番に実行されるようになっています。
では何故、接続に失敗してしまうのか?
調べてみるとドキュメントにまさに注意点として書かれていました。笑
depends_on 使用時に注意すべき点
depends_on では、 web を開始する前に db と redis の「準備」が整うのを待ちません。単に、順番通り開始するだけです。
condtionオプション、healthcheckで実行順序を制御
準備が整うまでに待たせるために、depends_onオプションには
conditionというサブオプションがあります。
condtionの仕様
condition :依存関係を満たしているとみなす状態
service_started :前述の短い構文のものと同等
service_healthy :依存先のサービスを起動する前に、依存元のサービスが「 正常healthy 」( healthcheck で示す)な状態を指定
service_completed_successfully :依存先のサービスを起動する前に、依存元のサービスは正常に実行済みの状態を指定
service_healthyを指定し、条件を指定することで、その条件を満たすまで待機させることができるようです。
helthcheckの仕様
healthcheck
このサービスのコンテナが「 正常healthy 」かどうかを判断するために実行する、確認用コマンドを設定します。
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
helthcheckは依存先の方、今回で言うと「db」コンテナの方に書きます。
各項目の意味ですが
test = 確認用のコマンドを指定
interval = 確認コマンドを実行する間隔を指定
timeout = タイムアウト時間を指定
retries = リトライ、つまり再実行を行う数を指定
start_period = helthcheck開始後、失敗を無視する期間を指定
となっています。
今回はmysqlが接続できる状態になっていることを確認できれば良いのでそのコマンドを指定する必要があります。
mysqlではping
というコマンドで接続可能かの確認ができるようです
ping
サーバーが使用可能かどうかをチェックします。 サーバーが稼働中の場合は mysqladmin のリターンステータスは 0 になり、稼働していない場合は 1 になります。 Access denied のようなエラーの場合でも 0 となります。これは、サーバーは稼働しているが接続を拒否したことを意味しており、サーバーが稼働していない状態とは異なるからです。
設定を反映
version: "3"
services:
backend:
container_name: pa_backend
build:
context: .
dockerfile: ./infra/backend/Dockerfile
volumes:
- ./backend:/usr/src/app
stdin_open: true
tty: true
env_file:
- ./infra/env/backend.env
ports:
- 3000:3000
command: sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
platform: linux/amd64
container_name: pa_database
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "db", "-u", "root", "-proot"]
interval: 5s
timeout: 10s
retries: 5
volumes:
db-data:
driver: local
変更点
backend側にconditionオプションを追加しました。
service_healthyを指定
depends_on:
db:
condition: service_healthy
db側にはhealthcheckオプションを追加
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "db", "-u", "root", "-proot"]
interval: 5s
timeout: 10s
retries: 5
CMDは「,」区切りで書くようです。
とりあえず
intervalは5秒感覚
timeoutは10秒
retriesは5回
に設定しました。
再度実行
docker compose run backend rails db:create
[+] Building 0.0s (0/0) docker:desktop-linux
[+] Creating 1/1
✔ Container pa_database Recreated 0.8s
[+] Running 1/1
✔ Container pa_database Started 0.1s
[+] Building 0.0s (0/0) docker:desktop-linux
Created database 'app_development'
無事、接続可能状態になるまで待機して、db:createコマンドを実行することができました。
まとめ
以上になります。
何となくではなく1つひとつのコマンド、処理の仕様を理解することの大切さを改めて学びました。焦ってとりあえず動いたら良いで進めてしまいがちになってしまうので気をつけていきたいです。
初学者なので、間違っている点などあれば教えていただけると幸いです。