Externalise and Configure Frontend Environment Variables on Kubernetes

Build Once, Run Anywhere

Zing Zai
4 min readOct 31, 2019

Background

This article applies for most frontend JavaScript Frameworks, i.e. React and Vue. In this article, I will share how you can externalise Vue environment (env) variables by declaring them in your Kubernetes (K8) yaml file and injecting them into your Vue app. It can also be easily adapted for React projects.

GitHub Project for reference.

Pre-requisites

You must be fairly familiar with a JavaScript framework, Docker and K8.

Vue’s way for handling env variables…

Vue taught us that we can specify different env variables for different env modes by placing different files in our project root.

Sample project to illustrate different .env files.
Development env (npm run serve): .env.development configs used
Production env (npm run build, serve -s dist): .env.production configs used

Problem

The problem occurs when we:
(1) Do not want to build using prod variables in dev env;
(2) Only want to bring our build folder into prod env; We do not want to bring every god-damn file into prod!
(3) Just want to do a minor config change; In a micro-service (MS) world we live in today, most of the times we do not have control over MSs we use. If an endpoint of a MS is being renamed, we do not want to redeploy our app…

Idea Overview

1. Read variables from static JavaScript file - env-config.js.
2. Reference to these variables in your code (Change
process.env to window)
3. Create shell scripts and edit Dockerfile to generate new env-config.js file at each build.
4. Add env variables in K8 yaml for new containers.

Detailed Steps

1. Read config from env-config.js

public/env-config.js
public/index.html (Line 8)

Traditional JavaScript reference of files. No explanation required here.

2. Reference to variables across your Vue code

Reference variables in code (Line 42)

Before moving on, test out on your app on devt mode — npm run serve/dev.
You should see that your app is reading variables defined in env-config.js.

You can choose to delete .env, .env.development, .env.production files.
We chose to keep .env file to store configs such as long strings that seldom change (i.e. tooltip descriptions).

After ensuring that your app is working fine, we want automate the creation of env-config.js file when we build and dockerise our project.

3. Create shell scripts (generate_env-config.sh, docker_entrypoint.sh) and edit Dockerfile to generate new env-config.js file at the start of each build.

generate_env-config.sh

Contains pre-defined env configs. This script takes env config values, assign them to env variables and creates our env-config.js file.

docker_entrypoint.sh

Line 1: Executes generate_env-config.sh and outputs env-config.js to build folder.
Line 2: Serves our build folder.

Multi-stage builds — With generating env-config.js

Don’t jump! This Dockerfile was written using multi-stage builds to reduce our Docker image size. With multi-stage builds, instead of manually building the image twice and maintaining multiple Dockerfiles, we just have to build our Docker image once using 1 Dockerfile.
Note: Multi-stage builds are only introduced in Docker 17.06.

Line 1–7: Install dependencies & build project that outputs build folder (dist)
Line 10–16: Copy only the build folder & shell scripts to new Docker image. Next, install serve to host the project later. Makes the shell scripts executable. Expose the port and run docker_entry.sh script which creates the env-config.js in the build folder and host the project on port 5000.

Injecting VUE_APP_FOO=”docker_foo” on localhost:5000
docker build -t vue-env-sample:1 .
docker run -p 5000:5000 -e VUE_APP_FOO="docker_foo" vue-env-sample:1

Now you can try building and running image. Inject an env variable to the container, you will be able to see your env variable being injected.

4. Add env configs in K8 yaml for new containers.

Kubernetes deployment yaml sample

This step is pretty easy after you’ve got your Docker image working. Add the env configs accordingly and you can start scaling your app on K8.

Conclusion

Modern JavaScript frameworks have new ways of having defining their env variables. I prefer the traditional way of injecting my configs using static scripts, but with the spirit of CI/CD, I want to be able to build my app once and deploy it anywhere. This approach allowed us to change our env variables for different envs easily and have saved us significant amount of time and effort.

Acknowledgements

Few months ago, I wrote a Medium post on how you can externalise your Spring Boot environment variables on Kubernetes. Friends reached out and taught me how we can apply to the frontend too.

A BIG THANK YOU to my developer friends!

--

--