Kubernetes: basic concepts – part 1

By | 01/06/2017

Introduction

In this post, we will use the (multi-node) environment we created in this post. The idea is to walk through a very simple application and get familiar with some basic Kubernetes concepts.

Getting started with Kubernetes commands

First, we will look at all the pods that are running on our Kubernetes cluster. A pod basically is just a group of one or more containers.

To view the current pods, issue the following command

WAUTERW-M-T3ZT:vagrant wim$ kubectl get pods
No resources found.

Obviously, we have not yet created a deployment (or pod) and hence kubectl reports back that there are no resources. This is only partially true though. It is more correct to state that there are no pods running in the default namespace. Kubernetes has various namespaces. For those wondering, a Kubernetes namespace provides the scope for pods, services, and deployments in the Kubernetes cluster.

You can see the namespaces in the web user interface:

As an alternative to see the namespaces, you can also issue the following command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl get namespaces
NAME          STATUS    AGE
default       Active    2h
kube-system   Active    2h

To see the pods per namespace, you can use the following command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl --namespace default get pods
No resources found.
WAUTERW-M-T3ZT:vagrant wim$ kubectl --namespace kube-system get pods
NAME                                    READY     STATUS    RESTARTS   AGE
heapster-v1.2.0-4088228293-8mlpr        2/2       Running   0          2h
kube-apiserver-172.17.4.101             1/1       Running   0          2h
kube-controller-manager-172.17.4.101    1/1       Running   0          2h
kube-dns-782804071-zp42z                4/4       Running   0          2h
kube-dns-autoscaler-2715466192-79db1    1/1       Running   0          2h
kube-proxy-172.17.4.101                 1/1       Running   0          2h
kube-proxy-172.17.4.201                 1/1       Running   0          2h
kube-proxy-172.17.4.202                 1/1       Running   0          2h
kube-proxy-172.17.4.203                 1/1       Running   0          2h
kube-scheduler-172.17.4.101             1/1       Running   0          2h
kubernetes-dashboard-3543765157-5tchb   1/1       Running   0          2h

You can see here that the namespace called ‘system-kube’ is running all the containers needed for the Kubernetes cluster.

In case you want to see all pods across all namespaces, you can issue the following command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   heapster-v1.2.0-4088228293-8mlpr        2/2       Running   0          1h
kube-system   kube-apiserver-172.17.4.101             1/1       Running   0          1h
kube-system   kube-controller-manager-172.17.4.101    1/1       Running   0          1h
kube-system   kube-dns-782804071-zp42z                4/4       Running   0          1h
kube-system   kube-dns-autoscaler-2715466192-79db1    1/1       Running   0          1h
kube-system   kube-proxy-172.17.4.101                 1/1       Running   0          1h
kube-system   kube-proxy-172.17.4.201                 1/1       Running   0          1h
kube-system   kube-proxy-172.17.4.202                 1/1       Running   0          1h
kube-system   kube-proxy-172.17.4.203                 1/1       Running   0          1h
kube-system   kube-scheduler-172.17.4.101             1/1       Running   0          1h
kube-system   kubernetes-dashboard-3543765157-5tchb   1/1       Running   0          1h

An interesting option (I find) is the -o wide option. It lists all the pods in ps output format with more information, such as the Ip address and the name.

WAUTERW-M-T3ZT:vagrant wim$ kubectl get pods -o wide --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE       IP             NODE
kube-system   heapster-v1.2.0-4088228293-8mlpr        2/2       Running   0          1h        10.2.53.2      172.17.4.203
kube-system   kube-apiserver-172.17.4.101             1/1       Running   0          1h        172.17.4.101   172.17.4.101
kube-system   kube-controller-manager-172.17.4.101    1/1       Running   0          1h        172.17.4.101   172.17.4.101
kube-system   kube-dns-782804071-zp42z                4/4       Running   0          1h        10.2.38.2      172.17.4.201
kube-system   kube-dns-autoscaler-2715466192-79db1    1/1       Running   0          1h        10.2.38.4      172.17.4.201
kube-system   kube-proxy-172.17.4.101                 1/1       Running   0          1h        172.17.4.101   172.17.4.101
kube-system   kube-proxy-172.17.4.201                 1/1       Running   0          1h        172.17.4.201   172.17.4.201
kube-system   kube-proxy-172.17.4.202                 1/1       Running   0          1h        172.17.4.202   172.17.4.202
kube-system   kube-proxy-172.17.4.203                 1/1       Running   0          1h        172.17.4.203   172.17.4.203
kube-system   kube-scheduler-172.17.4.101             1/1       Running   0          1h        172.17.4.101   172.17.4.101
kube-system   kubernetes-dashboard-3543765157-5tchb   1/1       Running   0          1h        10.2.38.3      172.17.4.201

