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