在多环境React Redux生产构建中通过环境变量配置API_ROOT
针对你提到的需求——不用硬编辑src/agent.js,而是通过环境变量在staging、live等生产环境中动态设置API_ROOT,同时遵循12factor的配置与代码分离原则,这里有两种可行方案,推荐第二种更贴合12factor的运行时配置方式:
方案一:构建时注入环境变量(基于Create React App默认机制)
这个项目用的是Create React App(CRA)模板,CRA会自动注入所有以REACT_APP_开头的环境变量到生产构建中,步骤如下:
修改
src/agent.js读取环境变量
把原来硬编码的API_ROOT替换为从环境变量读取,保留默认值作为 fallback:// src/agent.js const API_ROOT = process.env.REACT_APP_API_ROOT || 'https://api.realworld.io/api';在OpenShift中配置构建时环境变量
为staging和live环境分别创建BuildConfig(或者用同一个BuildConfig加参数切换),注入对应环境的API地址:- 先创建一个ConfigMap存储各环境的API地址:
apiVersion: v1 kind: ConfigMap metadata: name: realworld-api-config data: staging_api_root: "https://staging-api.realworld.io/api" live_api_root: "https://api.realworld.io/api" - 针对staging环境的BuildConfig:
apiVersion: build.openshift.io/v1 kind: BuildConfig metadata: name: realworld-staging-build spec: source: git: uri: https://github.com/gothinkster/react-redux-realworld-example-app ref: master strategy: sourceStrategy: from: kind: ImageStreamTag name: nodejs:18 env: - name: REACT_APP_API_ROOT valueFrom: configMapKeyRef: name: realworld-api-config key: staging_api_root output: to: kind: ImageStreamTag name: realworld-app:staging - live环境的BuildConfig只需要把
key换成live_api_root,输出镜像标签改为live即可。
- 先创建一个ConfigMap存储各环境的API地址:
部署对应环境的镜像
构建完成后,用staging/live的镜像分别部署到对应的环境,每个环境的API_ROOT已经打包在镜像里了。
方案二:运行时注入环境变量(更符合12factor原则,推荐)
12factor强调配置应该在运行时与代码分离,同一个镜像可以在任意环境运行。这种方式不需要为不同环境构建镜像,而是在容器启动时动态注入API_ROOT:
修改
public/index.html添加全局环境占位符
在public/index.html的<head>标签里添加一段脚本,预留占位符供后续替换:<!-- public/index.html --> <head> <!-- 其他内容 --> <script> window.env = { API_ROOT: "%REACT_APP_API_ROOT%" }; </script> </head>修改
src/agent.js读取全局环境变量
把API_ROOT改为读取window上的全局变量:// src/agent.js const API_ROOT = window.env.API_ROOT || 'https://api.realworld.io/api';编写容器启动脚本替换占位符
创建一个entrypoint.sh脚本,在容器启动时用环境变量替换index.html里的占位符:#!/bin/sh # 替换index.html中的API_ROOT占位符 sed -i "s|%REACT_APP_API_ROOT%|${REACT_APP_API_ROOT:-https://api.realworld.io/api}|g" /usr/share/nginx/html/index.html # 启动nginx exec nginx -g "daemon off;"更新Dockerfile使用启动脚本
这个项目的生产构建是静态文件,用nginx托管,所以修改Dockerfile:FROM nginx:alpine # 复制构建产物到nginx目录 COPY build /usr/share/nginx/html # 复制启动脚本 COPY entrypoint.sh /entrypoint.sh # 赋予执行权限 RUN chmod +x /entrypoint.sh # 设置启动入口 ENTRYPOINT ["/entrypoint.sh"]在OpenShift Deployment中配置环境变量
为staging和live环境分别创建Deployment,设置对应的REACT_APP_API_ROOT:- staging环境的Deployment片段:
spec: containers: - name: realworld-app image: your-registry/realworld-app:latest env: - name: REACT_APP_API_ROOT valueFrom: configMapKeyRef: name: realworld-api-config key: staging_api_root ports: - containerPort: 80 - live环境只需要把
key换成live_api_root即可,用同一个镜像部署。
- staging环境的Deployment片段:
为什么方案二更贴合12factor?
- 配置与代码完全分离:不需要修改代码或为不同环境构建镜像,同一个镜像可以在staging、live甚至本地运行
- 动态调整:不需要重新构建,修改环境变量就能切换API地址,更灵活
- 符合容器化最佳实践:镜像不可变,配置通过环境变量注入
内容的提问来源于stack exchange,提问作者simbo1905

