skip to Main Content

Running pods have some environment variables defined inside, for example:

/ # printenv
REACT_APP_ENV_VARIABLE=Variable from Kube!
REDIS_SERVICE_PORT=6379
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.38.0.1:443
REDIS_PORT=tcp://10.38.61.225:6379
REDIS_PORT_6379_TCP_ADDR=10.38.61.225
HOSTNAME=playground-pod
PLAYGROUND_SERVICE_SERVICE_HOST=10.38.0.53
REDIS_PORT_6379_TCP=tcp://10.38.61.225:6379
PLAYGROUND_SERVICE_SERVICE_PORT=80
PLAYGROUND_SERVICE_PORT=tcp://10.38.0.53:80
PLAYGROUND_SERVICE_PORT_80_TCP_ADDR=10.38.0.53
KUBERNETES_PORT_443_TCP_PROTO=tcp
PLAYGROUND_SERVICE_PORT_80_TCP_PORT=80
PLAYGROUND_SERVICE_PORT_80_TCP_PROTO=tcp
REACT_APP_ENV_VARIABLE_TWO=192.168.1.12
PLAYGROUND_SERVICE_PORT_80_TCP=tcp://10.38.0.53:80

How should I configure a React app like this one:

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <p>
            <code>ENV. VARIABLE: </code> {x.REACT_APP_ENV_VARIABLE}
        </p>
      </header>
    </div>
  );
}

export default App;

to read and inject some of the variables present in the pod?

The main reason I want to know it, is dynamic update of e.g. backend or Redis URLs – they might change when the app is restarted, rescheduled, etc.

My first approach was using a config.json file imported to the app, but this way I can’t import dynamic values generated by running pods.

3

Answers


  1. Chosen as BEST ANSWER

    As @Harsh Manvar suggested (thanks a lot!), the react-dotenv library can be used, but just adding it to the project is not enough.

    Firstly, you have to follow all steps described in react-dotenv documentation (adding .env file to your project, editing package.json file).

    In my case, .env file looked like this:

    REACT_APP_DEPLOY_SETUP='__dps__'
    REACT_APP_PORT='__prt__'
    REACT_APP_BACKEND_URL='__bur__'
    

    These are just placeholders for real values that will be added during runtime.

    Having the .env file ready, npm scripts prepended with react-dotenv command, and the variables whitelisted (as described in the library documentation), you can build your app image.

    When the image is ready, add it to the kubernetes pod config file and replace your variables' placeholders with real values, like this:

    [...]
    spec:
      containers:
      - name: plg-frontend
        image: localhost:5000/frontend:1.22
        ports:
        - containerPort: 80
        command:
        - sh
        - -c
        args:
        - sed -i "s/__prt__/$REACT_APP_PORT/g" /usr/share/nginx/html/env.js;
          sed -i "s/__bur__/http://$BACKEND_SERVICE_HOST/g" /usr/share/nginx/html/env.js;
          sed -i "s/__dps__/$REACT_APP_DEPLOY_SETUP/g" /usr/share/nginx/html/env.js;
          nginx -g 'daemon off;'
        env:
        - name: REACT_APP_DEPLOY_SETUP
          value: "development"
        - name: REACT_APP_PORT
          value: "5089"
    

    What happened up there, was replacing placeholder values with actual values:

    • $BACKEND_SERVICE_HOST is an environment variable that exists in the pod and can be read from the running container,
    • $REACT_APP_DEPLOY_SETUP is a regular string defined by user,
    • $REACT_APP_PORT is an integer value (it has to be in quotes, like strings!).

    And the replacing happened with sed command (or rather: sh -c "sed -i ..."). All of the commands are chained, so don't forget about semicolons at the end of each argument.

    All of the replacements were made in /usr/share/nginx/html/env.js file, which is created by react-dotenv library in project root. The actual location depends on where you mounted your build image (it's defined in Dockerfile).

    Lastly, nginx command is called, since this is the final command invoked in image's Dockerfile. Without this explicit call, the command from the Dockerfile would be overwritten with the commands related to the pod container and, in this case, nginx wouldn't start your app.

    After the pod is started, you can check whether the variables are present in the container:

    kubectl exec <pod-name> -- printenv | grep REACT_APP
    

    But it doesn't mean they were read by your app during runtime. To see if they were changed to the values from the pod definition, you can either exec running pod container and preview the env.js file or add some console logging in the app code.


  2. You can use the library dot-env

    import React from "react";
    import env from "react-dotenv";
    
    export function MyComponent() {
      return <div>{env.REACT_APP}</div>;
    }
    

    while in deployment you can pass secret from secret and configmap

    spec:
          containers:
            - name: example-site
              image: example/app:v1
              ports:
                - containerPort: 80
              env:
              - name: REACT_APP
                value: "123456"
    

    The main reason I want to know it, is dynamic update of e.g. backend
    or Redis URLs – they might change when the app is restarted,
    rescheduled, etc.

    Above scenario fit well with your requirement, instead of using the config.json.

    You can pass multiple values to deployment using configmap and secrets.

    Login or Signup to reply.
  3. I don’t have enough reputation to comment under someone else’s answer yet, but just to add to @AbreQueVoy’s answer:

    I had trouble with one of my variables: It was a URL.


    sed -i "s/__prt__/$REACT_APP_URL/g" /usr/share/nginx/html/env.js;
    

    The first character after the s (/) is our separator: Thus SED freaked out when getting a URL. I simply replaced the / and changed the separator to a , like so:

    args:
        - sed -i "s,__prt__,$REACT_APP_PORT,g" /usr/share/nginx/html/env.js;
          sed -i "s,__bur__,http://$BACKEND_SERVICE_HOST,g" /usr/share/nginx/html/env.js;
          sed -i "s,__dps__,$REACT_APP_DEPLOY_SETUP,g" /usr/share/nginx/html/env.js;
          nginx -g 'daemon off;'
    

    I did find this tutorial useful for learning a bit about SED.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search