はじめに#
手動でDockerイメージをビルドしてレジストリにプッシュする作業を、毎回繰り返していませんか。本番環境への反映が遅れたり、タグ付けを間違えたりといったヒューマンエラーは、開発チームの生産性を大きく損なう原因となります。
CI/CDパイプラインにDockerを組み込むことで、コードのプッシュからイメージのビルド、テスト、レジストリへのプッシュまでを完全に自動化できます。本記事では、GitHub ActionsとDockerを連携させ、自動化されたビルド・デプロイパイプラインを構築する方法を体系的に解説します。
この記事を読み終えると、以下のことができるようになります。
- GitHub ActionsでDockerイメージを自動ビルドできる
- Docker Hubへのプッシュを自動化できる
- ARM/AMD64のマルチプラットフォームビルドを実行できる
- 効果的なイメージタグ戦略を設計・実装できる
前提として、GitHubリポジトリとDocker Hubアカウントを所持していること、Dockerfileの基本を理解していることを想定しています。
CI/CDパイプラインにおけるDockerの役割#
まず、CI/CDパイプラインでDockerがどのような役割を果たすのかを理解しておきましょう。
従来のデプロイ課題とDockerによる解決#
従来のデプロイでは、以下のような課題がありました。
| 課題 |
説明 |
| 環境差異 |
開発・ステージング・本番で動作が異なる |
| 依存関係の管理 |
各環境でのライブラリバージョン管理が煩雑 |
| デプロイの再現性 |
同じ手順でも結果が異なることがある |
| ロールバックの困難さ |
以前の状態に戻すのに時間がかかる |
Dockerコンテナを使うことで、これらの課題を解決できます。アプリケーションとその依存関係をイメージとしてパッケージ化することで、どの環境でも同じ動作を保証できます。
CI/CDにおけるDockerワークフロー#
CI/CDパイプラインでのDockerの典型的なワークフローは以下のとおりです。
flowchart LR
Push["コード\nプッシュ"] --> Build["ビルド\nイメージ\n作成"]
Build --> Test["テスト\n実行"]
Test --> Tag["タグ付け\n署名"]
Tag --> Registry["レジストリ\nプッシュ"]
Registry --> Deploy["本番\nデプロイ"]このワークフローをGitHub Actionsで自動化することで、コードをプッシュするだけで本番環境へのデプロイまでを一気通貫で実行できます。
GitHub ActionsでのDockerイメージビルド#
GitHub Actionsは、GitHubが提供するCI/CDプラットフォームです。Dockerイメージのビルドに必要な公式アクションが豊富に用意されており、簡単にパイプラインを構築できます。
Docker公式GitHub Actions#
Dockerは以下の公式アクションを提供しています。
| アクション |
用途 |
docker/login-action |
レジストリへのログイン |
docker/setup-buildx-action |
BuildKitビルダーのセットアップ |
docker/setup-qemu-action |
マルチプラットフォームビルド用QEMUのセットアップ |
docker/build-push-action |
イメージのビルドとプッシュ |
docker/metadata-action |
タグとラベルの自動生成 |
基本的なビルドワークフロー#
最もシンプルなDockerイメージビルドのワークフローを見てみましょう。リポジトリのルートに.github/workflows/docker-build.ymlを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
name: Docker Build
on:
push:
branches: ['main']
pull_request:
branches: ['main']
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: myapp:latest
|
このワークフローのポイントは以下のとおりです。
onでトリガー条件を定義(mainブランチへのプッシュとPR)
actions/checkout@v4でリポジトリをチェックアウト
docker/setup-buildx-action@v3でBuildKitを有効化
docker/build-push-action@v6でイメージをビルド(push: falseでプッシュせずビルドのみ)
ビルドキャッシュの活用#
CI/CDでのビルド時間を短縮するには、キャッシュの活用が重要です。GitHub Actionsのキャッシュ機能と連携させることで、レイヤーキャッシュを再利用できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
name: Docker Build with Cache
on:
push:
branches: ['main']
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
|
cache-fromとcache-toにtype=ghaを指定することで、GitHub Actionsのキャッシュストレージを利用できます。mode=maxを指定すると、すべてのレイヤーをキャッシュします。
Docker Hubへの自動プッシュ設定#
ビルドしたイメージをDocker Hubへ自動的にプッシュする設定を行います。
Docker Hubアクセストークンの作成#
まず、Docker Hubでアクセストークンを作成します。
- Docker Hubにログインし、Account Settings > Security に移動
- New Access Token をクリック
- トークン名を入力し、Read & Writeの権限を選択
- 生成されたトークンをコピー
GitHubシークレットの設定#
次に、GitHubリポジトリにシークレットを登録します。
- リポジトリのSettings > Secrets and variables > Actions に移動
- New repository secret をクリック
- 以下の2つのシークレットを登録
| シークレット名 |
値 |
DOCKERHUB_USERNAME |
Docker Hubのユーザー名 |
DOCKERHUB_TOKEN |
作成したアクセストークン |
Docker Hubへのプッシュワークフロー#
シークレットを使用してDocker Hubにログインし、イメージをプッシュするワークフローです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
name: Build and Push to Docker Hub
on:
push:
branches: ['main']
tags: ['v*']
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/myapp
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
|
このワークフローのポイントは以下のとおりです。
docker/login-action@v3でDocker Hubにログイン
docker/metadata-action@v5でGitのリファレンスから自動的にタグを生成
push: trueでビルド後に自動プッシュ
GitHub Container Registryへのプッシュ#
Docker Hubの代わりに、GitHub Container Registry(ghcr.io)を使用することもできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
name: Build and Push to GHCR
on:
push:
branches: ['main']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
|
GitHub Container Registryを使用する場合、GITHUB_TOKENを自動的に使用できるため、追加のシークレット設定が不要です。
マルチプラットフォームビルド(ARM/AMD64)#
Apple Silicon MacやAWSのGravitonインスタンスなど、ARMアーキテクチャの普及に伴い、マルチプラットフォーム対応のDockerイメージの需要が高まっています。
マルチプラットフォームビルドの仕組み#
Docker Buildxを使用すると、1回のビルドで複数のアーキテクチャ向けのイメージを生成できます。
flowchart TD
Dockerfile["Dockerfile"]
Dockerfile --> Buildx["Buildx +\nQEMU"]
Buildx --> amd64["linux/amd64"]
Buildx --> arm64["linux/arm64"]
Buildx --> armv7["linux/arm/v7"]
amd64 --> Manifest["Manifest List\n(マルチアーキテクチャ)"]
arm64 --> Manifest
armv7 --> ManifestQEMUを使用してエミュレーションすることで、AMD64ランナー上でもARM向けイメージをビルドできます。
QEMUを使用したマルチプラットフォームビルド#
以下のワークフローで、AMD64とARM64の両方に対応したイメージをビルドできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
name: Multi-Platform Build
on:
push:
branches: ['main']
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/myapp
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
|
重要なポイントは以下のとおりです。
docker/setup-qemu-action@v3でQEMUをセットアップ(ARMエミュレーションに必要)
platforms: linux/amd64,linux/arm64で対象プラットフォームを指定
高速化のためのマトリクスビルド#
QEMUによるエミュレーションは時間がかかります。ビルド時間を短縮するには、プラットフォームごとに並列ビルドを実行し、最後にマニフェストをマージする方法が有効です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
name: Multi-Platform Build (Matrix)
on:
push:
tags: ['v*']
env:
REGISTRY_IMAGE: username/myapp
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ env.REGISTRY_IMAGE }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs: build
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: |
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
この方法では、ARM64向けビルドをARMネイティブランナー(ubuntu-24.04-arm)で実行するため、エミュレーションのオーバーヘッドがなく高速にビルドできます。
イメージタグ戦略のベストプラクティス#
イメージタグはバージョン管理やデプロイ戦略において重要な役割を果たします。適切なタグ戦略を設計することで、運用の安全性と効率が大幅に向上します。
タグ戦略の基本パターン#
以下のタグパターンを組み合わせて使用することが推奨されます。
| タグパターン |
例 |
用途 |
| セマンティックバージョン |
v1.2.3 |
特定バージョンの追跡 |
| メジャー.マイナー |
v1.2 |
マイナーバージョン内の最新追跡 |
| メジャーのみ |
v1 |
メジャーバージョン内の最新追跡 |
| latest |
latest |
最新の安定版 |
| SHA |
sha-a1b2c3d |
特定コミットの追跡 |
| ブランチ名 |
main, develop |
開発・ステージング環境向け |
docker/metadata-actionを使用すると、Gitのリファレンスから自動的に適切なタグを生成できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: username/myapp
tags: |
# デフォルトブランチでのプッシュ時
type=raw,value=latest,enable={{is_default_branch}}
# ブランチ名
type=ref,event=branch
# PRの場合
type=ref,event=pr
# Git tag
type=ref,event=tag
# セマンティックバージョニング(v1.2.3 -> 1.2.3, 1.2, 1)
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
# SHAの短縮形
type=sha,prefix=sha-
|
この設定により、以下のようにタグが自動生成されます。
| イベント |
生成されるタグ |
| mainブランチへのプッシュ |
main, latest, sha-a1b2c3d |
| featureブランチへのプッシュ |
feature-xxx, sha-a1b2c3d |
| v1.2.3タグの作成 |
v1.2.3, 1.2.3, 1.2, 1, sha-a1b2c3d |
| PRの作成 |
pr-123, sha-a1b2c3d |
latestタグの扱いに関する注意点#
latestタグは便利ですが、本番環境での使用には注意が必要です。
[注意] latestタグの問題点
・どのバージョンか特定できない
・ロールバックが困難
・キャッシュの影響で古いイメージが使われる可能性
・再現性の確保が難しい
本番環境では、必ず特定のバージョンタグ(v1.2.3など)を使用することを推奨します。latestタグは開発環境やクイックテスト用途に限定しましょう。
イミュータブルタグの活用#
セキュリティと再現性を高めるために、イミュータブル(不変)タグの活用を検討しましょう。
1
2
3
4
5
|
tags: |
# SHAベースのイミュータブルタグ
type=sha,format=long,prefix=sha-
# タイムスタンプ
type=raw,value={{date 'YYYYMMDD-HHmmss'}}
|
SHAやタイムスタンプベースのタグは一意であるため、同じタグで異なるイメージがプッシュされるリスクを排除できます。
実践的なCI/CDワークフロー例#
ここまでの内容を組み合わせた、本番運用で使える完全なワークフロー例を紹介します。
リリース時の自動プッシュワークフロー#
GitHubでリリースを作成した際に、自動的にマルチプラットフォームイメージをビルドしてプッシュするワークフローです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
name: Release
on:
release:
types: [published]
env:
REGISTRY_IMAGE: username/myapp
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_IMAGE }}
ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
|
このワークフローでは、Docker HubとGitHub Container Registryの両方に同時プッシュを行い、冗長性を確保しています。
PRでのビルドテストワークフロー#
プルリクエスト時にビルドテストを実行し、プッシュはしないワークフローです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
name: PR Build Test
on:
pull_request:
branches: ['main']
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build image (no push)
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: myapp:test
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run container tests
run: |
docker run --rm myapp:test /app/test.sh
|
load: trueを指定することで、ビルドしたイメージをローカルのDockerデーモンにロードし、後続のテストで使用できます。
まとめ#
本記事では、CI/CDパイプラインでのDocker活用について、GitHub Actionsを中心に解説しました。
学習した内容を整理すると、以下のとおりです。
- GitHub ActionsでのDockerビルド: 公式アクションを使用して効率的にイメージをビルドできます
- Docker Hubへの自動プッシュ: シークレット設定とワークフロー定義で完全自動化が可能です
- マルチプラットフォームビルド: QEMUとBuildxにより、ARM/AMD64両対応のイメージを作成できます
- イメージタグ戦略: metadata-actionを活用して、セマンティックバージョニングに基づく自動タグ付けを実現できます
CI/CDパイプラインにDockerを組み込むことで、コードの変更からデプロイまでを自動化し、開発チームの生産性を大幅に向上させることができます。まずは基本的なビルドワークフローから始めて、徐々にマルチプラットフォームビルドやセキュリティ強化へと発展させていくことをお勧めします。
次のステップとして、Docker Scoutによる脆弱性スキャンやアーティファクト署名を組み込むことで、よりセキュアなCI/CDパイプラインを構築できます。
参考リンク#