我正在经历一些奇怪的事情,我试图在我的React应用程序中实现CI/CD,以将代码作为Docker Hub中的图像推送,但是当我尝试使用env变量拉取并运行它时,即使env变量带有前缀REACT_APP,它们在前端也是未定义的,但由于某种原因,它们存储在容器中,因为我做了echo,它会打印出来。另一方面,如果我只是在本地构建dockerimage,而不是通过拉取镜像(运行dockerfile),则env变量会正确存储。
驳接文件:
FROM node:16-alpine as builder
# Set the working directory to /app inside the container
WORKDIR /app
# Copy app files
COPY . .
# Install dependencies (npm ci makes sure the exact versions in the lockfile gets installed)
RUN npm ci
# Build the app
RUN npm run build
# Bundle static assets with nginx
FROM nginx:1.21.0-alpine as production
ENV NODE_ENV production
# Copy built assets from `builder` image
COPY --from=builder /app/build /usr/share/nginx/html
# Add your nginx.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]
字符串
conf(在根目录下作为文件不在nginx文件夹中)
server {
listen 80;
location / {
root /usr/share/nginx/html/;
include /etc/nginx/mime.types;
try_files $uri $uri/ /index.html;
}
}
型
.github/workflow/docker-image.yml
name: Push Backend as Docker Image
on:
push:
branches: ["master"]
pull_request:
branches: ["master"]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: dummy-frontend/test:latest
型
.env
REACT_APP_URL=url
REACT_APP_API_URL=url
REACT_APP_VEHICLE_IMAGE_URL=url
REACT_APP_NAME=app_name
型
代码示例:
{process.env.REACT_APP_NAME === null || process.env.REACT_APP_NAME === undefined
? "undefined"
: process.env.REACT_APP_NAME}
型
当我不是通过docker pull而是从docker文件在本地运行应用程序时,这些是命令(它显示了react应用程序名称):
docker build . -t dockerized-react
docker run -p 3000:80 --env-file .env -d dockerized-react
型
当我通过docker pull在本地运行应用程序时,这些是命令(env变量是undefined的,但我可以通过容器回显env变量):
docker run -p 3000:80 --env-file .env -d dummy-frontend/test:latest
型
在这里,它是undefined。
我不明白问题出在哪里。
3条答案
按热度按时间h5qlskok1#
当您构建React应用程序时,it inlines the value of environment variables into the static JavaScript bundle that it produces。这种情况发生在 * 构建 * 时,而不是运行时。
这就是为什么环境变量在Docker容器中是未定义的,因为它们在构建过程中不存在(由GitHub Action完成的过程)。
当您在本地机器上构建Docker映像时,您有
.env
file present,因此环境变量被正确地内联到静态包中。但是,当您使用GitHub Actions构建Docker映像时,环境变量并不存在,因为您的Dockerfile显示您没有将它们复制到Docker映像中。因此,它们在生成时不可用,也不能内联到包中。
可以考虑在运行时构建React应用程序。
但是......,这将显著增加容器的启动时间,因为每次启动容器时都需要构建React应用程序。
我更喜欢在GitHub Actions上构建Docker映像时包含您的环境变量。
您可以使用GitHub Actions秘密来存储您的环境变量,然后使用
build-args
输入将它们传递到工作流的docker/build-push-action
步骤。字符串
这样,您的环境变量将在构建时可用,并且可以内联到静态React包中。
同样,用你的GitHub密码的实际名称替换
secrets
语法中的占位符。但是,这将使用内嵌到静态包中的环境变量构建Docker映像,因此同一Docker映像不能用于不同的环境变量。
最后,不要忘记在Dockerfile中为每个构建参数添加
ARG
指令:型
这些指令将放在
npm run build
命令 * 之前 *。但事实上,我想在Docker容器上运行这些,我是说
ENV
属性,因为在GitHub上我不认为是非常安全的。运行容器时是否有机会编辑这些内容?如果您希望在运行容器时使用不同的环境变量,则需要对应用程序的服务方式进行一些更改。
由于环境变量在React中是在构建时内联的,因此您将无法在运行时以传统方式更改它们。另请参阅React environment variables: A developer’s guide和Lee Brandt。
然而,有一个解决方法。
一种常见的方法是在容器启动时运行一个服务器端脚本,该脚本将生成一个包含环境变量的JavaScript文件。然后,该文件可以包含在HTML中,从而允许JavaScript代码访问环境变量。
在公用文件夹中添加一个脚本(我们将其命名为
env-config.js
):型
在
public/index.html
中包括脚本:型
更新JavaScript代码以使用
window._env_
而不是process.env
:型
然后在Dockerfile中添加一个在运行时生成
env-config.js
的脚本:下面是使用shell脚本
scripts/env.sh
的示例:型
然后,您可以在容器启动时调用此脚本。Dockerfile可能如下所示:
型
现在,当您运行容器时,可以传入环境变量,应用程序将获取这些变量:
型
这意味着您的环境变量不会被烘焙到Docker映像中,这意味着同一个映像可以在不同的环境中使用。而且,由于环境变量是在运行时插入HTML的,因此只需使用不同的变量重新启动Docker容器就可以更改它们。
但是,它会在HTML的源代码中公开环境变量,因此不应将其用于敏感变量。如果您需要使用敏感变量,请考虑在服务器端调用一个可以返回您的变量的安全终结点。
您还可以使用Joel Lord中的“使环境变量可在前端容器中访问”作为替代方法。
kr98yfug2#
您遇到的问题是由于React应用程序中环境变量的性质以及Docker的工作方式。
当使用
create-react-app
构建React应用程序时,它有一个构建步骤,其中它采用以REACT_APP_
开头的环境变量并将其直接内联到构建中。这意味着这些值是在生成时设置的,而不是在运行时设置的。因此,环境变量需要在执行npm run build
命令的环境中可用。这意味着当你在本地构建Docker镜像并使用
--env-file .env
运行它时,环境变量在构建时被正确读取并内联到JavaScript包中。这就是为什么您的应用程序按预期工作的原因。但是,当Docker镜像在GitHub Actions上构建时,
.env
文件不存在,因此环境变量不会在构建中内联。稍后,当您拉取映像并使用--env-file .env
运行它时,它不会产生预期的效果,因为这些环境变量是在运行时读取的,这已经太晚了。在没有这些环境变量的情况下,构建已经发生。要解决这个问题,您需要在构建时在GitHub Actions工作流中构建映像时使环境变量可用。
您可以在GitHub Actions工作流中添加一个步骤,以便在构建步骤之前创建
.env
文件:字符串
您需要在GitHub存储库中将
REACT_APP_URL
、REACT_APP_API_URL
、REACT_APP_VEHICLE_IMAGE_URL
和REACT_APP_NAME
设置为secret。这允许您保持这些值的安全性,并防止它们在存储库或GitHub Actions日志中暴露。pgvzfuti3#
其他答案已经详细说明了为什么会有问题。然而,我想提出一个替代解决方案。
您可以在运行时将变量写入JS文件并将其导入React应用程序中。这样,您就不需要在构建时知道它应该具有什么值。在我看来,构建时技术违背了容器精神。
所以一个简单的实现看起来像这样。
1.为容器创建一个入口点脚本。在该入口点中,您可以选择env变量并将其写入一个最小化的JS文件。
字符串
使用nginx时,可以将此脚本添加到
entrypoint.d
。型
1.现在,您可以向JavaScript应用添加一些代码来导入文件,并在应用中使用它。
比如说
型
注意我不是一个react开发人员,所以这段代码只是给予一个大致的概念。您可能更清楚如何以及在何处传递变量。