在使用 docker 的 create-react-app 生产中使用不同的 API URL 的最佳方法是什么?

What is the best way to use a different API URL in production for a create-react-app using docker?

好吧,我整天都在敲脑袋,但仍然想不出一个优雅的解决方案。我创建了一个 React 应用程序,它对后端的 Django 服务器进行了一些 API 调用。 我创建了一个 settings.js 文件,它会根据应用程序是 运行 开发还是生产来更改 API url。 settings.js 文件如下所示:

let API_SERVER_VAL = '';
let MEDIA_SERVER_VAL = '';
let FRONTEND_SERVER_VAL = ''; 

switch (process.env.NODE_ENV) {
    case 'development':
        API_SERVER_VAL = 'http://localhost:8000';
        MEDIA_SERVER_VAL = 'http://localhost:8000';
        FRONTEND_SERVER_VAL = 'http://localhost:3000';
        break;
    case 'production':
        API_SERVER_VAL = process.env.API_SERVER;
        MEDIA_SERVER_VAL = process.env.API_SERVER;
        FRONTEND_SERVER_VAL = process.env.API_SERVER;
        break;
    default:
        API_SERVER_VAL = 'http://localhost:8000';
        MEDIA_SERVER_VAL = 'http://localhost:8000';
        FRONTEND_SERVER_VAL = 'http://localhost:3000';
        break;
}

export const API_SERVER = API_SERVER_VAL;
export const MEDIA_SERVER = MEDIA_SERVER_VAL;
export const FRONTEND_SERVER = FRONTEND_SERVER_VAL;

export const SESSION_DURATION = 5*3600*1000;

基于上述内容的示例API如下所示:

import * as settings from '../../settings';
....
axios.post(`${settings.API_SERVER}/api/auth/logout/`, ...)

Dockerfile 如下所示。我正在执行图像的两阶段构建。

###########
# BUILDER #
###########

# pull official base image
FROM node:12.18.3-alpine3.9 as builder

# set work directory
WORKDIR /usr/src/app

# install dependencies and avoid `node-gyp rebuild` errors
COPY package.json .
RUN apk add --no-cache --virtual .gyp \
        python \
        make \
        g++ \
    && npm install \
    && apk del .gyp

# copy our react project
COPY . .

# perform npm build
ARG API_SERVER
ENV API_SERVER=${API_SERVER}
RUN API_SERVER=${API_SERVER} \ 
  npm run build

#########
# FINAL #
#########

# pull official base image
FROM node:12.18.3-alpine3.9 

# set work directory
WORKDIR /usr/src/app

# install serve - deployment static server suggested by official create-react-app
RUN npm install -g serve

# copy our build files from our builder stage
COPY --from=builder /usr/src/app/build ./build

docker-compose 文件如下所示(反应服务)。基本上 docker-compose react 服务将一个名为 API_SERVER=127.0.0.1 的参数传递给 Dockerfile 的构建器阶段。

version: "3.7"

services:
  django:
    build:
      context: ./backend/app
      dockerfile: Dockerfile.prod
    command: gunicorn mainapp.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - django_static_volume:/usr/src/app/files_static
      - app_media_volume:/usr/src/app/files_media
    expose:
      - 8000
    # ports:
    #   - 8000:8000
    env_file:
      - ./backend/.env.prod
    depends_on:
      - db
  db:
    image: postgres:12.0-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    env_file:
      - ./backend/.env.prod.db
  react:
    build:
      context: ./frontend/app
      dockerfile: Dockerfile.prod
      args: 
          - API_SERVER=127.0.0.1 
    volumes:
      - react_static_volume:/usr/src/app/build/static
    expose:
      - 3000
    command: serve -s build -l 3000
    depends_on:
      - django
  nginx:
    restart: always
    build: ./nginx
    volumes:
      - django_static_volume:/usr/src/app/django_files/static
      - app_media_volume:/usr/src/app/media
      - react_static_volume:/usr/src/app/react_files/static
    ports:
      - 80:80
      # - 8000:80
    depends_on:
      - react

volumes:
  postgres_data:
  django_static_volume:
  app_media_volume:
  react_static_volume:

我也在检查 settings.API_SERVER 是否已从 docker 正确传递,方法是在主页的控制台中打印它。所以在 Home.js 内我有以下内容:

console.log(`The environment is ${process.env.NODE_ENV}`);
console.log(`The API server is ${process.env.API_SERVER}`);
console.log(`The settings API server is ${settings.API_SERVER}`);
console.log(`The PUBLIC URL is ${process.env.PUBLIC_URL}`);

但是,我在控制台中看到了这一点。

所以只有 process.env.NODE_ENV 被正确显示了!!我认为这是因为这是从图像的第二阶段而不是构建器阶段显示环境变量。

我做错了什么?不需要额外的 npm-packages 的最优雅的解决方法是什么?

其实我是从create-react-app的官方页面找到答案的。问题是 create-react-app 不会获取环境变量,除非它以 REACT_APP_ 开头。就是这么说的 here:

Note: You must create custom environment variables beginning with REACT_APP_. Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name. Changing any environment variables will require you to restart the development server if it is running.

这正是我能够在控制台中打印出 process.env.NODE_ENV 而不是其他变量的原因,因为它们不是以 REACT_APP_.

开头的

所以我更改了 Dockerfile:

# perform npm build
ARG API_SERVER
ENV REACT_APP_API_SERVER=${API_SERVER}
RUN REACT_APP_API_SERVER=${API_SERVER} \ 
  npm run build 

并且 settings.js 具有:

case 'production':
    API_SERVER_VAL = process.env.REACT_APP_API_SERVER;
    MEDIA_SERVER_VAL = process.env.REACT_APP_API_SERVER;
    FRONTEND_SERVER_VAL = process.env.REACT_APP_API_SERVER;
    break;

控制台现在打印出我从 docker-compose.yaml 文件中传递的任何内容!!