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

This command creates a new React + TypeScript application inside a folder named my-react-app. It installs the necessary npm packages and starts the development server, which will be available at http://localhost:5173.
Vite has become the standard choice for React projects because it’s significantly faster, more lightweight, and easier to use compared to the old Create React App (CRA), which is no longer actively maintained.

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

In the root directory of your project, create the Dockerfiles that define how your React app will run inside containers. We’ll use two separate Dockerfiles:

  • Dockerfile.dev → optimized for local development (with hot reload support).
  • Dockerfile → optimized for production (builds and serves the static files).

This separation keeps development lightweight while ensuring the production image is small, secure, and ready for deployment.

開発用のDockerfile

For development, create a file named Dockerfile.dev. This makes it clear that the file is intended only for development and keeps it separate from your production Dockerfile. A dedicated dev Dockerfile ensures faster builds, hot reloading, and a smoother developer experience without mixing in production concerns.

# =========================================
# 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: We’re using the latest Node.js  LTS version of Node.js based on Alpine Linux, 
    Note: The Node.js image should be monitored regularly, as tags may expire or be rebuilt to include security patches and updates. It’s important to keep the base image tag up to date to ensure your application always benefits from the latest stable and secure release.
  • WORKDIR /app: コンテナ内の作業ディレクトリを設定します。
  • *COPY package.json ./**: package.jsonpackage-lock.json を作業ディレクトリにコピーします。
  • RUN npm install: package.json で指定された依存関係をインストールします。
  • COPY . .: ローカル ディレクトリからコンテナにすべてのファイルをコピーします。
  • RUN chown -R node:node /app: Ensure the “node” user owns the application files. This prevents permission errors when installing dependencies or writing build artifacts inside the container.
  • USER node :  Switches to the built-in non-root “node” user provided by the official Node.js image, improving security by avoiding root privileges inside the container.
  • EXPOSE 5173: Exposes port 5173 ,which is the default port used by the Vite React.js development server.
  • CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]: Defines the default command for the container, starting the Vite development server and making it accessible on all network interfaces.

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

When Dockerizing a React app, we need a way to serve the production build. Traditionally, and preferred for production, the developers used Nginx inside Docker to serve the compiled static files. For example, you can follow the official Docker sample guide created by Kristiyan Velkov: Containerize a React application with Docker

Instead in this example , we’ll use the serve package, which is a lightweight static file server built with 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;"]

説明

Build stage (Stage 1 – builder):

  • FROM node:${NODE_VERSION} AS builder: Uses a lightweight Node.js 24 Alpine image to build the React (Vite) app.
  • COPY package.json package-lock.json ./: Copies dependency manifests first to optimize Docker layer caching.
  • RUN npm ci: Installs dependencies in a clean and reproducible way based on the lockfile.
  • COPY . .: Copies the full application source code into the container.
  • RUN npm run build: Compiles the app into optimized static files inside the dist/ directory.

Production stage (Stage 2 – runner):

  • FROM node:${NODE_VERSION} AS runner: Starts a fresh Node.js 24 Alpine image for running the app.
  • ENV NODE_ENV=production: Ensures Node.js runs in production mode.
  • WORKDIR /app: Sets the working directory.
  • COPY --from=builder /app/dist ./dist: Copies only the built production files from the builder stage.
  • RUN npm install serve@^14.2.6 --omit=dev: Installs only the serve package (pinned version) to serve the static build — no global install, no dev dependencies.
  • USER node: Drops root privileges for security best practices.
  • EXPOSE 3000: Exposes port 3000, the port used by serve.
  • CMD ["npx", "serve", "-s", "dist", "-l", "3000"]: Starts the serve process to host the static build.

利点

  • イメージ サイズが小さい: 最終的なイメージには、運用ビルドと Nginx のみが含まれます。
  • セキュリティの強化: 開発の依存関係と Node.js ランタイムを運用イメージから除外します。
  • Performance optimization: Serve efficiently serves static files.

ステップ 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/

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

To learn more, visit the .dockerignore reference.

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

Build the production image:
From the root of your project, run:

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

This command builds the production image using your multi-stage Dockerfile and tags it as my-react-app. By default, it uses the final runner stage, resulting in a smaller, optimized image that contains only the compiled React app and the lightweight serve package.

Run the container:

docker run -p 3000: my-react-app3000

Now your app is available at http://localhost:3000.

Build the development image:
If you want to run the Vite development server inside Docker, use your dedicated dev Dockerfile:

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

Run the container:

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


This image includes all build tools and runs the Vite dev server, giving you hot reload at http://localhost:5173.

手記:

Building with Dockerfile.dev (development) produces a larger image since it contains the full toolchain needed for compiling and hot reloading.
Building with Dockerfile (production) produces a smaller, deployment-ready image that only serves the final build output.

Docker コンテナの実行

開発イメージの場合:

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

製品イメージの場合:

docker run -p 30000: my-react-app30000

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

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

  • http://localhost:5173 (開発用)
  • http://localhost:3000 (for production)

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

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

The following docker-compose.yml acts as a reference configuration for running your React frontend as a service with Docker. It defines two services:

  • dev → runs your app in development mode using Vite.
  • prod → runs your app in production mode using the optimized build served with serve.

Create a file named compose.yml in your project root:

Learn more:  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

説明

  • services: Defines a list of services (containers) that Docker Compose will manage.
  • dev: The development service, built from Dockerfile.dev. It runs the Vite development server inside the container.
    • build: Specifies the build context (. = current directory) and which Dockerfile to use (Dockerfile.dev).
      ports: Maps port 5173 on the container to 5173 on the host, making the Vite dev server accessible at http://localhost:5173.
    • environment: Sets environment variables, here NODE_ENV=development.
    • watch: Monitors local files; syncs source changes instantly and rebuilds the container when key config or dependency files change.
  • prod: The production service, built from Dockerfile. It runs the optimized React build served with serve.
    • build: Uses the same context (.) but points to the production Dockerfile (Dockerfile).
    • ports: Maps port 3000 on the container to 3000 on the host, making the production build accessible at http://localhost:3000.

environment: Sets environment variables, here NODE_ENV=production.

ドッカー作成の使用
Start all services (foreground mode):
ドッカー作成
Start all services in detached mode (background):
ドッカーコンポーズアップ-d
Start only the dev service:
docker compose up dev --watch
Start only the prod service:
docker compose up prod
Stop services:
ドッカー作成ダウン

ステップ 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

Example 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でアプリにアクセスします。

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

    Solution 1: Use Docker Compose with the --watch flag.
    Instead of mounting volumes manually, you can use the new develop.watch feature in Docker Compose (v2.22+). This tells Compose to automatically sync changes from your host into the container and rebuild when needed.

    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

    Solution 2: Use Docker volumes to enable hot-reloading. In your compose.yml, ensure you have the following under volumes:

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

    These two approaches allow your local changes to be mirrored inside the container.

    問題: ビルド時間が遅い

    解決: 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 レイヤーを賢く使用する

    Each command in your Dockerfile creates a new layer. Combine commands where appropriate to reduce the number of layers for example:

    RUN npm install && npm cache clean --force
    

    結論

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

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

    さらに詳しく

    関連記事