如何使用create-react-app,Docker和Nginx实现运行时环境变量

by Krunoslav Banovac

通过克鲁诺斯拉夫·巴诺瓦克(Krunoslav Banovac)

如何使用create-react-app,Docker和Nginx实现运行时环境变量 (How to implement runtime environment variables with create-react-app, Docker, and Nginx)

There are many ways to configure your React application. Let’s use an approach which respects Twelve-Factor App methodology. This means it enforces reconfiguration during runtime. Therefore no build per environment would be required.

有很多方法可以配置您的React应用程序。 让我们使用一种尊重十二要素应用程序方法的方法。 这意味着它将在运行时强制重新配置。 因此,不需要按环境构建。

? 我们要实现什么? (? What do we want to achieve?)

We want to be able to run our React application as a Docker container that is built once. It runs everywhere by being configurable during runtime. The output should be a lightweight and performant container which serves our React application as static content, which we achieve by using Ngnix Alpine. Our application should allow configuration within docker-compose file such as this:

我们希望能够将React应用程序作为构建一次的Docker容器运行。 通过在运行时进行配置它可以在任何地方运行。 输出应该是一个轻量级的高性能容器,该容器将我们的React应用程序用作静态内容,这是通过使用Ngnix Alpine实现的。 我们的应用程序应允许在docker-compose文件中进行配置,例如:

version: "3.2"
services:
  my-react-app:
    image: my-react-app
    ports:
      - "3000:80"
    environment:
      - "API_URL=https://production.example.com"

We should be able to configure our React application using -e flag (environment variables) when using Docker run command.

使用Docker run命令时,我们应该能够使用-e标志(环境变量)配置React应用程序。

On first glance, this approach may seem to bring too small of a benefit for the extra work it requires for initial setup. But once setup is done, environment specific configurations and deployment will be way easier to handle. So for anyone targeting dynamic environments or using orchestration systems, this approach is definitely something to consider.

乍一看,这种方法似乎给初始安装所需的额外工作带来的好处太小。 但是,一旦设置完成,特定于环境的配置和部署将更易于处理。 因此,对于以动态环境为目标或使用业务流程系统的任何人,绝对可以考虑使用这种方法。

? 问题 (? The problem)

First of all, it must be clear that there is no such thing as environment variables inside the browser environment. Whichever solution we use nowadays is nothing but a fake abstraction.

首先,必须明确的是,浏览器环境中没有环境变量之类的东西。 如今,无论使用哪种解决方案,都不过是假的抽象。

But, then you might ask, what about .env files and REACT_APP prefixed environment variables which come straight from documentation? Even inside the source code, these are used as process.env just like we use environment variables inside Node.js.

但是,然后您可能会问, 直接来自文档的 .env文件和REACT_APP前缀的环境变量呢? 即使在源代码内部,它们也被用作process.env ,就像我们在Node.js中使用环境变量一样。

In reality, the object process does not exist inside the browser environment, it’s Node-specific. CRA by default doesn’t do server-side rendering. It can’t inject environment variables during content serving (like Next.js does). During transpiling, Webpack process replaces all occurrences of process.env with a string value that was given. This means it can only be configured during build time.

实际上,对象process并不存在于浏览器环境中,而是特定于节点的。 默认情况下,CRA不执行服务器端渲染。 它不能在内容提供期间注入环境变量(就像Next.js一样)。 在编译期间 ,Webpack进程将使用给定的字符串值替换所有出现的process.env 。 这意味着只能在构建期间进行配置

? 解 (? Solution)

The specific moment when it is still possible to inject environment variables happens when we start our container. Then we can read environment variables from inside the container. We can write them into a file which can be served via Nginx (which also serves our React app). They are imported into our application using <script> tag inside the head section of index.html. So at that moment, we run a bash script which creates JavaScript file with environment variables assigned as properties of the global window object. Injected to be globally available within our application the browser way.

