Depending on the environment, there may be multiple front-end deployed images, but only the request API is different. How can one image be used to match the code of multiple environments

Currently, our front-end application deployment images generate different images according to different environments, that is, our front-end application environment has been generated at compile time. In general, there is no problem in application, but the problem caused by different environment images is that they cannot be migrated well, that is, the same image cannot be applied to different environments. For example, one of the most common scenarios is that our request API will vary depending on the environment.

To solve this problem, we need to pass in our runtime environment variables to the front-end application. However, our front-end application runs on the browser side, so how do we pass variables in? There are ways to do this: first, change the variables of the front-end application on the server side; second, pass the environment variables to the browser and hang them under the Window object; third, use nginx.conf to configure nginx for interface forwarding.

Let’s start with the method of modifying front-end application variables on the server side

Let’s take a look at front-end application packaging. Generally, front-end applications are built like this:

// package.json
scripts: {"xxx-stage": "cross-env BUILD_TYPE=stage MOCK=none umi build",}Copy the code

As can be seen above, the environment variable stage was introduced into the project during the construction process. When using it, we can use our environment variable in the following ways:

const BaseApi= {
    dev: {
      env: 'dev'.loginUrl: '//xxx-common-app.dev.xxx.com',},test: {
      env: 'test'.loginUrl: '//xxx-common-app.test.xxx.com',},stage: {
      env: '(stage)'.loginUrl: 'https://xxx-common-app.stage.xxx.com',},prod: {
      env: ' '.loginUrl: 'https://xxx-common-app.xxx.com',}};export defaut BaseApi[BUILD_TYPE]
Copy the code

In fact, when the build is complete, the BUILD_TYPE is the string value stage, which is simply written dead. The object used in API addresses is actually this object:

 {
      env: '(stage)'.loginUrl: 'https://xxx-common-app.stage.xxx.com',}Copy the code

Okay, so before we get to this method, let’s talk a little bit about docker’s environment variables. For example, docker run -e NODE_ENV=’stage’ -e BASEAPI=’ ‘[stage.xxx.com](http://stage.xxx.com)’ ‘-p 8808:80 vuedocker

This passes the NODE_ENV and BASEAPI environment variables to the container. It looks exciting…. However… What’s the use? The code for the WEB application is built, both on the server side, not the client side.

Back to our approach, Linux provides a command: envsubst with which you can modify the files in the container. Ok, let’s go through the logic briefly:

  1. Front-end construction, generate dist folder and front-end files, such as index.html /.js /.css files; Suppose the API address is currentlyapi.dev.xxx.com
  2. Build docker image, currently the API address is stillapi.dev.xxx.com
  3. Start the image and execute the script after startup (this step can be configured in the dockfile file)ENTRYPOINT [ "sh", "/nginx-entrypoint.sh" ]), the script depends on the environment variable passed instage, violently traverses all the JS files in the Docker imageapi.dev.xxx.comModified toapi.stage.xxx.com
  4. When the user visits, they get the REQUESTED APIapi.stage.xxx.com.

This method of brute force modification is functionally feasible, but it does not feel safe to modify the built file, so it can be developed into the second method

Hang the variable under the window object

This method also uses one of the commands mentioned above, envsubst. The difference is that we don’t have to iterate through all the JS, we just need to modify our configuration file. Taking the VUE project as an example, here are the steps:

  1. In the public folder, create a uri.config.template.js file that will be used to replace the template online with the following:
window.my_app_name_env= {
    NODE_ENV: '${NODE_ENV}'.BASEAPI: '${BASEAPI}'};Copy the code

${NODE_ENV} ${BASEAPI} docker uses envsubst to replace environment variables. In addition, since we need to develop locally, we also need a configuration file uri.config.js, which contains:

window.my_app_name_env= {
    NODE_ENV: 'dev'.BASEAPI: '//api.dev.xxx.com'};Copy the code

Add the above uri.config.js to the template file index. HTML:

<script src="./uri.config.js"></script>
Copy the code

At this point, in local development, you can already get the environment variables and API addresses from the Window object. So let’s do the substitution on the line.

  1. Create a docker startup script:nginx-entrypoint.sh, as follows:
WWW_DIR=/usr/share/nginx/html
CONFIGJSON_FILE_SRC="${WWW_DIR}/uri.config.template.js"
CONFIGJSON_FILE_DST="${WWW_DIR}/uri.config.js"
envsubst < "${CONFIGJSON_FILE_SRC}" > "${CONFIGJSON_FILE_DST}"

[ -z "$@" ] && nginx -g 'daemon off; ' || $@
Copy the code

Configuration dockerfile:

FROM registry.xxx.com:8500/library/nginx:alpine-v1 AS server
WORKDIR /usr/share/nginx/html
COPY dist ./
RUN apk add --no-cache gettext
COPY nginx-entrypoint.sh /
ENTRYPOINT [ "sh"."/nginx-entrypoint.sh" ]
Copy the code

All right, we’re almost done with the paperwork.

3.1 Build the application locally, that is, yarn Build generates the dist folder and file, which should contain uri.config.template.js and uri.config.template.js.

3.2 Build a mirror and package Dist as a mirror;

Docker run -e NODE_ENV=’stage’ -e BASEAPI=’stage.xxx.com’ -p 8808:80 vuedocker, ${NODE_ENV} ${BASEAPI} will be replaced with the actual values of the nginx-entryPoint.sh script and generated into the uri.config.js file. These files will normally be sent to the client’s browser. And then hang it under the window object, so you can get the value.

Conf to configure nginx for interface forwarding

Nginx. conf (uri.confg. Js); nginx.conf (uri.confg. Js);

# proxy_params
index  index.html index.htm;
proxy_connect_timeout 3000s;
proxy_send_timeout 3000s;
proxy_read_timeout 3000s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
client_max_body_size    100m;
Copy the code
# nginx.conf
server
{
    Listen on port 80
    listen 80;
    WEB_DOMAIN is a specific domain name, for example, www.baidu.com
    server_name WEB_DOMAIN;
    index index.html index.htm;
    The match path contains/API /v1 requests, and/API /v1/ exists in runtime or Workbench to forward these requests to the specific proxy_pass
    location ~ ^/api/ {
        Import configuration information for proxy_params file
        include    proxy_params;
        The host and post addresses are specified by the proxy
        proxy_pass https://api.stage.xxx.com;
        Before forwarding, filter some parameters in the URL
        rewrite ^/api/(.*)$ /The $1 break;
    }
    # # log
    access_log logs/*.log;
}
Copy the code

Change the value of proxy_pass based on your environment.

Finally, our server cluster is managed by K8S, so it is easier to operate, the above method two and method three, we do not need to run the modified script after docker started, K8S can directly configure the corresponding files in different environments, problem solved!