あなたがフロントエンド開発者で Docker を使ったことがないなら、おそらく「自分のマシンでは動くんだけど」というセリフを聞いたことがあるでしょう。Docker はまさにその問題を解決します — アプリケーションをすべての依存関係とともに コンテナ にパッケージ化し、どこでも同じように動かせます。あなたの Mac でも、同僚の PC でも、CI でも、本番環境でも。このガイドでは、実際のフロントエンドプロジェクトに焦点を当てて、ゼロからデプロイまでを解説します。
Docker とは何か(回りくどい説明なしで)
Docker は、アプリケーションを実行するための隔離された環境(コンテナ)を作成するツールです。仮想マシンとは異なり、コンテナは軽量です — システムのカーネルを共有し、数秒で起動します。
- イメージ — 「型」。ベースとなる OS、Node.js、依存関係、そしてあなたのコードを含みます。
- コンテナ — イメージから実行されている「インスタンス」。同じイメージから複数のコンテナを起動できます。
- Dockerfile — イメージをどう構築するかを定義する「レシピ」。
- docker-compose — 複数のコンテナ(アプリ + データベース + Redis)を 1 つのコマンドでオーケストレーションします。
インストール
お使いの OS 向けの Docker Desktop をインストールしてください。Docker Engine、Docker CLI、Docker Compose が含まれています:
- macOS: docker.com/products/docker-desktop からダウンロードするか、
brew install --cask dockerを使用 - Windows: Docker Desktop をダウンロード(WSL2 が必要)
- Linux: apt/yum 経由でインストールするか、公式ドキュメントに従う
インストールを確認します:
docker --version
# Docker version 24.x.x
docker compose version
# Docker Compose version v2.x.x最初の Dockerfile:Next.js アプリケーション
Next.js アプリケーション用の Dockerfile を作成しましょう。概念を理解するために、可能な限りシンプルなものにします:
# Dockerfile
FROM node:20-alpine
WORKDIR /app
# 依存関係を先にコピー(レイヤーキャッシュ)
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
# コードをコピー
COPY . .
# ビルド
RUN pnpm build
# 実行
EXPOSE 3000
CMD ["pnpm", "start"]各行が 1 つの レイヤー です。Docker は各レイヤーをキャッシュします — package.json が変更されていなければ、依存関係のインストールをスキップします。だからこそ、コードよりも 先に 依存関係をコピーするのです。
# イメージをビルド
docker build -t meu-app .
# コンテナを実行
docker run -p 3000:3000 meu-app
# アクセス: http://localhost:3000.dockerignore:イメージに含めないもの
.gitignore と同じように、.dockerignore はイメージ内にコピーすべきでないものを定義します。これによりサイズが削減され、ビルドが高速化します:
# .dockerignore
node_modules
.next
.git
.env
.env.local
README.md
.vscode
.claudeマルチステージビルド:最適化された本番イメージ
基本的な Dockerfile は動作しますが、最終イメージは重くなります — すべての devDependencies、ソースコード、ビルドファイルを含んでいるからです。本番環境ではビルドの出力だけが必要です。マルチステージビルドがこれを解決します:
# Dockerfile (multi-stage)
# Stage 1: 依存関係をインストール
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
# Stage 2: ビルド
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN corepack enable && pnpm build
# Stage 3: 本番(必要なものだけ)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# ビルドから必要なものだけをコピー
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]違いは非常に大きいです:
- 基本的な Dockerfile: 約 1GB(完全な node_modules、ソースコード、devDependencies)
- マルチステージ: 約 150MB(Node.js + ビルド出力 + 静的ファイルのみ)
Docker Compose:完全な開発環境
ローカル開発では、docker-compose が複数のサービスをオーケストレーションします。実際のシナリオ:Next.js アプリ + PostgreSQL + Redis:
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
volumes:
- .:/app
- /app/node_modules
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:16-alpine
ports:
- '5432:5432'
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
cache:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
pgdata:# 1 つのコマンドですべてを起動
docker compose up -d
# ログを見る
docker compose logs -f app
# すべてを停止
docker compose down
# 停止してボリュームをクリア(DB のリセット)
docker compose down -vvolumes: - .:/app はローカルコードをコンテナ内にマウントします — コードの変更はリビルドなしで即座に反映されます。- /app/node_modules は、ローカルの node_modules がコンテナのものを上書きするのを防ぎます。
開発環境 vs 本番環境
2 つの Dockerfile を用意するか、compose で target を使うのが一般的です:
# docker-compose.dev.yml
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- '3000:3000'
volumes:
- .:/app
- /app/node_modules
command: pnpm dev# Dockerfile.dev (シンプル、マルチステージなし)
FROM node:20-alpine
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install
COPY . .
EXPOSE 3000
CMD ["pnpm", "dev"]# 開発
docker compose -f docker-compose.dev.yml up
# 本番
docker compose up --build環境変数
シークレットを Dockerfile に決して書かないでください。.env または docker-compose 経由で環境変数を使います:
# .env (コミットしない)
DATABASE_URL=postgresql://user:pass@db:5432/myapp
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_API_URL=http://localhost:3000/api# docker-compose.yml
services:
app:
env_file:
- .env
# または個別に:
environment:
- NODE_ENV=production必須コマンド
私が日常的に使うコマンドです:
# イメージ
docker build -t app . # イメージをビルド
docker images # イメージを一覧表示
docker rmi app # イメージを削除
# コンテナ
docker ps # 実行中のコンテナ
docker ps -a # すべて(停止中を含む)
docker logs -f <container> # リアルタイムのログ
docker exec -it <container> sh # コンテナに入る
docker stop <container> # 停止
docker rm <container> # 削除
# Compose
docker compose up -d # バックグラウンドで起動
docker compose down # 停止
docker compose down -v # 停止 + ボリュームをクリア
docker compose logs -f # すべてのサービスのログ
docker compose build --no-cache # キャッシュなしでリビルド
# クリーンアップ
docker system prune -a # 未使用のものをすべて削除本番環境へのデプロイ
マルチステージイメージが用意できれば、デプロイはレジストリに送信してサーバーで実行するだけです:
# ビルドとタグ付け
docker build -t meu-app:latest .
# Docker Hub(または ECR、GCR など)へプッシュ
docker tag meu-app:latest usuario/meu-app:latest
docker push usuario/meu-app:latest
# サーバー上で(プルして実行)
docker pull usuario/meu-app:latest
docker run -d -p 3000:3000 --env-file .env usuario/meu-app:latestRender、Railway、Fly.io などのサービスは Dockerfile を自動的に検出し、追加設定なしでビルドとデプロイを実行します。
よくあるエラー
- イメージ内の node_modules + ローカルボリューム — ボリュームがコンテナの node_modules を上書きします。解決策:volumes に
- /app/node_modulesを追加する。 - イメージが重すぎる —
node:20-alpineではなくnode:20を使っている。Alpine は約 5 倍小さいです。 - ビルドが遅い — レイヤーキャッシュを活用できていません。コードよりも先に
package.jsonをコピーしましょう。 - ポートにアクセスできない —
EXPOSEを忘れているか、docker run -pでのポートマッピングを忘れています。 - 環境変数が届かない — Next.js では、
NEXT_PUBLIC_*変数はランタイムだけでなく ビルド時 に利用可能である必要があります。
まとめ
Docker は DevOps だけのものではありません — あらゆる開発者にとっての 生産性 ツールです。Dockerfile と docker-compose があれば、開発からデプロイまで環境が一貫していることを保証できます。学習への投資はリターンに比べて小さいです:環境起因のバグの減少、より速いオンボーディング、そして予測可能なデプロイ。
