Docker Compose build の使い方:初心者向け徹底解説
はじめに
Docker は、アプリケーションをコンテナという独立した環境で実行するための強力なプラットフォームです。そして Docker Compose は、複数のコンテナで構成されるアプリケーション(例えば、Web サーバー、データベース、キャッシュサーバーなど)をまとめて定義し、管理するためのツールです。
Docker Compose を使うことで、複雑なアプリケーションの構築、実行、停止、削除といった一連の操作を、一つの設定ファイル(docker-compose.yml
)を使って簡単に行えるようになります。
Docker Compose でアプリケーションを動かすためには、まず使用するコンテナの「設計図」である Docker Image を準備する必要があります。Docker Image は、Docker Hub のような公開リポジトリからダウンロードすることもできますが、多くの場合、自分たちのアプリケーションに合わせてカスタマイズした独自の Image を作成する必要があります。
この、独自の Docker Image を作成する際に中心的な役割を果たすのが、今回詳しく解説する docker-compose build
コマンドです。
この記事では、Docker や Docker Compose を初めて使う方でも理解できるように、docker-compose build
コマンドの基本的な使い方から、その内部で行われていること、様々なオプション、そして実践的な活用方法、さらにはトラブルシューティングまで、約5000語をかけて徹底的に解説します。
この記事を読めば、あなたは以下のことができるようになります。
docker-compose build
コマンドが何のためにあるのかを理解する。docker-compose.yml
ファイルで Image をビルドする方法を定義する。- 基本的な
docker-compose build
コマンドを実行し、出力内容を理解する。 - さまざまなオプションを使って、ビルドをカスタマイズ・制御する。
- 特定のサービスだけをビルドしたり、ビルド引数を渡したりする方法を学ぶ。
- ビルドキャッシュの仕組みを理解し、効率的なビルドを行うためのヒントを得る。
- ビルド時の一般的な問題に対処できるようになる。
- より実践的な Dockerfile や Docker Compose の書き方を知る。
さあ、Docker Compose を使った Image ビルドの世界へ飛び込みましょう!
Docker と Docker Compose の基本(超初心者向け)
docker-compose build
を理解するためには、まず Docker と Docker Compose の基本的な考え方を抑えておくことが重要です。
Docker とは何か?
Docker は、アプリケーションとその実行に必要なすべてのもの(ライブラリ、フレームワーク、設定ファイルなど)を一つにまとめて「コンテナ」という形で提供するプラットフォームです。
- コンテナ: アプリケーションを分離して実行するための軽量な仮想化技術です。OS 全体を仮想化する従来の仮想マシン(VMware, VirtualBox など)とは異なり、ホストOS のカーネルを共有するため、起動が速く、リソース消費も少ないのが特徴です。コンテナは、どこでも同じように実行できるポータブルな環境を提供します。
- Docker Image: コンテナを実行するための「設計図」であり、ファイルシステムの構成、実行するプログラム、設定などが含まれています。Image は不変(Immutable)であり、一度作成されると変更できません。Image からコンテナを「生成」して実行します。
- Dockerfile: Docker Image を作成するための手順を記述したテキストファイルです。「FROM (ベースイメージを指定)」「COPY (ファイルをコピー)」「RUN (コマンドを実行)」などの命令を順番に記述します。Docker Engine はこの Dockerfile を読み込み、ステップごとに実行して Docker Image をビルド(構築)します。
- Docker Engine: Docker Image をビルドしたり、コンテナを実行したりする中心的なプログラムです。CLI コマンド(
docker build
,docker run
,docker ps
など)を受け付け、実際の処理を行います。
要するに、私たちは Dockerfile を書いて Docker Engine に渡すことで Docker Image をビルドし、その Image から コンテナ を起動してアプリケーションを実行する、というのが Docker の基本的な流れです。
Docker Compose とは何か?
単一のコンテナで完結するシンプルなアプリケーションもたくさんありますが、多くの実用的なアプリケーションは複数のコンポーネント(例: Web アプリ、データベース、API サーバー、キュー)で構成されています。これらのコンポーネントはそれぞれ異なるコンテナで実行されることが一般的です。
例えば、Python の Web アプリケーションを動かすには、Python アプリケーションを動かすコンテナと、データを保存するための PostgreSQL データベースコンテナ、さらにキャッシュとして Redis コンテナが必要になるかもしれません。これら3つのコンテナを個別に管理するのは手間がかかります。
Docker Compose は、このように複数の関連するコンテナ(サービス)をまとめて定義し、管理するためのツールです。
docker-compose.yml
: Docker Compose が使う設定ファイルです。YAML 形式で記述され、アプリケーションを構成する各サービス(コンテナ)の名前、使用する Docker Image、ポートマッピング、ボリュームマッピング、環境変数、サービス間の依存関係などを定義します。
docker-compose.yml
ファイルを用意すれば、docker-compose up
コマンド一つで、定義されたすべてのサービスをまとめて起動したり、停止したり、削除したりといった操作が可能になります。手作業で個別の docker run
コマンドを何回も実行する必要がなくなります。
Docker Compose は、開発環境の構築や、本番環境でのアプリケーションデプロイを効率化するための強力なツールなのです。
docker-compose.yml
ファイルの基本構造(build
に関連する部分にフォーカス)
docker-compose build
コマンドを使うには、まず docker-compose.yml
ファイルで、どのサービスに対してどのような方法で Docker Image をビルドするかを定義する必要があります。
docker-compose.yml
ファイルの基本的な構造は以下のようになります。
“`yaml
version: ‘3.8’ # Compose ファイルフォーマットのバージョンを指定
services: # アプリケーションを構成するサービスを定義するセクション
web: # サービス名(任意)
# このサービスのコンテナに使用する Docker Image を指定する方法は2通り
# 1. 既存のイメージを使う場合: image: ‘nginx:latest’
# 2. Dockerfile から自分でビルドする場合:
build:
context: . # Dockerfile やビルドに必要なファイルがあるディレクトリを指定
dockerfile: Dockerfile # Dockerfile のファイル名を指定 (デフォルトは ‘./Dockerfile’)
args: # ビルド引数を渡す場合
APP_VERSION: ‘1.0’
# cache_from: # キャッシュ元を指定 (応用)
# target: # マルチステージビルドのターゲットを指定 (応用)
ports: # ホストとコンテナ間のポートマッピング
– “80:80”
volumes: # ホストとコンテナ間のボリュームマッピング
– ./html:/usr/share/nginx/html
depends_on: # サービス間の依存関係
– app
app: # 別のサービス(例: アプリケーション本体)
build:
context: ./app
dockerfile: Dockerfile.app
ports:
– “5000:5000”
volumes:
– ./app:/app
environment: # 環境変数
DATABASE_URL: ‘postgresql://user:password@db:5432/mydb’
db: # データベースサービス
image: ‘postgres:13’ # このサービスは既存のイメージを使用
volumes:
– db_data:/var/lib/postgresql/data
volumes: # 永続化するボリュームの定義
db_data:
“`
このファイルの中で、docker-compose build
コマンドに最も関連が深いのは、各サービス定義の中にある build
キーです。
image
vs build
サービス定義において、そのサービスに使用する Docker Image を指定する方法は主に以下の2つです。
-
image
キーを使う:- すでに Docker Hub やプライベートレジストリに存在する Docker Image を利用する場合に使います。
- 例:
image: 'nginx:latest'
(Docker Hub からnginx:latest
イメージをプルして使用) - この場合、
docker-compose build
コマンドを実行しても、このサービスに対しては何も行われません(ビルドする必要がないため)。docker-compose pull
でイメージを取得します。
-
build
キーを使う:- ローカルの Dockerfile から独自の Docker Image をビルドして利用する場合に使います。
- 例: 上記の
web
サービスやapp
サービスのように定義します。 - この場合、
docker-compose build
コマンドを実行すると、このサービス定義に基づいて Docker Image がビルドされます。
build
キーを使うことで、アプリケーションのソースコードや必要な設定を含んだ、オリジナルの Docker Image を作成できます。これが docker-compose build
コマンドが活躍する場面です。
build
キーの詳細
build
キーの下には、Image をビルドするための様々な設定を記述できます。
-
context
(必須):- 意味: Docker ビルドを実行する際に、Dockerfile とともに Docker Engine に送信されるローカルファイル群のパス(ビルドコンテキスト)を指定します。通常は、Dockerfile が置いてあるディレクトリ、またはその親ディレクトリを指定します。
- 重要性: Dockerfile の
COPY
やADD
命令は、このcontext
で指定されたディレクトリからの相対パスでファイルを指定します。もしcontext: .
(カレントディレクトリ) と指定した場合、Dockerfile
でCOPY ./app /app
と書くと、カレントディレクトリ直下のapp
ディレクトリの内容がイメージにコピーされます。 - 注意: 必要のないファイル(大きなデータファイル、Git リポジトリ情報、ビルド生成物など)がコンテキストに含まれると、ビルド速度が遅くなったり、不要な情報がイメージに含まれたりする可能性があります。
.dockerignore
ファイルを使って、コンテキストに含めないファイルを指定することが推奨されます。 - 指定方法: ローカルファイルシステムのパス(例:
.
、./app
、/path/to/docker/dir
)または Git リポジトリの URL を指定できます(後者は応用)。
-
dockerfile
(任意):- 意味:
context
で指定したディレクトリ内にある、使用する Dockerfile のファイル名を指定します。 - デフォルト: 指定しない場合、デフォルトで
context
ディレクトリ直下のDockerfile
というファイルが使用されます。 - 指定方法: ファイル名(例:
Dockerfile.app
、./prod/Dockerfile
)。context
からの相対パスで指定します。
- 意味:
-
args
(任意):- 意味: Dockerfile 内の
ARG
命令で定義されたビルド時変数に値を渡すために使用します。ビルド中に動的に値を変更したい場合(例: アプリケーションのバージョン番号、ダウンロードするパッケージのバージョン)に便利です。 - 指定方法: キーバリュー形式でリストまたはマップとして指定します。
“`yaml
args:- APP_VERSION=1.0.0
- BUILD_DATE=2023-10-27
または
args:
APP_VERSION: 1.0.0
BUILD_DATE: 2023-10-27
“` - これらの値は、Dockerfile の
ARG
命令で受け取ります。
dockerfile
# Dockerfile
ARG APP_VERSION
ARG BUILD_DATE
LABEL version=$APP_VERSION build_date=$BUILD_DATE
... docker-compose build
コマンド実行時にも--build-arg
オプションで値を上書きできます。
- 意味: Dockerfile 内の
-
cache_from
(任意):- 意味: ビルドキャッシュとして利用する既存のイメージを指定します。これにより、リモートリポジトリにあるイメージをキャッシュとして活用し、ビルド時間を短縮できます。特に CI/CD 環境などで有効です。
- 指定方法: イメージ名のリストを指定します。
“`yaml
cache_from:- ‘your-registry/your-image:latest’
- ‘ubuntu:latest’
“`
- この機能は、BuildKit という新しいビルドバックエンドでより強力に機能します。
-
labels
(任意):- 意味: ビルドされる Docker Image にメタデータ(ラベル)を付与します。バージョン情報、作成者、ライセンス情報などを埋め込むのに便利です。
- 指定方法: キーバリュー形式のマップとして指定します。
yaml
labels:
maintainer: "Your Name <[email protected]>"
version: "1.0"
org.opencontainers.image.licenses: "MIT" - これらのラベルは
docker image inspect <image_id>
コマンドなどで確認できます。
-
target
(任意):- 意味: Dockerfile でマルチステージビルドを行っている場合に、最終的にビルドするステージ(ターゲット)を指定します。
-
例:
“`dockerfile
# Dockerfile
FROM node:18 as builder # ビルダーステージ
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build # アプリをビルドFROM nginx:alpine as production # 本番用ステージ
COPY –from=builder /app/dist /usr/share/nginx/html # ビルダーステージから成果物をコピー
COPY nginx.conf /etc/nginx/nginx.conf
CMD [“nginx”, “-g”, “daemon off;”]
この Dockerfile で本番用イメージだけをビルドしたい場合、`docker-compose.yml` に以下のように記述します。
yaml
services:
web:
build:
context: .
dockerfile: Dockerfile
target: production # production ステージのみビルドする
ports:
– “80:80”
“`
* これにより、ビルダーステージでインストールした大量の開発ツールやソースコードが最終イメージに含まれるのを防ぎ、イメージサイズを小さく保つことができます。
これらの build
キー配下の設定を適切に使うことで、あなたのアプリケーションに最適な Docker Image を効率的にビルドできるようになります。
docker-compose build
コマンドの基本
docker-compose.yml
ファイルでサービスの build
設定が完了したら、いよいよ docker-compose build
コマンドを使って Docker Image をビルドします。
コマンドの書式
bash
docker-compose build [OPTIONS] [SERVICE...]
[OPTIONS]
: 後述する様々なオプションを指定します。[SERVICE...]
: オプションで、ビルドしたいサービス名を指定します。省略した場合、docker-compose.yml
ファイル内でbuild
キーが定義されているすべてのサービスの Image がビルドされます。
何をするコマンドか?
docker-compose build
コマンドを実行すると、Docker Compose は以下の処理を行います。
- カレントディレクトリ、または指定された Compose ファイル(
-f
オプションなどで指定した場合)内のdocker-compose.yml
ファイルを読み込みます。 - ファイル内で定義されているサービスの中から、
build
キーが指定されているサービス、またはコマンドラインで指定されたサービスの定義を探します。 - それぞれのサービス定義にある
build
設定(context
,dockerfile
,args
など)を確認します。 - 各サービスに対して、指定された
context
を Docker Engine にビルドコンテキストとして送信します(.dockerignore
があれば考慮されます)。 - 指定された
dockerfile
(またはデフォルトのDockerfile
)を使って、Docker Engine に Image のビルドを指示します。このビルドプロセスは、通常のdocker build
コマンドで行われることと同じです。 - ビルドが成功すると、Docker Compose はサービス名と関連付けられたタグ(例:
myproject_web
,myproject_app
のような形式)を Image に自動で付けます。これにより、後続のdocker-compose up
コマンドなどで、このビルドされた Image を簡単に参照できるようになります。 - ビルドの進捗や結果がコンソールに出力されます。
docker build
との違い
docker-compose build
は、内部的に docker build
コマンドを呼び出していますが、単に docker build
を実行するのとはいくつかの違いがあります。
- 複数のサービスを一括管理:
docker-compose.yml
ファイルを見ることで、複数のサービスの Image ビルドをコマンド一つで実行できます。各サービスに対して個別にdocker build
コマンドを実行する必要がありません。 - 設定の一元化: ビルドに関する設定(コンテキストパス、Dockerfile 名、ビルド引数など)を
docker-compose.yml
ファイルに記述しておけます。これにより、ビルド手順が文書化され、チーム内で共有しやすくなります。 - 自動的なタグ付け: Docker Compose は通常、プロジェクト名とサービス名を組み合わせた形式で Image にタグを自動で付けます。これにより、Compose プロジェクト内でどのサービスがどの Image を使うかが明確になります。
- サービス間の連携:
docker-compose.yml
にはサービス間の依存関係やネットワーク設定なども記述されていますが、build
コマンド自体は Image をビルドするだけで、依存関係に基づいてビルド順序を調整したりはしません(ただし、後述のup --build
は依存関係を考慮します)。
基本的な実行例
最も基本的な docker-compose build
コマンドの実行例です。
- プロジェクトディレクトリに移動します。
docker-compose.yml
ファイルと、各サービスが必要とするDockerfile
やソースコードが配置されていることを確認します。- 以下のコマンドを実行します。
bash
docker-compose build
このコマンドは、docker-compose.yml
ファイルに build
キーが設定されているすべてのサービスの Image をビルドします。
もし特定のサービスだけをビルドしたい場合は、サービス名を指定します。
bash
docker-compose build web app # web と app サービスのみビルド
ビルドが開始されると、各サービスのビルドプロセスが順番に(または並行して、Compose バージョンや設定による)実行され、docker build
と同様の出力が表示されます。各ステップ(Dockerfile の各命令)が実行されるたびに、その進捗と結果が示されます。
成功すると、「Successfully built
例: docker image ls
コマンドで確認すると、myproject_web
や myproject_app
のようなタグが付いた Image が見つかるはずです(myproject
はカレントディレクトリ名などから自動で決まるプロジェクト名)。
“`bash
$ docker-compose build
[+] Building 2.0s (10/10) FINISHED
=> [web internal] load build definition from Dockerfile
=> => transferring dockerfile: 32B
=> [web internal] load .dockerignore
=> => transferring context: 34B
=> [web internal] load metadata for docker.io/library/nginx:alpine
=> [web 1/3] FROM docker.io/library/nginx:alpine
=> [web internal] load build context
=> => transferring context: 1.21kB
=> [web 2/3] COPY html /usr/share/nginx/html
=> [web 3/3] COPY nginx.conf /etc/nginx/nginx.conf
=> [web] Exporting fs layers
=> => exporting layers
=> => writing image sha256:xxxxxxxxxxxxxxx
=> => naming to docker.io/library/myproject_web:latest
[+] Building 3.5s (12/12) FINISHED
=> [app internal] load build definition from Dockerfile.app
=> => transferring dockerfile: 38B
=> [app internal] load .dockerignore
=> => transferring context: 45B
=> [app internal] load metadata for docker.io/library/python:3.9-slim
=> [app 1/7] FROM docker.io/library/python:3.9-slim
=> [app internal] load build context
=> => transferring context: 5.67kB
=> [app 2/7] WORKDIR /app
=> [app 3/7] COPY requirements.txt ./
=> [app 4/7] RUN pip install –no-cache-dir -r requirements.txt
=> [app 5/7] COPY . .
=> [app 6/7] EXPOSE 5000
=> [app 7/7] CMD [“python”, “app.py”]
=> [app] Exporting fs layers
=> => exporting layers
=> => writing image sha256:yyyyyyyyyyyyyyy
=> => naming to docker.io/library/myproject_app:latest
“`
上記の例は、Compose V2 のビルド出力例です。ステップごとの詳細な情報と、最後に成功した Image ID が表示されます。
docker-compose build
コマンドの詳細オプション
docker-compose build
コマンドには、ビルドプロセスをより細かく制御するための様々なオプションが用意されています。ここでは主なオプションを紹介します。
--help
, -h
コマンドの使い方やオプションリストを表示します。
bash
docker-compose build --help
--compress
ビルドコンテキスト(Dockerfile と一緒に送信されるファイル群)を Docker Engine に送信する際に gzip で圧縮します。コンテキストが非常に大きい場合に、ネットワーク転送時間を短縮できる可能性があります。
bash
docker-compose build --compress
--force-rm
ビルドプロセス中に作成される中間コンテナ(Dockerfile の各 RUN
命令などが実行される際に一時的に作成されるコンテナ)を、ビルド成功後でも強制的に削除します。通常、エラーが発生した場合にデバッグのために中間コンテナが残されることがありますが、このオプションを使うと成功・失敗に関わらずクリーンアップされます。ディスクスペースを節約したい場合に有用ですが、デバッグが少し難しくなることがあります。
bash
docker-compose build --force-rm
--no-cache
Image ビルド時に、既存のビルドキャッシュを一切使用しません。Dockerfile の各ステップはすべて最初から実行されます。
bash
docker-compose build --no-cache
- いつ使うか?
- ビルドキャッシュが原因で予期しない動作(例: 新しいファイルがコピーされない、古い依存関係が使われる)が発生していると思われる場合。
- クリーンな状態で Image をビルドしたい場合(例: 本番環境用の Image をビルドする際)。
- Dockerfile やソースコードを大幅に変更し、キャッシュがほとんど役に立たないと考えられる場合。
- 注意点:
--no-cache
を使うと、ビルド時間が大幅に長くなる可能性があります。通常の開発中はキャッシュを活用するのが効率的です。
--pull
Image ビルドの前に、Dockerfile の FROM
命令で指定されているベースイメージを常に最新版としてプル(ダウンロード)します。
bash
docker-compose build --pull
- いつ使うか?
- ベースイメージ(例:
ubuntu:latest
,python:3.9
) が頻繁に更新され、常に最新の状態からビルドを開始したい場合。
- ベースイメージ(例:
- 注意点: ベースイメージのタグに
:latest
のような「可変」なタグを使っている場合、このオプションを使うと意図せずベースイメージが変更され、ビルド結果が変わる可能性があります。特定のバージョン(例:ubuntu:22.04
,python:3.9.18
) を指定している場合は、そのバージョンがローカルに存在しない場合にのみプルが行われます。再現性を重視する場合は、可変タグの使用は避け、固定バージョンを指定し、必要に応じて手動でプルするのが安全です。
--quiet
, -q
ビルドプロセスの詳細な出力を抑制し、最終的な Image ID のみを表示します。CI/CD 環境など、ログが大量になるのを避けたい場合に便利です。
bash
docker-compose build -q
--parallel
(Compose V2 以降推奨)
docker-compose.yml
で定義されている複数のサービスを並行してビルドします。これにより、ビルド時間が短縮される可能性があります(ただし、サービスの数やシステムリソースによります)。Compose V2 以降ではデフォルトで並行ビルドが有効になっていることが多いですが、明示的に指定することもできます。
bash
docker-compose build --parallel # 明示的に並行ビルドを有効にする
--no-parallel
(Compose V1 の場合)
Compose V1 では、デフォルトで並行ビルドが行われるため、それを無効にしてサービスを順番にビルドしたい場合にこのオプションを使いました。Compose V2 では --parallel
がデフォルトまたは推奨のため、あまり使いません。
“`bash
Compose V1 の場合
docker-compose build –no-parallel
“`
--build-arg key=value
docker-compose.yml
の build.args
で指定したビルド引数の値を、コマンドラインから上書き、または追加します。複数の引数を渡す場合は、オプションを複数回指定します。
“`bash
docker-compose.yml の args を上書きまたは追加
docker-compose build –build-arg APP_VERSION=1.1.0 –build-arg ENVIRONMENT=production app
“`
このオプションで指定された値は、docker-compose.yml
で同じ名前の引数が定義されていればその値を上書きし、定義されていなければ新しい引数として渡されます(Dockerfile で ARG
で受け取っている必要があります)。
リソース制限オプション (--memory
, --memswap
, --cpu-shares
, etc.)
これらのオプションは、ビルドプロセス自体(具体的には、Dockerfile の RUN
命令などが実行される際の一時的なコンテナ)が使用できるシステムリソースを制限します。ビルドホストのリソースが限られている場合や、特定のビルドがリソースを過剰に消費するのを防ぎたい場合に利用できます。これらは Docker Engine のビルドオプションを Compose 経由で渡しているものです。
--memory SIZE
: ビルドコンテナが使用できるメモリの最大値を設定します(例:--memory 1g
,--memory 512m
)。--memswap SIZE
: メモリとスワップ領域を合わせた最大値を設定します。-1
を指定すると、メモリ制限のみが適用され、スワップは無制限になります。--cpu-shares SHARES
: CPU の使用率の相対的な重みを設定します(デフォルトは 1024)。数値が大きいほど、他のコンテナと比較して多くの CPU 時間が割り当てられやすくなります。--cpu-period PERIOD
,--cpu-quota QUOTA
: CPU 使用率を絶対値で制限します。--cpu-period
を 100000 (1秒) とした場合、--cpu-quota
を 50000 に設定すると、そのコンテナは CPU 時間の最大 50% を使用できます。--cpuset-cpus CPUS
: ビルドコンテナを実行する CPU コアを指定します(例:--cpuset-cpus 0,1
,--cpuset-cpus 0-3
)。--shm-size SIZE
:/dev/shm
のサイズを設定します。特定のアプリケーション(例: 一部のビルドツール)が共有メモリを大量に使う場合に必要になることがあります(例:--shm-size 2g
)。
これらのリソース制限オプションは、主にビルドホストの安定性を保つために使用され、ビルドされる最終イメージに影響を与えるものではありません。
--progress string
(Compose V2 以降)
ビルドの進捗表示の形式を指定します。
auto
: 環境に応じて ‘tty’ または ‘plain’ を自動選択します。plain
: 進捗がテキスト形式で表示されます。これは、CI/CD 環境などでログをファイルに保存する場合や、非対話的な環境で便利です。tty
: 対話的なターミナルに適した、リッチな表示形式です(デフォルト)。
bash
docker-compose build --progress plain
--ssh string
(Compose V2 以降)
BuildKit が SSH エージェントに接続して、ビルド中にリモートリソース(プライベート Git リポジトリなど)にアクセスできるようにします。これは Dockerfile の RUN --mount=type=ssh ...
命令と組み合わせて使用します。
“`bash
ローカルのSSHエージェントに接続を許可
docker-compose build –ssh default web
“`
このオプションは、SSH 経由でコードをクローンしたり、プライベートパッケージをインストールしたりするビルドステップがある場合に便利です。
実践的な使い方とシナリオ
ここでは、より具体的なシナリオを通して docker-compose build
コマンドの使い方を見ていきましょう。
シナリオ1:初めてのビルド
簡単な Node.js アプリケーションを例に、初めてビルドする手順を解説します。
ディレクトリ構成:
my-node-app/
├── docker-compose.yml
├── app/
│ ├── Dockerfile
│ ├── package.json
│ └── server.js
└── nginx/
├── Dockerfile
└── nginx.conf
app/package.json
:
json
{
"name": "my-node-app",
"version": "1.0.0",
"description": "A simple node app",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.17.1"
}
}
app/server.js
:
“`javascript
const express = require(‘express’);
const app = express();
const port = 3000;
app.get(‘/’, (req, res) => {
res.send(‘Hello from Node.js App!’);
});
app.listen(port, () => {
console.log(App listening at http://localhost:${port}
);
});
“`
app/Dockerfile
:
“`dockerfile
ベースイメージの指定
FROM node:18-alpine
作業ディレクトリの設定
WORKDIR /app
package.json と package-lock.json をコピー
これらを先にコピーして依存関係をインストールすることで、
ソースコードの変更があっても依存関係のインストールステップがキャッシュされやすくなる
COPY package*.json ./
依存関係のインストール
RUN npm install
アプリケーションのソースコードをコピー
COPY . .
アプリケーションが待ち受けるポートを指定 (ドキュメント用)
EXPOSE 3000
コンテナ起動時に実行されるコマンド
CMD [“npm”, “start”]
“`
nginx/Dockerfile
:
“`dockerfile
ベースイメージ
FROM nginx:alpine
カスタム設定ファイルをコピー
COPY nginx.conf /etc/nginx/nginx.conf
デフォルトのindex.htmlを削除し、静的コンテンツディレクトリを作成
RUN rm /usr/share/nginx/html/index.html
RUN mkdir /usr/share/nginx/html/static
静的ファイルをコピーする場合(例)
COPY static/ /usr/share/nginx/html/static/
デフォルトのポート80を公開
EXPOSE 80
CMDはベースイメージで設定済みなので不要
“`
nginx/nginx.conf
: (簡単な設定例)
“`nginx
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
location / {
# リクエストを Node.js アプリコンテナに転送
proxy_pass http://app:3000; # 'app' は docker-compose.yml で定義するサービス名
}
location /static/ {
# 静的ファイルは Nginx が直接配信
root /usr/share/nginx/html;
}
}
}
“`
docker-compose.yml
:
“`yaml
version: ‘3.8’
services:
app:
build:
context: ./app # app サービスのビルドコンテキストは ./app ディレクトリ
dockerfile: Dockerfile # ./app/Dockerfile を使用
ports:
– “3000:3000” # 開発中は直接アクセスできるようにポートを公開
volumes:
– ./app:/app # ソースコードをマウント(開発中のホットリロード用)
– /app/node_modules # node_modules はマウントしない(コンテナ内でインストールしたものを使う)
nginx:
build:
context: ./nginx # nginx サービスのビルドコンテキストは ./nginx ディレクトリ
dockerfile: Dockerfile # ./nginx/Dockerfile を使用
ports:
– “80:80” # Nginx が待ち受けるポートをホストの80番ポートにマッピング
# static ディレクトリをマウントする場合
# volumes:
# – ./nginx/static:/usr/share/nginx/html/static
depends_on: # app サービスが起動してから nginx サービスを起動する
– app
“`
ビルドの実行:
my-node-app
ディレクトリに移動し、以下のコマンドを実行します。
bash
cd my-node-app
docker-compose build
Docker Compose は docker-compose.yml
を読み込み、app
サービスと nginx
サービスの build
設定を見つけます。
- まず
app
サービスのビルドが開始されます。context: ./app
なので、./app
ディレクトリ以下のファイルがビルドコンテキストとして Docker Engine に送られます。dockerfile: Dockerfile
なので、./app/Dockerfile
の内容に従って Image がビルドされます。 - 次に
nginx
サービスのビルドが開始されます。context: ./nginx
なので、./nginx
ディレクトリ以下のファイルがコンテキストとして送られ、./nginx/Dockerfile
に従って Image がビルドされます。
ビルドの進捗がコンソールに表示され、各 RUN
, COPY
などのステップが実行されていく様子がわかります。
成功すると、以下のような Image が作成されていることを確認できます(プロジェクト名が my-node-app
の場合)。
bash
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my-node-app_app latest <app_image_id> X minutes ago XXXMB
my-node-app_nginx latest <nginx_image_id> X minutes ago XXMB
... (ベースイメージなど)
これで、アプリケーションを実行するためのカスタム Docker Image が準備できました。次は docker-compose up
コマンドでこれらの Image を使ってコンテナを起動できます。
シナリオ2:Dockerfile やソースコードの変更と再ビルド
アプリケーション開発では、頻繁にソースコードや Dockerfile
を変更します。変更後、どのように Image を再ビルドすればよいでしょうか?
-
ソースコード (
server.js
など) を変更した場合:
Dockerfile
のCOPY . .
ステップ以降のキャッシュが無効になります。COPY
より前のステップ(ベースイメージの指定、依存関係のインストールなど)に変更がなければ、その部分はキャッシュが使われるため、ビルドは比較的速く完了します。
この場合も、単にdocker-compose build
を再度実行すればOKです。Compose は変更を検知し、キャッシュが利用できる部分はスキップしてくれます。 -
package.json
(依存関係) を変更した場合:
Dockerfile
のCOPY package*.json ./
ステップ以降のキャッシュが無効になります。特にRUN npm install
のステップは必ず再実行され、新しい依存関係がインストールされます。これもdocker-compose build
を実行すれば自動的に処理されます。 -
Dockerfile
を変更した場合:
変更した命令以降のステップのキャッシュが無効になります。例えば、新しいRUN
命令を追加したり、既存の命令の順番を変えたりした場合、その変更がキャッシュキーに影響を与え、キャッシュが使われなくなります。もちろん、docker-compose build
コマンドで変更が反映されます。
再ビルドの実行:
“`bash
ソースコードなどを変更した後
docker-compose build app # 特定のサービスだけ再ビルドする場合
または
docker-compose build # すべてのサービスを再ビルドする場合
“`
Compose は賢くキャッシュを利用してくれるため、変更があった部分だけが再実行されるのが一般的です。
シナリオ3:ビルドキャッシュを使わないクリーンビルド
前述の --no-cache
オプションを使います。何らかの問題が発生している場合や、本番リリース用の Image をビルドする場合などに使用します。
“`bash
docker-compose build –no-cache app # app サービスのみキャッシュなしでビルド
または
docker-compose build –no-cache # すべてのサービスをキャッシュなしでビルド
“`
これにより、すべてのビルドステップが最初からやり直されるため、時間がかかりますが、最も確実な方法です。
シナリオ4:特定のサービスのみビルド
アプリケーションに複数のサービスがあり、そのうち一部だけ Image を再ビルドしたい場合。
bash
docker-compose build web # web サービスのみビルド
docker-compose build app db # app と db サービスをビルド (db が image でなく build 定義なら)
コマンドの最後にビルドしたいサービス名を指定するだけです。依存関係はビルド時には考慮されませんが(Image があるかどうかは関係なくビルド自体を実行)、docker-compose up --build
の場合は依存関係を見て起動順序などが調整されます。
シナリオ5:ビルド引数 (args
) の活用
アプリケーションのバージョン番号を Image に埋め込みたい、開発環境と本番環境で異なる設定をビルド時に渡したい、といった場合にビルド引数が役立ちます。
app/Dockerfile
:
“`dockerfile
FROM node:18-alpine
ARG APP_VERSION=”development” # デフォルト値を指定
ARG BUILD_ENV=”development”
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
アプリケーションのバージョン情報をファイルに書き出す例
RUN echo “Version: ${APP_VERSION}, Environment: ${BUILD_ENV}” > /app/version.txt
EXPOSE 3000
CMD [“npm”, “start”]
“`
docker-compose.yml
:
“`yaml
version: ‘3.8’
services:
app:
build:
context: ./app
dockerfile: Dockerfile
args: # docker-compose.yml でデフォルト値を指定
APP_VERSION: ‘1.0.0’
BUILD_ENV: ‘staging’ # デフォルトは staging としておく
# … 他の設定
“`
ビルド引数を指定してビルド:
“`bash
docker-compose.yml のデフォルト値 (APP_VERSION=1.0.0, BUILD_ENV=staging) でビルド
docker-compose build app
コマンドラインオプションで上書きしてビルド (APP_VERSION=1.1.0, BUILD_ENV=production)
docker-compose build –build-arg APP_VERSION=1.1.0 –build-arg BUILD_ENV=production app
コマンドラインオプションで追加の引数を渡す(DockerfileでARGで受け取っている必要あり)
docker-compose.yml に BUILD_USER がなくても渡せる
docker-compose build –build-arg BUILD_USER=me app
“`
ビルド後に Image 内の /app/version.txt
を確認すると、渡した引数の値が反映されていることがわかります。
ビルド引数は、環境ごとに異なる設定ファイルを選択してコピーしたり、特定のフラグを立てたり、インストールするパッケージのバージョンを制御したり、といった高度な用途にも利用できます。
シナリオ6:マルチステージビルドとの連携 (target
)
前述の nginx
サービスの例で、もし静的ファイルをコピーするステップがあり、そのファイルを何らかのビルドプロセス(例: Webpack, Gulp など)で生成する必要がある場合、マルチステージビルドが有効です。
Dockerfile
(nginx 用 – マルチステージ化):
“`dockerfile
Stage 1: アセットビルドステージ
FROM node:18-alpine as asset_builder # asset_builder という名前を付ける
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build-assets # 静的ファイルを生成するスクリプトを実行 (例: dist ディレクトリに生成される)
Stage 2: 最終的な Nginx イメージステージ (軽量)
FROM nginx:alpine as production # production という名前を付ける
asset_builder ステージからビルド成果物をコピー
COPY –from=asset_builder /app/dist /usr/share/nginx/html/static/
カスタム設定ファイルをコピー
COPY nginx.conf /etc/nginx/nginx.conf
… 他の設定
“`
docker-compose.yml
:
“`yaml
version: ‘3.8’
services:
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
target: production # production ステージのみを最終イメージとする
ports:
– “80:80”
# … 他の設定
“`
ビルドの実行:
bash
docker-compose build nginx
このコマンドを実行すると、Docker Engine は Dockerfile 全体を解析し、production
ステージをビルドするために必要な asset_builder
ステージも自動的にビルドします。しかし、最終的に Image として保存されるのは production
ステージの結果のみになります。asset_builder
ステージで使用したビルドツールや中間ファイルは最終イメージに含まれないため、Image サイズを小さく保てます。
もし開発中に asset_builder
ステージだけをテストしたい場合などがあれば、一時的に target: asset_builder
に変更してビルドすることも可能です。
トラブルシューティング
docker-compose build
コマンドの実行中にエラーが発生した場合、原因と対処法を知っておくと迅速に解決できます。
ビルドエラーが発生した場合の確認点
ビルドエラーが発生すると、通常はエラーが発生した Dockerfile のステップ番号と、そのステップで実行された命令が表示されます。エラーメッセージをよく読んで、何が問題なのかを特定することが重要です。
-
Dockerfile の構文エラー:
unknown instruction
,invalid argument
などのメッセージが表示されます。- Dockerfile の記述ミス(命令のスペルミス、不正な引数、形式の誤りなど)を確認します。各命令の正しい使い方を Dockerfile の公式ドキュメントで参照しましょう。
- YAML インデントなどの問題は
docker-compose.yml
ではなくDockerfile
自体の問題として現れます。
-
ファイルが見つからない (
COPY
やADD
命令):failed to compute cache key: ... no such file or directory
のようなメッセージが表示されます。Dockerfile
のCOPY
やADD
命令で指定したファイルのパスが、docker-compose.yml
のbuild.context
で指定したディレクトリからの相対パスとして正しいか確認します。- ビルドコンテキストとして送られているか確認します。
context
ディレクトリ内に目的のファイルが存在するか、.dockerignore
で除外されていないか確認します。
-
コマンド実行エラー (
RUN
命令):The command '/bin/sh -c ...' returned a non-zero code: <エラーコード>
のようなメッセージが表示されます。RUN
命令で実行しているコマンド自体に問題があります。- コマンドのスペルミス、引数の間違い、必要なファイルやコマンドがコンテナイメージ内に存在しない、ネットワークの問題(
apt-get update
やnpm install
でパッケージダウンロードに失敗)、パーミッションの問題などが考えられます。 - エラーが発生した直前のステップまでで中間イメージが作成されている場合、そのイメージからコンテナを起動して、手動でコマンドを実行してみると原因を特定しやすいです。例えば、エラーが
RUN npm install
で発生したら、その直前のCOPY package*.json
まで実行された中間イメージを使ってdocker run -it <intermediate_image_id> bash
のようにコンテナに入り、手動でnpm install
を実行してみます。
-
リソース不足:
no space left on device
(ディスク容量不足)、out of memory
(メモリ不足) など。- ビルドホストのディスク容量やメモリが不足している可能性があります。不要な Docker Image やコンテナを削除するか、ビルド時にリソース制限オプションを試すか、よりリソースの多いホストでビルドします。
--no-cache
を試す
上記のような特定のエラーメッセージが出ないものの、なぜかビルドがうまくいかない、期待通りの結果にならない、という場合は、ビルドキャッシュが悪影響を与えている可能性があります。docker-compose build --no-cache
を実行して、キャッシュを使わないクリーンなビルドを試してみてください。時間がかかりますが、問題がキャッシュにあるのか、Dockerfile やコード自体にあるのかを切り分けるのに役立ちます。
詳細出力を見る
デフォルトのビルド出力はかなり詳細ですが、-q
オプションを使っている場合はそれを外します。より詳細なデバッグ情報を得るためには、Dockerfile の各 RUN
命令の前に set -eux
を追記して、実行されたコマンドとエラーコードを明示的に表示させるなどのテクニックもあります。
Compose ファイルや Dockerfile のパスを確認
docker-compose.yml
の build.context
や build.dockerfile
のパス指定が正しいか、また Dockerfile
内の COPY
や ADD
命令のパス指定が context
からの相対パスとして正しいか、改めて確認します。
Docker Engine の状態確認
Docker Engine 自体が正常に動作しているか確認します。docker info
, docker version
などのコマンドを実行したり、Docker デーモンを再起動したりしてみることも有効です。
build
コマンドの注意点とベストプラクティス
docker-compose build
コマンドを効果的に使うために、いくつかの注意点とベストプラクティスがあります。
ビルドコンテキスト (context
) の範囲
- 必要最低限のファイルだけを含める:
build.context
で指定したディレクトリ内のファイルは、すべて Docker Engine に送信されます(.dockerignore
で除外したものを除く)。関係ない大きなファイル(別のプロジェクト、ダウンロードした ISO イメージ、動画ファイルなど)が含まれていると、コンテキスト送信に時間がかかり、ビルド速度が遅くなります。また、意図しない情報(.git
ディレクトリなど)が Image に含まれる可能性もあります。 .dockerignore
の活用:.gitignore
と同様に、.dockerignore
ファイルをcontext
ディレクトリに配置することで、ビルドコンテキストから除外したいファイルやディレクトリを指定できます。これにより、転送量を減らし、不要なファイルを Image に含めないようにできます。必ず.dockerignore
を使いましょう。例:
gitignore
.git
.vscode
node_modules
dist
*.log
temp/
Dockerfile のベストプラクティス
docker-compose build
は内部で docker build
を実行するため、Dockerfile の書き方はビルドの効率や最終的な Image の品質に直結します。
- 小さいイメージを作る: ベースイメージとして Alpine Linux のような軽量なディストリビューションを選ぶ、マルチステージビルドを活用して最終イメージに必要なものだけを含める、不要なパッケージをインストールしない、インストール後にキャッシュファイルを削除する(
apt-get clean
,rm -rf /var/lib/apt/lists/*
など)といった工夫でイメージサイズを小さく保ちます。 - キャッシュを最大限活用する: Docker は Dockerfile の各命令をレイヤーとしてキャッシュします。ある命令とその前の命令からのファイルシステムの状態が変わっていなければ、その命令以降はキャッシュが使われます。これを意識して Dockerfile を書くことが重要です。
- 変更頻度の低いもの(依存関係のファイル
package.json
,requirements.txt
など)を先にCOPY
し、その後に依存関係のインストール (RUN npm install
,RUN pip install
など) を行うと、ソースコード (COPY . .
) を変更しても依存関係のインストールステップがキャッシュでスキップされやすくなります。 - 複数の
RUN
命令を&&
で連結して一つにまとめると、中間レイヤーの数を減らせます。ただし、読みやすさとのバランスも重要です。
- 変更頻度の低いもの(依存関係のファイル
- 各命令の役割を理解する:
FROM
: ベースイメージを指定。WORKDIR
: その後の命令を実行する作業ディレクトリを設定。COPY
: ローカルファイルやディレクトリをイメージにコピー。.dockerignore
を考慮。ADD
:COPY
と似ているが、リモート URL からファイルをダウンロードしたり、アーカイブを自動展開したりする機能もある。通常はCOPY
を使うのが推奨されます。RUN
: イメージ内でコマンドを実行し、新しいレイヤーを作成。パッケージインストールなどで使う。CMD
: コンテナ起動時にデフォルトで実行されるコマンド。Dockerfile に一つだけ。ENTRYPOINT
: コンテナ起動時に必ず実行される命令。CMD
はENTRYPOINT
の引数として扱われることが多い。ENV
: 環境変数を設定。ARG
: ビルド時変数。RUN
命令などで利用できる。ENV
と異なり、最終イメージには引き継がれません。EXPOSE
: コンテナがリッスンするポートをドキュメントとして公開。ネットワーク設定ではありません。VOLUME
: 永続化するデータを格納する場所を指定(ドキュメントや初期化用)。
- 公式ドキュメントやベストプラクティスガイドを参照する: Docker 公式ドキュメントには、効率的で安全な Dockerfile を作成するための詳細なベストプラクティスが掲載されています。これらを参考にしましょう。
docker-compose.yml
で image
と build
を混在させる場合の注意
一つの docker-compose.yml
ファイル内で、あるサービスは build
を使い、別のサービスは image
を使うのはよくあるパターンです(例: アプリケーションコードはビルド、データベースは公式 Image)。
docker-compose build
コマンドは、build
キーが定義されているサービスのみを対象とします。image
キーのみが定義されているサービスは無視されます。これらのサービスの Image を取得するには、別途 docker-compose pull
コマンドを実行する必要があります。
通常は docker-compose up --build
コマンドを使うことが多いでしょう。このコマンドは、Image が存在しないか、build
設定に変更があった場合に自動的にビルドを実行し、image
が指定されている場合はプルを行います。
CI/CD パイプラインでの build
コマンド
CI/CD パイプラインで Docker Image をビルドする場合、いくつか考慮点があります。
- ビルドキャッシュ: パイプラインの実行速度を上げるために、ビルドキャッシュをどのように扱うかが重要です。パイプラインによっては、前回のビルドで作成された Image をキャッシュとして利用できる機能(例: GitLab CI/CD の Docker in Docker with cache、BuildKit のリモートキャッシュ)があります。
cache_from
オプションや BuildKit の機能を活用しましょう。 - イメージのリポジトリへのプッシュ: ビルドした Image は、次のステップ(デプロイなど)や他の環境で利用できるように、Docker Registry (Docker Hub, GitLab Container Registry, AWS ECR など) にプッシュするのが一般的です。ビルド成功後に
docker-compose push
コマンドを実行したり、パイプラインの機能を使ったりします。 - 再現性: CI 環境でのビルドの再現性を確保するために、Dockerfile で使用するベースイメージのタグは
:latest
のような可変タグではなく、特定のバージョンを固定すること(例:node:18.18.0-alpine
)が強く推奨されます。
まとめ
この記事では、Docker Compose を使って独自の Docker Image をビルドするための docker-compose build
コマンドについて、初心者向けに詳細な解説を行いました。
docker-compose build
は、docker-compose.yml
ファイルのbuild
設定に基づいて Docker Image をビルドするコマンドです。docker-compose.yml
のbuild
キーでは、ビルドコンテキスト (context
)、Dockerfile
の指定、ビルド引数 (args
) などを設定できます。- 基本的な使い方は
docker-compose build
またはdocker-compose build [SERVICE...]
です。 --no-cache
,--pull
,--build-arg
などのオプションを使うことで、ビルドプロセスを制御・カスタマイズできます。- ビルドキャッシュの仕組みを理解し、
Dockerfile
の書き方を工夫することで、ビルド時間を大幅に短縮できます。 - エラーメッセージをよく確認し、
--no-cache
を試すことがトラブルシューティングの基本的なアプローチです。 .dockerignore
を適切に使い、Dockerfile のベストプラクティスに従うことで、より効率的でメンテナブルなビルドを実現できます。
docker-compose build
は、Docker Compose を使ったアプリケーション開発において非常に重要なコマンドです。この記事で学んだ知識を活かして、あなたのプロジェクトで独自の Docker Image を構築し、Docker Compose の利便性を最大限に引き出してください。
ビルドした Image を使ってコンテナを起動するには、引き続き docker-compose up
や docker-compose run
コマンドを学ぶことになります。これらのコマンドも合わせて習得し、Docker と Docker Compose を活用した快適な開発・運用ワークフローを構築しましょう!
参考資料
- Docker 公式ドキュメント: https://docs.docker.com/
- Docker Compose 公式ドキュメント: https://docs.docker.com/compose/
- Dockerfile リファレンス: https://docs.docker.com/reference/dockerfile/
- Dockerfile ベストプラクティス: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
これらの公式ドキュメントも合わせて参照すると、より深い理解が得られます。