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.
- Go to the Cloud Console.
- 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.
- 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.
- Initialize gcloud by running
gcloud init
. You'll be asked for the name of your project. - 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:
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:
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:
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.