React アプリを Docker 化する方法: 開発者向けのステップバイステップガイド

投稿日 12月 10日, 2024年

あなたが私のような人なら、Reactを使用して洗練された応答性の高いユーザーインターフェイスを作成するのが大好きです。 しかし、一貫した開発環境を設定し、スムーズなデプロイを確保することは、複雑になることもあります。 そこで、Docker が窮地を救うお手伝いをします。

シニアDevOpsエンジニアおよびDockerキャプテンとして、私はコンテナ化の海をナビゲートし、Dockerがワークフローにどのように革命をもたらすかを直接目の当たりにしてきました。 このガイドでは、React アプリを Docker 化して開発プロセスを合理化し、「自分のマシンで動作する」という厄介な問題を排除し、シームレスなデプロイで同僚を感動させる方法を共有します。

DockerとReactの世界に飛び込みましょう!

2400×1260 ドッカーエバーグリーンロゴブログD 1

なぜReactアプリケーションをコンテナ化するのですか?

「なぜわざわざReactアプリをコンテナ化する必要があるのか」と疑問に思われるかもしれません。いい質問ですね! コンテナ化には、開発とデプロイメントのゲームを向上させる次のような魅力的な利点があります。

  • CI/CD パイプラインの合理化: React アプリを Docker コンテナにパッケージ化することで、開発から本番環境まで一貫した環境を作成できます。 この一貫性により、継続的インテグレーションと継続的デプロイ (CI/CD) パイプラインが簡素化され、ビルドとデプロイ中の環境固有の問題のリスクが軽減されます。
  • 依存関係管理の簡素化: Docker は、アプリのすべての依存関係をコンテナ内にカプセル化します。 これは、悪名高い「私のマシンで動作する」というジレンマに対処する必要がなくなることを意味します。 すべてのチームメンバーとデプロイメント環境が同じセットアップを使用しているため、スムーズなコラボレーションが保証されます。
  • リソース管理の改善: コンテナは軽量で効率的です。 仮想マシンとは異なり、Docker コンテナはホストシステムのカーネルを共有するため、同じハードウェア上でより多くのコンテナを実行できます。 この効率性は、アプリケーションをスケーリングしたり、本番環境でリソースを管理したりするときに重要です。
  • 競合のない孤立した環境: Docker は、アプリケーションに分離された環境を提供します。 この分離により、同じマシン上の異なるプロジェクトの依存関係または構成間の競合が防止されます。 それぞれが独自の依存関係を持つ複数のアプリケーションを実行でき、互いに足を引っ張ることはありません。

React と Docker を使い始める

先に進む前に、Reactアプリのコンテナ化を開始するために必要なものがすべて揃っていることを確認しましょう。

必要なツール

Docker の簡単な紹介

Docker は、エンタープライズ対応ツール、クラウド サービス、信頼できるコンテンツ、およびワークフローの合理化と開発効率の最大化を支援する共同コミュニティの包括的なスイートを提供します。 Docker 生産性プラットフォームを使用すると、開発者はアプリケーションをコンテナ (ソフトウェアの実行に必要なすべてのものを含む標準化されたユニット) にパッケージ化できます。 コンテナは、アプリケーションがデプロイされている場所に関係なく、同じように実行されることを保証します。

ReactプロジェクトをDocker化する方法

それでは、本題に入りましょう。 このプロセスを段階的に進め、最後には、Dockerコンテナ内でReactアプリを実行できるようになります。

ステップ 1:Reactアプリをセットアップする

すでに React アプリをお持ちの場合は、この手順をスキップできます。 そうでない場合は、作成しましょう。

npm create vite@latest my-react-app -- --template react-ts

このコマンドは、 my-react-appというフォルダ内に新しいReact + TypeScriptアプリケーションを作成します。必要なnpmパッケージをインストールし、開発サーバーを起動します。サーバーは http://localhost:5173で利用可能になります。
Viteは、旧来のCreate React App(CRA)に比べてはるかに高速で軽量かつ使いやすいため、Reactプロジェクトの 標準的な選択肢 となっています。CRAは現在は積極的にメンテナンスされていません。

ステップ 2: Dockerfile を作成する

プロジェクトのルートディレクトリで、コンテナ内でReactアプリの動作を定義するDockerファイルを作成します。ここでは 2つの別々のDockerファイルを使います。

  • Dockerfile.dev →ローカル開発に最適化されており(ホットリロード対応付き)、
  • Dockerfile →本番環境向けに最適化されています(静的ファイルのビルドと提供)。

この分離により、開発は軽量化されつつ、本番イメージが小さく、安全で、展開に備えられる状態を保証します。

