Docker コンテナの起動を待つ - MySQL の使い方 - [GitHub Actions]
概要
Docker コンテナを扱う際に起動を待ちたいケースは多々あると思います。
この記事では MySQL コンテナを例に3パターンと、 Docker ではないのですが MySQL の場合プレインストールもされているので併せて紹介します。
TL;DR
長々と説明していますが重要なポイントはこの2つです。
- Docker コンテナのヘルスチェック
- CLI から起動する場合は
until
やdocker compose up -d --wait
で待機
プレインストール
GitHub ホストランナーには MySQL や PostgreSQL がインストールされています。
最初は無効化されているので sudo systemctl start postgresql.service
で有効化します。
すぐに使える状態になるので起動時間は最速です。
jobs:
pre-installed:
runs-on: ubuntu-20.04
timeout-minutes: 5
env:
MYSQL_PWD: root
steps:
- run: sudo systemctl start mysql.service
- run: mysql -uroot -e "SHOW DATABASES"
サービスコンテナ
options
でコンテナのヘルスチェックを設定しておくと起動してから steps を実行してくれます。
ヘルスチェックの内容は各イメージに依存します。
ランナーマシン上で直接のジョブの実行
runs-on: ubuntu-20.04
で steps を実行します。
jobs:
service-container-on-host:
runs-on: ubuntu-20.04
timeout-minutes: 5
env:
MYSQL_PWD: root
services:
mysql-server:
image: mysql:8.0.28
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: ${{ env.MYSQL_PWD }}
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- run: mysql --protocol=tcp -uroot -e "SHOW DATABASES"
# - run: docker exec -e MYSQL_PWD ${CONTAINER_ID} mysql -uroot -e "SHOW DATABASES"
# env:
# CONTAINER_ID: ${{ job.services.mysql-server.id }}
Docker のネットワークを使わない通信になるのでちょっと強引です。
--protocol=tcp
を付ける必要があります。
付けていないと以下のエラーが発生します。
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
コメントで docker exec
でコンテナへアクセスする方法も記載していますが、通常はサーバーコンテナ外部からアクセスすると思うので参考程度に。
コンテナ内でのジョブの実行
container: mysql:8.0.28
で steps を実行します。
コンテナ内で実行すると同一ネットワークになり mysql-server:3306
(サービス名)でアクセスできるようになります。
jobs:
service-container-on-container:
runs-on: ubuntu-20.04
container: mysql:8.0.28 # 実際は Node.js や PHP などのアプリコンテナ
timeout-minutes: 5
env:
MYSQL_PWD: root
services:
mysql-server:
image: mysql:8.0.28
env:
MYSQL_ROOT_PASSWORD: ${{ env.MYSQL_PWD }}
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- run: mysql -hmysql-server -uroot -e "SHOW DATABASES"
Docker
Docker CLI も使えます。
コンテナのヘルスチェックを設定して until
で起動を待ちます。
jobs:
docker:
runs-on: ubuntu-20.04
timeout-minutes: 5
env:
MYSQL_PWD: root
steps:
- run: docker network create bridge-network
- run: |
docker run \
--name mysql-server \
-e MYSQL_ROOT_PASSWORD=${MYSQL_PWD} \
--network bridge-network \
-d \
--health-cmd "mysqladmin ping" \
--health-interval 10s \
--health-retries 5 \
--health-timeout 5s \
mysql:8.0.28
- name: Wait for starting
run: |
set -x
until [ "$(docker inspect --format='{{.State.Health.Status}}' mysql-server)" = 'healthy' ]; do
sleep 1s
done
# 実際は Node.js や PHP などのアプリコンテナ
- run: |
docker run \
--name mysql-client \
--rm \
-e MYSQL_PWD \
--network bridge-network \
mysql:8.0.28 \
mysql -hmysql-server -uroot -e "SHOW DATABASES"
# - run: docker exec -e MYSQL_PWD mysql-server mysql -uroot -e "SHOW DATABASES"
docker inspect --format='{{.State.Health.Status}}' mysql-server
でヘルスチェックの状態が確認できます。
healthy
になるまで until
で待機します。
Docker Compose
Docker Compose CLI も使えます。
コンテナのヘルスチェックを設定して until
--wait
オプションで起動を待ちます。
version: "3.9"
services:
mysql-server:
image: mysql:8.0.28
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_PWD}
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
interval: 10s
timeout: 5s
retries: 5
mysql-client: # 実際は Node.js や PHP などのアプリコンテナ
image: mysql:8.0.28
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_PWD}
jobs:
docker-compose:
runs-on: ubuntu-20.04
timeout-minutes: 5
env:
MYSQL_PWD: root
steps:
- uses: actions/checkout@v3
- run: docker compose up -d --wait
# - name: Wait for starting
# run: |
# set -x
# until [ "$(docker inspect --format='{{.State.Health.Status}}' $(docker compose ps -q mysql-server))" = 'healthy' ]; do
# sleep 1s
# done
- run: docker compose exec -T -e MYSQL_PWD mysql-client mysql -hmysql-server -uroot -e "SHOW DATABASES"
# - run: docker compose exec -T -e MYSQL_PWD mysql-server mysql -uroot -e "SHOW DATABASES"
docker compose には inspect がないので docker compose ps -q mysql-server
でコンテナ ID を取得して docker inspect に渡しています。
Docker Compose V2 では --wait
オプションが追加されてシンプルに書けるようになりました。
docker compose
の部分は旧コマンドの docker-compose
(ハイフンあり)でも動きます。
exec だけ -e MYSQL_PWD=${MYSQL_PWD}
に変更してあげてください。
--wait
オプションは使えないのでコメントアウトしている until
を使用してください。
どれを使うべき?
サービスコンテナだと GitHub Actions に依存してしまうのでローカル環境の構築が難しいです。
当然ローカルでは別途 Docker コマンドを利用して構築こともできますが CI と同じ環境を構築できていることを保証できませんしオプションが増えてきたりすると大変です。
Docker Compose を使うとネットワーク等を含めて定義できるのでローカルでも CI でも同じ環境で実行しやすいのでおすすめです。
起動時間を気にする場合はプレインストールされているものを使ってもよいと思いますが、バージョンが GitHub ホストランナーに依存します。
各ジョブの実行時間はこんな感じでした。
補足
コードの簡略化のため root ユーザーを使用していますが、本来は別途ユーザーを作成した方が良いです。
root ユーザーだと本番で運用するユーザーが参照できないテーブルも参照できてしまってテストにならない可能性があるので本番で運用するユーザーと同じ権限にしておきましょう。
使い捨て環境のためパスワードはべた書きしています。
気になるようでしたら Secrets を使用するとよいかと思います。
Discussion
こんにちは
mysqladmin ping は 実際はhealth checkに使えないようです。