The Messaging Service Showcase application is quite simple. It is 2 node.js APIs (CMSG - see NR Email Microservice and CHES - see ches-backend) and a React.js UI (see frontend). In production, both backends and the frontend are placed behind a reverse proxy (see reverse-proxy).
For the frontend, we provide a set of npm scripts in package.json file; the most important of which is: npm run build
. The reverse proxy has no installation scripts (but does have runtime configuration requirements, see ../reverse-proxy).
The showcase application is deployed to OpenShift. Here you will find documentation about how this application is built and deployed. Since this application is ready for deployment, we will also give some pointers on how to transistion from nothing in OpenShift to having ready-to-go build and deploy configurations. It is assumed that you are familiar with OpenShift commands and resources (ex. buildconfigs, deployconfigs, imagestreams, secrets, configmaps).
Our builds and deployments are performed via Jenkins (see tools). And see Jenkinsfile or Jenkinsfile.cicd to see how the templates are used for building and deploying in our CI/CD pipeline.
There are some requirements for each namespace/environment, that are NOT built and deployed by the CI/CD process. For this project, we need to ensure each namespace has a secret (credentials to connect to the Common Messaging Service - CMSG), and we need a configmap that we can use to set environment variables.
At a minimum, you will need to have your a Service Client ID and secret, and the OAuth Url for that client/environment (basically who, where, how we authenticate). The service client id and password will be created through Get Token. You will need to get the Common Messaging Service (CMSG) api url and the OAuth url for whichever Common Services environment you are targeting. You will also use Get Token to get your service client and url configurations for CHES (Common Hosted Email Service).
Please see Email Microservice OpenShift Readme for more details on secrets, configmap and other configuration details. We use the default names for secrets and config maps.
The following oc command creates a new secret, cmsg-client, that will be used to set environment variables in the application.
cmsg-client.username sets environment variable CMSG_CLIENT_ID cmsg-client.password sets environment variable CMSG_CLIENT_SECRET
oc create secret -n <namespace> generic cmsg-client --from-literal=username=<client id> --from-literal=password=<client secret> --type=kubernetes.io/basic-auth
The following oc command creates a new secret, ches-client, that will be used to set environment variables in the application.
ches-client.username sets environment variable CHES_CLIENT_ID ches-client.password sets environment variable CHES_CLIENT_SECRET
oc create secret -n <namespace> generic ches-client --from-literal=username=<ches client id> --from-literal=password=<ches client secret> --type=kubernetes.io/basic-auth
The following oc command creates a new secret, mssc-keycloak-client, that will be used to set environment variables in the application. Note that the username/password (client id/secret) should be the same values and for the same client as we use when setting up the NR Email Microservice Config (see below).
mssc-keycloak-client.username sets environment variable KC_CLIENTID mssc-keycloak-client.password sets environment variable KC_CLIENTSECRET
oc create secret -n <namespace> generic mssc-keycloak-client --from-literal=username=<ches client id> --from-literal=password=<ches client secret> --type=kubernetes.io/basic-auth
The following oc command creates a new configmap, cmsg-config, that will be used to set environment variables in the application. OAUTH_TOKEN_URL and CMSG_TOP_LEVEL_URL must be set. These will be available as environment variables matching their name (hence the Uppercase underscore convention for their names).
oc create configmap -n <namespace> cmsg-config --from-literal=OAUTH_TOKEN_URL=<oauth token url> --from-literal=CMSG_TOP_LEVEL_URL=<common messaging api top level url>
The following oc command creates a new configmap, ches-config, that will be used to set environment variables in the application for the CHES backend.
oc create configmap -n <namespace> ches-config --from-literal=CHES_API_URL=<ches token url> --from-literal=CHES_TOKEN_URL=<ches authorization token url> --from-literal=SERVER_BODYLIMIT=30mb
The following oc command creates a new configmap, mssc-keycloak-config, that will be used to set environment variables in the application for the CHES backend.
oc create configmap -n <namespace> mssc-keycloak-config --from-literal=KC_REALM=<KeyCloak Realm ID> --from-literal=KC_SERVERURL=<KeyCloak Auth. URL ex. https://dev.oidc.gov.bc.ca/auth>
We also need to set up secrets and config map for our backend deployment. See User Authentication Secrets and ConfigMap in the microservice project for more. Note that the username/password (client id/secret) should be the same values and for the same client as we use when setting up the MSSC - CHES Backend (mssc-keycloak-client secret) (see above).
To deploy the 3 components (backend, frontend, reverse-proxy), into multiple enviroments (one per pull-request, one each for master in dev, test, and prod); we use a series of templates that allows us to configure these deployments. We do our builds and deploys through Jenkins, and the coordination and configuration of the templates is done in Jenkinsfiles. See Jenkinsfile and Jenkinsfile.cicd to see how the templates are used for building and deploying in our CI/CD pipeline.
Since we employ node.js for both the backend and frontend builds, we have used chained builds to help reduce the time to deploy. That is, we create an image that contains our required npm installs and we only re-build that image if the package.json changes. Downloading and installing all the npm modules can be time consuming, so we do not want to incur that hit for each deployment. Once we have our valid npm image, we can use that for our frontend build.
The overall build/deployment process is:
- Create/Update NPM Images (if needed)
- "Compile"/Build Frontend production bundle
- Create Runtime images for Backend, Frontend, Reverse Proxy
- Deploy Runtime image and service for Backend
- Deploy Runtime image and service for Frontend
- Deploy Runtime image, service, and route for Reverse Proxy
The backend runtime is NR Email Microservice. We use their build config and we provide parameters specific for our needs.
We pass the following to the build template:
Name | Description |
---|---|
SOURCE_REPO_REF | this is which tag, or release of the microservice we are building |
APP_LABEL | we want this to reflect our MSSC app label (ex. mssc-pr-5, mssc-master) |
IMAGE_NAME | this is the name we use for the objects required for the microservice (ex. mssc-pr-5-backend or mssc-master-backend) |
This template is used to create our Frontend specific NPM image. This image will only be created and updated if the frontend/package.json changes. This becomes the "base" image for our Frontend Builder image. We use an inline Dockerfile to create a fairly slim node.js container with libraries required to build our Frontend static file bundle.
The template expects 5 parameters:
Name | Description |
---|---|
REPO_NAME | name of the repository |
JOB_NAME | this will be master or a pull request (ex. pr-3) |
SOURCE_REPO_URL | complete url to the repo (including .git) |
SOURCE_REPO_REF | master or pull request ref (ex. pull/3/head) |
APP_NAME | name of the application: mssc |
This template is used to create our Frontend Builder image, it is 2nd in the Frontend chained build and is based on frontend-npm.bc.yaml. This image will only be created and updated if the frontend/* code changes - basically if we update any Frontend code. This will call our frontend "npm run build" script and produce a minified React production bundle. In order to have this bundle configured correctly, we need to tell it the relative path to our backend. This is done with and parameter named PATH_ROOT that will subsequently be used to configure an environment variable: REACT_APP_API_ROOT that is compiled into the application.
The template expects 7 parameters:
Name | Description |
---|---|
REPO_NAME | name of the repository |
JOB_NAME | this will be master or a pull request (ex. pr-3) |
SOURCE_REPO_URL | complete url to the repo (including .git) |
SOURCE_REPO_REF | master or pull request ref (ex. pull/3/head) |
APP_NAME | name of the application: mssc |
NAMESPACE | the Openshift namespace where we can source our Frontend NPM image (ex. z208i4-tools) |
PATH_ROOT | compiled into our React code, the path were we will be hosting the backend and frontend (ex. /pr-3 or /mssc) |
This template is used to create our CHES backend Runtime image.
The template expects 4 parameters:
Name | Description |
---|---|
JOB_NAME | this will be master or a pull request (ex. pr-3) |
SOURCE_REPO_URL | complete url to the repo (including .git) |
SOURCE_REPO_REF | master or pull request ref (ex. pull/3/head) |
APP_NAME | name of the application: mssc |
This template is used to create our Frontend Runtime image, it is 3rd in the Frontend chained build and uses frontend-builder.bc.yaml. This image will only be created and updated if the frontend/* code changes - basically if we update any Frontend code. The minified React production bundle is copied into a new image that is a Caddy server, which serves that static file.
The template expects 6 parameters:
Name | Description |
---|---|
REPO_NAME | name of the repository |
JOB_NAME | this will be master or a pull request (ex. pr-3) |
SOURCE_REPO_URL | complete url to the repo (including .git) |
SOURCE_REPO_REF | master or pull request ref (ex. pull/3/head) |
APP_NAME | name of the application: mssc |
NAMESPACE | the Openshift namespace where we can source our Frontend Builder image (ex. z208i4-tools) |
This template is used to create our Reverse Proxy Runtime image. Our source (a Caddyfile) is copied into a BcGov Caddy base image.
The template expects 5 parameters:
Name | Description |
---|---|
REPO_NAME | name of the repository |
JOB_NAME | this will be master or a pull request (ex. pr-3) |
SOURCE_REPO_URL | complete url to the repo (including .git) |
SOURCE_REPO_REF | master or pull request ref (ex. pull/3/head) |
APP_NAME | name of the application: mssc |
This template deploys our Backend Runtime image (nodejs server) and creates a service for it. This service is internal and used by the Reverse Proxy to handle backend requests.
We provide the following parameters to the deployment config:
Name | Description |
---|---|
APP_LABEL | we want this to reflect our MSSC app label (ex. mssc-pr-5, mssc-master) |
IMAGE_NAME | this is the name we use for the objects required for the microservice (ex. mssc-pr-5-backend or mssc-master-backend) |
NAMESPACE | the Openshift namespace to which these objects are deployed (ex. z208i4-dev) |
SECRET_NAME | this is the name of our secrets for CMSG Service Client: cmsg-client |
CONFIG_MAP_NAME | this is the name of our config map for CMSG Service Client Urls: cmsg-config |
CMSG_SENDER | the email address we use as our default sender. NR.CommonServiceShowcase@gov.bc.ca |
HOST_URL | this is the full url that will be exposed by our reverse proxy. (ex. https://${HOST_ROUTE}${PATH_ROOT}) |
This template deploys our Frontend Runtime image (Caddy static files server) and creates a service for it. This service is internal and used by the Reverse Proxy to handle frontend requests.
The template expects 5 parameters:
Name | Description |
---|---|
REPO_NAME | name of the repository |
JOB_NAME | this will be master or a pull request (ex. pr-3) |
APP_NAME | name of the application: mssc |
NAMESPACE | the Openshift namespace to which these objects are deployed (ex. z208i4-dev) |
PATH_ROOT | Same as we used to build the React code, the path were we will be hosting the backend and frontend (ex. /pr-3 or /mssc) |
This template deploys our CHES Backend Runtime image creates a service for it. This service is internal and used by the Reverse Proxy to handle ches api requests.
The template expects 4 parameters:
Name | Description |
---|---|
JOB_NAME | this will be master or a pull request (ex. pr-3) |
APP_NAME | name of the application: mssc |
NAMESPACE | the Openshift namespace to which these objects are deployed (ex. z208i4-dev) |
SERVER_HOST_URL | this is the full url that will be exposed by our reverse proxy. (ex. https://${HOST_ROUTE}${PATH_ROOT}) |
This template deploys our Reverse Proxy Runtime image (Caddy reverse proxy server), creates a service for it and exposes a route (publicly accessible Url).
The template expects 6 parameters:
Name | Description |
---|---|
REPO_NAME | name of the repository |
JOB_NAME | this will be master or a pull request (ex. pr-3) |
APP_NAME | name of the application: mssc |
HOST_ROUTE | domain for this instance (ex. z208i4-dev.pathfinder.gov.bc.ca) |
PATH_ROOT | Same as we used to build the React code, the path were we will be hosting the backend and frontend (ex. /pr-3 or /mssc) |
HOST_ROUTE and PATH_ROOT are combined to make the Url.
The deployment templates also accept parameters for resource limits and requests, so they can be adjusted per environment/deployment.
Please see Email Microservice OpenShift Readme for examples on how one would call openshift templates and provide parameters on the command line. The following demonstrates calls to test a branch before a pull request is made. Not all possible parameters are passed in, we make use of some defaults for secrets and config map names.
cd openshift
export proj=<project namespace>
export REPO_NAME=nr-email-microservice
export JOB_NAME=pr-x
export SOURCE_REPO_URL=https://github.com/bcgov/nr-email-microservice.git
export SOURCE_REPO_REF=feature/oidc-auth
export APP_NAME=mssc
export NAMESPACE=$proj
export PATH_ROOT=/pr-x
export HOST_URL=https://$APP_NAME-dev.pathfinder.gov.bc.ca$PATH_ROOT
oc -n $proj process -f frontend-npm.bc.yaml -p REPO_NAME=$REPO_NAME -p JOB_NAME=$JOB_NAME -p SOURCE_REPO_URL=$SOURCE_REPO_URL -p SOURCE_REPO_REF=$SOURCE_REPO_REF -p APP_NAME=$APP_NAME -o yaml | oc -n $proj create -f -
imagestream.image.openshift.io/mssc-pr-x-frontend-npm created
buildconfig.build.openshift.io/mssc-pr-x-frontend-npm created
oc -n $proj start-build mssc-pr-x-frontend-npm
build.build.openshift.io/mssc-pr-x-frontend-npm-1 started
oc logs build/mssc-pr-x-frontend-npm-1 --follow
oc -n $proj process -f frontend-builder.bc.yaml -p REPO_NAME=$REPO_NAME -p JOB_NAME=$JOB_NAME -p SOURCE_REPO_URL=$SOURCE_REPO_URL -p SOURCE_REPO_REF=$SOURCE_REPO_REF -p APP_NAME=$APP_NAME -p PATH_ROOT=$PATH_ROOT -p NAMESPACE=$NAMESPACE -o yaml | oc -n $proj create -f -
imagestream.image.openshift.io/mssc-pr-x-frontend-builder created
buildconfig.build.openshift.io/mssc-pr-x-frontend-builder created
oc -n $proj start-build mssc-pr-x-frontend-builder
build.build.openshift.io/mssc-pr-x-frontend-builder-1 started
oc logs build/mssc-pr-x-frontend-builder-1 --follow
oc -n $proj process -f frontend.bc.yaml -p REPO_NAME=$REPO_NAME -p JOB_NAME=$JOB_NAME -p SOURCE_REPO_URL=$SOURCE_REPO_URL -p SOURCE_REPO_REF=$SOURCE_REPO_REF -p APP_NAME=$APP_NAME -p NAMESPACE=$NAMESPACE -o yaml | oc -n $proj create -f -
imagestream.image.openshift.io/mssc-pr-x-frontend created
buildconfig.build.openshift.io/mssc-pr-x-frontend created
oc -n $proj start-build mssc-pr-x-frontend
build.build.openshift.io/mssc-pr-x-frontend-1 started
oc logs build/mssc-pr-x-frontend-1 --follow
oc -n $proj process -f ches-backend.bc.yaml -p JOB_NAME=$JOB_NAME -p SOURCE_REPO_URL=$SOURCE_REPO_URL -p SOURCE_REPO_REF=$SOURCE_REPO_REF -p APP_NAME=$APP_NAME -o yaml | oc -n $proj create -f -
oc -n $proj start-build mssc-pr-x-ches-backend
oc logs build/mssc-pr-x-ches-backend-1 --follow
oc -n $proj process -f ches-backend.dc.yaml -p JOB_NAME=$JOB_NAME -p APP_NAME=$APP_NAME -p NAMESPACE=$NAMESPACE -p HOST_URL=$HOST_URL -o yaml | oc -n $proj create -f -
oc -n $proj rollout latest dc/mssc-pr-x-ches-backend
oc -n $proj process -f frontend.dc.yaml -p REPO_NAME=$REPO_NAME -p JOB_NAME=$JOB_NAME -p APP_NAME=$APP_NAME -p NAMESPACE=$NAMESPACE -p PATH_ROOT=$PATH_ROOT -p HOST_URL=$HOST_URL -o yaml | oc -n $proj create -f -
service/mssc-pr-x-frontend created
deploymentconfig.apps.openshift.io/mssc-pr-x-frontend created
oc -n $proj rollout latest dc/mssc-pr-x-frontend
oc -n $proj delete all,template,secret,configmap,pvc,serviceaccount,rolebinding --selector app=$APP_NAME-$JOB_NAME