Use environment variables to make a containerized Vue.js frontend application more flexible and deploy it to Code Engine

This blog post addresses the topic, how to use environment variables to make a containerized Vue.js frontend application more flexible. We will touch following technologies Vue.jsNGINXJavaScriptDockerfile and Code Engine.

When you run a containerized application on a container orchestration platform like KubernetesOpen Shift or on a serverless frameworks like Knative or Code Engine or other platforms, it’s helpful to pass endpoints to other applications to the containerized application by using environment variables. When the container will be restarted, these variables can be provided to the container and no adjustment in the source code is necessary. You can use configmaps or in Code Engine simply the environment variable itself.

The example source code you find in this workshop.

In addition, it’s useful when you take a look into this awesome blog post: Externalise and Configure Frontend Environment Variables on Kubernetes which was used for the starting point for the configuration.

ARCHITECTURE OF THE EXAMPLE APPLICATION

The following diagram shows the architecture of the example application, that runs on Code Engine. There is a web-app frontend application that serves the Javascript/Vue.js code to the browser. The web-app code running in the browser invokes a REST API of the web-api microservice. The web-api microservice in turn invokes a REST API of the articles microservice. Keycloak is used for the Identity and Access Management of that microservices based application. To see the results in the web application, users need to be authenticated and they need to have the role user.

FOCUS ON THE FRONTEND APPLICATION

We will focus on the implementation of the web-app frontend, where we use environment variables to define the container configuration at the startup of the container. Therefor we need to understand some of the implementation in Vue.js and configuration of the NGINX webserver.

The image below shows the part of the architecture we focus on.

The web-app fronted uses two endpoints to consume information, one is Keycloak and one is the web-api microsservice. We need two environment variables for that. In additional we want to be flexible in the route definition for the web fronted and therefore we define one additional environment variable for the application route.

Therefore, we will cover following topics in a row:

  1. Define global variables in JavaScript.
  2. Use of the global variable to define a custom route for the frontend.
  3. Use of the global variable to set the endpoints for Keycloak and the web-api microservice.
  4. Configuration of the NGINX server
  5. Automatic creation of the file for the global variables using a bash script.
  6. Definition of a docker_entrypoint.sh bash script.
  7. Definition of the Dockerfile for the container.
  8. Run the container locally.
  9. Deploy to Code Engine.

Define global variables in JavaScript

First we define the global variables using window.variableName. This global variables in that file can be used later to be generated using the values from environment variables. To make this flexible we need to generate this file with the value of the environment variables, when the container will be restarted by any container management or serverless framework. Later we will implement the creation of the file by using a bash script.

The web application will be loaded in the browser, so we need to ensure that the global variables are available during the execution of the web application. Therefor we will save the configuration env-config.js file in the public folder, which will be loaded into the browser.

This is the example code webapp/public/env-config.js.

window.VUE_APP_ROOT="/"
window.VUE_APP_KEYCLOAK="http://localhost:8282/auth"
window.VUE_APP_WEBAPI="http://localhost:8081/"

You need to add also the resource file webapp/public/env-config.js information to the webapp/public/index.html file.

    <!-- used as 'environment' variables, when we going to deploy to OpenShift-->
    <script src="env-config.js" type="text/javascript"></script>
    <!-- used as 'environment' variables, when we going to deploy to OpenShift-->

Use of the global variable to define a custom route for the frontend

To be flexible to run multiple different configurations for different instances of the example application on the same OpenShift cluster, we need to define different routes for those different instances, to avoid an overlap. The route definition happens in the webapp/src/router.js file. For more details, please visit Vue.js

In the example code you see the definition of the path with the window.VUE_APP_ROOT variable.

...
export default new Router({
  mode: 'history',
  routes: [
    {
      path: window.VUE_APP_ROOT,
      name: 'home',
      component: Home
    }
  ],
});
...

Use of the global variable to set the endpoints for Keycloak and the web-api microservice.

The following code shows, where we use the window.VUE_APP_KEYCLOAK and window.VUE_APP_WEBAPI variables defined in the env-config.js file to set the endpoints for the web-api and Keycloak. You find the code example here webapp/src/main.js.

...
else {
  console.log("--> log: option 2");
  let keycloakUrl = window.VUE_APP_KEYCLOAK;
  let webapiUrl = window.VUE_APP_WEBAPI;
  let cnsRedirectUrl = 'https://' + currentHostname + window.VUE_APP_ROOT; // logout
  urls = {
    api: webapiUrl,
    login: keycloakUrl,
    cns: cnsRedirectUrl 
  }
  console.log("--> log: urls ", urls);
  store.commit("setAPIAndLogin", urls);
}
...

Configuration of the NGINX webserver

To provide the needed web application resources to a browser for our web-app container, we use a NGINX webserver. We need to ensure that the file webapp/public/env-config.js is available in the public folder, that our web application can access the values in the browser instance.

In addition, we need to define the usage of a different port than 80 for the NGINX webserver, because we would like to run the container for example on OpenShift. Here we are only allowed to run NGINX as non-root application. Here you find a useful blog posts related to that topic, one made by Josf Bilka and one available on OpenShift blog post at Red Hat deploy Vue.js applications on OpenShift.

We need to change the configuration for the NGINX webserver. Here you find the example source code.

# nginx.conf
worker_processes auto;
# customization
daemon off;
user nginxuser;

pid /tmp/nginx.pid;

events {
 worker_connections 1024;
}

