Starting with Kubernetes on Google Container Engine

This is a tutorial of how to run a simple Kubernetes app on GKE (Google Container Engine).

Kubernetes is a container orchestration software that started at Google. Right now it's an open source project maintained by Cloud Native Computing Foundation.

So what's Kubernetes all about? Roughly, it's about managing a cluster (a set of machines) on which we have containers running.

Why containers?

Well, that's a question that probably has a lot of answers, but containers are very lightweight virtual machines more or less. The technology under them is different than virtual machines though. We can orchestrate a set of VMs, but containers are much easier to work with. For example I have one VM running on my MacBook - it eats a lot of memory, it's managed by VirtualBox, it has a lot of stuff installed on it. I can't imagine having to run a bunch of these. Containers though, they're much easier to setup (you just write a text file that describes them - the Dockerfile) and much more lightweight. Their memory footprint can be pretty low. Containers can run on different OSs, some of which are very lightweight. Ok, enough about containers - back to Kubernetes.

What's in a cluster?

I mentioned clusters. But what's a cluster. A cluster has a set of nodes (think computers) on which we have pods, where each pod is a unit that contains several containers. That's the basic structure.

Running a simple Kubernetes app on GKE.

Alright, let's roll our sleeves. Google has its own Quickstart tutorial, but what I don't like about it is that it doesn't describe how to create your own container and it doesn't talk about the Kubernetes dashboard. But a lot of the steps here you can see in Google's tutorial as well.

So let's start.

Create a project.

  1. Go to the Cloud Console.
  2. Create a project. Mine is called "mykube". Every project has an id that you're mostly working with. Mine is mykube-160819.

Install necessary tools and initialize them.

  1. Install the Google Cloud SDK. There's a web interface for working with the SDK, called Google Cloud Shell, but I like having the tools installed locally.
  2. Initialize gcloud by running gcloud init. You'll be asked for the name of your project.
  3. Set a Compute Engine zone, like this:
gcloud config set compute/zone us-central1-b

That's it. You can view your configuration:

$ gcloud config list
Your active configuration is: [mykube]

[compute]
zone = us-central1-b
[core]
account = pminkov@gmail.com
disable_usage_reporting = False
project = mykube-160819

Let's authenticate gcloud too:

gcloud auth application-default login

Run a container image.

Our container is going to be a node.js application that we'll build ourselves. I wanted to experiment with an app that takes a bit more memory, so here's how my code looks like:

$ cat ./server.js 
function randomInt(n) {
  return Math.floor(Math.random() * n);
}

var http = require('http');

var N = 20000000;
var nums = new Array(N);

for (var i = 0; i < N; i++) {
  nums[i] = randomInt(N) * 10;
}

var handleRequest = function(request, response) {
  console.log('Received request for URL: ' + request.url);
  response.writeHead(200);
  var index = randomInt(N);
  response.write('Returning element at index ' + index + ': ' + nums[index] + '\n');
  response.end('Hello World!');
};
var www = http.createServer(handleRequest);

console.log('Listening on port 8080');
www.listen(8080);

We also need a Dockerfile:

$ cat ./Dockerfile 
FROM node:4
EXPOSE 8080
COPY server.js .
CMD node server.js

In order to run a container on GKE, we need to upload it to Google Container Registry. Let's do it:

$ export PROJECT_ID=mykube-160819
$ docker build -t gcr.io/$PROJECT_ID/myserver .
Sending build context to Docker daemon 3.584 kB
Step 1/4 : FROM node:4
 ---> d7efee1f035d
Step 2/4 : EXPOSE 8080
 ---> Using cache
 ---> 147e7888542d
Step 3/4 : COPY server.js .
 ---> Using cache
 ---> b610e7975d20
Step 4/4 : CMD node server.js
 ---> Using cache
 ---> 4e15133cdab2
Successfully built 4e15133cdab2
$ gcloud docker -- push gcr.io/$PROJECT_ID/myserver
The push refers to a repository [gcr.io/mykube-160819/myserver]
# Note that I already have a project called "hikube" which has the same docker image.
# The "mykube" project is something I created for this tutorial.
1bcf3881e79d: Mounted from hikube-160719/myserver 
65e403c25ee9: Mounted from hikube-160719/myserver 
4732c3666dd7: Mounted from hikube-160719/myserver 
a1fbf6fa923f: Mounted from hikube-160719/myserver 
1b8ef9ac5116: Mounted from hikube-160719/myserver 
41ef8cc0bccb: Mounted from hikube-160719/myserver 
100396c46221: Mounted from hikube-160719/myserver 
7b4b54c74241: Mounted from hikube-160719/myserver 
d17d48b2382a: Mounted from hikube-160719/myserver 
latest: digest: sha256:4ad68f056e870938823f6c9555355c149cf7c42a213d7243d915f1a4bcfb9cb1 size: 2213

If you go to the Google Cloud Console and open the Google Container Registry, you'll see the container uploaded:

Google Container Registry

Now let's create a cluster that we'll be deploying our server to:

$ gcloud container clusters create example-cluster
Creating cluster example-cluster...done.                                                                                                                                               
Created [https://container.googleapis.com/v1/projects/mykube-160819/zones/us-central1-b/clusters/example-cluster].
kubeconfig entry generated for example-cluster.
NAME             ZONE           MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
example-cluster  us-central1-b  1.5.3           104.198.190.52  n1-standard-1  1.5.3         3          RUNNING

Congratulations! We have a cluster running.

A little segway. kubectl has a config that determines which cluster you're working with. You can switch between different clusters. Try it:

$ kubectl config current-context
gke_mykube-160819_us-central1-b_example-cluster

We're good here - our context is for the "mykube" cluster. The cluster is empty, we can verify it like this:

$ kubectl get pods
No resources found.

Now let's start out server finally:

$ kubectl run myserver --image=gcr.io/$PROJECT_ID/myserver --port=8080
deployment "myserver" created

We have created a deployment that contains a pod. Let's see what pods we have now:

$ kubectl get pods
NAME                        READY     STATUS              RESTARTS   AGE
myserver-3430466764-04b36   0/1       ContainerCreating   0          17s

Nice, our container is getting spinned. We wait for a bit and we see:

$ kubectl get pods
NAME                        READY     STATUS    RESTARTS   AGE
myserver-3430466764-04b36   1/1       Running   0          58s

We can now expose the container:

$ kubectl expose deployment myserver --type="LoadBalancer"
service "myserver" exposed

This will also take some time:

$ kubectl get service myserver
NAME       CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
myserver   10.3.247.6   <pending>     8080:31574/TCP   34s

Aaand, it's done:

$ kubectl get service myserver
NAME       CLUSTER-IP   EXTERNAL-IP      PORT(S)          AGE
myserver   10.3.247.6   104.155.177.47   8080:31574/TCP   1m

If we go to http://104.155.177.47:8080/, we'll see:

Returning element at index 6110645: 116527640
Hello World!

Now, the tedious part is over. Let's have some fun. We can monitor our cluster through the Kubernetes dashboard. For a reason unclear to me, this dashboard is not available on the Google website. You have to run a proxy to do it. Like this:

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

We can see the dashboard at localhost:8001/ui. It looks like this:

Kubernetes Dashboard

This dashboard is a lot of fun. You can dig into everything available in it. You can probably see everything it shows through kubectl as well, but it's easier to do it by using an UI.

Here's something else that's fun. Your cluster doesn't run on thin air. It runs on Google Compute Engine instances (I believe this is equivalent to AWS' EC2). In your cloud console, you can navigate to your instances and you can even SSH to an instance from the web UI (I wow-ed the first time I did this, much easier than setting up ssh access on AWS).

Our cluster has three nodes.

$ kubectl get nodes
NAME                                             STATUS    AGE
gke-example-cluster-default-pool-2567fc65-1h40   Ready     21m
gke-example-cluster-default-pool-2567fc65-g7lc   Ready     21m
gke-example-cluster-default-pool-2567fc65-n0cp   Ready     21m

These are three GCE instances. Where is our pod running at?

$ kubectl get pods -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP         NODE
myserver-3430466764-04b36   1/1       Running   0          18m       10.0.1.5   gke-example-cluster-default-pool-2567fc65-g7lc

It's running on gke-example-cluster-default-pool-2567fc65-g7lc. Now I can navigate to the web page for this instance and ssh to it. Here's a screenshot of how that looks like:

Compute Engine SSH

I ran ps aux --sort '%mem' to see which process takes most memory. Since my server uses a lot of memory, it's at the top. It's using 179MB of resident memory.

It's pretty nice that you're able to nagivate from a high level system like Kubernetes all the way down to ssh-ing to a machine that runs your containers. When you're ssh-ed you can execute docker ps to see what containers are running, run top to see what's going on on the machine and do all of the other systems debugging tasks that you can think of.

And finally, let's delete our cluster:

$ gcloud container clusters delete example-cluster
The following clusters will be deleted.
 - [example-cluster] in [us-central1-b]

Do you want to continue (Y/n)?  

Deleting cluster example-cluster...done.                                                                                                                                               
Deleted [https://container.googleapis.com/v1/projects/mykube-160819/zones/us-central1-b/clusters/example-cluster].

That's all for today - enjoy.

social