Create deployments

A deployment can be created using the webinterface. See screenshot below to get started.

This will create a deployment. In the screenshot below you can see that also a replication set and a pod get automatically created.

Using the CLI, you can see this also using the following command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     1         1         1            1           1m
WAUTERW-M-T3ZT:vagrant wim$ kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP         NODE
nginx-3296323448-cd1fz   1/1       Running   0          1m        10.2.5.7   172.17.4.202

Instead of doing this using the web interface, you can also do this via the command line. The following command will create a deployment. In fact, the command is fairly similar to a docker run command.

WAUTERW-M-T3ZT:vagrant wim$ kubectl run my-web --image=nginx:alpine --port=80
deployment "my-web" created

If we want to see the deployment that was created, this is easily done with the below command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP           NODE
my-web-361611043-fqgmw   1/1       Running   0          21s       10.2.38.10   172.17.4.201
nginx-3296323448-cd1fz   1/1       Running   0          2m        10.2.5.7     172.17.4.202

How do we know now if these containers are effectively working (e.g. running nginx). The easiest way would be to log into the container and retrieve the nginx default page. Let’s do so using the following command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl run -i -t --rm cli --image=tutum/curl --restart=Never

This will effective run a container based on the tutum/curl image (basically a container that allows us to run the curl command). Our container will be called ‘cli’. The flag –restart is set to Never so it will just create a regular pod (not a deployment and replica set).

In any case, running the above command will give us a prompt into the tutum/curl based container. So we can execute the curl command and see if the nginx default page is seen.

WAUTERW-M-T3ZT:vagrant wim$ kubectl run -i -t --rm cli --image=tutum/curl --restart=Never
If you don't see a command prompt, try pressing enter.
root@cli:/# curl 10.2.38.10

Welcome to nginx!

Convinced it works? 🙂

In case you are not convinced yet, let’s have a look at the logs of the container (my-web):