http {
 include /etc/nginx/mime.types; 
 client_body_temp_path /tmp/client_temp;
 proxy_temp_path       /tmp/proxy_temp_path;
 fastcgi_temp_path     /tmp/fastcgi_temp;
 uwsgi_temp_path       /tmp/uwsgi_temp;
 scgi_temp_path        /tmp/scgi_temp;

 server {
   listen 8080;
   charset utf-8;
   sendfile on;
   server_name _;

   index index.html;
   error_log  /tmp/error.log;
   access_log /tmp/access.log;

   location / {
     root /code;
     expires -1;
     add_header Pragma "no-cache";
     add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
     try_files $uri /index.html = 404;
   }
 }
}

Automatic creation of the file for the global variables

We use a bash script for the automatic creation of the file for the global variables. You find the example file here generate_env-config.sh. Therefor we will use the environment variables, which we provide the container later during the invocation based on the environment variables given by the Docker run command using the -e parameter VUE_APP_ROOT,VUE_APP_KEYCLOAK and VUE_APP_WEBAPI.

########################################
cat <<EOF
window.VUE_APP_ROOT="${VUE_APP_ROOT}"
window.VUE_APP_KEYCLOAK="${VUE_APP_KEYCLOAK}"
window.VUE_APP_WEBAPI="${VUE_APP_WEBAPI}"
EOF

Definition of a docker_entrypoint.sh bash script

We define a docker_entrypoint.sh bash script, which helps us to run all needed steps during the startup of the container in a structed and controlled way. Bash script to start the ngnix server and to create a env-config.js file. You find the example source code here webapp/docker_entrypoint.sh. The script also contains more steps we can use for the verification during the startup of the container.

The is extract of the example code which creates the needed file in our container in the NGINX webserver public folder.

...
"/bin/sh" ./generate_env-config.sh > ./code/env-config.js
nginx
...

Definition of the Dockerfile for the container

You find the example Dockerfile here webapp/Dockerfile.os4-webapp. As you see we start the container with the command CMD ["/bin/sh","docker_entrypoint.sh"] and we expose the port 8080. We create a new user nginxuser and provide the needed rights to access the folders and files we need to access during the startup of the container, but we will not run the container as root.

...
##############################
#           PRODUCTION
##############################
FROM nginx:1.20.0-alpine

RUN apk add --update coreutils

# Add a user how will have the rights to change the files in code
RUN addgroup -g 1500 nginxusers 
RUN adduser --disabled-password -u 1501 nginxuser nginxusers 

# Configure ngnix server
COPY nginx-os4-webapp.conf /etc/nginx/nginx.conf
WORKDIR /code
COPY --from=BUILD /usr/src/app/dist .

# Configure web-app for environment variable usage
WORKDIR /
COPY docker_entrypoint.sh .
COPY generate_env-config.sh .
RUN chown nginxuser:nginxusers docker_entrypoint.sh
RUN chown nginxuser:nginxusers generate_env-config.sh
RUN chmod 777 docker_entrypoint.sh generate_env-config.sh
RUN chown -R nginxuser:nginxusers /code
RUN chown -R nginxuser:nginxusers /etc/nginx
RUN chown -R nginxuser:nginxusers /tmp
RUN chmod 777 /code
RUN chmod 777 /tmp
RUN chmod 777 /etc/nginx

USER nginxuser

EXPOSE 8080:8080
CMD ["/bin/sh","docker_entrypoint.sh"]

RUN THE CONTAINER LOCALLY¶

Now we can build and run the container locally to verify does it work and pass the needed environment variables to the container, which are consumed by the Vue.js frontend application web-app.

  • build
docker build -t "quay.io/$REPOSITORY/web-app-ce:v2" -f Dockerfile.os4-webapp .

  • run locally
docker run -it -p 8080:8080 -e VUE_APP_ROOT="/" -e VUE_APP_KEYCLOAK="http://localhost:8282/auth" -e VUE_APP_WEBAPI="localhost" "quay.io/tsuedbroecker/web-app-secure-https:v2"

DEPLOY TO CODE ENGINE

When we deploy the application to Code Engine, we can simply pass the environment variables using the Code Engine CLI and we don’t need to define an additional configmap as we would need in Kubernetes, Open Shift or Knative.

This is the Code Engine CLI command used in the bash script you can find here, which is used to deploy the example application.

   ibmcloud ce application create --name web-app \
                                --image "quay.io/$REPOSITORY/web-app-ce:v2" \
                                --cpu 0.5 \
                                --memory 1G \
                                --env VUE_APP_KEYCLOAK="$KEYCLOAK_URL/auth" \
                                --env VUE_APP_ROOT="/" \
                                --env VUE_APP_WEBAPI="$WEBAPI_URL/articles" \
                                --max-scale 1 \
                                --min-scale 0 \
                                --port 8080 

Summary

When you going to use environment variables to make a Vue.js containerized application flexible to deploy to Code Engine, you need to understand following technologies Vue.jsNGINXJavaScriptDockerfile and Code Engine. That combination to makes your Vue.js frontend more flexible, when you run the fronted as a containerized application in a serverless framework environment and you want to configure different dependencies or configurations of your application, without building a new version of your application. Once again, we used successfully the environment variables for the containers, it was also helpful in my last blog post related to the Quarkus application example.



I hope this was useful for you and let’s see what’s next?

Greetings,

Thomas

#JavaScript, #codeengine, #NGNIX, #Docker, #container, #vuejs, #environment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.