为了账号安全,请及时绑定邮箱和手机立即绑定

使用Docker部署React应用:构建时 vs 运行时注入环境变量

标签:
Docker React

简介

最近在做一个 React 项目时,需要在多个环境中部署应用程序,使用 Docker 镜像。其中一个关键问题是如何将环境变量注入到应用程序中,经过一番研究和尝试,我找到了两种主要的方法:可以在构建时注入这些变量,或者在运行时注入。在这篇文章里,我会详细聊聊这两种方式,并聊聊我为什么最终选择了其中一种而不是另一种。

项目设置简介

我工作的一个 React 应用程序是:

  • 一个 **.env** 文件用于管理环境变量。
  • 一个 Dockerfile 用于构建 Docker 镜像。
  • 一个自定义的 Nginx 配置文件 (nginx-custom.conf) 被复制到容器内 Nginx 的配置目录中,以根据 React 应用程序的需求调整服务器的行为,使其更好地适应应用。

下面,我将在这里谈谈在这个 Docker 化的环境中处理环境变量的两种方式。

在构建时注入环境变量(第一种方式)

在这种方法中,环境变量是在Docker构建阶段传递的。如下所示:

理解Dockerfile(Docker文件):

FROM node:22.4.1-slim as build

# 定义构建过程中使用的参数
ARG APP_API_URL  
ARG APP_ENV  

# 设置环境变量VITE_API_URL和VITE_APP_ENV
ENV VITE_API_URL=$APP_API_URL  
ENV VITE_APP_ENV=$APP_ENV  

# 设置工作目录为/app
WORKDIR /app  
# 将当前目录的所有文件复制到/app目录
COPY . .  

# 清除node_modules和package-lock.json文件
RUN rm -rf node_modules package-lock.json  

# 安装项目依赖
RUN npm install  
# 构建项目
RUN npm run build  

# 使用nginx:1.27.0-alpine作为运行时基础镜像
FROM nginx:1.27.0-alpine  
# 将构建完成的应用程序文件复制到nginx的web根目录
COPY --from=build /app/dist /usr/share/nginx/html  
# 复制自定义nginx配置文件
COPY nginx-custom.conf /etc/nginx/conf.d/default.conf  
# 暴露80端口
EXPOSE 80
  • 构建参数值:这些是在构建时提供的占位符,用于提供特定环境的值。例如
    例如:
    ARG APP_API_URL  
    ARG APP_ENV
  • 环境变量:在构建过程中,通过设置构建参数的值,这些变量被设定,并在构建过程中嵌入到 React 应用程序。
# 设置API URL和应用环境变量
ENV VITE_API_URL=$APP_API_URL  
ENV VITE_APP_ENV=$APP_ENV

这怎么用:

下面是如何在构建 Docker 镜像时传递参数值的一个示例:

docker build --build-arg APP_API_URL=https://api.example.com --build-arg APP_ENV=正式 - docker-approach:1 .

运行Docker镜像的方法如下:

在终端中输入以下命令来运行容器:

docker run -p 3000:80 docker-approach:1

你可以看到,你注入的这些环境变量值在Web应用中已经正确发挥了作用。

注意:如果你需要在不同的环境里运行React应用,你需要为每个环境分别创建Docker镜像。

方法二:在Docker运行时环境变量注入

这种方法是在运行时传递环境变量,而不是在构建时传递环境变量。

面临挑战

