Deploy Applications on Kubernetes¶
Objective:
- Review process of creating K8s:
- Liveness Probes
 - Readiness Probes
 - Secrets
 - ConfigMaps
 - Requests
 - Limits
 - HPA
 - VPA
 
 
Prepare the Cloud Source Repository Environment with Module 6 Assignment¶
This lab can be executed in you GCP Cloud Environment using Google Cloud Shell.
Open the Google Cloud Shell by clicking on the icon on the top right of the screen:

Once opened, you can use it to run the instructions for this lab.
Cloud Source Repositories: Qwick Start
Step 1 Locate directory where kubernetes YAML manifest going to be stored.
cd ~/ycit019_2022/
git pull       # Pull latest Mod9_assignment
In case you don't have this folder clone it as following:
cd ~
git clone https://github.com/Cloud-Architects-Program/ycit019_2022
cd ~/ycit019_2022/Mod9_assignment/
ls
Step 2 Go into the local repository you've created:
export student_name=<write_your_name_here_and_remove_brakets>
Important
Replace above with your project_id student_name
cd ~/$student_name-notepad
Step 3 Copy Mod9_assignment folder to your repo:
git pull                              # Pull latest code from you repo
cp -r ~/ycit019_2022/Mod9_assignment/ .
Step 4 Commit Mod9_assignment folder using the following Git commands:
git status 
git add .
git commit -m "adding `Mod9_assignment` with kubernetes YAML manifest"
Step 5 Once you've committed code to the local repository, add its contents to Cloud Source Repositories using the git push command:
git push origin master
Step 6 Review Cloud Source Repositories
Use the Google Cloud Source Repositories code browser to view repository files. 
You can filter your view to focus on a specific branch, tag, or comment.
Browse the Mod9_assignment files you pushed to the repository by opening the Navigation menu and selecting Source Repositories:
Click Menu -> Source Repositories > Source Code.
Result
The console shows the files in the master branch at the most recent commit.
1.1 Create GKE Cluster with Cluster and Vertical Autoscaling Support¶
Step 1 Enable the Google Kubernetes Engine API.
gcloud services enable container.googleapis.com
Step 2 From the cloud shell, run the following command to create a cluster with two nodes:
gcloud container clusters create k8s-features \
--zone us-central1-c \
--enable-vertical-pod-autoscaling \
--num-nodes 2 \
--enable-autoscaling --min-nodes 1 --max-nodes 3
Output:
NAME          LOCATION       MASTER_VERSION   MASTER_IP      MACHINE_TYPE  NODE_VERSION     NUM_NODES  STATUS
k8s-features  us-central1-c  1.19.9-gke.1400  34.121.222.83  e2-medium     1.19.9-gke.1400  2          RUNNING
Step 3 Authenticate to the cluster.
gcloud container clusters get-credentials k8s-features --zone us-central1-c
2.1 Externalize Web Application Configuration¶
Let’s make some minor modifications to the web application to externalize its configuration, and make it easier to manage and update at deployment time.
Step 1: Move config file outside compiled application
First, let’s move the web application’s configuration into a folder outside the main compilation path
cd ~
mkdir ~/$student_name-notepad/Mod9_assignment/gowebapp/config
cp ~/$student_name-notepad/Mod9_assignment/gowebapp/code/config/config.json \
~/$student_name-notepad/Mod9_assignment/gowebapp/config
Remove config folder that is located in code directory
rm -rf ~/$student_name-notepad/Mod9_assignment/gowebapp/code/config
ls ~/$student_name-notepad/Mod9_assignment/gowebapp
Result
Your gowebapp folder should look like following:
code  config  Dockerfile
Step 2: Modify app to support setting DB password through environment variable Next, let’s make a minor modification to the Go application code to allow setting the DB password through an environment variable. This will make it easier to dynamically inject this value at deployment time.
Use a text editor of your choice (edit, VS code) to modify:
edit ~/$student_name-notepad/Mod9_assignment/gowebapp/code/vendor/app/shared/database/database.go
- Add an import for the 
"os"package at line 8. After making this change, your imports list will look like the following: 
import (
  "encoding/json"
  "fmt"
  "log"
  "time"
  "os"
  "github.com/boltdb/bolt"
  _ "github.com/go-sql-driver/mysql" // MySQL driver
  "github.com/jmoiron/sqlx"
  "gopkg.in/mgo.v2"
)
- Add the following code at line 89 after 
var err error: 
// Check for MySQL Password environment variable and update configuration if present
if os.Getenv("DB_PASSWORD") != "" {
    d.MySQL.Password = os.Getenv("DB_PASSWORD")
}
2.2 Build new Docker image for your frontend application¶
Step 1 Set the Project ID in Environment Variable:
export PROJECT_ID=<project_id>
Step 2: Update Dockerfile for your gowebapp frontend application and 
define environment variable declaration for a default DB_PASSWORD as well
as add a volume declaration for the container configuration path 
cd ~/$student_name-notepad/Mod9_assignment/gowebapp
edit Dockerfile
FROM golang:1.16.4
LABEL maintainer "student@mcgill.ca"
LABEL gowebapp "v1"
EXPOSE 80
ENV GO111MODULE=auto
ENV GOPATH=/go
ENV PASSWORD=rootpasswd
COPY /code $GOPATH/src/gowebapp/
WORKDIR $GOPATH/src/gowebapp/
RUN go get && go install
VOLUME $GOPATH/src/gowebapp/config
ENTRYPOINT $GOPATH/bin/gowebapp
Step 2: Build updated gowebapp Docker image locally
cd ~/$student_name-notepad/Mod9_assignment/gowebapp
Build and push the gowebapp image to GCR. Make sure to include “.“ at the end of build command. 
docker build -t gcr.io/${PROJECT_ID}/gowebapp:v3 .
docker push gcr.io/${PROJECT_ID}/gowebapp:v3
2.3 Run and test new Docker image locally¶
Before deploying to Kubernetes, let’s test the updated gowebapp Docker image locally, to ensure that the frontend and backend containers run and integrate properly.
Step 1: Launch frontend and backend containers
First, we launch the backend database container, using a previously created Docker image, as it will take a bit longer to startup, and the frontend container depends on it.
Note
Update user-name with command below with you docker-hub id
docker network create gowebapp -d bridge
docker run --net gowebapp --name gowebapp-mysql --hostname \
gowebapp-mysql -d -e MYSQL_ROOT_PASSWORD=rootpasswd gcr.io/${PROJECT_ID}/gowebapp-mysql:v1
Step 2: Now launch a frontend container using the updated gowebapp image, mapping the container port 80 - where the web application is exposed - to port 30005 on the host machine. Notice how we're mapping a host volume into the container for configuration, and setting a container environment variable with the MySQL DB password:
Note
Update user-name with command below with you docker-hub id
docker run -p 8080:80 \
-v ~/$student_name-notepad/Mod9_assignment/gowebapp/config:/go/src/gowebapp/config \
--net gowebapp -d --name gowebapp \
--hostname gowebapp gcr.io/${PROJECT_ID}/gowebapp:v3
Step 3 Test the application locally
Now that we've launched the application containers, let's try to test the web application locally.
You should be able to access the application at Google Cloud Web Preview Console:

