容器化你的React应用

172 阅读10分钟

如果你和我一样,你喜欢用React制作响应式的用户界面。但是,设置一致的开发环境并确保顺利部署会变得复杂。这就是Docker可以拯救你的地方。

让我们深入了解Docker和React的世界!

为什么要容器化你的React应用?

你可能想知道,“为什么我要费心容器化我的React应用程序?”问得好!容器化提供了几个引人注目的好处,可以提升您的开发和部署,例如:

  • 简化的CI/CD管道: 通过将React应用打包到Docker容器中,您创建了从开发到生产的一致环境。这种一致性简化了持续集成和持续部署(CI/CD),降低了构建和部署期间的风险。
  • 简化的依赖项管理: Docker将应用的所有依赖项封装在容器中。每个团队成员和部署环境都使用相同的设置,确保顺利协作。
  • 更好的资源管理: 容器是轻量级和高效的。与虚拟机不同,Docker容器共享主机系统的内核,这意味着您可以在同一硬件上运行更多容器。
  • 无冲突的隔离环境: Docker为您的应用程序提供隔离环境。这种隔离可以防止同一机器上不同项目的依赖项或配置之间的冲突。

开始使用React和Docker

在我们继续之前,让我们确保你已经拥有了容器化你的React应用所需的一切。

你需要的工具

Docker简介

Docker提供了一套企业工具、云服务和协作社区。有助于简化工作流程并,最大限度地提高开发效率。Docker平台允许开发人员将应用程序打包到容器中。容器确保您的应用程序无论部署在哪里都能以相同的方式运行。

如何将React项目虚拟化

现在让我们进入正题。我们将一步一步地完成这个过程。到最后,你的React应用程序将在Docker容器中运行。

步骤1:设置React应用

如果你已经有一个React应用程序,你可以跳过这一步。如果没有,让我们创建一个:

npx create-react-app my-react-app
cd my-react-app

步骤2:创建Dockerfile

在项目的根目录中,创建一个名为Dockerfile(无扩展名)的文件。此文件将包含构建Docker镜像的说明。

关于Dockerfile

您可以创建一个简单的Dockerfile

# 使用基于Alpine Linux的最新LTS版本的Node.js
FROM node:18-alpine
 
# 设置容器内的工作目录
WORKDIR /app
 
# 将package.json和package-lock.json复制到工作目录
COPY package*.json ./
 
# 安装package.json中指定的依赖项
RUN npm install
 
# 将本地目录中的所有文件复制到容器中
COPY . .
 
# 暴露容器上的端口3000(React的默认端口)
EXPOSE 3000
 
# 告诉Docker在容器启动时运行npm start
CMD ["npm", "start"]

多阶段分步构建Dockerfile

对于生产镜像,我们多阶段分步构建,优化镜像大小并增强安全性。

# Build Stage 构建阶段
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
 
# Production Stage 生产阶段
# 使用Nginx
FROM nginx:stable-alpine AS production
# 复制前一阶段的构建产物到 nginx目录下面
COPY --from=build /app/build /usr/share/nginx/html
# 暴露端口80
EXPOSE 80
#  Nginx 在前台运行。确保 Nginx 进程与容器的生命周期保持一致,容器停止时 Nginx 也会随之停止。
CMD ["nginx", "-g", "daemon off;"]

好处

  • 较小的镜像大小:最终镜像仅包含Nginx。
  • 增强的安全性:从生产镜像中排除开发依赖项和Node.js运行时。
  • 性能优化:Nginx有效地提供服务。

步骤3:创建.dockerignore文件

就像.gitignore帮助Git忽略某些文件一样,.dockerignore告诉Docker在构建镜像时要排除哪些文件或目录。在项目的根目录下创建一个.dockerignore文件:

node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
.env

排除不必要的文件可以减小镜像大小并加快构建过程。

步骤4:构建并运行你的React应用

导航到项目的根目录并运行:

docker build -t my-react-app .

此命令指定构建上下文(当前目录)并构建my-react-app镜像。

如果您的Dockerfile中有多个阶段,并且需要针对特定的构建阶段(例如build阶段),则可以使用--target选项。举例来说:

docker build -t my-react-app-dev --target build .

运行Docker容器

对于开发阶段的镜像:

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

对于生产镜像:

docker run -p 80:80 my-react-app

访问应用

接下来,打开浏览器:

  • http://localhost:3000(用于开发)
  • http://localhost(用于生产)

步骤5:使用Docker Compose进行多容器设置

这里有一个示例,说明如何使用Docker Compose将React应用配置为服务。

创建一个compose.yml文件:

# 定义服务(容器)列表
services:
  # 服务名称
  web:
    # 在当前目录中构建Dockerfile
    build: . 
    # 将容器上的端口3000映射到主机上的端口3000
    ports:
      - "3000:3000"
    # 挂载当前目录和node_modules进行热重载
    volumes:
     - .:/app
     - ./node_modules:/app/node_modules
    # 设置环境变量
    environment:
      NODE_ENV: development
    # 保持容器运行和交互
    stdin_open: true
    tty: true
    command: npm start

步骤6:将镜像发布到Docker Hub

共享Docker镜像允许其他人运行您的应用,而无需自己设置环境。

Log in to Docker Hub:

docker login