WAUTERW-M-T3ZT:vagrant wim$ kubectl logs -f my-web-361611043-fqgmw
10.2.5.8 - - [25/Jul/2017:10:50:03 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.35.0" "-"

On the above output you can see we get a 200 OK response back.

Note: when you exit the ‘cli’ container, you won’t see this pod listed any longer in the ‘kubectl get pods’command or in the web interface.

Create Services

At this point, we have a deployment, a replication set and a pod. But now we also need to create a service, so that the nginx server can be accessed from the outside. The below command is exposing this deployment through port 80. The –type NodePort attribute is basically just telling that we can access it through the node IP address.

WAUTERW-M-T3ZT:vagrant wim$ kubectl expose deployment my-web --target-port=80 --type=NodePort
service "my-web" exposed

We can list all our services using the below command. It will show us that indeed port 80 is exposed. Also note that Kubernetes has chosen port 31012 on the container.

WAUTERW-M-T3ZT:vagrant wim$ kubectl get svc
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes   10.3.0.1             443/TCP        3h
my-web       10.3.0.165          80:31012/TCP   18s

Obviously, what we did through the command line in above snippets could also have been done in the Kubernetes dashboard. Also the result of the commands are visible in the Kubernetes dashboard. See below screenshot for the deployment and pod information.

And some more details on this service in the below screenshot:

Now we need to find out on which node our pod is running. The easiest way is to do this via the Kubernetes user interface. Click in the dashboard on the service you just created and then on the pod belonging to that services. In the details section you will see the node displayed.

Another way, would be to see retrieve the pod name, which was assigned by Kubernetes and then view the details of this pod. Let’s first see what is the name that was assigned to the pod we just created:

WAUTERW-M-T3ZT:vagrant wim$ kubectl describe pods my-web-361611043-fqgmw
Name:		my-web-361611043-fqgmw
Namespace:	default
Node:		172.17.4.201/172.17.4.201
Start Time:	Tue, 25 Jul 2017 12:47:30 +0200
Labels:		pod-template-hash=361611043
		run=my-web
Annotations:	kubernetes.io/created-by={"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicaSet","namespace":"default","name":"my-web-361611043","uid":"a8706709-7126-11e7-aa19-0800278ad49a","...
Status:		Running
IP:		10.2.38.10
Controllers:	ReplicaSet/my-web-361611043
Containers:
  my-web:
    Container ID:	docker://6b24dcf0ffa6e447cc40c028dc1b6a652d8ce450e5342563655ea82240223840
    Image:		nginx:alpine
    Image ID:		docker-pullable://nginx@sha256:24a27241f0450b465f9e9deb30628c524aa81a1aa6936daa41ef7c4345515272
    Port:		80/TCP
    State:		Running
      Started:		Tue, 25 Jul 2017 12:47:31 +0200
    Ready:		True
    Restart Count:	0
    Environment:	
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-hzpg8 (ro)
Conditions:
  Type		Status
  Initialized 	True
  Ready 	True
  PodScheduled 	True
Volumes:
  default-token-hzpg8:
    Type:	Secret (a volume populated by a Secret)
    SecretName:	default-token-hzpg8
    Optional:	false
QoS Class:	BestEffort
Node-Selectors:	
Tolerations:	
Events:
  FirstSeen	LastSeen	Count	From			SubObjectPath		Type		Reason		Message
  ---------	--------	-----	----			-------------		--------	------		-------
  26m		26m		1	default-scheduler				Normal		Scheduled	Successfully assigned my-web-361611043-fqgmw to 172.17.4.201
  26m		26m		1	kubelet, 172.17.4.201	spec.containers{my-web}	Normal		Pulled		Container image "nginx:alpine" already present on machine
  26m		26m		1	kubelet, 172.17.4.201	spec.containers{my-web}	Normal		Created		Created container with docker id 6b24dcf0ffa6; Security:[seccomp=unconfined]
  26m		26m		1	kubelet, 172.17.4.201	spec.containers{my-web}	Normal		Started		Started container with docker id 6b24dcf0ffa6

It’s quite a lengthy output and it contains a very detailed description of the pod, but on the third line of the above snippet you can see that the pod is running on node with IP address 172.17.4.201.

So we know have all the information to display the Nginx webpage. The pod is running on 172.17.4.201 and is using a port 31012 (this was the output of the command: kubectl get svc).

Going to a browser on http://172.17.4.201:31012 will display the Nginx page. Nice!

Loadbalancing

Kubernetes uses a TCP/UDP load balancer (kube-proxy) to dispatch the traffic between “replicated” pods. We will launch 2 replicates using the following commands:

WAUTERW-M-T3ZT:vagrant wim$ kubectl run my-nginx --image=nginx:alpine --replicas=2 --port=80 --record
deployment "my-nginx" created
WAUTERW-M-T3ZT:vagrant wim$ kubectl expose deployment my-nginx --type=LoadBalancer --port=80
service "my-nginx" exposed

So essentially we created a deployment called ‘my-nginx’, consisting of two pods and we are exposing the deployment using a service that is loadbalanced (look at the –type for that).

Then let’s see the loadbalancing in action. To do that, open two terminals and open the logs for the both pods. Do it as follows:

First have a look again at the names for the pods, these are my-nginx-64405743-d5n47 and my-nginx-64405743-m0hv8 in this example.

WAUTERW-M-T3ZT:vagrant wim$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
my-nginx-64405743-d5n47   1/1       Running   0          30m
my-nginx-64405743-m0hv8   1/1       Running   0          30m
my-web-361611043-fqgmw    1/1       Running   0          1h
nginx-3296323448-cd1fz    1/1       Running   0          1h

Then let’s open the terminals to see the logs:

WAUTERW-M-T3ZT:vagrant wim$ kubectl logs -f my-nginx-64405743-d5n47

and in another terminal

WAUTERW-M-T3ZT:vagrant wim$ kubectl logs -f my-nginx-64405743-m0hv8

Once this is done, let’s open yet another terminal and issue the following command:

WAUTERW-M-T3ZT:vagrant wim$ kubectl run -i -t --rm cli --image=tutum/curl --restart=Never

This is nothing new, we have done it before, right?

Next, launch a couple of times the ‘curl 10.3.0.165’ command and you will see that Kubernetes is automatically loadbalancing them to the two pods.

Note: why did we have to curl into 10.3.0.165? Have a look at the services output below (indeed, it is the cluster-IP for the service called my-nginx):

WAUTERW-M-T3ZT:vagrant wim$ kubectl get svc
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes   10.3.0.1             443/TCP        4h
my-nginx     10.3.0.15         80:31028/TCP   35m
my-web       10.3.0.165          80:31012/TCP   46m

Leave a Reply

Your email address will not be published. Required fields are marked *