Note
Web Preview using port 8080 by default. If you application using other port, you can edit this as needed.
Step 4 Create an account and login. Write something on your Notepad and save it. This will verify that the application is working and properly integrates with the backend database container.
Result
By externalizing application configuration, you have made it easier to manage and modify your application configuration at deployment time. This will be very helpful as we deploy our applications to Kubernetes
Step 5 Cleanup environment
docker rm -f $(docker ps -q)
docker network rm gowebapp
3.1 Create a Secret¶
Step 1 Base64 Encode MySQL password rootpasswd. See Lab 8 for more details.
echo -n "rootpasswd" | base64
Result
MySQL password has been Base64 Encoded
Step 2 Edit a secret for the MySQL password
cd ~/$student_name-notepad/Mod9_assignment/deploy
edit secret-mysql.yaml
kind: Secret
apiVersion: v1
# TODO1 Create secret name: mysql
# TODO2 Secret should be using arbitrary user defined type stringData: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
# TODO3 Define Mysql Password in base64 encoded format
kubectl apply -f secret-mysql.yaml
kubectl describe secret mysql
Step 2  Update gowebapp-mysql-deployment.yaml under
~/$student_name-notepad/Mod9_assignment/deploy
edit ~/$student_name-notepad/Mod9_assignment/deploy/gowebapp-mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gowebapp-mysql
  labels:
    run: gowebapp-mysql
    tier: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      run: gowebapp-mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        run: gowebapp-mysql
    spec:
      containers:
      - env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
          #TODO: replace value: rootpasswd with secretKeyRef
          #TODO: name is mysql
          #TODO: key is password
        image: gcr.io/${PROJECT_ID}/gowebapp-mysql:v1
        name: gowebapp-mysql
        ports:
        - containerPort: 3306
        #TODO add a livenessProbe which performs tcpSocket probe
        #aginst port 3306 with an initial
        # deay of 30 seconds, and a timeout of 2 seconds
        #TODO add a readinessProbe for tcpSocket port 3306 with a 25 second 
        #initial delay, and a timeout of 2 seconds     
