The goal of this codelab is for you to turn your code (a simple Hello World node.js app here) into a replicated application running on Kubernetes. We will show you how to take code that you have developed on your machine, turn it into a Docker container image, and then run that image on Google Kubernetes Engine.

Here's a diagram of the various parts in play in this codelab to help you understand how pieces fit with one another. Use this as a reference as we progress through the codelab; it should all make sense by the time we get to the end (but feel free to ignore this for now).


For the purpose of this codelab, using a managed environment such as Google Kubernetes Engine (a Google-hosted version of Kubernetes) will allow you to focus more on experiencing Kubernetes rather than setting up the underlying infrastructure.

If you are interested in running Kubernetes on your local machine, say a development laptop, you should probably look into Minikube: This offers a simple setup of a single node Kubernetes cluster for development and testing purposes. You can use Minikube to go through this codelab if you wish.

Codelab-at-a-conference setup





Here's what you should see once logged in:


Google Cloud Shell





Click the "Start Cloud Shell" button:

Screen Shot 2017-06-14 at 10.13.43 PM.png


gcloud auth list

Command output

Credentialed accounts:
gcloud config list project

Command output

project = <PROJECT_ID>

Cloud Shell also sets some environment variables by default which may be useful as you run future commands.


Command output



gcloud config set project <PROJECT_ID>



IMPORTANT: Finally, set the default zone and project configuration:

gcloud config set compute/zone us-central1-f


The first step is to write the application that we want to deploy to Google Kubernetes Engine. Here is a simple Node.js server (use Cloud Shell):

vi server.js

With this content:

