フリーキーズ | 独学プログラミング

Docker BuildKitで高速ビルド!初心者でもわかるキャッシュ活用術

リンドくん

リンドくん

たなべ先生、Dockerでイメージをビルドするたびにすごく時間がかかるんです...何か早くする方法ないですか?

たなべ

たなべ

それならBuildKitを使ってみるといいよ!
Dockerの新しいビルドエンジンで、ビルド時間を劇的に短縮できるんだ。キャッシュも賢く使えるから、開発効率がグッと上がるよ。

Dockerを使った開発において、イメージのビルド時間は開発効率に直結する重要な要素です。
特に大規模なアプリケーションでは、ビルドに数分から数十分かかることも珍しくありません。

そんな中、Docker BuildKitはDocker 18.09以降で利用できる次世代のビルドエンジンとして登場し、従来のビルドプロセスを大きく改善しました。
並列処理やキャッシュの最適化により、ビルド時間を大幅に短縮できるだけでなく、より効率的な開発ワークフローを実現できます。

この記事では、Docker BuildKitの基本的な概念から実践的な使い方まで、初心者の方でも理解できるように丁寧に解説していきます。

プログラミング学習でお悩みの方へ

HackATAは、エンジニアを目指す方のためのプログラミング学習コーチングサービスです。 経験豊富な現役エンジニアがあなたの学習をサポートします。

✓ 質問し放題

✓ β版公開中(2025年内の特別割引)

HackATAの詳細を見る

Docker BuildKitとは何か?

リンドくん

リンドくん

BuildKitって、普通のDockerビルドと何が違うんですか?

たなべ

たなべ

従来のDockerビルドは順番に一つずつ処理していたんだ。
でもBuildKitは並列処理ができるから、依存関係のない処理を同時に実行できるんだよ。これが速さの秘密なんだ。

BuildKitの基本概念

Docker BuildKitは、Docker 18.09から導入された新しいビルドエンジンです。
従来のビルドシステムを完全に書き直したもので、以下のような特徴を持っています。

  • 並列ビルド → 依存関係のない処理を同時に実行
  • 効率的なキャッシュ → レイヤーキャッシュをより賢く活用
  • ビルドの可視化 → 進行状況をリアルタイムで確認可能
  • セキュリティの向上 → ビルド時のセキュリティ機能が強化

これらの特徴により、ビルド時間を30〜70%短縮できることもあります。
特に大規模なプロジェクトや複雑な依存関係を持つアプリケーションで、その効果は顕著です。

なぜBuildKitが必要なのか

従来のDockerビルドでは、以下のような課題がありました。

  1. 逐次処理による時間のロス - すべての処理を順番に実行するため無駄が多い
  2. キャッシュの非効率 - ファイル変更時に不必要なレイヤーまで再ビルドされる
  3. ビルド状況の不透明さ - どの処理に時間がかかっているか分かりにくい

BuildKitはこれらの課題を解決し、より快適な開発体験を提供してくれます。
CI/CDパイプラインでのビルド時間短縮にも大きく貢献するため、チーム開発においても重要な技術と言えるでしょう。

BuildKitの有効化と基本的な使い方

リンドくん

リンドくん

BuildKitってどうやって使い始めればいいんですか?難しい設定が必要ですか?

たなべ

たなべ

実はとても簡単なんだよ!
環境変数を一つ設定するだけで使えるようになるんだ。Docker 23.0以降ではデフォルトで有効になっているよ。

BuildKitの有効化方法

BuildKitを有効にする方法はいくつかあります。

一時的に有効化(コマンド実行時のみ)

DOCKER_BUILDKIT=1 docker build -t myapp:latest .

この方法は、一度だけBuildKitを試してみたい場合に便利です。

恒久的に有効化(推奨)

Linux/Macの場合は以下のように設定します。

# ~/.bashrc または ~/.zshrc に追加
export DOCKER_BUILDKIT=1

Windows(PowerShell)の場合はこちらです。

# 環境変数をPowerShellで設定
[System.Environment]::SetEnvironmentVariable('DOCKER_BUILDKIT', '1', 'User')

