多阶段构建Reactjs_Nginx的builder阶段无法读取环境变量
我用Docker Compose搭建了Django+Nginx+Reactjs_Nginx技术栈,Django和Nginx都能正常读取.env里的环境变量,但Reactjs_Nginx不行。本地跑React时,代码const hostName = process.env.REACT_APP_BACKEND_HOST || "http://localhost:8000/";能正常读环境变量,执行npm run build后用npx serve -s运行也没问题。但docker-compose up --build之后,进入reactjs_nginx容器执行printenv能看到需要的环境变量,可为什么builder阶段读不到环境变量?
对应的Dockerfile:
FROM node:16.15-alpine AS builder ENV NODE_ENV production WORKDIR /app COPY package* /app/ RUN npm install --only=production COPY . . RUN npm run build 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 CMD ["nginx", "-g", "daemon off;"]
对应的docker-compose.yml:
version: '3.9' services: backend: build: context: ./backend dockerfile: ./docker/django/Dockerfile env_file: - .env command: gunicorn resume_builder.wsgi -w ${GUNICORN_WORKER_COUNT} -b 0.0.0.0:${DJANGO_PORT} networks: - resume_builder_network backend_nginx: build: ./backend/docker/nginx volumes: - ./backend/docker/nginx/templates:/etc/nginx/templates ports: - "${BACKEND_PORT}:${BACKEND_PORT}" env_file: - .env networks: - resume_builder_network depends_on: - backend frontend: build: context: ./frontend target: production env_file: - .env ports: - ${FRONTEND_PORT}:${FRONTEND_PORT} networks: - resume_builder_network networks: resume_builder_network:
核心问题很明确:你在docker-compose.yml里给frontend服务配置的env_file,只会把环境变量注入到容器运行阶段,完全不会传递到Docker镜像的构建阶段(也就是builder阶段)。
React的环境变量是在npm run build时就被静态打包进构建产物里的,这个过程正好发生在Docker的builder阶段——此时env_file里的变量还没被注入,自然读不到。而你进容器后能看到变量,是因为那是容器运行时的环境变量,和构建阶段根本没关系。
方法1:通过构建参数传递(最常用)
修改docker-compose.yml的frontend服务,添加build.args来传递构建阶段需要的变量:
frontend: build: context: ./frontend target: production args: - REACT_APP_BACKEND_HOST=${REACT_APP_BACKEND_HOST} env_file: - .env ports: - ${FRONTEND_PORT}:${FRONTEND_PORT} networks: - resume_builder_network
然后修改Dockerfile的builder阶段,接收并设置这个参数为环境变量:
FROM node:16.15-alpine AS builder ARG REACT_APP_BACKEND_HOST # 接收构建参数 ENV REACT_APP_BACKEND_HOST=$REACT_APP_BACKEND_HOST # 转为环境变量供build使用 ENV NODE_ENV production WORKDIR /app COPY package* /app/ RUN npm install --only=production COPY . . RUN npm run build 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 CMD ["nginx", "-g", "daemon off;"]
方法2:临时构建时直接传参(测试用)
如果只是临时测试,不需要修改配置文件,可以直接在构建命令里指定变量:
REACT_APP_BACKEND_HOST=http://your-backend-host docker-compose build frontend
方法3:用.env文件给构建阶段传参(Docker 17.06+支持)
在frontend目录下创建.env.build文件,写入需要的变量:
REACT_APP_BACKEND_HOST=http://your-backend-host
修改Dockerfile头部启用BuildKit,然后挂载这个文件:
# syntax=docker/dockerfile:1.2 FROM node:16.15-alpine AS builder ENV NODE_ENV production WORKDIR /app COPY package* /app/ RUN npm install --only=production COPY . . # 挂载.env.build文件供build使用 RUN --mount=type=secret,id=env,target=/app/.env.build npm run build 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 CMD ["nginx", "-g", "daemon off;"]
最后在docker-compose.yml中添加secret配置:
services: frontend: build: context: ./frontend target: production secrets: - env # 其他原有配置不变 secrets: env: file: ./frontend/.env.build
内容的提问来源于stack exchange,提问作者UMR

