Deploying a containerized web application

This tutorial shows you how to package a web application in a Docker container image, and run that container image on a Google Kubernetes Engine (GKE) cluster. Then, you deploy the web application as a load-balanced set of replicas that can scale to the needs of your users.

Objectives

  • Package a sample web application into a Docker image.
  • Upload the Docker image to Container Registry.
  • Create a GKE cluster.
  • Deploy the sample app to the cluster.
  • Manage autoscaling for the deployment.
  • Expose the sample app to the internet.
  • Deploy a new version of the sample app.

Before you begin

Take the following steps to enable the Kubernetes Engine API:
  1. Visit the Kubernetes Engine page in the Google Cloud Console.
  2. Create or select a project.
  3. Wait for the API and related services to be enabled. This can take several minutes.
  4. Make sure that billing is enabled for your Cloud project. Learn how to confirm that billing is enabled for your project.

Option A: Use Cloud Shell

You can follow this tutorial using Cloud Shell, which comes preinstalled with the gcloud, docker, and kubectl command-line tools used in this tutorial. If you use Cloud Shell, you don't need to install these command-line tools on your workstation.

To use Cloud Shell:

  1. Go to the Google Cloud Console.
  2. Click the Activate Cloud Shell Activate Shell Button button at the top of the Cloud Console window.

    A Cloud Shell session opens inside a new frame at the bottom of the Cloud Console and displays a command-line prompt.

    Cloud Shell session

Option B: Use command-line tools locally

If you prefer to follow this tutorial on your workstation, follow these steps to install the necessary tools.

  1. Install the Cloud SDK, which includes the gcloud command-line tool.

  2. Using the gcloud command line tool, install the Kubernetes command-line tool. kubectl is used to communicate with Kubernetes, which is the cluster orchestration system of GKE clusters:

    gcloud components install kubectl
  3. Install Docker Community Edition (CE) on your workstation. You use this to build a container image for the application.

  4. Install the Git source control tool to fetch the sample application from GitHub.

Building the container image

In this tutorial, you deploy a sample web application called hello-app, a web server written in Go that responds to all requests with the message Hello, World! on port 8080.

GKE accepts Docker images as the application deployment format. Before deploying hello-app to GKE, you must package the hello-app source code as a Docker image.

To build a Docker image, you need source code and a Dockerfile. A Dockerfile contains instructions on how the image is built.

  1. Download the hello-app source code and Dockerfile by running the following commands:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/hello-app
    
  2. Set the PROJECT_ID environment variable to your Google Cloud project ID (project-id). The PROJECT_ID variable associates the container image with your project's Container Registry.

    export PROJECT_ID=project-id
    
  3. Confirm that the PROJECT_ID environment variable has the correct value:

    echo $PROJECT_ID
    
  4. Build and tag the Docker image for hello-app:

    docker build -t gcr.io/${PROJECT_ID}/hello-app:v1 .
    

    This command instructs Docker to build the image using the Dockerfile in the current directory and tag it with a name, such as gcr.io/my-project/hello-app:v1. The gcr.io prefix refers to Container Registry, where the image is hosted. Running this command does not upload the image.

  5. Run the docker images command to verify that the build was successful:

    docker images
    
    Output:
    REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
    gcr.io/my-project/hello-app    v1                  25cfadb1bf28        10 seconds ago      54 MB
    

Running your container locally (optional)

  1. Test your container image using your local Docker engine:

    docker run --rm -p 8080:8080 gcr.io/${PROJECT_ID}/hello-app:v1
    
  2. If you're using Cloud Shell, click the Web Preview button Web Preview Button and then select the 8080 port number. GKE opens the preview URL on its proxy service in a new browser window.

  3. Otherwise, open a new terminal window (or a Cloud Shell tab) and run the following command to verify that the container works and responds to requests with "Hello, World!":

    curl http://localhost:8080

    After you've seen a successful response, you can shut down the container by pressing Ctrl+C in the tab where the docker run command is running.

Pushing the Docker image to Container Registry

You must upload the container image to a registry so that your GKE cluster can download and run the container image. In Google Cloud, Container Registry is available by default.

  1. Configure the Docker command-line tool to authenticate to Container Registry:

    gcloud auth configure-docker
    
  2. Push the Docker image that you just built to Container Registry:

    docker push gcr.io/${PROJECT_ID}/hello-app:v1
    

Creating a GKE cluster

Now that the Docker image is stored in Container Registry, create a GKE cluster to run hello-app. A GKE cluster consists of a pool of Compute Engine VM instances running Kubernetes, the open source cluster orchestration system that powers GKE.