開発用のDockerfile

開発時には、 Dockerfile.devという名前のファイルを作成します。これにより、そのファイルは開発専用であることが明確になり、本番のDockerファイルとは別に保存されます。専用の開発用Dockerファイルは、より高速なビルド、ホットリロード、そして本番環境の問題を混ぜることなくスムーズな開発者体験を保証します。

# =========================================
# Stage: Development (Vite React.js App)
# =========================================
ARG NODE_VERSION=24.14.0-alpine

FROM node:${NODE_VERSION} AS dev

# Set working directory inside the container
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy rest of the source code
COPY . .


# Ensure the "node" user owns the application files
RUN chown -R node:node /app


# Switch to the built-in non-root "node" user
USER node


# Expose Vite dev server port
EXPOSE 5173

# Run Vite in dev mode, accessible outside the container
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]


ここで何が起こっているのですか?

  • FROM node:24.14.0-alpine:最新のNode.jsを使っていますAlpine Linuxを基盤としたNode.jsのLTSバージョンです。
    注意:Node.jsイメージは定期的に監視する必要があります。タグは期限切れやセキュリティパッチやアップデートを含むために再構築される場合があります。ベースイメージタグを常に最新の状態に保つことは、アプリケーションが最新の安定的かつ安全なリリースの恩恵を受けられるようにすることが重要です。
  • WORKDIR /app: コンテナ内の作業ディレクトリを設定します。
  • *COPY package.json ./**: package.jsonpackage-lock.json を作業ディレクトリにコピーします。
  • RUN npm install: package.json で指定された依存関係をインストールします。
  • COPY . .: ローカル ディレクトリからコンテナにすべてのファイルをコピーします。
  • RUN chown -R node:node /app: 「ノード」ユーザーがアプリケーションファイルを所有していることを確認しましょう。これにより、依存関係のインストールやビルドアーティファクトの書き込み時の権限エラーを防ぎます。
  • USER node :公式Node.jsイメージが提供する組み込みの非root「ノード」ユーザーに切り替え、コンテナ内のroot権限を回避することでセキュリティを向上させます。
  • EXPOSE 5173: はVite React.js開発サーバーで使われているデフォルトのポートであるポート 5173 を公開します。
  • CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]:コンテナのデフォルトコマンドを定義し、Vite開発サーバーを起動し、すべてのネットワークインターフェースでアクセスできるようにします。

多段階ビルドを使用した運用 Dockerfile

ReactアプリをDocker化する際には、本番ビルドを配信する方法が必要です。従来、そして本番環境では、開発者はDocker内でNginxを使ってコンパイル済みの静的ファイルを提供していました。例えば、Kristiyan Velkovが作成した公式Dockerサンプルガイドに従うことができます: DockerでReactアプリケーションをコンテナ化する

代わりに、この例では serveパッケージ を使用します。これはNode.jsで構築された軽量の静的ファイルサーバーです。

運用環境に対応したイメージの場合は、マルチステージ ビルドを使用してイメージ サイズを最適化し、セキュリティを強化します。 

# =========================================
# Stage 1: Build the React (Vite) Application
# =========================================

ARG NODE_VERSION=24.14.0-alpine
# Use a lightweight Node.js image for building (customizable via ARG)
FROM node:${NODE_VERSION} AS builder


# Set the working directory inside the container
WORKDIR /app


# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json package-lock.json ./


# Install project dependencies using npm ci (ensures a clean, reproducible install)
RUN --mount=type=cache,target=/root/.npm npm ci


# Copy the rest of the application source code into the container
COPY . .

# Build the React.js application (outputs to /app/dist)
RUN npm run build


# =========================================
# Stage 2: Serve static files with Node.js + `serve`
# =========================================
FROM node:${NODE_VERSION} AS runner


# Set the environment to production for smaller + optimized installs
ENV NODE_ENV=production


# Set the working directory inside the container
WORKDIR /app


# Copy only the production build output from the builder stage
COPY --link --from=builder /app/dist ./dist


# Install only the `serve` package (no global install, pinned version)
RUN --mount=type=cache,target=/root/.npm npm install serve@^14.2.6 --omit=dev


# Run the container as a non-root user for security best practices
USER node


# Expose port 3000 (the same port configured in "serve -l 3000")
EXPOSE 3000


# Run `serve` directly to serve the built app
CMD ["npx", "serve", "-s", "dist", "-l", "3000"]


# Build Stage
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
 
# Production Stage
FROM nginx:stable-alpine AS production
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

説明

ビルドステージ(ステージ 1 – ビルダー):

  • FROM node:${NODE_VERSION} AS builder: 軽量Node.js 24 Alpineイメージを使ってReact(Vite)アプリを構築します。
  • COPY package.json package-lock.json ./:Docker層キャッシュを最適化するために、依存関係マニフェストを最初にコピーします。
  • RUN npm ci: ロックファイルに基づいて依存関係をクリーンかつ再現可能な方法でインストールします。
  • COPY . .: アプリケーションの完全なソースコードをコンテナにコピーします。
  • RUN npm run build: は、dist/ディレクトリ内でアプリを最適化された静的ファイルにコンパイルします。

制作段階(ステージ 2 – ランナー):

  • FROM node:${NODE_VERSION} AS runnerアプリを実行するために新しいNode.js 24 Alpine画像が始まります。
  • ENV NODE_ENV=production:本番モードでNode.js実行を保証します。
  • WORKDIR /app: はワーキングディレクトリを設定します。
  • COPY --from=builder /app/dist ./dist:ビルダー段階で作成された本番ファイルのみをコピーします。
  • RUN npm install serve@^14.2.6 --omit=dev: は静的ビルドを提供する serve パッケージ(ピン留めされたバージョン)のみをインストールし、グローバルインストールや開発者依存関係なし。
  • USER node:セキュリティのベストプラクティスのためにルート権限を解除します。
  • EXPOSE 3000:serveが使用しているポート3000を露出します。
  • CMD ["npx", "serve", "-s", "dist", "-l", "3000"]静的ビルドをホストする serve プロセスが開始されます。

利点

  • イメージ サイズが小さい: 最終的なイメージには、運用ビルドと Nginx のみが含まれます。
  • セキュリティの強化: 開発の依存関係と Node.js ランタイムを運用イメージから除外します。
  • パフォーマンス最適化:静的ファイルを効率的に Serve します。

ステップ 3: .dockerignore を作成する ファイル

.gitignoreと同じように Git が特定のファイルを無視するのを助けます .dockerignore イメージのビルド時に除外するファイルまたはディレクトリを Docker に指示します。 .dockerignoreを作成する プロジェクトのルートディレクトリにあるファイル:

# =========================================
# Dependencies and build output
# =========================================
node_modules/
dist/
out/
.tmp/
.cache/

# =========================================
# Vite, Webpack, and React-specific artifacts
# =========================================
.vite/
.vitepress/
.eslintcache
.npm/
coverage/
jest/
cypress/
cypress/screenshots/
cypress/videos/
reports/

# =========================================
# Environment and config files (sensitive)
# =========================================
*.env*
!.env.production  # Allow production env file if needed
*.log

# =========================================
# TypeScript build artifacts
# =========================================
*.tsbuildinfo

# =========================================
# Debug and lockfiles
# =========================================
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# =========================================
# Local development files
# =========================================
.git/
.gitignore
.vscode/
.idea/
*.swp
.DS_Store
Thumbs.db

# =========================================
# Docker-related files
# =========================================
Dockerfile
.dockerignore
docker-compose.yml
docker-compose.override.yml

# =========================================
# AI / LLM-related files
# =========================================
.openai/
anthropic/
.azureai/
.vercel_ai/
*.ai.json
*.ai.yaml
*.ai.yml
*.llm.json
*.llm.yaml
*.llm.yml
chat_history/
ai_logs/
ai_cache/

不要なファイルを除外すると、画像サイズが小さくなり、ビルドプロセスが高速化されます。

詳しくは .dockerignoreの参考文献をご覧ください。

ステップ 4: Docker 化された React アプリをビルドして実行する

制作イメージを構築する:
プロジェクトのルートから、次のステップを実行してください:

docker build -t my-react-app -f Dockerfile .

このコマンドはマルチステージ Dockerfile を使って本番イメージを構築し、 my-react-appとしてタグ付けします。デフォルトでは最終ランナーステージを使用し、コンパイルされたReactアプリと軽量な serve パッケージのみを含む、より小さく最適化されたイメージになります。

コンテナを動かす:

docker run -p 3000: my-react-app3000

現在、あなたのアプリは http://localhost:3000で利用可能になりました。

開発イメージを構築する:
Vite開発サーバーをDocker内で動かしたい場合は、専用の開発用Dockerファイルを使いましょう:

Docker build -t my-react-app-dev -f Dockerfile.dev .

コンテナを動かす:

docker run -p 5173: my-react-app-dev5173


このイメージにはすべてのビルドツールが含まれており、Viteの開発サーバーを動かすため、 http://localhost:5173 でホットリロードが可能です。

手記:

Dockerfile.devビルド(開発)は、コンパイルやホットリロードに必要なツールチェーン全体を含むため、より大きな画像を生成します。
Dockerfileでビルド(本番環境)を使えば、より小さく展開可能なイメージが生成され、最終ビルド出力のみに対応します。

Docker コンテナの実行

開発イメージの場合:

docker run -p 5173: my-react-app-dev5173

製品イメージの場合:

docker run -p 30000: my-react-app30000

アプリケーションへのアクセス

次に、ブラウザを開いて次の場所に移動します。

  • http://localhost:5173 (開発用)
  • http://localhost:3000 (制作用)

Dockerコンテナ内でReactアプリが実行されていることがわかります。

ステップ 5: Docker Compose を使用してマルチコンテナのセットアップを行う

以下の docker-compose.yml は、DockerでReactフロントエンドをサービスとして動かす ためのリファレンス構成 として機能します。このシステムは2つのサービスを定義しています。

  • dev →Viteを使って 開発モードで アプリを動かします。
  • prod →サービスを提供した最適化されたビルドを使って 、本番モードで アプリを動かします。

プロジェクトのルートに compose.yml という名前のファイルを作成します:

詳しくはこちら:Docker Compose

compose.ymlファイルを作成します。

services:
  prod:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production


  dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "5173:5173"
    environment:
      - NODE_ENV=development
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
          ignore:
            - node_modules/
        - action: rebuild
          path: ./package.json
          target: /app/package.json
        - action: rebuild
          path: ./vite.config.ts
          target: /app/vite.config.ts

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
     - .:/app
     - ./node_modules:/app/node_modules
    environment:
      NODE_ENV: development
    stdin_open: true
    tty: true
    command: npm start

説明

  • サービス: Docker Composeが管理するサービス(コンテナ)のリストを定義します。
  • デヴ: 開発サービスは Dockerfile.dev年から構築されました。コンテナ内でVite開発サーバーを動かします。
    • ビルド: ビルドコンテキスト(. = current directory)と使用するDockerfile(Dockerfile.dev)を指定します。
      移植版: Mapsはコンテナ上の 5173 をホスト上で 5173 し、Viteの開発サーバーは http://localhost:5173でアクセス可能になります。
    • 環境: 環境変数を設定します NODE_ENV=development.
    • ご覧ください: ローカルファイルの監視、ソースの変更を即座に同期し、キー設定や依存ファイルが変わった際にコンテナを再構築します。
  • プロダクション:Dockerfileから構築された生産サービス。serveで提供された最適化されたReactビルドを実行します。
    • ビルド: 同じコンテキスト(.)を使いながら、本番のDockerfile(Dockerfile)を指しています。
    • 移植版:Maps 3000コンテナ上でポートし、ホスト上で3000するため、本番ビルドは http://localhost:3000でアクセス可能です。

環境: 環境変数を設定します NODE_ENV=production.

ドッカー作成の使用
すべてのサービスを起動(フォアグラウンドモード):
ドッカー作成
すべてのサービスを切り離したモードで開始(背景):
ドッカーコンポーズアップ-d
開発サービスだけを起動する:
Docker Compose Up Dev --watch
本番サービスのみを起動:
Docker Compose Up Prod
停車サービス:
ドッカー作成ダウン

ステップ 6: イメージを Docker Hub に公開する

Docker イメージを共有すると、他のユーザーは自分で環境を設定せずにアプリを実行できます。

Docker Hubにログイン:

ドッカーログイン

プロンプトが表示されたら、Docker Hub のユーザー名とパスワードを入力します。

画像にタグを付ける:

dockerタグmy-react-app your-dockerhub-username/my-react-app

your-dockerhub-username を実際の Docker Hub ユーザー名に置き換えます。

イメージをプッシュします。

docker push your-dockerhub-username/my-react-app

これで、イメージが Docker Hub で利用可能になり、他のユーザーがプルして実行できるようになります。

イメージをプルして実行します。

docker pullyour-dockerhub-username/my-react-app

Docker run -p 30000:3000

your-dockerhub-username/my-react-app

これで、イメージをプルすることで、誰でもアプリを実行できるようになりました。

環境変数の安全な取り扱い

環境変数を安全に管理することは、APIキーやデータベース資格情報などの機密情報を保護するために重要です。

.env の使用 ファイル

.envを作成する プロジェクトのルートにファイル:

REACT_APP_API_URL=https://api.example.com

compose.yml:

services:

  prod:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    env_file:
      - .env
    environment:
      - NODE_ENV=production

セキュリティに関する注意:.envを確保 ファイルは .gitignore.dockerignore に追加され、バージョン管理にコミットされたり、Docker イメージに含まれたりするのを防ぎます。

compose.ymlで定義されているすべてのサービスをデタッチモードで開始するには、次のコマンドを実行します。

ドッカーコンポーズアップ-d

実行時の環境変数の受け渡し

または、コンテナの実行時に変数を渡すこともできます。

docker run -p 5173:5173 -e REACT_APP_API_URL=https://api.example.com my-react-app-dev

Docker シークレットの使用 (上級)

本番環境での機密データについては、機密情報を安全に管理するために Docker Secrets の使用を検討してください。

    Docker と React の一般的な問題のトラブルシューティング

    最善の指示があっても、問題が発生する可能性があります。 ここでは、一般的な問題とその修正方法を示します。

    問題: 「ポート 3000 は既に使用されています」

    解決: ポート 3000 を使用してサービスを停止するか、コンテナーの実行時にアプリを別のポートにマップします。

    docker run -p 4000:3000 my-react-app
    

    http://localhost:4000でアプリにアクセスします。

    問題: 開発中に変更が反映されない

    解決策 1: Docker Composeを --watch フラグで使ってください。
    ボリュームを手動でマウントする代わりに、Docker Composeの 新しい develop.watch 機能 (v2.22+)を使うことができます。これにより、Composeはホストからコンテナに変更を自動的に同期し、必要に応じて再構築します。

    compose.ymlの例:

    services:
      dev:
        build:
          context: .
          dockerfile: Dockerfile.dev
        ports:
          - "5173:5173"
        environment:
          - NODE_ENV=development
        develop:
          watch:
            - action: sync
              path: ./src
              target: /app/src
              ignore:
                - node_modules/
            - action: rebuild
              path: ./package.json
              target: /app/package.json
            - action: rebuild
              path: ./vite.config.ts
              target: /app/vite.config.ts
    
    

    Docker Compose Up Dev --watch

    解決策 2: Dockerボリュームを使ってホットリロードを有効にしてください。あなたの compose.ymlに、以下の事項を volumesしてください:

    volumes:
      - .:/app
      - ./node_modules:/app/node_modules
    

    これら2つのアプローチにより、ローカルな変更をコンテナ内でミラーリングできます。

    問題: ビルド時間が遅い

    解決: Dockerfile を最適化してキャッシングを活用します。 npm installを実行する前に、package.jsonpackage-lock.jsonのみをコピーします。このように、Docker はこれらのファイルが変更されない限り、レイヤーをキャッシュします。

    COPY package*.json ./
    RUN npm install
    COPY . .
    

    問題: コンテナがすぐに終了する

    原因: React 開発サーバーは、デフォルトではコンテナを実行し続けない場合があります。

    解決: コンテナーを対話形式で実行していることを確認します。

    docker run -it -p 3000:3000 my-react-app
    

    問題: ファイルのアクセス許可エラー

    解決: ファイルのアクセス許可を調整するか、 USER ディレクティブを使用して Dockerfile でユーザーを指定します。

    # Add before CMD
    USER node
    

    React Docker セットアップの最適化

    いくつかの高度なテクニックでセットアップを強化しましょう。

    画像サイズを縮小する

    特にクラウド環境にデプロイする場合は、すべてのメガバイトが重要です。

    • 小さい基本イメージを使用する: アルパインベースの画像は大幅に小さくなります。
    • 依存関係のインストール後にクリーンアップします。
    RUN npm install && npm cache clean --force
    
    • 不要なファイルのコピーは避けてください。.dockerignoreを使用する 効果的。

    Docker ビルドキャッシュの活用

    キャッシュを不必要に無効にしていないことを確認します。 各ビルドステップに必要なファイルのみをコピーします。

    Docker レイヤーを賢く使用する

    Dockerfile内の各コマンドは新しいレイヤーを作成します。適切な場合、レイヤー数を減らすためにコマンドを組み合わせる方法があります。例えば:

    RUN npm install && npm cache clean --force
    

    結論

    React アプリの Docker 化は、ゲームチェンジャーです。 これにより、開発ワークフローに一貫性、効率性、スケーラビリティがもたらされます。 アプリケーションをコンテナ化することで、環境の不一致を排除し、デプロイメントを合理化し、コラボレーションを簡単に行うことができます。

    ですから、次にReactプロジェクトを設定するときは、Dockerを試してみてください。 これにより、開発者としての生活が大幅に楽になります。 コンテナ化の世界へようこそ!

    さらに詳しく

    関連記事