React 通常在构建过程中将配置变量嵌入到静态文件中。例如,API端点的URL(如https://api.example.com)、认证密钥(如secret_key_123)或功能标志(如feature_toggle=true)会被硬编码进生成的JavaScript文件里。构建完成后,这些文件变得静态且不会改变,除非重新构建应用程序。

解决办法:env.sh 脚本

为了应对这个挑战,我用了一个env.sh脚本,来在运行时修改这些静态文件。

    #!/bin/sh  
    for i in $(env | grep APP_)  
    do  
        key=$(echo $i | cut -d '=' -f 1)  
        value=$(echo $i | cut -d '=' -f 2-)  
        echo $key=$value  
        # 注释:以下代码将对所有文件进行处理  
        # find /usr/share/nginx/html -type f -exec sed -i "s|${key}|${value}|g" '{}' +  

        # 注释:以下代码将仅处理 .js 和 .css 文件  
        find /usr/share/nginx/html -type f -name '*.js' -exec sed -i "s|${key}|${value}|g" '{}' +  
    done  
    echo '完成'

它是这样工作的。

  • 遍历环境变量值:脚本遍历环境变量,只筛选出以特定前缀(例如 APP_)开头的变量。
    # 遍历环境变量中以APP_开头的变量
    for i in $(env | grep APP_); do  
      # 获取变量的值
      value=$(printenv $i)  
      ...  
    done
  • 替换 JavaScript 文件中的内容 : 脚本会在 /usr/share/nginx/html 目录下找到所有的 JavaScript 文件,并将这些占位符替换为实际值。
# 找到所有.js文件并替换特定键值对
find /usr/share/nginx/html -type f -name '*.js' -exec sed -i "s|${key}|${value}|g" '{}' +
在 Dockerfile 中加入 env.sh

为了确保「env.sh」脚本在 Docker 容器启动时运行,你需要把它加到 Dockerfile 里。

    FROM node:22.4.1-slim as build  
    WORKDIR /app  
    COPY package*.json ./  
    RUN npm install  
    COPY . .  
    RUN npm run build  

    FROM nginx:1.27.0-alpine  
    COPY --from=build /app/dist /usr/share/nginx/html  
    COPY /nginx-custom.conf /etc/nginx/conf.d/default.conf  

    # 将 env.sh 移动到 docker-entrypoint.d  
    COPY env.sh /docker-entrypoint.d/env.sh  
    RUN chmod +x /docker-entrypoint.d/env.sh

env.sh 脚本复制到容器的 /docker-entrypoint.d/ 目录中。/docker-entrypoint.d/ 目录是 Docker 入口脚本用来执行脚本的一个特殊位置。任何放在这里的脚本在容器启动后,Nginx 开始提供静态文件之前自动运行。

这使得 env.sh 脚本在运行 Docker 容器时可以使用在运行时注入的环境变量来更新这些静态文件。

它是怎么工作的

这里是如何使用这种方法构建和运行Docker镜像的:

构建 Docker 镜像的步骤:

运行 docker build -t docker-approach:2 . 命令。这会构建一个名为 docker-approach:2 的Docker镜像。

运行 Docker 容器,并传递必要的环境变量。

使用 Docker 运行容器,并将主机的 3000 端口映射到容器的 80 端口,同时设置环境变量 APP_API_URL 为 https://test-api.example.com 和 APP_ENV 为 staging。命令如下:

docker run -p 3000:80 -e APP_API_URL=https://test-api.example.com -e APP_ENV=staging docker-approach:2


其中 `-e APP_API_URL=https://test-api.example.com` 设置了 API 的 URL,而 `-e APP_ENV=staging` 设置了应用环境为 staging。这里的版本标签 `docker-approach:2` 表示特定的应用版本。

你可以看到,你设置的环境变量在 Web 应用中运行得很好。

好处是:这种方法允许你在不同环境中部署同一个Docker镜像,无需重新构建。你可以在运行时根据需要更改环境变量,从而使它更加灵活高效。

为什么我选择了运行时注入?

我选择了在运行时注入环境变量,因为这样提供了更大的灵活性和更高的效率,使得一个 Docker 镜像可以在不同环境中使用。这种方法遵循了 DRY 原则,通过避免重复,避免了每次环境变化都需要重新构建镜像的麻烦。

它也符合CI/CD最佳实践,降低了复杂性,并确保了开发、测试和生产环境之间的一致性,从而简化了部署流程并简化了流程。

参考文献

为进一步了解运行时注入环境变量,请可以看看这篇:为 Docker 化的 React 应用设置动态环境变量

请访问我的 GitHub 存储库,查看与此文章相关的代码示例:GitHub Repository Link

祝你编程愉快,谢谢!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消