Laurent is a Fullstack developer at vpTech. He likes solving problems and helping people to enhance their skills. Now working for the S&OP (Sales & Operations Planning) team, his work is to provide back office and other features to have better UIs, more friendly and easy to develop. Also looking at the performance and security and try to find solutions to do even better.
One cool thing that you’ve learned with docker is that you can create a unique docker image and deploy to any environments (integration, staging, pre-production, production…) regardless variables you previously defined for each environments. You know that your app will read these environments variables at runtime and so, adapt its behaviour.
Things are a bit different with React App…
- The short answer is: React App CAN’T read environment variables at runtime !
- The long one: Environment variables are embedded into the build, meaning anyone can view them by inspecting your app’s files. Since the build produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime !
However, even if it was permitted you’ll be able to read client’s environment variables (through the browser | security issue) instead of server’s environment variables (what we want !). That said it leaves us in bad shape, we are stuck and it’s not as easy as it is for backend services.
How to solve it ?
- Wrong way: create as much docker image as you have environnements.
- Good way: create a docker image that can build your code OTA*.
The wrong way could unlock you but it’s definitely not following the 12-factor-app principles that says separate build and release. Here we are creating a dependency between build and the environment you are releasing.
The good way, on the contrary, let you reuse the same docker image for each environment you have. In addition to be compliant with the 12-factor-app principles, it also let you saves some space on your repository manager. Been aware of a React App docker image could be ~350MB, if you have a lot of environments it could quickly become a mess.
OTA*: over the air
- Configure your docker image to run a script
RUN npm install -g serve
COPY . .
Nothing fancy, instead of launching the build of the React App directly inside the docker image we copy all the source code (optimize it with .dockerignore) and we call the “run” script.
- Add your run script at the root of your project
set -o nounset \
-o errexit \
-o verbose \
# Set environment values if they exist as arguments
if [ $# -ne 0 ]; then
echo “===> Overriding env params with args …”
for var in “$@”
echo “===> Install dependencies …”
echo “===> Building …”
npm run build
echo “===> Running … “
exec serve -l 3000 -s dist
Now on your code something like “process.env.MY_API_HOST” will be replaced by the value given by the environment.
note: give executable permission to the file !
- Quick test of our setup
# INT: MY_API_HOST=my-api-int.com
# STAG: MY_API_HOST=my-api-stag.com
# PROD: MY_API_HOST=my-api.com# docker image build
docker build -t myimage .# run in INTEGRATION
docker run --name myapp-int -p 8080:3000 \
MY_API_HOST=my-api-int.com --rm myimage# run in STAGING
docker run --name myapp-stag -p 8081:3000 \
MY_API_HOST=my-api-stag.com --rm myimage# run in PRODUCTION
docker run --name myapp-prod -p 8082:3000 \
MY_API_HOST=my-api.com --rm myimage
That’s it ! you can now use one docker image and deploy it on all the environments you have :-)
Kubernetes stuff (extra)
Actions like npm install or npm run build could take some time for your application (~2minutes). When deploying to kubernetes your pod won’t be aware if your app is yet ready. To avoid downtime of your app you can add a small setup on your deployment.yml.
More details here.
Author : Laurent