🌊
GithubActionsでdockerのビルドをキャッシュしてテストを動かす
GithubActions内で、docker composeを使ってテストを動かそうとすると、毎回Dockerfileをビルドせねばならず、ビルドに時間が掛かってしまいます。
そのため、ビルドファイルをキャッシュしておいて、Dockerfile
に変更が無いなら、キャッシュをimportして使うようにするという、そんな設定ファイルです。
細かくコメントを書いたyamlファイルを用意したので、見てもらえればと思います。
(おまけで、DBサーバの起動待ちでハマったので、リトライによる回避方法も書いておきました。)
また、step名の先頭と最後に✨を付けていますが、ログを見る時に自分の処理とGithubActions側の処理が一発で見分けられて良いので、何かしら絵文字を使ってみるのもおすすめします。
# GithubActionsの名前
name: hoge
# 動かすタイミングの設定
on:
workflow_dispatch: # 手動実行できるようにする
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Githubの利用権限設定(contentsがないとチェックアウトすら動かないなど)
permissions:
id-token: write
contents: read
actions: read
# GithubActions内で使える環境変数の設定
env:
IMEGE_CACHE_DIR: /tmp/docker-img
IMAGE_NAME: aaa:latest
# ここからが実際の処理の流れ
jobs:
Tests:
runs-on: ubuntu-latest
steps:
# リポジトリをチェックアウトする設定
- name: ✨ actions/checkout@v3 ✨
uses: actions/checkout@v3
# キャッシュディレクトリの復元(保存処理は実行完了後に後処理で行われる)
# actions/cache@v3は、ディレクトリ単位でキャッシュする仕組みとなる
# そのため、ディレクトリにゴミが溜まっていくようだと、キャッシュサイズが肥大化していくため、定期的に削除する必要がある。
# このサンプルでは、ファイル名固定のため、上書きとなり、キャッシュサイズが肥大化することはないため、削除処理は入れていない。
- name: ✨ actions/cache@v3 docker dir ✨
# キャッシュを複数使っている場合、キャッシュごとに復元可否判定を行えるように、idを指定する
# 今回のサンプルでは、不要かもしれないが、書いておいた方が良い。
id: image-cache-id
uses: actions/cache@v3
with:
# 保存するディレクトリ名を指定する
path: ${{ env.IMEGE_CACHE_DIR }}
# keyに、Dockerfileのhash値を含めている
# そのため、Dockerfileに変更があったらkeyに該当するキャッシュが無いと判定され、復元されず、新たにビルドされる
key: ${{ runner.os }}-docker-image-${{ hashFiles('./Dockerfile') }}
# envだと動的な名称設定ができないため、ここで作成して$GITHUB_ENVに書き込んでいる
- name: ✨ イメージキャッシュファイル名をフルパスで作成 ✨
run: echo "FILE_PATH=${{env.IMEGE_CACHE_DIR}}/aaa-latest.tar" >> "$GITHUB_ENV"
- name: ✨ イメージキャッシュがある場合、キャッシュからイメージをロードする ✨
# steps.image-cache-idの部分が、ターゲットを指定している部分
if: steps.image-cache-id.outputs.cache-hit == 'true'
# イメージのloadは、下記のコマンドで行える
run: docker load --input ${{ env.FILE_PATH }}
- name: ✨ イメージキャッシュが無い場合、ビルドし、作成したイメージをキャッシュディレクトリに出力する ✨
if: steps.image-cache-id.outputs.cache-hit != 'true'
# イメージのbuildとディレクトリへのファイルアウトプットは、下記のコマンドで行える
run: |
mkdir -p ${{ env.IMEGE_CACHE_DIR }}
docker build . --file ./Dockerfile --tag ${{ env.IMAGE_NAME }}
docker save --output ${{ env.FILE_PATH }} ${{ env.IMAGE_NAME }}
- name: ✨ docker compose up -d ✨
run: |
docker compose up -d
- name: ✨ DBの起動を待ってmigrationとかする ✨
run: |
# docker-composeのlinksやdepends_onを使えば、コンテナの起動順を制御することはできるが、コンテナ内サーバなどの起動を待つことはできない
# そのため、sleepとリトライで待つ処理を入れた(wait-for-it または dockerize を入れれば待てるらしいが、確認はしていない)
# また、実行エラーで処理が止まってしまうため、set +eで、一時的にエラーオプションを外している
# さらに、まれにだが、mysqlのselect実行が、失敗したのにリトライにならず次に進んでしまうことがあるため、3行書いている。
set +e
MAX_RETRY=10
INTERVAL=1
for i in $(seq 1 $MAX_RETRY); do
echo "$i 回目"
docker exec -e MYSQL_PWD=password aaa-mysql mysql -uroot db_name -e 'select 1;'
docker exec -e MYSQL_PWD=password aaa-mysql mysql -uroot db_name -e 'select 1;'
docker exec -e MYSQL_PWD=password aaa-mysql mysql -uroot db_name -e 'select 1;' && break
sleep $INTERVAL && /bin/false
done
if [ $? -eq 0 ]; then
echo "migration実行"
else
echo "ギブアップ!"
exit 1
fi
# エラーオプションを戻す(セクションごとにリセットであれば、要らないはずだが、確認していないため書いとく)
set -e
# サンプルなのでechoを出力するだけ
- name: ✨ テストの実行 ✨
run: docker exec aaa echo "テスト実行"
# 上記の実行が正常終了すれば、この後にキャッシュディレクトリの保存処理が実行される
# 復元するディレクトリにゴミが溜まっていくのであれば、ここで削除すると良い
Discussion