Step 2 Start the rolling upgrade and record the command used in the rollout history:
kubectl apply -f gowebapp-mysql-deployment.yaml --record
Step 3 Verify that rollout was successful
kubectl rollout status deploy gowebapp-mysql
Step 4 Check if pods are running
kubectl get pods
Step 5 Create a Service object for MySQL
kubectl apply -f gowebapp-mysql-service.yaml --record
Step 6 Check to make sure it worked
kubectl get service -l "run=gowebapp-mysql"
3.2 Create ConfigMap and Probes for gowebapp¶
Step 1: Create ConfigMap for gowebapp's config.json file
cd ~/$student_name-notepad/Mod9_assignment/gowebapp/config/
kubectl create configmap gowebapp --from-file=webapp-config-json=config.json
kubectl describe configmap gowebapp
Note
The entire file contents from config.json are stored under the key webapp-config-json
3.4 Deploy webapp by Referencing Secret, ConfigMap and define Probes¶
Step 1: Update gowebapp-deployment.yaml under ~/$student_name-notepad/Mod9_assignment/deploy/
cd ~/$student_name-notepad/Mod9_assignment/deploy/
edit gowebapp-deployment.yaml
In this exercise, we will add liveness/readiness probes to our deployments. For more information, see here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gowebapp
  labels:
    run: gowebapp
spec:
  replicas: 2
  selector:
    matchLabels:
      run: gowebapp
  template:
    metadata:
      labels:
        run: gowebapp
    spec:
      containers:
      - env:
      name: DB_PASSWORD
          #TODO: replace value: `rootpasswd` with valueFrom:
            secretKeyRef:
              #TODO: name is mysql
              #TODO: key is password
        image: gcr.io/${PROJECT_ID}/gowebapp:v3
        name: gowebapp
        ports:
          - containerPort: 80
        livenessProbe:
        #TODO add a livenessProbe which performs httpGet
        #aginst  the /register endpoint on port 80 with an initial
        # deay of 15 seconds, and a timeout of 5 seconds
        #TODO add a livenessProbe which performs httpGet 
        # aginst the /register endpoint on port 80 with an initial
        # deay of 25 seconds, and a timeout of 5 seconds
        volumeMounts:
          - #TODO: give the volume a name:config-volume
            #TODO: specify the mountPath: /go/src/gowebapp/config
      volumes: 
      - #TODO: define volume name: config-volume
        configMap:
          #TODO: identify your ConfigMap name: gowebapp
          items:
          - key: webapp-config-json
            path: config.json
kubectl apply -f gowebapp-deployment.yaml --record
Result
This will start the rolling upgrade and record the command used in the rollout history
Step 3: Verify that rollout was successful
kubectl rollout status deploy gowebapp
Step 4: Get rollout history
kubectl rollout history deploy gowebapp
Step 5: Get rollout history details for specific revision (use number show in output to previous command)
kubectl rollout history deploy gowebapp --revision=<latest_version_number
Step 6 Check if pods are running
kubectl get pods
Step 7 Create a Service object for gowebapp
kubectl apply -f gowebapp-service.yaml --record
Step 8 Access your application on Public IP via automatically created Loadbalancer 
created for gowebapp service.
To get the value of Loadbalancer run following command:
kubectl get svc gowebapp -o wide
Expected output:
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
gowebapp         Loadbalancer 10.107.15.39  XXXXXX        9000:32634/TCP   30m
gowebapp-mysql   ClusterIP   None           <none>        3306/TCP         1h
**Step ** Access Loadbalancer IP via browser:
Result
Congrats!!! You've deployed you app to Kubernetes with Secrets, Configmaps and Probes.
4.1 Configure VPA for gowebapp-mysql to find optimal Resource Request and Limits values¶
requests and limits is the way Kubernetes set's QoS for Pods, as well as enable's features like HPA, CA, Resource Quota's and more.
However setting best values for resource requests and limits is hard, VPA is here to help. Set VPA for gowebapp and observe usage recommendation for requests and limits
Step 1 Check our gowebapp-mysql app: 
kubectl get deploy
Result
Our Deployment is up, however without request and limits it will be treated as Best Effort QoS resource on the Cluster.
Step 2 Edit a  manifest for gowebapp-mysql Vertical Pod Autoscaler resource:
cd ~/$student_name-notepad/Mod9_assignment/deploy
edit gowebapp-mysql-vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: gowebapp-mysql
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: gowebapp-mysql
  updatePolicy:
    updateMode: "Off"
