Run an application on Kubernetes

Namespaces

Namespaces allow to logically distribute your applications, generally based on teams, projects or applications stacks. Resources names are unique within a namespace. That means that you could have a service named webserver on 2 different namespaces.

They can be used to isolate applications using network policies, or to define quotas.

For this training we'll work on a namespace named foo

1kubectl create ns foo
2namespace/foo created

And we'll make use of the plugin ns installed in the previous section to set the default namespace as follows

1kubectl ns
2Context "k3d-workshop" modified.
3Active namespace is "foo".

Check that your kubectl is properly configured, here you can see the cluter and the namespace:

1kubectl config get-contexts
2CURRENT   NAME           CLUSTER        AUTHINFO             NAMESPACE
3*         k3d-workshop   k3d-workshop   admin@k3d-workshop   foo

Create your first pod

Creating resources in Kubernetes is often done by applying a yaml/json definition through the API.

First of all we need to clone this repository and change the current path to its root

1git clone https://github.com/Smana/workshop_kubernetes_2021.git
2
3cd workshop_kubernetes_2021.git

Start by creating a pretty simple pod:

1kubectl apply -f content/resources/kubernetes_workshop/pod.yaml --namespace foo
2pod/web created
3
4kubectl get po
5NAME   READY   STATUS    RESTARTS   AGE
6web    1/1     Running   0          98s

We can get detailed information about the pod as follows

 1kubectl describe po web
 2Name:         web
 3Namespace:    foo
 4Priority:     0
 5Node:         k3d-workshop-agent-0/172.20.0.3
 6Start Time:   Fri, 18 Jun 2021 17:05:46 +0200
 7Labels:       run=web
 8Annotations:  <none>
 9Status:       Running
10IP:           10.42.1.6
11...

Or even get a specific attribute, here is an example to get the pod's IP

1kubectl get po web --template={{.status.podIP}}
210.42.1.6

This is worth noting that a pod isn't controlled by a replicaset-controller. That means that when it is deleted, it is not restarted automatically.

1kubectl delete po web
2pod "web" deleted
3
4kubectl get po
5No resources found in foo namespace.

A pod with 2 containers

Now create a new pod using the manifest content/resources/kubernetes_workshop/pod2containers.yaml. Look at its content, we will be using a shared temporary directory and we'll mount its content on both containers. That way we can share data between 2 containers of a given pod.

1kubectl apply -f content/resources/kubernetes_workshop/pod2containers.yaml
2pod/web created
3
4kubectl get pod
5NAME   READY   STATUS    RESTARTS   AGE
6web    2/2     Running   0          36s

We can check that the logs are accessible on the 2 containers

 1kubectl logs web -c logger --tail=6 -f
 2Mon Jun 28 21:06:20 2021
 3Mon Jun 28 21:06:21 2021
 4Mon Jun 28 21:06:22 2021
 5Mon Jun 28 21:06:23 2021
 6Mon Jun 28 21:06:24 2021
 7
 8kubectl exec web -c web -- tail -n 5 /log/out.log
 9Mon Jun 28 21:07:19 2021
10Mon Jun 28 21:07:20 2021
11Mon Jun 28 21:07:21 2021
12Mon Jun 28 21:07:22 2021
13Mon Jun 28 21:07:23 2021

Delete the pod

1kubectl delete po web
2pod "web" deleted

Create a simple webserver deployment

A deployment is a resource that describes the desired state of an application. Kubernetes will ensure that its current status is aligned with the desired one.

Creating a simple deployment can be done using kubectl

1kubectl create deployment podinfo --image stefanprodan/podinfo
2deployment.apps/podinfo created

After a few seconds the deployment will be up to date, meaning that the a pod is up and running.

1kubectl get deploy
2NAME      READY   UP-TO-DATE   AVAILABLE   AGE
3podinfo   1/1     1            1           14s

Replicas and scaling

A deployment creates a replicaset under the hood in order to ensure that the number of replicas (pods) matches the desired one.

1kubectl get replicasets
2NAME                 DESIRED   CURRENT   READY   AGE
3podinfo-7fbb45ccfc   1         1         1       36s

Creating a deployment without specifying the number of replicas will create a single replica. We can scale it on demand using

1kubectl scale deploy podinfo --replicas 6
2deployment.apps/podinfo scaled
3
4kubectl rollout status deployment podinfo
5Waiting for deployment "podinfo" rollout to finish: 4 of 6 updated replicas are available...
6Waiting for deployment "podinfo" rollout to finish: 5 of 6 updated replicas are available...
7deployment "podinfo" successfully rolled out

The default Kubernetes scheduler will try to spread evenly the pods according to the available resources on worker nodes.

1kubectl get po -o wide
2NAME                       READY   STATUS    RESTARTS   AGE    IP           NODE                    NOMINATED NODE   READINESS GATES
3podinfo-7fbb45ccfc-dwxtx   1/1     Running   0          114s   10.42.1.8    k3d-workshop-agent-0    <none>           <none>
4podinfo-7fbb45ccfc-p2djv   1/1     Running   0          34s    10.42.1.11   k3d-workshop-agent-0    <none>           <none>
5podinfo-7fbb45ccfc-4fk9z   1/1     Running   0          34s    10.42.1.9    k3d-workshop-agent-0    <none>           <none>
6podinfo-7fbb45ccfc-gqwz6   1/1     Running   0          34s    10.42.1.10   k3d-workshop-agent-0    <none>           <none>
7podinfo-7fbb45ccfc-4qgvs   1/1     Running   0          34s    10.42.0.8    k3d-workshop-server-0   <none>           <none>
8podinfo-7fbb45ccfc-r6dn5   1/1     Running   0          34s    10.42.0.9    k3d-workshop-server-0   <none>           <none>

