Github Actions上でキャッシュを効かせてDocker Composeを実行する
Docker Composeは複数のDockerコンテナを効率的に管理・運用するためのツールです。ローカルでの開発ではもちろん、CI/CD環境でのテスト実行にも利用されます。この記事では、Github Actions上でDocker Composeをキャッシュを効かせて実行する方法について解説します。
1. Github ActionsでのDocker Composeの利点
Docker Composeを使用することで、データベースなどの依存サービスを持つアプリケーションのテストが簡単になります。Github Actionsでもこの利点はそのまま活かせます。
今回は、データベースにMySQLを利用し、Go言語で書かれたプログラムをテストするケースを紹介します。
Goのコードとテストは、DBに接続してpingを送るだけの内容となっています。
package main
import (
"fmt"
"os"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func ping() (bool, error) {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/?charset=utf8mb4&parseTime=true&loc=Local",
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"))
conn, err := gorm.Open(mysql.Open(dsn))
if err != nil {
return false, err
}
db, err := conn.DB()
if err != nil {
return false, err
}
if err := db.Ping(); err != nil {
return false, err
}
return true, nil
}
package main
import "testing"
func TestPing(t *testing.T) {
_, err := ping()
if err != nil {
t.Fatal(err)
}
}
2. Github ActionsでのDocker Composeの問題点
毎回のビルドでDocker Composeを使用すると、同じ手順でのコンテナのビルドが繰り返されるため、ビルドに多くの時間がかかってしまいます。これは特に大規模なプロジェクトや複数のサービスを持つアプリケーションで顕著です。
そこで、キャッシュ機能を活用し、ビルド時間の短縮を行います。キャッシュを利用したビルドを行うために、Buildxというツールを利用します。
3. Buildxとは?
https://matsuand.github.io/docs.docker.jp.onthefly/buildx/working-with-buildx/
Buildxは、Dockerのプラグインの一つで、キャッシュ機能やマルチプラットフォームのビルドをサポートしています。この機能を利用することで、前回のビルド結果を再利用し、ビルド時間を大幅に短縮することができます。
Docker ComposeとBuildxのビルド
Docker ComposeからBuildxのコマンドを実行できますが、細やかな引数をしていできることができません。そこで、Github Actionsでは、Buildxを直接使用してビルドを行い、作成されたDockerイメージをDocker Composeで使用するように設定します。
4. Github ActionsでのBuildxとキャッシュの設定
では、Github ActionsでBuildxを利用してキャッシュを有効にする方法を解説します。以下は、Github Actionsワークフロー設定となります。
name: Test
on:
push:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# 1. Buildxのセットアップ
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
# 2. キャッシュ設定
- name: Cache Docker layers - App
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache-app # Buildxのキャッシュを指定
key: ${{ github.ref }}-${{ github.sha }} # キャッシュのキーを指定
restore-keys: |
${{ github.ref }}
refs/head/main
- name: Cache Docker layers - DB
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache-db # Buildxのキャッシュを指定
key: ${{ github.ref }}-${{ github.sha }} # キャッシュのキーを指定
restore-keys: |
${{ github.ref }}
refs/head/main
# 3. Buildxでイメージをビルドする
- name: Build images - App
uses: docker/build-push-action@v4
with:
push: false
builder: ${{ steps.buildx.outputs.name }} # Buildxでビルドすることを指定
tags: testapp-app-cached:latest # イメージ名を指定/docker-compose.ymlで参照する名前
load: true
context: .
file: ./docker/app/Dockerfile
cache-from: type=local,src=/tmp/.buildx-cache-app # キャッシュを指定
cache-to: type=local,dest=/tmp/.buildx-cache-new-app # キャッシュを指定
- name: Build images - DB
uses: docker/build-push-action@v4
with:
push: false
builder: ${{ steps.buildx.outputs.name }} # Buildxでビルドすることを指定
tags: testapp-db-cached:latest # イメージ名を指定/docker-compose.ymlで参照する名前
load: true
context: ./docker/db
cache-from: type=local,src=/tmp/.buildx-cache-db # キャッシュを指定
cache-to: type=local,dest=/tmp/.buildx-cache-new-db # キャッシュを指定
# 4. docker composeビルド/起動
- name: docker compose build & up
run: |
docker compose -f docker-compose.ci.yml build
docker compose -f docker-compose.ci.yml up -d
# 5. テスト実行
- name: Test
run: |
docker compose -f docker-compose.ci.yml run --rm app go test -v ./...
# 6. 肥大化対策
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
- name: Update cache
run: |
rm -rf /tmp/.buildx-cache-app
rm -rf /tmp/.buildx-cache-db
mv /tmp/.buildx-cache-new-app /tmp/.buildx-cache-app
mv /tmp/.buildx-cache-new-db /tmp/.buildx-cache-db
1. Buildxのセットアップ
こちらでは、 docker/setup-buildx-action@v1
を利用して、Buildxのセットアップを行っています。後ステップで、この結果を参照するため、 id: buildx
を設定しています。これにより、 steps.buildx.outputs
で、参照することができるようになります。
2. キャッシュ設定
actions/cache@2
を使用して、キャッシュの設定を行っています。アプリコンテナ用とDBコンテナ用の2つを設定しています。 path
ではキャッシュを保存するディレクトリを指定してます。 key
はキャッシュを特定するキーを指定しています。 restore-keys
では、 key
の値でキャッシュがヒットしなかった場合に参照する「キャッシュキーのプレフィクス」を指定してます。
ここで、 key
と restore-keys
の働きを整理します。 key
は、github.ref と github.sha の組み合わせで指定してます。そのため、pushするタイミングで、 key
の値が変化し、キャッシュがヒットしません。代わりに restore-keys
が参照され、前に作成されたキャッシュが参照されます。この流れで、以前のビルド結果が再利用されることになります。
3. Buildxでイメージをビルドする
docker/build-push-action@v4
を利用して、Buildxを使用したビルドを行っています。
builder
では、前述の steps.buildx.outputs.name
を指定してます。
tags
では、Docker Composeで参照するイメージ名を指定しています。
cache-from
cache-to
では、キャッシュを指定指定しています。 cache-to
では、後述する理由で、キャッシュ名を新しい名前に指定しています。
4. docker composeビルド/起動
docker-compose.ci.yml を以下のように記述しています。
version: '3'
services:
db:
image: testapp-db-cached # Buildxのビルド時に指定したイメージ名
build:
context: ./docker/db
dockerfile: Dockerfile
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u$MYSQL_USER -p$MYSQL_PASSWORD
app:
image: testapp-app-cached # Buildxのビルド時に指定したイメージ名
build:
context: ./
dockerfile: ./docker/app/Dockerfile
depends_on:
db:
condition: service_healthy
environment:
- DB_HOST=db
- DB_PORT=3306
- DB_USER=test
- DB_PASSWORD=test
コメントを付けている部分で、Buildxを使用してビルドしたDockerイメージを指定しています。
5. テスト実行
ビルドが完了している状態なので、ここではGoのテストコマンドを実行しているだけとなります。
6. 肥大化対策
Buildxのビルドに不具合がある関係で、キャッシュの読み書きが同一のディレクトリの場合、肥大化してしまうそうです。そのため、ここでは、新旧のキャッシュを置き換えるようにしています。
キャッシュの確認
上記の設定で、Githubにpushすると、Github Actionsのワークフローが実行されます。
Github上のUIで作成されたキャッシュを確認することができます。
Goコンテナ用とMySQLコンテナ用のキャッシュが2つ作成されていることが確認できます。
また、2回目以降のワークフローのログを見ると、Dockerのビルド時に CACHED
という表記が確認できます。
今回の例では、アプリ自体はシンプルなものにしたので、ビルド時間が大幅に改善するわけではありませんが、実プロジェクトで同じ設定を適用したところ、数分の時間短縮の改善が見られました。
CIではなるべく実行時間を短くしたいので、ぜひキャッシュを設定したいですね。
5. まとめ
Github ActionsでDocker Composeをキャッシュを効かせて使用することで、テストの実行時間を短縮することができます。特に大規模なプロジェクトや多くのサービスを持つアプリケーションでの利用は、非常に効果的です。Buildxを使用することで、さらなるビルドの高速化を実現することができます。