Step 3 Apply the manifest for gowebapp-mysql-vpa
kubectl apply -f gowebapp-mysql-vpa.yaml
Step 4 Wait a minute, and then view the VerticalPodAutoscaler
kubectl describe vpa gowebapp-mysql
Note
If you don't see it, wait a little longer and try the previous command again.
Step 5 Locate the "Container Recommendations" at the end of the output from the describe command.
Result
We will be using Lower Bound values to set our request value and Upper Bound as our limits value.
4.2 Set Recommended Request and Limits values to gowebapp-mysql¶
Step 1 Edit a manifest for gowebapp deployment resource:
cd ~/$student_name-notepad/Mod9_assignment/deploy
edit gowebapp-mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gowebapp-mysql
...
        #resources:
        #TODO define a resource request and limits based on VPA Recommender
Step 2 Redeploy our application with defined resource request and limits
cd ~/$student_name-notepad/Mod9_assignment/deploy/
kubectl delete -f gowebapp-mysql-deployment.yaml
kubectl apply -f gowebapp-mysql-deployment.yaml
4.3 Configure VPA for gowebapp to find optimal Resource Request and Limits values¶
Step 1: Create ConfigMap for gowebapp's config.json file
Step 2 Deploy gowebapp app under ~/$student_name-notepad/Mod9_assignment/deploy/
cd ~/$student_name-notepad/Mod9_assignment/deploy/
kubectl apply -f gowebapp-service.yaml    #Create Service
kubectl apply -f gowebapp-deployment.yaml #Create Deployment
kubectl get deploy
Result
Our Deployment is up, however without request and limits it will be treated as Best Effort QoS resource on the Cluster.
Step 3 Edit a  manifest for gowebapp Vertical Pod Autoscaler resource:
cd ~/$student_name-notepad/Mod9_assignment/deploy
edit gowebapp-vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: gowebapp
#TODO: Ref: https://cloud.google.com/kubernetes-engine/docs/how-to/vertical-pod-autoscaling
#TODO: Configure VPA with updateMode:OFF
Step 4 Apply the manifest for gowebapp-vpa
kubectl apply -f gowebapp-vpa.yaml
Step 5 Wait a minute, and then view the VerticalPodAutoscaler
kubectl describe vpa gowebapp
Note
If you don't see it, wait a little longer and try the previous command again.
Step 6 Locate the "Container Recommendations" at the end of the output from the describe command.
Result
We will be using Lower Bound values to set our request value and Upper Bound as our limits value.
4.4 Set Recommended Request and Limits values to gowebapp¶
Step 1 Edit a manifest for gowebapp deployment resource:
cd ~/$student_name-notepad/Mod9_assignment/deploy
edit gowebapp-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gowebapp
  labels:
    run: gowebapp
spec:
.....
        resources:
        #TODO define a resource request and limits based on VPA Recommender
Step 2 Redeploy our application with defined resource request and limits
cd ~/$student_name-notepad/Mod9_assignment/deploy/
kubectl delete -f gowebapp-deployment.yaml
kubectl apply -f gowebapp-deployment.yaml
4.5 Configure HPA for gowebapp¶
Our NotePad Application is going to Production soon. To make sure our application can scale based on requests we will set HPA for our deployment resource using Horizontal Pod Autoscaler.
Step 1 Create HPA for gowebapp based on CPU with minReplicas 1 and maxReplicas 5 with target 50.
cd ~/$student_name-notepad/Mod9_assignment/deploy
cat gowebapp-hpa.yaml
Ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: gowebapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: gowebapp
  minReplicas: 1
  maxReplicas: 5
  targetCPUUtilizationPercentage: 50
Step 2 Apply the manifest for gowebapp-hpa
kubectl apply -f gowebapp-hpa.yaml
Step 3 Take a closer look at the HPA and observe autoscaling or downscaling if any.
kubectl describe hpa gowebapp
Note
It will take some time to collect metrics information about current cpu usage and since our does't have real load it might not trigger any scaling
5.1 Commit K8s manifests to repository and share it with Instructor/Teacher¶
Step 1 Commit deploy folder using the following Git commands:
git add .
git commit -m "k8s manifests for Hands-on Assignment 4"
Step 2 Push commit to the Cloud Source Repositories:
git push origin master
5.2 Cleaning Up¶
Step 1 Delete the cluster
gcloud container clusters delete k8s-features