简体   繁体   中英

Use enviroment variables in dockerized react app

The goal I am trying to achieve is to build a docker image (with a react app within) that is using environment variables from the host.

Planned workflow:

  1. Build the docker image locally
  2. Upload the docker image
  3. Call command docker-compose up

I want the environment variable REACT_APP_SOME_ENV_VARIABLE of the system (where the image is hosted) to be usable by the react app.

Current solution:

// App.js
function App() {
  return (
    <p>SOME_ENV_VARIABLE = {process.env.REACT_APP_SOME_ENV_VARIABLE}</p>
  );
}
# Dockerfile
FROM    node:13.12.0-alpine as build-step

# Install the app
RUN     mkdir /app
WORKDIR /app
COPY    package.json /app
RUN     npm install --silent

# Build the app
COPY    . /app
RUN     npm run-script build

# Create nginx server and copy build there
FROM    nginx:1.19-alpine
COPY    --from=build-step /app/build /usr/share/nginx/html

# docker-compose.yml
version: '3.5'

services:
  react-env:
    image:  react-env
    ports:
      - 80:80/tcp
    environment: 
      - REACT_APP_SOME_ENV_VARIABLE=FOO

What am I doing wrong and how do I fix it?

In your approach, the environment variables are injected when the container starts, and by that time your App is built and docker image is created, and also you cannot access process.env on client side. Therefore to access them on client side, we have to do the below steps.

You must be using webpack in your React App for bundling and other stuff.

So in you webpack.config.js, declare your environment variable REACT_APP_SOME_ENV_VARIABLE using Define plugin which will result in declaring the variables as global variables for the app.

Your webpack config should look something like this:

const path = require("path");
const webpack = require("webpack");

module.exports = {
  target: "web",
  performance: {
    hints: false,
  },
  node: {
    fs: "empty"
  },
  entry: "./src/index.js",
  output: {
    path: path.join(__dirname, "/build"),
    filename: "[name].[contenthash].js"
  },
  
  module: {
    rules: [
      //your rules
    ]
  },

  plugins: [
    new webpack.DefinePlugin({
      "envVariable": JSON.stringify(process.env.REACT_APP_SOME_ENV_VARIABLE),
    }),
  ],
};

And in your App, you can use the variable like this

// App.js
function App() {
  return (
    <p>SOME_ENV_VARIABLE = {envVariable}</p>
  );
}

NOTE: Make sure before RUN npm run-script build command is run, your environment variables are injected to docker container. For that you should declare your environment variables in DockerFile using ENV before RUN npm run-script build step.

This was solved by using an NGINX docker package, inject the compiled React production code into the NGINX html folder, then modify the docker-entrypoint.sh file.

FROM nginx:1.19-alpine
COPY --from=build-step /app/build /usr/share/nginx/html
COPY ./docker/docker-entrypoint.sh /docker-entrypoint.sh

Then in that file add the following code at the end of the old script

#!/bin/sh
#!/bin/sh
# vim:sw=4:ts=4:et

set -e

if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
    exec 3>&1
else
    exec 3>/dev/null
fi

if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then
    if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
        echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"

        echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/"
        find "/docker-entrypoint.d/" -follow -type f -print | sort -n | while read -r f; do
            case "$f" in
                *.sh)
                    if [ -x "$f" ]; then
                        echo >&3 "$0: Launching $f";
                        "$f"
                    else
                        # warn on shell scripts without exec bit
                        echo >&3 "$0: Ignoring $f, not executable";
                    fi
                    ;;
                *) echo >&3 "$0: Ignoring $f";;
            esac
        done

        echo >&3 "$0: Configuration complete; ready for start up"
    else
        echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration"
    fi
fi

# Set up endpoint for env retrieval
echo "window._env_ = {" > /usr/share/nginx/html/env_config.js

# Collect enviroment variables for react
eval enviroment_variables="$(env | grep REACT_APP.*=)"

# Loop over variables
env | grep REACT_APP.*= | while read -r line; 
do
    printf "%s',\n" $line | sed "s/=/:'/" >> /usr/share/nginx/html/env_config.js
    
    # Notify the user
    printf "Env variable %s' was injected into React App. \n" $line | sed "0,/=/{s//:'/}"

done

# End the object creation
echo "}" >> /usr/share/nginx/html/env_config.js

echo "Enviroment Variable Injection Complete."


exec "$@"

Functionality:

This will find all environment variable sent to the docker container running the frontend to extract all variables starting with REACT_APP and add them to a file named env_config.js .

All you need to do in the react app is to load that script file, then access the environment variables using window._env_.<property> .

DISCLAIMER Environment variables injected with this method is fully readable by anyone using the site. This is not a secure method for sensitive information. Only use this for things such as "where is the backend api endpoint" or other non-sensitive information that can be extracted just as easily.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM