React应用Docker镜像拉取后REACT_APP环境变量无法识别
React CI/CD构建镜像后运行时REACT_APP环境变量显示undefined的排查与解决
核心问题根源
React的REACT_APP_*前缀环境变量是在构建阶段(npm run build)由Webpack注入到静态文件中的,运行时(容器启动时)传入的环境变量对前端静态内容完全无效:
- 本地构建时,你的
.env文件在构建目录下,Webpack的dotenv插件自动读取并将变量编译进静态产物,所以即使容器运行时不传环境变量也能正常显示; - CI/CD构建时,没有在构建阶段传入这些环境变量,导致最终镜像里的静态文件根本没有注入这些变量。容器运行时传入的环境变量只能被Nginx进程读取,前端静态文件无法获取。
解决方案
方案1:CI构建阶段注入环境变量(推荐,适合固定环境的镜像)
通过GitHub Secrets存储敏感变量,在构建镜像时传入构建参数,让Webpack在构建阶段注入变量:
将环境变量存入GitHub Secrets
在仓库的Settings > Secrets and variables > Actions中,添加以下Secrets:REACT_APP_URL、REACT_APP_API_URL、REACT_APP_VEHICLE_IMAGE_URL、REACT_APP_NAME,值对应你的.env内容。修改GitHub Actions构建配置
更新.github/workflows/docker-image.yml的构建步骤,添加构建参数:
- name: Build and push Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: context: . push: true tags: dummy-frontend/test:latest build-args: | REACT_APP_URL=${{ secrets.REACT_APP_URL }} REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }} REACT_APP_VEHICLE_IMAGE_URL=${{ secrets.REACT_APP_VEHICLE_IMAGE_URL }} REACT_APP_NAME=${{ secrets.REACT_APP_NAME }}
- 修改Dockerfile接收构建参数
在builder阶段添加ARG参数并转为环境变量,让Webpack能读取到:
FROM node:16-alpine as builder WORKDIR /app # 添加ARG接收构建参数 ARG REACT_APP_URL ARG REACT_APP_API_URL ARG REACT_APP_VEHICLE_IMAGE_URL ARG REACT_APP_NAME # 转换为环境变量,供Webpack构建时注入 ENV REACT_APP_URL=$REACT_APP_URL ENV REACT_APP_API_URL=$REACT_APP_API_URL ENV REACT_APP_VEHICLE_IMAGE_URL=$REACT_APP_VEHICLE_IMAGE_URL ENV REACT_APP_NAME=$REACT_APP_NAME COPY . . RUN npm ci RUN npm run build # production阶段内容不变 FROM nginx:1.21.0-alpine as production ENV NODE_ENV production COPY --from=builder /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
完成后,CI构建的镜像会直接包含注入好变量的静态文件,拉取后直接运行即可,无需再传--env-file。
方案2:运行时注入环境变量(适合多环境复用同一镜像)
如果需要同一个镜像在不同环境使用不同变量,可以通过启动脚本动态替换静态文件中的变量占位符:
- 创建环境变量模板文件
在项目public目录下新建env.js:
window.env = { REACT_APP_URL: "%REACT_APP_URL%", REACT_APP_API_URL: "%REACT_APP_API_URL%", REACT_APP_VEHICLE_IMAGE_URL: "%REACT_APP_VEHICLE_IMAGE_URL%", REACT_APP_NAME: "%REACT_APP_NAME%" };
- 在index.html中引入模板
修改public/index.html的<head>部分:
<script src="%PUBLIC_URL%/env.js"></script>
- 修改代码读取window.env
将原来读取process.env的代码改为读取window.env:
{window.env.REACT_APP_NAME == null ? "undefined" : window.env.REACT_APP_NAME}
- 添加启动脚本并修改Dockerfile
- 项目根目录新建
entrypoint.sh:
#!/bin/sh # 替换env.js中的占位符为容器环境变量 sed -i "s/%REACT_APP_URL%/$REACT_APP_URL/g" /usr/share/nginx/html/env.js sed -i "s/%REACT_APP_API_URL%/$REACT_APP_API_URL/g" /usr/share/nginx/html/env.js sed -i "s/%REACT_APP_VEHICLE_IMAGE_URL%/$REACT_APP_VEHICLE_IMAGE_URL/g" /usr/share/nginx/html/env.js sed -i "s/%REACT_APP_NAME%/$REACT_APP_NAME/g" /usr/share/nginx/html/env.js # 执行原Nginx启动命令 exec "$@"
- 修改Dockerfile的production阶段:
FROM nginx:1.21.0-alpine as production ENV NODE_ENV production COPY --from=builder /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf # 复制启动脚本并赋予执行权限 COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh EXPOSE 80 ENTRYPOINT ["/entrypoint.sh"] CMD ["nginx", "-g", "daemon off;"]
完成后,拉取镜像用原来的docker run --env-file .env命令启动,脚本会自动将容器环境变量替换到静态文件中,前端即可读取。
内容的提问来源于stack exchange,提问作者n9p4
相关产品推荐
相关产品推荐