当我们启动容器时,仍然有可能注入环境变量的特定时刻发生了。 然后,我们可以从容器内部读取环境变量。 我们可以将它们写入一个文件,该文件可以通过Nginx提供服务(该文件也提供了我们的React应用程序)。 使用index.html头部内部的<script>标记将它们导入到我们的应用程序中。 因此,在那一刻,我们运行了一个bash脚本,该脚本创建了JavaScript文件,并将环境变量分配为全局window对象的属性。 旨在通过浏览器方式在我们的应用程序中全局可用。

? 逐步指南 (? Step by step guide)

Let’s start with a simple create-react-app project and create .env file with our first environment variable that we want to expose.

让我们从一个简单的create-react-app项目开始,并使用我们要公开的第一个环境变量创建.env文件。

Then let’s write a small bash script which will read.env file and extract environment variables that will be written into the file. If you set an environment variable inside the container, its value will be used, otherwise, it will fall back to the default value from .env file. It will create a JavaScript file which puts environment variable values as an object which is assigned as a property of window object.

然后,让我们编写一个小的bash脚本,它将读取.env文件并提取将写入该文件的环境变量。 如果在容器内设置环境变量,则将使用其值,否则,它将退回到.env文件中的默认值。 它将创建一个JavaScript文件,该文件将环境变量值作为一个对象,该对象被分配为window对象的属性。

We need to add the following line to <head> element inside index.htmlwhich then imports the file created by our bash script.

我们需要在index.html <head>元素中添加以下行,然后导入bash脚本创建的文件。

Let’s display our environment variable within the application:

让我们在应用程序中显示环境变量:

? 发展历程 (? Development)

During development, if we don’t want to use Docker, we can run bash script via npm script runner by modifying package.json:

在开发过程中,如果我们不想使用Docker,可以通过修改package.json通过npm script运行器运行bash脚本:

And if we run yarn dev we should see output like this:

如果我们运行yarn dev我们应该看到如下输出:

There are two ways to reconfigure environment variables within dev. Either change the default value inside .env file or override defaults by running yarn devcommand with environment variables prepended:

有两种方法可以在dev中重新配置环境变量。 可以更改.env文件中的默认值,也可以通过在环境变量前面运行yarn dev命令来覆盖默认值:

API_URL=https://my.new.dev.api.com yarn dev

And finally, edit .gitignore so that we exclude environment configurations out of the source code:

最后,编辑.gitignore以便我们从源代码中排除环境配置:

# Temporary env files
/public/env-config.js
env-config.js

As for the development environment, that’s it! We are half-way there. We haven’t made a huge difference at this point compared to what CRA offered by default for the development environment. The true potential of this approach shines in production.

至于开发环境,就是这样! 我们到了一半。 与在默认情况下为开发环境提供的CRA相比,我们在这一点上没有太大的改变。 这种方法的真正潜力在于生产。

? 生产 (? Production)

Now we are going to create minimal Nginx configuration so that we can build an optimized image which serves the production-ready application.

现在,我们将创建最小的Nginx配置,以便我们可以构建一个优化的映像,该映像可用于生产就绪的应用程序。

# Create directory for Ngnix configuration
mkdir -p conf/conf.d
touch conf/conf.d/default.conf conf/conf.d/gzip.conf

The main configuration file should look somewhat like this:

主配置文件应如下所示:

It’s also useful to enable gzip compression so that our assets are more lightweight during network transition:

启用gzip压缩也很有用,这样在网络过渡期间我们的资产将更加轻巧:

Now that our Nginx configuration is ready, we can finally create Dockerfile and docker-compose files:

现在我们的Nginx配置已经准备就绪,我们终于可以创建Dockerfile和docker-compose文件:

Initially, we use node:alpine image to create an optimized production build of our application. Then, we build a runtime image on top of nginx:alpine .

最初,我们使用node:alpine映像创建应用程序的优化生产版本。 然后,我们在nginx:alpine之上构建一个运行时映像。

# => Build container
FROM node:alpine as builder
WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN yarn
COPY . .
RUN yarn build