または、Dockerの設定ファイル(~/.docker/config.json)に以下を追加します。

{
  "features": {
    "buildkit": true
  }
}

基本的なビルドコマンド

BuildKitを有効にした状態でのビルドは、従来と同じコマンドを使用します。

docker build -t myapp:latest .

ただし、BuildKitを使用すると出力形式が変わり、より詳細なビルド情報が表示されます。

# 進行状況の詳細表示
docker build --progress=plain -t myapp:latest .

# 最終的な出力のみ表示
docker build --progress=auto -t myapp:latest .

BuildKitならではの便利な機能

BuildKitを使用すると、以下のような便利な機能が利用できます。

ビルド情報の確認

# ビルド履歴の確認
docker buildx ls

# キャッシュの確認
docker buildx du

不要なキャッシュの削除

# ビルドキャッシュをクリア
docker buildx prune

# すべてのビルドキャッシュを削除
docker buildx prune -a

これらのコマンドを使うことで、BuildKitの状態を管理しやすくなります。

キャッシュを最大限に活用するDockerfile作成術

リンドくん

リンドくん

キャッシュって何ですか?それを使うとなぜ速くなるんでしょうか?

たなべ

たなべ

いい質問だね!Dockerはビルドの各ステップの結果を保存しておくんだ。
次回同じステップを実行するとき、変更がなければ保存した結果を再利用できる。これがキャッシュなんだよ。

レイヤーキャッシュの仕組み

Dockerのイメージはレイヤーという単位で構成されています。
Dockerfileの各命令(RUN、COPY、ADDなど)が一つのレイヤーを作成します。

BuildKitは、以下のような条件でキャッシュを使用します。

  • Dockerfileの命令が前回と同じ
  • 参照するファイルに変更がない
  • ベースイメージが同じ

つまり、変更のない部分は再ビルドせず、キャッシュを再利用することでビルド時間を短縮できるのです。

キャッシュを活かすDockerfileの書き方

以下は、キャッシュを効果的に活用するための基本的な原則です。

悪い例 キャッシュを活かせないDockerfile

FROM node:20

# アプリケーション全体をコピー(変更のたびに全体が再ビルドされる)
COPY . /app
WORKDIR /app

# 依存関係のインストール(毎回実行される)
RUN npm install

# アプリケーションの起動
CMD ["npm", "start"]

このDockerfileでは、ソースコードを少し変更しただけでnpm installが毎回実行されてしまいます。

良い例 キャッシュを活かせるDockerfile

FROM node:20

WORKDIR /app

# package.jsonとpackage-lock.jsonのみを先にコピー
COPY package*.json ./

# 依存関係のインストール(package.jsonが変更されない限りキャッシュされる)
RUN npm install

# アプリケーションコードをコピー
COPY . .

# アプリケーションの起動
CMD ["npm", "start"]

この書き方なら、ソースコードを変更してもpackage.jsonが変わっていなければ、npm installはキャッシュから再利用されます。

キャッシュ最適化のポイント

変更頻度の低いものから順に配置

FROM python:3.14

WORKDIR /app

# 1. システムパッケージ(ほとんど変更しない)
RUN apt-get update && apt-get install -y \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# 2. Pythonの依存関係(時々変更する)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 3. アプリケーションコード(頻繁に変更する)
COPY . .

CMD ["python", "app.py"]

不要なファイルはコピーしない

.dockerignoreファイルを作成して、不要なファイルを除外します。

# .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
tests/

これにより、変更監視の対象が減り、キャッシュがより効果的に働きます。

マルチステージビルドでさらなる最適化

リンドくん

リンドくん

「マルチステージビルド」って何ですか?普通のビルドと違うんですか?

たなべ

たなべ

これは複数の段階に分けてビルドする技術なんだ。
ビルドに必要なツールと、実行に必要なものを分けることで、最終的なイメージサイズを劇的に小さくできるんだよ。

マルチステージビルドとは

マルチステージビルドは、一つのDockerfile内で複数のFROM命令を使用し、段階的にイメージを構築する手法です。