输入您的Docker Hub用户名和密码。

标记您的镜像:

docker tag 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 pull your-dockerhub-username/my-react-app

docker run -p 80:80 your-dockerhub-username/my-react-app

环境变量的处理

环境变量对于保护API密钥和数据库凭据等敏感信息至关重要。

使用.env文件

在项目根目录下创建一个.env文件:

REACT_APP_API_URL=https://api.example.com

更新compose.yml

services:
  web:
    build: .
    ports:
      - "3000:3000"
    volumes:
     - .:/app
     - ./node_modules:/app/node_modules
    env_file:
      - .env
    stdin_open: true
    tty: true
    command: npm start

安全提示: 确保.env文件已添加到.gitignore.dockerignore中,以防止它被提交到版本控制系统或包含在Docker镜像中。

以分离模式启动compose.yml中定义的所有服务,命令为:

docker compose up -d

在运行时传递环境变量

或者,您可以在运行容器时传递环境变量:

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

使用Docker Secrets(高级)

对于生产环境中的敏感数据,可以使用Docker Secrets管理机密信息。

具有多阶段构建的生产Dockerfile

在为生产环境准备React应用时,多阶段构建可以保持精简。允许您将构建过程与最终的运行时环境分离。这不仅可以减少镜像大小,还有助于防止不必要的开发依赖项混入生产环境。

下面是一个更进一步的例子:我们将创建一个专用的构建阶段、一个开发环境阶段和一个生产阶段:

# Stage 1: Build the React app
FROM node:18-alpine AS build
WORKDIR /app

# Leverage caching by installing dependencies first
COPY package.json package-lock.json ./
RUN npm install --frozen-lockfile

# Copy the rest of the application code and build for production
COPY . ./
RUN npm run build

# Stage 2: Development environment
FROM node:18-alpine AS development
WORKDIR /app

# Install dependencies again for development
COPY package.json package-lock.json ./
RUN npm install --frozen-lockfile

# Copy the full source code
COPY . ./

# Expose port for the development server
EXPOSE 3000
CMD ["npm", "start"]

# Stage 3: Production environment
FROM nginx:alpine AS production

# Copy the production build artifacts from the build stage
COPY --from=build /app/build /usr/share/nginx/html

# Expose the default NGINX port
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker和React的常见问题

问题:“端口3000已在使用中”

解决方案: 在运行容器时将应用映射到其他端口。

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

问题:开发过程中文件没有同步更改

解决方案: 使用Docker卷来启用热更新。在compose.yml中,确保在volumes下有以下内容:

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

问题:构建时间缓慢

解决方案: 优化您的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

问题:macOS和Windows上的性能问题

主机系统和Docker容器之间的文件共享机制,在macOS和Windows上悠着显著的开销,特别是在包含许多文件的大型项目时。osxfsgRPC FUSE等传统方法通常难以在这些环境中有效地扩展。

解决方法:

启用同步文件共享(Docker Desktop 4.27+): Docker Desktop 4.27+引入了同步文件共享,通过在Docker Desktop 中创建高性能双向缓存,显著增强了挂载性能。

主要优点:

  • 针对大型项目进行优化: 高效处理包含数千个文件的存储库。
  • 性能改进: 解决了旧文件共享机制的瓶颈。
  • 实时同步: 自动同步主机和容器之间的文件更改。
  • 减少文件所有权冲突: 最大限度地减少主机和容器之间的文件权限问题。

如何启用:

  • 打开Docker Desktop并转到 Settings > Resources > File Sharing.
  • Synchronized File Shares部分中,选择要共享的文件夹,然后单击Initialize File Share
  • 在指向共享目录的compose.yml或Docker CLI命令中绑定挂载。

使用 .syncignore优化: 在共享目录的根目录中创建一个.syncignore文件,以排除不必要的文件(例如,node_modules,.git/)以获得更好的性能。

.syncignore文件示例:

node_modules
.git/
*.log

compose.yml示例:

services:
  web:
    build: .
    volumes:
      - ./app:/app
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development

在Windows上利用WSL 2: 对于Windows用户,通过在轻量级Linux VM中运行Docker引擎,提供接近原生Linux的性能。

如何启用WSL 2:

  • 确保已安装Windows 10 2004或更高版本。
  • 安装Windows Subsystem for Linux 2
  • 在Docker Desktop中,转到Settings > General并启用Use the WSL 2 based engine

在挂载卷中使用更新的缓存选项: 虽然:cached:delegated等旧选项已被弃用,但仍然允许优化:

  • consistent 严格一致性(默认)。
  • cached 允许主机缓存内容。
  • delegated 允许容器缓存内容。

卷配置示例:

volumes:
  - type: bind
    source: ./app
    target: /app
    consistency: cached

优化React设置

缩小镜像尺寸

每个字节都很重要,尤其是在部署到云环境时。

  • 使用较小的基本图像: 基于Alpine-base的镜像明显较小。
  • 安装依赖项后清理: RUN npm install && npm cache clean --force
  • 避免复制不必要的文件: 有效使用.dockerignore

使用Docker层

Dockerfile中的每个命令都会创建一个新层。在适当的位置组合命令以减少层数。

RUN npm install && npm cache clean --force

原文:www.docker.com/blog/how-to…