V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gamesover
V2EX  ›  React

React 在 docker, buildkite 部署,如何读取不同的 staging, qa 环境文件

  •  
  •   gamesover · 283 天前 · 1867 次点击
    这是一个创建于 283 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现在 deployment 一般都是 docker 或者类似系统 在生产环境之外,都会有不同的测试环境,比如 staging,qa 等 假设 build 好的 image 都是一样的,你只要通过设置不同的当前 environment ,app 就会读取对应的环境文件

    比如在 rails 下,设置当前 environment = staging,rails 就会读取.env.staging + .env 当前 environment = production,rails 就会读取.env.production + .env

    但是 react 太怪了,只有 3 种环境,而且你不能自己修改 NODE_ENV 我看了好多种办法,比较经典比如 https://www.codingdeft.com/posts/react-environment-variables/,利用 env-cmd 读取一些 custom env file

    但是 env-cmd 有个大问题,这是在 build image 阶段执行的,本来一模一样的 image ,仅仅是环境文件的不同 我要生成不同的 image 吗?比如弄个 staging image ,跑 yun run build-staging; qa image 运行 yun run build-qa ?

    本来明明只要一个 image ,pass 不同的当前 env 过去就可以了。另一种奇淫技巧是,在一个 image 内生产不同 build 放在不同的 folder 下,然后制定读取不同 folder ,这个办法被我们 ops team 骂半死

    我想因为各种环境都会读取.env ,有没有办法在 deploy image 前,弄个脚本,比如当前是 staging env ,执行 .env.staging >> .env

    这样可行吗?或者大家有其他好办法?

    32 条回复    2021-12-24 23:16:47 +08:00
    gamesover
        1
    gamesover  
    OP
       283 天前
    找到一个非常类似的 https://stackoverflow.com/a/53228931/2251303 ,这个人就是把 build 的不同环境,放到一个 image 的不同文件夹下,结果被我们 ops 老大骂死了

    我没有其他办法了,谁有招?否则就只能根据不同环境,成生不同的 image ,这个也不好
    gamesover
        2
    gamesover  
    OP
       283 天前
    我试着本地跑了下 yarn run build ,发现 react 把环境变量直接在生产文件中替换了,是 hard code 进去的
    所以一旦 yarn run build 跑完,你再修改.env 都是没用的
    不像其他 app framework ,变量文件是运行时动态加载的

    我想不出任何比 build 的不同环境,放到一个 image 的不同文件夹下的办法了
    lff0305
        3
    lff0305  
       283 天前
    Dockerfile:

    ENV NODE_ENV production
    ARG FILE
    COPY .env ./.env
    COPY $FILE ./.env.production

    Build 的时候

    docker build . --build-arg FILE=.env.staging
    risky
        4
    risky  
       283 天前
    在 public 下放置环境 env.json 管理环境变量, 使用 fetch('/env.json') 来加载环境变量, 这样一个镜像只要挂载文件或者 configMap 就能用在多个环境下
    oott123
        5
    oott123  
       283 天前 via Android   ❤️ 1
    大哥 react 是前端框架,构建出来之后是静态文件的。你一个静态文件,自然也不可能运行的时候有变量哇。
    解决的办法也有,那就是别把变量编译到代码里,用别的办法识别环境…比如放到 HTML 里,再由 js 读出来。
    InternetExplorer
        6
    InternetExplorer  
       283 天前
    前端的运行时在浏览器里,.env 只在 build 的时候有用。有多个环境的话,可以考虑后端返回不同的配置来改变前端的状态
    gamesover
        7
    gamesover  
    OP
       283 天前
    上面提的涉及到其他问题,这样把所有的变量都编译到一个文件,生产文件可以看到任何其他环境的变量

    总之,按照后台编译,动态加载环境变量是无解了
    gamesover
        8
    gamesover  
    OP
       283 天前
    @InternetExplorer 可以,但是就不能一个 image 在各个环境通用了

    staging 环境需要生成 staging image
    qa 环境需要生成 qa image
    pre-prod 环境需要生成 pre-prod image

    这些 image 除了环境变量不同,其他其实都一样
    gamesover
        9
    gamesover  
    OP
       283 天前
    @InternetExplorer sorry ,我理解你错误了,你意思是前端通过 api 从后端获取环境变量

    这样子肯定不行的
    ddch1997
        10
    ddch1997  
       283 天前
    是需要区分密钥之类的吗?我能想到需要区分环境的就是密钥了
    XiLingHost
        11
    XiLingHost  
       283 天前
    如果文件完全一样,只有环境变量的值不同,那么你可以在 Dockerfile 中用 ENV 指定环境变量名称,然后通过.env 文件来配置环境变量
    XiLingHost
        12
    XiLingHost  
       283 天前
    另外和你部署的方法也有关系,如果是 docker-compose 部署,除了写在 envfiles 里,你也可以直接通过 environment 来把它配置到 docker-compose.yml 中,如果是 kubernetes 部署也可以配置到 pod 的环境变量中
    gamesover
        13
    gamesover  
    OP
       283 天前
    @ddch1997 是的,比如要 call 三方 api ,人家有提供测试环境和生产环境
    gamesover
        14
    gamesover  
    OP
       283 天前
    @XiLingHost 不管各种花样,react 生成的静态文件是直接把环境变量 hard code 进去的,不是动态加载的

    只有生成多个生产文件,才能解决环境变量的问题
    fujishimamao
        15
    fujishimamao  
       283 天前 via Android
    如果 image 是一样,那就不是 webpack 里面用到的环境变量,学 4 楼说的那样写个 json 在 public 下,html 直接引,不同的环境用 configmap 挂文件覆盖之类的方法不就能解决了
    fujishimamao
        16
    fujishimamao  
       283 天前 via Android
    还有这跟 react 也没关系吧
    xiaoming1992
        17
    xiaoming1992  
       283 天前 via Android
    在命令行中加入 NODE_ENV=xxx 就可以改变 NODE_ENV 。process.env 在前端是通过 webpack DefinePlugin 构建时直接文本替换,在构建代码执行的时候也是存在内存中,肯定不会实时读取 .env 文件
    7anshuai
        18
    7anshuai  
       283 天前
    CRA 的话可以使用 REACT_APP_ENV
    7anshuai
        19
    7anshuai  
       283 天前
    gamesover
        20
    gamesover  
    OP
       283 天前
    当初这么搞的一大原因,是 leader 说要隐藏生产文件中在环境变量中的的 password 和 keys

    我现在一看,react 生成的生产文件,直接是把环境变量 hard code 进去的,用户一看明明白白,根本没有任何隐藏的必要
    7anshuai
        21
    7anshuai  
       283 天前   ❤️ 1
    迫于上条及上上条都答非所问,午休时间琢磨了下
    react app 从 html script 里读取环境变量的配置吧,docker nginx image ( 1.19 版本开始) 支持使用 /etc/nginx/templates/*.template 模板文件来提取环境变量到 /etc/nginx/conf.d 中,所以你可以试试在启动 nginx 时把想要的环境变量动态的通过 nginx http_sub_filter 指令,注入到 index.html 中
    arischow
        22
    arischow  
       283 天前
    @7anshuai #21 我碰到和楼主一样的问题,现方法是 build N 个环境,等我来研究下你说的,多谢~
    7anshuai
        23
    7anshuai  
       283 天前
    @arischow 我也是刚去看了下 nginx image 的文档,发现可以这么搞 差不多就是利用 nginx 来做动态化下发页面配置了吧 😂
    gamesover
        24
    gamesover  
    OP
       283 天前
    @7anshuai 我看了下,这个方法也是厉害,code + server 配置

    但是还要我之前说的,前端是没有任何秘密的
    用户无论如何都是可以看到想看的变量和密码,如果你植入了前端的话

    我们老大弄环境变量的一大原因是,想影响前端调用位于环境变量的 password 和 key ,这其实是暴露于用户的
    arischow
        25
    arischow  
       283 天前
    @gamesover #24 抱歉。我想了想,跟你不一样的情况应该是我只需要在 index.html 有不同的 baseUrl 就可以了,因为到生产环境的时候静态文件会经过 CDN
    CodingNaux
        26
    CodingNaux  
       283 天前
    需求:
    1. 打包一次
    2. 每个环境变量不同

    所以打包永远都是 webpac production mode, 环境变量通过 nodejs,通过模版写入全局变量.这样代码就能读取不同环境的变量
    CodingNaux
        27
    CodingNaux  
       283 天前
    #26 如果有 node 层,可以用这个
    jessynt
        29
    jessynt  
       283 天前
    参考: https://developers.redhat.com/blog/2021/03/04/making-environment-variables-accessible-in-front-end-containers

    在容器启动时,替换环境变量到打包好的 js 文件中
    leoskey
        30
    leoskey  
       282 天前
    react build 后的文件是无法修改配置的,建议一下两种
    1.在 docker build 时指定参数,--build-arg host=https:// 参考 #3
    2.利用环境域名不同,react 里加载相对路径的配置文件 /config.js ,实际路径是 http://stg/config.js 或 http://pd/config.js
    walpurgis
        31
    walpurgis  
       282 天前
    仔细想想,容器注入的环境变量是给谁看的?是容器内启动的进程,前端代码想得到这些变量就必须通过它们来传递
    7anshuai
        32
    7anshuai  
       282 天前
    @gamesover 正如 create-react-app 文档中说的那样:WARNING: Do not store any secrets (such as private API keys) in your React app!
    API 的密钥应该存在服务器,React app 通过服务端接口再去调用第三方服务
    @arischow 晚上闲着没事,写了篇博客记录一下上面提到的思路,供参考 https://7anshuai.js.org/blog/add-react-app-env-vars-by-nginx/
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2101 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 62ms · UTC 08:56 · PVG 16:56 · LAX 01:56 · JFK 04:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.