主なメリットは以下の通りです。

  • 最終イメージのサイズ削減 - ビルドツールを含めない
  • セキュリティの向上 - 不要なツールを含まない
  • ビルドの明確化 - 各ステージの役割が明確になる

マルチステージビルドの例

Node.jsアプリケーションの例

# ステージ1: ビルド環境
FROM node:20 AS builder

WORKDIR /app

# 依存関係のインストール
COPY package*.json ./
RUN npm ci

# アプリケーションのビルド
COPY . .
RUN npm run build

# ステージ2: 本番環境
FROM node:20-alpine

WORKDIR /app

# 本番用の依存関係のみインストール
COPY package*.json ./
RUN npm ci --production

# ビルド済みファイルをコピー
COPY --from=builder /app/dist ./dist

# ユーザーを作成して権限を最小化
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
USER nodejs

EXPOSE 3000

CMD ["node", "dist/index.js"]

このDockerfileでは、ビルドツールを含むbuilderステージと、実行に必要な最小限のファイルのみを含む最終ステージを分離しています。

Go言語アプリケーションの例

# ステージ1: ビルド
FROM golang:1.25 AS builder

WORKDIR /app

# Go modulesの依存関係をキャッシュ
COPY go.mod go.sum ./
RUN go mod download

# ソースコードをコピーしてビルド
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .

# ステージ2: 最小限の実行環境
FROM alpine:latest

# セキュリティアップデート
RUN apk --no-cache add ca-certificates

WORKDIR /root/

# ビルドしたバイナリのみをコピー
COPY --from=builder /app/main .

CMD ["./main"]

Go言語の場合、静的リンクされたバイナリを作成できるため、最終イメージは非常に小さくなります(数MBから数十MB程度)。

BuildKitとマルチステージビルドの相乗効果

BuildKitはマルチステージビルドと組み合わせることで、さらに効果を発揮します。

# syntax=docker/dockerfile:1.4

FROM node:20 AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci

FROM node:20 AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

--mount=type=cacheを使用することで、npmのキャッシュをビルド間で共有できます。

BuildKitの高度な機能

リンドくん

リンドくん

基本はわかったんですけど、もっと高度なテクニックってあるんですか?

たなべ

たなべ

もちろん!BuildKitにはシークレットの安全な管理SSHエージェント転送など、実践的な機能がたくさんあるんだ。
セキュリティを保ちながら効率的にビルドできるよ。

シークレットマウント

プライベートリポジトリへのアクセスやAPIキーなど、秘密情報をイメージに残さずビルド時だけ使用できます。

# syntax=docker/dockerfile:1.4

FROM node:20

WORKDIR /app

# プライベートパッケージのインストール時にNPMトークンを使用
RUN --mount=type=secret,id=npmtoken \
    echo "//registry.npmjs.org/:_authToken=$(cat /run/secrets/npmtoken)" > ~/.npmrc && \
    npm install @private/package && \
    rm ~/.npmrc

COPY . .

CMD ["npm", "start"]

ビルド時の実行方法は以下です。

docker build --secret id=npmtoken,src=.npmtoken -t myapp .

この方法なら、トークンが最終イメージに含まれないため、セキュリティが保たれます。

SSHエージェント転送

プライベートGitリポジトリのクローン時に便利です。

# syntax=docker/dockerfile:1.4

FROM python:3.14

WORKDIR /app

# SSHキーを使ってプライベートリポジトリをクローン
RUN --mount=type=ssh \
    git clone git@github.com:private/repo.git

COPY requirements.txt .
RUN pip install -r requirements.txt

CMD ["python", "app.py"]

ビルド時の実行方法はこちらです。

docker build --ssh default -t myapp .

キャッシュマウント

パッケージマネージャーのキャッシュを永続化できます。

# syntax=docker/dockerfile:1.4

FROM python:3.14

WORKDIR /app

COPY requirements.txt .

# pipのキャッシュをビルド間で共有
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

COPY . .

CMD ["python", "app.py"]

これにより、依存関係のダウンロード時間を大幅に短縮できます。

ビルドターゲットの指定

開発環境と本番環境で異なるイメージを作成できます。

# syntax=docker/dockerfile:1.4

FROM node:20 AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci

# 開発環境用
FROM base AS development
COPY . .
RUN npm run build:dev
CMD ["npm", "run", "dev"]

# 本番環境用
FROM base AS production
COPY . .
RUN npm run build:prod
CMD ["npm", "start"]

ビルド時にターゲットを指定します。

# 開発環境用
docker build --target development -t myapp:dev .

# 本番環境用
docker build --target production -t myapp:prod .

トラブルシューティングとベストプラクティス

リンドくん

リンドくん

BuildKitを使っていて問題が起きたときはどうすればいいですか?

たなべ

たなべ

よくある問題とその解決法を知っておくと安心だよ。
ログの見方キャッシュのクリア方法を覚えておくと、トラブル時にスムーズに対応できるんだ。

よくある問題と解決法

問題1 キャッシュが効かない

原因 → ファイルのタイムスタンプや権限の変更

↓↓↓解決法↓↓↓

# キャッシュをクリアして再ビルド
docker buildx prune -a
docker build --no-cache -t myapp .

問題2 ビルドが途中で止まる

原因 → ネットワークの問題やリソース不足

↓↓↓解決法↓↓↓

# 詳細なログを出力
docker build --progress=plain -t myapp . 2>&1 | tee build.log

# Docker Desktopのリソース設定を確認・調整

問題3 マルチプラットフォームビルドで失敗する

原因 → QEMUエミュレータの不足

↓↓↓解決法↓↓↓解決法

# QEMUエミュレータのインストール
docker run --privileged --rm tonistiigi/binfmt --install all

# ビルダーの作成と使用
docker buildx create --use --name multiplatform
docker buildx build --platform linux/amd64,linux/arm64 -t myapp .

ベストプラクティス

1. .dockerignoreを適切に設定

# .dockerignore
node_modules
.git
.env
*.log
dist
coverage
.vscode

2. レイヤーの順序を最適化

# 変更頻度の低い順に配置
FROM base

# システムパッケージ
RUN apt-get update && apt-get install -y package

# 依存関係
COPY requirements.txt .
RUN pip install -r requirements.txt

# アプリケーションコード
COPY . .

3. BuildKitの構文バージョンを明示

# syntax=docker/dockerfile:1.4
FROM node:20
# ...

4. ビルド時間の測定

time docker build -t myapp .

5. CI/CD環境での設定

# GitHub Actionsの例
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: myapp:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

パフォーマンス測定のコツ

ビルド時間を正確に測定し、改善効果を確認しましょう。

# 初回ビルド(キャッシュなし)
time docker build --no-cache -t myapp:test1 .

# 2回目のビルド(キャッシュあり、変更なし)
time docker build -t myapp:test2 .

# 3回目のビルド(ソースコードのみ変更)
echo "// change" >> src/index.js
time docker build -t myapp:test3 .

これにより、キャッシュの効果を数値で確認できます。

まとめ

リンドくん

リンドくん

BuildKitって本当に便利なんですね!早速使ってみたいです!

たなべ

たなべ

そうだね!最初は基本的な使い方から始めて、徐々に高度な機能にも挑戦してみてね。
ビルド時間が短くなると開発が楽しくなるよ!

Docker BuildKitは、コンテナイメージのビルドプロセスを劇的に改善する強力なツールです。
この記事で解説した内容を実践すれば、ビルド時間の大幅な短縮が期待できるでしょう。

重要なポイントのおさらい

  • BuildKitの有効化は簡単 - 環境変数を設定するだけで使い始められます
  • キャッシュを活かすDockerfile設計 - 変更頻度の低いものから順に配置することが重要
  • マルチステージビルドの活用 - イメージサイズの削減とセキュリティの向上を実現
  • 高度な機能の活用 - シークレット管理やキャッシュマウントで効率化

特に、キャッシュの最適化はビルド時間に直結する重要な要素です。
Dockerfileの書き方を少し工夫するだけで、ビルド時間を数分から数秒に短縮できることもあります。

Docker BuildKitは、現代のコンテナ開発において必須のスキルと言えます。
ぜひこの記事を参考に、効率的なビルドプロセスを構築してください。

この記事をシェア

関連するコンテンツ