1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker初心者のためのNext.js+FastAPI開発環境構築ガイド

Last updated at Posted at 2025-06-21

Docker初心者のためのNext.js+FastAPI開発環境構築ガイド

はじめに

プログラミング初心者にとって、「私の環境では動くのに、なぜ他の人のPCでは動かないの?」という経験は誰しもあるのではないでしょうか。この問題を根本的に解決するのがDockerです。

この記事では、Docker完全初心者の私が、Next.js(フロントエンド)とFastAPI(バックエンド)を使った開発環境をDockerで構築する過程で学んだことを、実際のコードとともに解説します。

課題提起:「私の環境では動くのに...」問題

よくある開発現場の問題

  • 新メンバー参加時: 「環境構築に3日かかりました...」
  • チーム開発: 「私のPCでは動くのに、なぜ?」
  • デプロイ時: 「ローカルでは問題なかったのに本番で動かない」
  • ライブラリ更新: 「バージョン違いでエラーが...」

これらの問題を解決するのがDockerです。

Dockerの基本概念

Dockerとは何か

Docker = アプリケーションとその実行環境を一つにまとめるツール

重要な用語とその関係

Dockerfile(レシピ)
    ↓ docker build
イメージ(冷凍食品)
    ↓ docker run  
コンテナ(実際に動いているアプリ)
用語 役割 実際の例
Dockerfile 環境構築の手順書 「Node.js 18をインストールして、npmでパッケージを入れて...」
イメージ 環境をパッケージ化したもの アプリ + Node.js + 必要なライブラリが全て含まれた状態
コンテナ イメージから起動した実行環境 実際にブラウザでアクセスできる状態
Docker Compose 複数のコンテナを管理 フロントエンドとバックエンドを同時起動

実装:Next.js + FastAPI環境の構築

プロジェクト構造

my-docker-app/
├── docker-compose.yml          # 複数サービスの管理
├── .env                        # 環境変数の設定
├── frontend/
│   ├── Dockerfile             # フロントエンド環境の設計書
│   ├── package.json
│   └── src/app/page.tsx
└── backend/
    ├── Dockerfile             # バックエンド環境の設計書
    ├── main.py
    └── requirements.txt

Dockerfileの作成と最適化

フロントエンド(Next.js)のDockerfile

# ベースイメージ:Node.js 18の軽量版
FROM node:18-alpine

# 作業ディレクトリを設定
WORKDIR /app

# package.jsonを先にコピー(依存関係の変更がない限りキャッシュを利用)
COPY package*.json ./

# 依存関係をインストール
RUN npm install

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

# アプリケーションが使用するポートを公開
EXPOSE 3000

# アプリケーションを開発モードで起動
CMD ["npm", "run", "dev"]

最適化のポイント:

  • node:18-alpine:軽量なLinuxベース
  • package*.jsonを先にコピー:依存関係が変わらない限りキャッシュを活用
  • レイヤーキャッシュにより、ビルド時間を短縮

バックエンド(FastAPI)のDockerfile

# ベースイメージ:Python 3.11の軽量版
FROM python:3.11-slim

# 作業ディレクトリを設定
WORKDIR /app

# requirements.txtを先にコピー
COPY requirements.txt .

# Python依存関係をインストール
RUN pip install -r requirements.txt

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

# アプリケーションが使用するポートを公開
EXPOSE 8000

# FastAPIアプリケーションを起動
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

Docker Composeによる複数サービス構成

docker-compose.yml

version: '3.8'

services:
  # フロントエンドサービス
  frontend:
    build: ./frontend           # Dockerfileの場所
    ports:
      - "${FRONTEND_PORT}:3000"  # 環境変数でポート指定
    volumes:
      - ./frontend:/app        # ファイル変更をリアルタイム反映
      - /app/node_modules      # node_modulesは除外
    env_file:
      - .env                   # 環境変数ファイル読み込み
    environment:
      - NODE_ENV=${NODE_ENV}
      - API_URL=${API_URL}
    depends_on:
      - backend                # バックエンドが起動してから開始
    logging:                   # ログ管理設定
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  # バックエンドサービス  
  backend:
    build: ./backend
    ports:
      - "${BACKEND_PORT}:8000"   # 環境変数でポート指定
    volumes:
      - ./backend:/app         # ファイル変更をリアルタイム反映
    env_file:
      - .env                   # 環境変数ファイル読み込み
    environment:
      - DEBUG=${DEBUG}
      - PYTHONPATH=/app
    logging:                   # ログ管理設定
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Docker Composeの利点:

  • 複数のサービスを一括管理
  • サービス間の依存関係を定義
  • 開発時のファイル変更をリアルタイム反映
  • 環境変数による設定管理
  • ログローテーション機能