The deployment controller will ensure to start new pods if the number of replicas doesn't match its configuration.

 1kubectl delete po $(kubectl get po -l app=podinfo -o jsonpath='{.items[0].metadata.name}')
 2pod "podinfo-7fbb45ccfc-r6dn5" deleted
 3
 4kubectl describe rs podinfo-7fbb45ccfc
 5Name:           podinfo-7fbb45ccfc
 6Namespace:      foo
 7Selector:       app=podinfo,pod-template-hash=7fbb45ccfc
 8Labels:         app=podinfo
 9                pod-template-hash=7fbb45ccfc
10Annotations:    deployment.kubernetes.io/desired-replicas: 6
11                deployment.kubernetes.io/max-replicas: 8
12                deployment.kubernetes.io/revision: 5
13                deployment.kubernetes.io/revision-history: 1,3
14Controlled By:  Deployment/podinfo
15Replicas:       6 current / 6 desired
16Pods Status:    6 Running / 0 Waiting / 0 Succeeded / 0 Failed
17...
18Events:
19  Type    Reason            Age                From                   Message
20  ----    ------            ----               ----                   -------
21...
22  Normal  SuccessfulCreate  16h (x3 over 16h)  replicaset-controller  (combined from similar events): Created pod: podinfo-7fbb45ccfc-pkt4r
23  Normal  SuccessfulCreate  96s                replicaset-controller  Created pod: podinfo-7fbb45ccfc-bkm8n
24
25kubectl get deploy
26NAME      READY   UP-TO-DATE   AVAILABLE   AGE
27podinfo   6/6     6            6           18m

Rolling update

Using a deployment allows to manage the application lifecycle. Changing its configuration will trigger a rolling update.

First of all we'll change the image tag of our deployment

1kubectl set image deployment podinfo podinfo=stefanprodan/podinfo:5.2.1
2deployment.apps/podinfo image updated

During a rolling update a new replicaset is created in order to update the application in place without any downtime. New pods (with the current deployment state) will be created in the new replicaset while they will be deleted progressively from the previous replicaset.

1kubectl get rs -o wide
2NAME                 DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES                       SELECTOR
3podinfo-7fbb45ccfc   0         0         0       21m   podinfo      stefanprodan/podinfo         app=podinfo,pod-template-hash=7fbb45ccfc
4podinfo-564b4ddd7c   6         6         6       30s   podinfo      stefanprodan/podinfo:5.2.1   app=podinfo,pod-template-hash=564b4ddd7c

Keeping the old replicaset makes very easy to rollback

1kubectl rollout undo deployment podinfo
2deployment.apps/podinfo rolled back
3
4kubectl get rs -o wide
5NAME                 DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES                       SELECTOR
6podinfo-7fbb45ccfc   6         6         6       22m   podinfo      stefanprodan/podinfo         app=podinfo,pod-template-hash=7fbb45ccfc
7podinfo-564b4ddd7c   0         0         0       77s   podinfo      stefanprodan/podinfo:5.2.1   app=podinfo,pod-template-hash=564b4ddd7c

Expose a deployment

Now that we have a running web application we may want to access it. There are several ways to expose an app, here we'll use the easiest way: Create a service and run a port-forward.

The following command will create a service which will be in charge of forwarding calls through the tcp port 9898

1kubectl expose deploy podinfo --port 9898
2service/podinfo exposed

We can get more information on the service as follows

 1kubectl get svc -o yaml podinfo
 2apiVersion: v1
 3kind: Service
 4metadata:
 5  labels:
 6    app: podinfo
 7  name: podinfo
 8  namespace: foo
 9spec:
10  clusterIP: 10.43.47.17
11  clusterIPs:
12  - 10.43.47.17
13  ipFamilies:
14  - IPv4
15  ipFamilyPolicy: SingleStack
16  ports:
17  - port: 9898
18  selector:
19    app: podinfo

A service uses the selector above to identify on which pod to forward the traffic and usually creates the endpoints accordingly.

1kubectl get po -l app=podinfo
2NAME                       READY   STATUS    RESTARTS   AGE
3podinfo-7fbb45ccfc-bkm8n   1/1     Running   1          146m
4podinfo-7fbb45ccfc-sbqht   1/1     Running   2          18h
5...
6
7kubectl get endpoints
8NAME      ENDPOINTS                                                     AGE
9podinfo   10.42.0.16:9898,10.42.0.17:9898,10.42.1.18:9898 + 3 more...   92s

The service we've created has an IP that's only accessible from within the cluster. Using the port-forward command we're able to forward the traffic from our local machine to the application (through the API server). Note that you can target either a deployment, a service or a single pod

 1
 2kubectl port-forward svc/podinfo 9898 &
 3Forwarding from 127.0.0.1:9898 -> 9898
 4Forwarding from [::1]:9898 -> 9898
 5
 6curl http://localhost:9898
 7Handling connection for 9898
 8{
 9  "hostname": "podinfo-7fbb45ccfc-sbqht",
10  "version": "6.0.0",
11  "revision": "",
12  "color": "#34577c",
13  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
14  "message": "greetings from podinfo v6.0.0",
15  "goos": "linux",
16  "goarch": "amd64",
17  "runtime": "go1.16.5",
18  "num_goroutine": "6",
19  "num_cpu": "16"
20}

Cleanup

In this section we created 2 resources: a deployment and a service.

1fg
2kubectl port-forward svc/podinfo 9898
3^C
4
5kubectl delete svc,deploy podinfo
6service "podinfo" deleted
7deployment.apps "podinfo" deleted

➡️ Next: Deploy a Wordpress

Posts in this series