# => Run container
FROM nginx:1.15.2-alpine

# Nginx config
RUN rm -rf /etc/nginx/conf.d
COPY conf /etc/nginx

# Static build
COPY --from=builder /app/build /usr/share/nginx/html/

# Default port exposure
EXPOSE 80

# Copy .env file and shell script to container
WORKDIR /usr/share/nginx/html
COPY ./env.sh .
COPY .env .

# Add bash
RUN apk add --no-cache bash

# Make our shell script executable
RUN chmod +x env.sh

# Start Nginx server
CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]

Now our container is ready. We can do all the standard stuff with it. We can build a container, run it with inline configurations and push it to a repository provided by services such as Dockerhub.

现在我们的容器已经准备好了。 我们可以用它来做所有标准的事情。 我们可以构建一个容器, 以内联配置运行它,并将其推送到Dockerhub之类的服务提供的存储库中。

The above docker run command should output application like so:

上面的docker run命令应该像这样输出应用程序:

Lastly, let’s create our docker-compose file. You will usually have different docker-compose files depending on the environment and you will use -f flag to select which file to use.

最后,让我们创建我们的docker-compose文件。 根据环境的不同,通常会有不同的docker-compose文件,并且将使用-f标志选择要使用的文件。

version: "3.2"
services:
  cra-runtime-environment-variables:
    image: kunokdev/cra-runtime-environment-variables
    ports:
      - "5000:80"
    environment:
      - "API_URL=production.example.com"

And if we do docker-compose up we should see output like so:

如果我们做docker-compose up我们应该看到如下输出:

Great! We have now achieved our goal. We can reconfigure our application easily in both development and production environments in a very convenient way. We can now finally build only once and run everywhere!

大! 我们现在已经实现了目标。 我们可以以非常方便的方式在开发和生产环境中轻松地重新配置应用程序。 现在,我们最终只能构建一次并在任何地方运行!

If you got stuck or have additional ideas, access the source code on GitHub.

如果您遇到困难或有其他想法,请访问GitHub上源代码

? 下一步 (? Next steps)

The current implementation of the shell script will print all variables included within the .env file. Most of the time we don’t want to expose all of them. You could implement filters for variables you don’t want to expose using prefixes or a similar technique.

Shell脚本的当前实现将打印.env文件中包含的所有变量。 大多数时候,我们不想公开所有这些内容。 您可以使用前缀或类似技术为不想公开的变量实现过滤器。

? 替代解决方案 (? Alternative solutions)

As noted above, the build time configuration will satisfy most use cases. You can rely on the default approach using .env file per environment and build a container for each environment and inject values via CRA Webpack provided environment variables.

如上所述,构建时间配置将满足大多数用例。 您可以依赖每个环境使用.env文件的默认方法,并为每个环境构建一个容器,并通过CRA Webpack提供的环境变量来注入值。

You could also have a look at this CRA GitHub repository issue which covers this problem. By now, there should be more posts and issues which cover this topic. Each offers a similar solution as above. It’s up to you to decide how are you going to implement specific details. You might use Node.js to serve your application which means that you can also replace shells script with Node.js script. Note that Nginx is more convenient to serve static content.

您也可以查看涵盖此问题的CRA GitHub存储库问题。 到现在为止,应该有更多涉及此主题的帖子和问题。 每个都提供与上述类似的解决方案。 由您决定如何实施特定细节。 您可能使用Node.js来服务您的应用程序,这意味着您也可以用Node.js脚本替换shell脚本。 请注意,Nginx更易于提供静态内容。

If you have any questions or want to offer feedback; feel free to open issue on GitHub. Optionally follow me for further posts related to web technologies.

如果您有任何疑问或想提供反馈; 随时在GitHub上打开问题 (可选)关注我以获取与Web技术相关的其他帖子。

翻译自: https://www.freecodecamp.org/news/how-to-implement-runtime-environment-variables-with-create-react-app-docker-and-nginx-7f9d42a91d70/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值