Cloud Shell

  1. Set your project ID and Compute Engine zone options for the gcloud tool:

    gcloud config set project $PROJECT_ID
    gcloud config set compute/zone compute-zone

    Choose the zone that is closest to you.

  2. Create a cluster named hello-cluster:

    gcloud container clusters create hello-cluster
    

    It takes a few minutes for your GKE cluster to be created and health-checked.

  3. After the command completes, run the following command to see the cluster's three worker VM instances:

    gcloud compute instances list
    
    Output:
    NAME                                           ZONE        MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
    gke-hello-cluster-default-pool-d8b498d3-0d6r  us-east1-b   e2-medium                   10.142.0.4   35.237.4.149   RUNNING
    gke-hello-cluster-default-pool-d8b498d3-k29m  us-east1-b   e2-medium                   10.142.0.3   34.75.248.193  RUNNING
    gke-hello-cluster-default-pool-d8b498d3-vcrj  us-east1-b   e2-medium                   10.142.0.2   35.196.51.235  RUNNING
    

Console

  1. Visit the Google Kubernetes Engine menu in Cloud Console.

    Visit the Google Kubernetes Engine menu

  2. Click the Create cluster button.

  3. In the Cluster basics section, enter the name hello-cluster.

  4. Set the zone by choosing a Compute Engine zone from the drop-down menu.

  5. Click Create. This creates a GKE cluster with 3 nodes.

  6. Wait for the cluster to be created. When the cluster is ready, a green check mark appears next to the cluster name.

Deploying the sample app to GKE

You are now ready to deploy the Docker image you built to your GKE cluster.

Kubernetes represents applications as Pods, which are scalable units holding one or more containers. The Pod is the smallest deployable unit in Kubernetes. Usually, you deploy Pods as a set of replicas that can be scaled and distributed together across your cluster. One way to deploy a set of replicas is through a Kubernetes Deployment.

In this section, you create a Kubernetes Deployment to run hello-app on your cluster. This Deployment has replicas (Pods). One Deployment Pod contains only one container: the hello-app Docker image. You also create a HorizontalPodAutoscaler resource that scales the number of Pods from 3 to a number between 1 and 5, based on CPU load.

Cloud Shell

  1. Create a Kubernetes Deployment for your hello-app Docker image.

    kubectl create deployment hello-app --image=gcr.io/${PROJECT_ID}/hello-app:v1
    
  2. Set the baseline number of Deployment replicas to 3.

    kubectl scale deployment hello-app --replicas=3
    
  3. Create a HorizontalPodAutoscaler resource for your Deployment.

    kubectl autoscale deployment hello-app --cpu-percent=80 --min=1 --max=5
    
  4. To see the Pods created, run the following command:

    kubectl get pods
    
    Output:
    NAME                         READY   STATUS    RESTARTS   AGE
    hello-app-784d7569bc-hgmpx   1/1     Running   0          10s
    hello-app-784d7569bc-jfkz5   1/1     Running   0          10s
    hello-app-784d7569bc-mnrrl   1/1     Running   0          15s
    

Console

  1. Visit the Google Kubernetes Engine Workloads menu in Cloud Console.

    Visit the Workloads menu

  2. From the Workloads menu, click Deploy.

  3. In the Create Deployment window that appears, click Existing Container Image.

  4. Using the drop-down menu, click the hello-app image you pushed to Container Registry.

  5. Click View YAML. This opens a YAML configuration file representing the two Kubernetes API resources about to be deployed into your cluster: one Deployment, and one HorizontalPodAutoscaler for that Deployment.

  6. Click Deploy.

  7. Wait for the Deployment Pods to be ready. Blue spinning wheels appear as your GKE cluster deploys the three hello-app Pods, then green check marks appear when the Pods have been successfully deployed.

  8. Wait for the Pods to be ready by navigating to Kubernetes Engine, then Workloads. Click hello-app and scroll down to the Managed Pods section to see three hello-app Pods.

Exposing the sample app to the internet

While Pods do have individually-assigned IP addresses, those IPs can only be reached from inside your cluster. Also, GKE Pods are designed to be ephemeral, starting or stopping based on scaling needs. And when a Pod crashes due to an error, GKE automatically redeploys that Pod, assigning a new Pod IP address each time.

What this means is that for any Deployment, the set of IP addresses corresponding to the active set of Pods is dynamic. We need a way to 1) group Pods together into one static hostname, and 2) expose a group of Pods outside the cluster, to the internet.

Kubernetes Services solve for both of these problems. Services group Pods into one static IP address, reachable from any Pod inside the cluster. GKE also assigns a DNS hostname to that static IP: for instance:hello-app.default.svc.cluster.local.

The default Service type in GKE is called ClusterIP, where the Service gets an IP address reachable only from inside the cluster. To expose a Kubernetes Service outside the cluster, create a Service of type LoadBalancer. This type of Service spawns an External Load Balancer IP for a set of Pods, reachable through the internet.