var http = require('http');
var handleRequest = function(request, response) {
  response.end("Hello World!");
var www = http.createServer(handleRequest);

From Cloud Shell simply exit the editor and save the server.js file. Since CloudShell has the node executable installed we can now run this simple command (the command produces no output) :

node server.js

and use the built-in Web preview feature of CloudShell to open a new browser tab and proxy a request to the instance you just started on port 8080.

Now, more importantly, let's package this application in a Docker container.

Before we continue, stop the running node server by pressing Ctrl-C in CloudShell.

Next, create a Dockerfile which describes the image that you want to build. Docker container images can extend from other existing images so for this image, we'll extend from an existing Node image.

vi Dockerfile

With this content :

FROM node:12.12.0
COPY server.js .
CMD node server.js

This "recipe" for the Docker image will start from the node image found on the Docker hub, expose port 8080, copy our server.js file to the image and start the node server as we previously did manually.

Save this Dockerfile and build this image by running the following command :

docker build -t$GOOGLE_CLOUD_PROJECT/hello-node:v1 .

Once this completes (it'll take some time to download and extract everything) you can test the image locally with the following command which will run a Docker container as a daemon on port 8080 from our newly-created container image:

docker run -d -p 8080:8080$GOOGLE_CLOUD_PROJECT/hello-node:v1

This is the console output you should see something similar to this:


And again take advantage of the Web preview feature of CloudShell :

Or use curl or wget from your CloudShell prompt if you'd like :

curl http://localhost:8080

This is the console output you should see :

Hello World!

Let's now stop the running container. In this example, our app was running as Docker process 2c66d0efcbd4 :

docker ps

This is the console output you should see :

CONTAINER ID        IMAGE                              COMMAND
2c66d0efcbd4$GOOGLE_CLOUD_PROJECT/hello-node:v1    "/bin/sh -c 'node    

Stop the running container using the ID provide above :

docker stop 2c66d0efcbd4

This is the console output you should see :


Now that the image works as intended we can push it to the Google Container Registry, a private repository for your Docker images accessible from your Google Cloud projects. Let's first enable the registry. For that use the top-level search box in the console to look for "registry", select "Container Registry" and enable the feature :

Screen Shot 2017-05-17 at 07.25.52.pngScreen Shot 2017-05-17 at 07.26.04.png

You can now push your container image to this cloud private registry :

docker -- push$GOOGLE_CLOUD_PROJECT/hello-node:v1

This initial push may take a few minutes to complete:

The push refers to a repository []
ba6ca48af64e: Pushed 
381c97ba7dc3: Pushed 
604c78617f34: Pushed 
fa18e5ffd316: Pushed 
0a5e2b2ddeaa: Pushed 
53c779688d06: Pushed 
60a0858edcd5: Pushed 
b6ca02dfe5e6: Pushed 
v1: digest: sha256:8a9349a355c8e06a48a1e8906652b9259bba6d594097f115060acca8e3e941a2 size: 2002

At this point you should be able to see the container image listed in the console: Tools > Container Registry. At this point we now have a project-wide Docker image available which Kubernetes can access and orchestrate as we'll see in a few minutes.

Ok, you are now ready to create your Kubernetes cluster but before that, navigate to the Google Kubernetes Engine section of the web console and wait for the system to initialize (it should only take a few seconds).

A cluster consists of a Kubernetes master API server hosted by Google and a set of worker nodes. The worker nodes are Compute Engine virtual machines.

First, make sure you have set your project using gcloud :

gcloud config set project PROJECT_ID


gcloud container clusters create hello-world \
                --num-nodes 2 \
                --machine-type n1-standard-1 \
                --zone us-central1-f

This is the console output you should see :

Creating cluster hello-world...done.
Created [].
kubeconfig entry generated for hello-world.
hello-world  us-central1-f  1.13.10-gke.0  n1-standard-1  2          RUNNING



From now on we'll use the kubectl command line (already set up in your Cloud Shell environment).

A Kubernetes pod is a group of containers, tied together for the purposes of administration and networking. It can contain a single container or multiple. Here we'll simply use one container built with your Node.js image stored in our private container registry. It will serve content on port 8080.


So let's now create a pod with the kubectl create deployment command to create a Deployment that manages a Pod. The Pod runs a Container based on the provided Docker image.:

kubectl create deployment hello-node \$GOOGLE_CLOUD_PROJECT/hello-node:v1 \

This is the console output you should see :

deployment.apps/hello-node created

As you can see, we've created a deployment object. Deployments are the recommended way to create and scale pods. Here, a new deployment manages a single pod replica running the hello-node:v1 image.

To view the deployment we just created, simply run :

kubectl get deployments

This is the console output you should see :

hello-node   1/1     1            1           2m


kubectl get pods

This is the console output you should see :

NAME                         READY     STATUS    RESTARTS   AGE
hello-node-714049816-ztzrb   1/1       Running   0          6m


kubectl cluster-info
kubectl config view

And for troubleshooting :

kubectl get events
kubectl logs <pod-name>

At this point you should have our container running under the control of Kubernetes but we still have to make it accessible to the outside world.

By default, the pod is only accessible by its internal IP within the cluster. In order to make the hello-node container accessible from outside the Kubernetes virtual network, you have to expose the pod as a Kubernetes service.



This is the console output you should see :

service/hello-node exposed

The flag used in this command specifies that we'll be using the load-balancer provided by the underlying infrastructure (in this case the Compute Engine load balancer). Note that we expose the deployment, and not the pod directly. This will cause the resulting service to load balance traffic across all pods managed by the deployment (in this case only 1 pod, but we will add more replicas later).



kubectl get services

This is the console output you should see :

hello-node   LoadBalancer   8080:32036/TCP   3m6s
kubernetes   ClusterIP     <none>           443/TCP          8m

Note there are 2 IP addresses listed for our service, both serving port 8080. One is the internal IP that is only visible inside your cloud virtual network; the other is the external load-balanced IP. In this example, the external IP address is


At this point we've gained at least several features from moving to containers and Kubernetes - we do not need to specify on which host to run our workload and we also benefit from service monitoring and restart. Let's see what else we can gain from our new Kubernetes infrastructure.


kubectl scale deployment hello-node --replicas=4

This is the console output you should see :

deployment.extensions/hello-node scaled

At this point you can request a description of the updated deployment :

kubectl get deployment

This is the console output you should see :

hello-node   4/4     4            4           12m

You can also list the all pods :

kubectl get pods

This is the console output you should see :

NAME                         READY     STATUS    RESTARTS   AGE
hello-node-714049816-g4azy   1/1       Running   0          1m
hello-node-714049816-rk0u6   1/1       Running   0          1m
hello-node-714049816-sh812   1/1       Running   0          1m
hello-node-714049816-ztzrb   1/1       Running   0          16m


Here's a diagram summarizing the state of our Kubernetes cluster:

Kubernetes Codelab Diagram 2 (1).png


First, let's modify the application. From CloudShell, edit server.js and update the response message:

  response.end("Hello Kubernetes World!");


docker build -t$GOOGLE_CLOUD_PROJECT/hello-node:v2 . 
docker -- push$GOOGLE_CLOUD_PROJECT/hello-node:v2

We're now ready for Kubernetes to smoothly update our replication controller to the new version of the application. In order to change the image label for our running container, we will need to edit the existing hello-node deployment and change the image from to

To do this, we will use the kubectl set image command. This will trigger a new deployment rollout with the new updated image.

kubectl set image deployment.apps/hello-node \$GOOGLE_CLOUD_PROJECT/hello-node:v2

To track progress of the rollout, you can use this command :

kubectl rollout status deployment hello-node

This is the console output you should see :

Waiting for deployment "hello-node" rollout to finish: 3 of 4 updated replicas are available...
deployment "hello-node" successfully rolled out

While this is happening, the users of the services should not see any interruption. After a little while they will start accessing the new version of your application. You can find more details on rolling updates in this documentation.

Hopefully with these deployment, scaling and update features you'll agree that once you've setup your environment (your GKE cluster here), Kubernetes is here to help you focus on the application rather than the infrastructure.

Google Kubernetes Engine comes with rich admin features built right into the web console. This includes an explorer for workloads, for services and load-balancing, for secrets configuration, and for persistent volume claims.

There is no additional setup required to access all of these features.

You can also leverage the powerful set of Stackdriver logging and monitoring features to observe your workloads.