アプリケーションコード

コンテナ化するアプリケーションを用意するために、
ごくごくシンプルな画面を実装

フロントエンド(Next.js)

frontend/src/app/page.tsx

'use client'
import { useState } from 'react'

export default function Home() {
  const [message, setMessage] = useState<string>('')
  const [loading, setLoading] = useState<boolean>(false)

  const callAPI = async () => {
    setLoading(true)
    try {
      const response = await fetch('http://localhost:8000/hello')
      const data = await response.json()
      setMessage(data.message)
    } catch (error) {
      setMessage('エラー:バックエンドAPIに接続できません')
    } finally {
      setLoading(false)
    }
  }

  return (
    <div style={{ padding: '50px', textAlign: 'center' }}>
      <h1>Docker環境で動作するWebアプリ</h1>
      <button onClick={callAPI} disabled={loading}>
        {loading ? '通信中...' : 'バックエンドAPIを呼び出す'}
      </button>
      {message && <p style={{ marginTop: '20px', fontSize: '18px' }}>{message}</p>}
    </div>
  )
}

バックエンド(FastAPI)

backend/main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI(title="Docker練習API", version="1.0.0")

# CORS設定:フロントエンドからのアクセスを許可
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_methods=["GET", "POST"],
    allow_headers=["*"],
)

@app.get("/hello")
def get_hello_message():
    return {
        "message": "Hello from FastAPI! Dockerコンテナで動作中です!",
        "status": "success"
    }

@app.get("/")
def health_check():
    return {"status": "API is running in Docker container"}

backend/requirements.txt

fastapi==0.104.1
uvicorn==0.24.0

開発環境と本番環境の一致性確保

共通にするもの vs 分けるもの

Dockerの最大の利点は「アプリケーションコードと基本環境は同じ、設定だけを変える」ことです。

🔄 共通にするもの(重要!)

項目 理由
アプリケーションコード バグの原因となる差異を防ぐ
ベースイメージ node:18-alpine, python:3.11-slim
依存関係 package.json, requirements.txt
基本的なDockerfile コンテナの作り方を統一

⚙️ 分けるもの(設定のみ)

項目 開発環境 本番環境 理由
起動コマンド npm run dev npm run build && npm start 開発時はホットリロード、本番は最適化
ポート公開 3000:3000 80:3000 本番は標準ポート使用
ファイル同期 あり(volumes) なし 開発時のみコード変更を反映
環境変数 NODE_ENV=development NODE_ENV=production デバッグ情報の表示制御
ログレベル 詳細 エラーのみ 本番はパフォーマンス重視

実際の設定例

開発環境(docker-compose.yml)

version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"              # 開発用ポート
    volumes:
      - ./frontend:/app          # ホストの./frontendフォルダ ← → コンテナの/appフォルダを同期
      - /app/node_modules        # コンテナの/app/node_modulesは同期から除外
    environment:
      - NODE_ENV=development     # 開発モード
    command: ["npm", "run", "dev"]  # ホットリロード付きで起動

  backend:
    build: ./backend
    ports:
      - "8000:8000"              # 開発用ポート
    volumes:
      - ./backend:/app           # ホストの./backendフォルダ ← → コンテナの/appフォルダを同期
    environment:
      - DEBUG=true               # デバッグ情報を表示
    command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

volumes の仕組みを理解する

volumes = ホストPC(あなたのPC)とコンテナの間でファイルを共有する仕組み

書き方の違いによる意味の変化

volumes:
  - ./frontend:/app          # ホスト:コンテナ → 双方向同期
  - /app/node_modules        # コンテナのみ → 同期から除外

重要なポイント:

  • : (コロン)があるかないかで意味が変わる

詳しい解説

パターン1: ホスト連携(:あり)
- ./frontend:/app
  ↑ホスト側  ↑コンテナ側

意味:

  • あなたのPCの ./frontend フォルダとコンテナの /app フォルダを 双方向で同期
  • ファイル変更が即座に反映される
パターン2: 除外・独立(:なし)
- /app/node_modules
  ↑コンテナ側のみ

意味:

  • コンテナの /app/node_modules だけを指定
  • ホスト側とは 連携しない、コンテナ内で 独立して管理

処理の順序

volumes:
  - ./frontend:/app          # ① まず全体を同期
  - /app/node_modules        # ② その後、node_modulesだけ除外

Dockerの処理順序:

  1. 第1段階: ./frontend の全内容を /app に同期
  2. 第2段階: /app/node_modules だけは同期から外して、コンテナ独自のものを使う

具体例で理解