In this section, you expose the hello-app Deployment to the internet using a Service of type LoadBalancer.

Cloud Shell

  1. Use the kubectl expose command to generate a Kubernetes Service for the hello-app deployment.

    kubectl expose deployment hello-app --name=hello-app-service --type=LoadBalancer --port 80 --target-port 8080
    

    Here, the --port flag specifies the port number configured on the Load Balancer, and the --target-port flag specifies the port number that the hello-app container is listening on.

  2. Run the following command to get the Service details for hello-app-service.

    kubectl get service
    
    Output:
    NAME                 CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
    hello-app-service    10.3.251.122    203.0.113.0     80:30877/TCP     10s
    
  3. Copy the EXTERNAL_IP address to the clipboard (for instance: 203.0.113.0).

Console

  1. Visit the Google Kubernetes Engine Workloads menu in Cloud Console.

    Visit the Workloads menu

  2. Click hello-app.

  3. From the Deployment details page, click Expose.

  4. From the Expose a Deployment menu, set the Target Port to 8080. This is the port the hello-app container listens on.

  5. Click Expose to generate a Kubernetes Service for hello-app, called hello-app-service.

  6. Wait for the External Load Balancer to be created. A blue spinning wheel appears in the Google Cloud Console. When the Load Balancer is ready, you are redirected to the Service Details page for hello-app-service.

  7. Scroll down to the External endpoints field, and copy the address to the clipboard.

Now that the hello-app Pods are exposed to the internet through a Kubernetes Service, you can open a new browser tab, and navigate to the Service IP address you copied to the clipboard. A Hello, World! message appears, along with a Hostname field. The Hostname corresponds to one of the three hello-app Pods serving your HTTP request to your browser.

Deploying a new version of the sample app

In this section, you upgrade hello-app to a new version by building and deploying a new Docker image to your GKE cluster.

GKE's rolling update feature lets you to update your Deployments without downtime. During a rolling update, your GKE cluster incrementally replaces the existing hello-app Pods with Pods containing the Docker image for the new version. During the update, your load balancer service routes traffic only into available Pods.

  1. Return to Cloud Shell, where you have cloned the hello app source code and Dockerfile. Create a new version of the hello-app source code by updating main.go to report a new version, 2.0.0.

  2. Build and tag a new hello-app Docker image.

    docker build -t gcr.io/${PROJECT_ID}/hello-app:v2 .
    
  3. Push the image to Container Registry.

    docker push gcr.io/${PROJECT_ID}/hello-app:v2
    

Now you're ready to update your hello-app Kubernetes Deployment to use a new Docker image.

Cloud Shell

  1. Apply a rolling update to the existing Deployment with an image update:

    kubectl set image deployment/hello-app hello-app=gcr.io/${PROJECT_ID}/hello-app:v2
    
  2. Watch the running Pods running the v1 image stop, and new Pods running the v2 image start.

    watch kubectl get pods
    
    Output:
    NAME                        READY   STATUS    RESTARTS   AGE
    hello-app-89dc45f48-5bzqp   1/1     Running   0          2m42s
    hello-app-89dc45f48-scm66   1/1     Running   0          2m40s
    
  3. In a separate tab, navigate again to the hello-app-service External IP. You should now see the Version set to 2.0.0.

Console

  1. Visit the Google Kubernetes Engine Workloads menu in Cloud Console.

    Visit the Workloads menu

  2. Click hello-app.

  3. Under Actions, click Rolling update.

  4. In the window that appears, set the Image field to gcr.io/[YOUR_PROJECT_ID]/hello-app:v2.

  5. Click Update to begin the rolling update.

  6. Return to the Deployment view for hello-app, then scroll down to Active Revisions. You should now see two Revisions, 1 and 2. Revision 1 corresponds to the initial Deployment you created earlier. Revision 2 is the rolling update you just started.

  7. After a few moments, refresh the page. Under Managed Pods, all of the replicas of hello-app now correspond to Revision 2.

  8. In a separate tab, navigate again to the hello-app-service External IP. You should now see the Version set to 2.0.0.

Cleaning up

To avoid incurring charges to your Google Cloud Platform account for the resources used in this tutorial:

  1. Delete the Service: This deallocates the Cloud Load Balancer created for your Service:

    kubectl delete service hello-app-service
  2. Delete the cluster: This deletes the resources that make up the cluster, such as the compute instances, disks, and network resources:

    gcloud container clusters delete hello-cluster
  3. Delete your container images: This deletes the Docker images you pushed to Container Registry.

     gcloud container images delete gcr.io/${PROJECT_ID}/hello-app:v1  --force-delete-tags --quiet
     gcloud container images delete gcr.io/${PROJECT_ID}/hello-app:v2  --force-delete-tags --quiet
    

What's next