あなたのPC(ホスト側):

frontend/
├── src/
│   └── page.tsx
├── package.json
└── node_modules/     ← これは重くて問題あり
    └── (数万ファイル、Windows用)

volumes設定後のコンテナ内:

/app/
├── src/
│   └── page.tsx      ← ホストと同期 ✅
├── package.json      ← ホストと同期 ✅  
└── node_modules/     ← ホストとは別物、コンテナ独自 ✅
    └── (Linux用のファイル)

なぜ node_modules を除外するのか

項目 ホストのnode_modules コンテナ独自のnode_modules
OS対応 Windows/Mac用 Linux用
ファイル数 数万ファイル 数万ファイル
同期速度 非常に遅い 不要(同期しない)
エラーリスク OS違いでエラー 環境に最適化

volumes の実際の効果

# volumes設定により実現される開発フロー
1. VSCodeでpage.tsxを編集
2. 瞬時にコンテナに反映される(./frontend:/app)
3. でもnode_modulesはコンテナ専用のまま(/app/node_modules)
4. ブラウザで変更確認 ✅

本番環境(docker-compose.prod.yml)

version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "80:3000"                # 本番用標準ポート
    # volumes: なし             # ファイル同期は不要
    environment:
      - NODE_ENV=production      # 本番モード
    command: ["npm", "run", "build", "&&", "npm", "start"]  # 最適化ビルド

  backend:
    build: ./backend
    ports:
      - "8000:8000"
    # volumes: なし             # ファイル同期は不要
    environment:
      - DEBUG=false              # デバッグ情報を非表示
    command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]  # reloadなし

なぜこの分け方が重要なのか

❌ 間違った分け方

# 開発環境でPython 3.10、本番でPython 3.11を使う
❌ 「バージョン違いで動かない!」

✅ 正しい分け方

# 同じPython 3.11を使い、起動オプションだけ変える
✅ 「同じ環境で、設定だけ最適化」

開発環境での実行

# 開発環境で起動
docker-compose up

# 本番設定で起動(テスト用)
docker-compose -f docker-compose.prod.yml up

一致性確保の具体例

# 共通のDockerfile(frontend/Dockerfile)
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000

# ⚠️ コマンドはdocker-compose.ymlで指定(環境ごとに変更)
# CMD ["npm", "run", "dev"]  ← ここは書かない!

重要なポイント:

  • Dockerfileは共通、起動方法だけdocker-compose.ymlで変更
  • アプリケーションコードは100%同一
  • 環境変数で動作モードを切り替え

動作確認とトラブルシューティング

基本的な動作確認

  1. アプリケーション起動

    docker-compose up
    
  2. アクセス確認

    • フロントエンド: http://localhost:3000
    • バックエンドAPI: http://localhost:8000
    • APIドキュメント: http://localhost:8000/docs
  3. 動作テスト

    • ボタンをクリック
    • 「Hello from FastAPI! Dockerコンテナで動作中です!」が表示される

よくあるエラーと解決方法

1. ポートが既に使用されている

Error: Port 3000 is already in use

解決方法:

# 使用中のプロセスを確認
lsof -i :3000

# プロセスを終了またはポート番号を変更

2. イメージのビルドエラー

# キャッシュをクリアして再ビルド
docker-compose build --no-cache

3. コンテナの状態確認

# 動作中のコンテナを確認
docker ps

# ログを確認
docker-compose logs frontend
docker-compose logs backend

Dockerの利点まとめ

1. 環境統一の実現

  • 問題: 「私のPCでは動くのに...」
  • 解決: 全員が同じDockerイメージを使用

2. 新メンバーの環境構築簡素化

  • 従来: Node.js、Python、各種パッケージの個別インストール
  • Docker: docker-compose up一つで完了

3. 本番環境との一致性

  • 従来: 開発環境と本番環境の差異でトラブル
  • Docker: 同じイメージを使用して一貫性を保証

4. スケーラビリティ

  • 従来: サーバー追加時の環境構築作業
  • Docker: イメージの複製で簡単にスケールアウト

まとめ

Docker初心者として最も重要だった学びは、**「Dockerは開発・運用の効率化と安定性向上のためのツール」**だということです。

重要なポイント

  1. Dockerfile: 環境構築手順の標準化
  2. Docker Compose: 複数サービスの統合管理
  3. イメージの再利用: 開発から本番まで一貫した環境
  4. トラブルシューティング: ログとコンテナ状態の確認方法

次のステップ

  • マルチステージビルドによるイメージサイズ最適化
  • 環境変数を使った設定管理の改善
  • データベースコンテナの追加
  • CI/CDパイプラインとの連携
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?