All the services need to be in a running state. With Kubernetes, our ultimate aim is to deploy our application in the form of containers on a set of machines that are configured as worker nodes in a cluster. Kubernetes does not deploy containers directly on the worker nodes. The containers are encapsulated into a Kubernetes object known as pods. A pod is a single instance of an application. A pod is the smallest object that you can create in Kubernetes.
What is a Pod?
- A Pod is an abstraction of a server
- It can run multiple containers within a single NameSpace, exposed by a single IP address
 
- The Pod is the minimal entity that can be managed by Kubernetes
- From a container perspective, a Pod is an entity that runs typically one or more containers by using container images
- Typically, Pods are only started through a Deployment, because “naked” Pods are not rescheduled in case of a node failure
Naked Pod Disadvantages
- Naked Pods are not rescheduled in case of failure
- Rolling updates don’t apply to naked Pods; you can only bring it down and bring it up again with the new settings
- Naked Pods cannot be scaled
- Naked Pods cannot be replaced automatically
Using Deployments
- The Deployment is the standard way for running containers in Kubernetes
- Deployments are responsible for starting Pods in a scalable way
- The Deployment resource uses a ReplicaSet to manage scalability
- Also, the Deployment offers the RollingUpdate feature to allow for zero-downtime application updates
- To start a Deployment the imperative way, usekubectl create deploy…
Kubernetes Tools
- Before starting the installation, you’ll have to install the Kubernetes tools
- These include the following:
- kubeadm: used to install and manage a Kubernetes cluster
- kubelet: the core Kubernetes service that starts all Pods
- kubectl: the interface that allows you to run and manage applications in Kubernetes
 
kubectl
| 1 | $ kubectl run nginx --image nginx | 
This command deploys a Docker container by creating a pod, so it first creates a pod automatically and deploys an instance of the NGINX Docker image, but where does it get the application image from? For that, you need to specify the image name using the dash dash image parameter. The application image, in this case, the NGINX image, is downloaded from the Docker Hub repository. You could configure Kubernetes to pull the image from the public Docker Hub or a private repository within the organization.
Now that we have a pod created, how do we see the list of pods available?
| 1 | $ kubectl get pods | 
The kubectl get pods command helps us see the list of pods in our cluster. In this case,we see the pod is in a container creating state and soon changes to a running statewhen it is actually running.
To see detailed information about the pod, run:
| 1 | $ kubectl describe pod myapp-pod | 
This will tell you information about the pod when it was created, what labels are assigned to it, what docker containers are part of it and the events associated with that pod.
Examples
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | [root@k8s ~]# kubectl run testpod --image=nginx pod/testpod created [root@k8s ~]# kubectl get pods NAME READY STATUS RESTARTS AGE testpod 0/1 Pending 0 8s [root@k8s ~]# kubectl create deploy -h [root@k8s ~]# kubectl create deployment firstnginx --image=nginx --replicas=3 deployment.apps/firstnginx created [root@k8s ~]# kubectl get all NAME READY STATUS RESTARTS AGE pod/firstnginx-d8679d567-249g9 0/1 Pending 0 20s pod/firstnginx-d8679d567-66c4s 0/1 Pending 0 20s pod/firstnginx-d8679d567-72qbd 0/1 Pending 0 20s pod/testpod 0/1 Pending 0 7m47s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/firstnginx 0/3 3 0 20s NAME DESIRED CURRENT READY AGE replicaset.apps/firstnginx-d8679d567 3 3 0 20s | 
Understanding DaemonSets
- A DaemonSet is a resource that starts one application instance on each cluster node
- It is commonly used to start agents like the kube-proxy that need to be running on all cluster nodes
- It can also be used for user workloads
- lf the DaemonSet needs to run on control-plane nodes, a toleration must be configured to allow the node to run regardless of the control-plane taints
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | [root@k8s ~]# kubectl get ds No resources found in default namespace. [root@k8s ~]# kubectl get ds -A NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-system kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 5h4m [root@k8s ~]# kubectl get ds -n kube-system kube-proxy NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 5h9m [root@k8s ~]# kubectl get ds -n kube-system kube-proxy -o yaml apiVersion: apps/v1 kind: DaemonSet metadata: annotations: deprecated.daemonset.template.generation: "1" creationTimestamp: "2024-01-31T15:03:27Z" generation: 1 labels: k8s-app: kube-proxy name: kube-proxy namespace: kube-system resourceVersion: "4832" uid: 2c4a7965-c3a4-42f9-8ce0-caa49b6d41c5 spec: revisionHistoryLimit: 10 selector: matchLabels: k8s-app: kube-proxy template: metadata: creationTimestamp: null labels: k8s-app: kube-proxy spec: containers: - command: - /usr/local/bin/kube-proxy - --config=/var/lib/kube-proxy/config.conf - --hostname-override=$(NODE_NAME) env: - name: NODE_NAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName image: registry.k8s.io/kube-proxy:v1.28.3 imagePullPolicy: IfNotPresent name: kube-proxy resources: {} securityContext: privileged: true terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/lib/kube-proxy name: kube-proxy - mountPath: /run/xtables.lock name: xtables-lock - mountPath: /lib/modules name: lib-modules readOnly: true dnsPolicy: ClusterFirst hostNetwork: true nodeSelector: kubernetes.io/os: linux priorityClassName: system-node-critical restartPolicy: Always schedulerName: default-scheduler securityContext: {} serviceAccount: kube-proxy serviceAccountName: kube-proxy terminationGracePeriodSeconds: 30 tolerations: - operator: Exists volumes: - configMap: defaultMode: 420 name: kube-proxy name: kube-proxy - hostPath: path: /run/xtables.lock type: FileOrCreate name: xtables-lock - hostPath: path: /lib/modules type: "" name: lib-modules updateStrategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate status: currentNumberScheduled: 1 desiredNumberScheduled: 1 numberAvailable: 1 numberMisscheduled: 0 numberReady: 1 observedGeneration: 1 updatedNumberScheduled: 1 | 
Lets create a DaemmonSet as it is written at
https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
copy yaml from
https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/daemonset.yaml
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | [root@k8s ~]# vi daemondemo.yaml [root@k8s ~]# cat daemondemo.yaml apiVersion: apps/v1 kind: DaemonSet metadata:   name: fluentd-elasticsearch   namespace: kube-system   labels:     k8s-app: fluentd-logging spec:   selector:     matchLabels:       name: fluentd-elasticsearch   template:     metadata:       labels:         name: fluentd-elasticsearch     spec:       tolerations:       # these tolerations are to have the daemonset runnable on control plane nodes       # remove them if your control plane nodes should not run pods       - key: node-role.kubernetes.io/control-plane         operator: Exists         effect: NoSchedule       - key: node-role.kubernetes.io/master         operator: Exists         effect: NoSchedule       containers:       - name: fluentd-elasticsearch         image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2         resources:           limits:             memory: 200Mi           requests:             cpu: 100m             memory: 200Mi         volumeMounts:         - name: varlog           mountPath: /var/log       # it may be desirable to set a high priority class to ensure that a DaemonSet Pod       # preempts running Pods       # priorityClassName: important       terminationGracePeriodSeconds: 30       volumes:       - name: varlog         hostPath:           path: /var/log [root@k8s ~]# kubectl create deploy mydaemon --image=nginx --dry-run=client -o yaml > mydaemon.yaml [root@k8s ~]# vim mydaemon.yaml | 
After edit the kind and remove replicas and sttrategy spec mydaemon.yaml file looks like:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [root@k8s ~]# cat mydaemon.yaml apiVersion: apps/v1 kind: Daemonset metadata:   creationTimestamp: null   labels:     app: mydaemon   name: mydaemon spec:   selector:     matchLabels:       app: mydaemon   template:     metadata:       creationTimestamp: null       labels:         app: mydaemon     spec:       containers:       - image: nginx         name: nginx         resources: {} status: {} | 
Now lets create daemonset:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | [root@k8s ~]# kubectl apply -f mydaemon.yaml daemonset.apps/mydaemon created [root@k8s ~]# kubectl get ds NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE mydaemon   0         0         0       0            0           <none>          62s [root@k8s ~]# kubectl get pods -o wide NAME                         READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES firstnginx-d8679d567-249g9   0/1     Pending   0          35m   <none>   <none>   <none>           <none> firstnginx-d8679d567-66c4s   0/1     Pending   0          35m   <none>   <none>   <none>           <none> firstnginx-d8679d567-72qbd   0/1     Pending   0          35m   <none>   <none>   <none>           <none> testpod                      0/1     Pending   0          43m   <none>   <none>   <none>           <none> | 
We have no daemonset running because we use minikube which has no worker node.
Stateful and Stateless Applications
- A stateless application is an application that doesn’t store any session data
- Redirecting traffic in a stateless application is easy, the traffic can just be directed to another Pod instance
- A stateful application saves session data to persistent storage
- Databases are an example of stateful applications
- Even if stateful applications can be started by a Deployment, it’s better to start it in a StatefulSet
StatefulSet
A StatefulSet offers features that are needed by stateful applications
- 
- It provides guarantees about ordering and uniqueness of Pods
- It maintains a sticky identifier for each of the Pods it creates
- Pods in a StatefulSet are not interchangeable: each Pod has a persistent identifier that it maintains while being rescheduled
- The unique Pod identifiers make it easier to match existing volumes to replaced Pods
 
StatefulSet Considerations
- Storage must be automatically provisioned by a persistent volume provisioner. Pre-provisioning is challenging, as volumes need to be dynamically added when new Pods are scheduled
- When a StatefulSet is deleted, associated volumes will not be deleted
- A headless Service resource must be created in order to manage the network identity of Pods
- Pods are not guaranteed to be stopped while deleting a StatefulSet, and it is recommended to scale down to zero Pods before deleting the StatefulSet
Let’s look at the below yaml file:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | [root@k8s cka]# cat statefuldemo.yaml apiVersion: v1 kind: Service metadata:   name: nginx   labels:     app: nginx spec:   ports:   - port: 80     name: web   clusterIP: None   selector:     app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata:   name: web spec:   selector:     matchLabels:       app: nginx   serviceName: "nginx"   replicas: 3   template:     metadata:       labels:         app: nginx     spec:       terminationGracePeriodSeconds: 10       containers:       - name: nginx         image: k8s.gcr.io/nginx-slim:0.8         ports:         - containerPort: 80           name: web         volumeMounts:         - name: www           mountPath: /usr/share/nginx/html   volumeClaimTemplates:   - metadata:       name: www     spec:       accessModes: [ "ReadWriteMany" ]       resources:         requests:           storage: 1Gi | 
StatefullSets requires headless service so clusterIP is set to none in the above yaml file. Let’s run it and see what it is doing:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | [root@k8s cka]# kubectl apply -f statefuldemo.yaml service/nginx created statefulset.apps/web created [root@k8s cka]# kubectl get statefulset NAME   READY   AGE web    0/3     16s [root@k8s cka]# kubectl get pods NAME                         READY   STATUS    RESTARTS   AGE firstnginx-d8679d567-249g9   0/1     Pending   0          12h firstnginx-d8679d567-66c4s   0/1     Pending   0          12h firstnginx-d8679d567-72qbd   0/1     Pending   0          12h testpod                      0/1     Pending   0          12h web-0                        0/1     Pending   0          25s [root@k8s cka]# kubectl get pvc NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE www-web-0   Pending                                      standard       40s | 
The name of pods (web-0) which has been generated doesn’t contain the random id but there are nice names. Because of the availability here of the storage class the StatefulSets has been able to automatically allocate storage for very single instance in the StatefulSet.
Running Individual Pods
- Running individual Pods has disadvantages:
- No workload protection
- No load balancing
- No zero-downtime application update
- Use individual Pods only for testing, troubleshooting, and analyzing
- In all other cases, use Deployment, DaemonSet, or StatefulSet
Here is how we can run an idividual pod:
| 1 2 3 4 5 6 7 8 9 10 11 12 | [root@k8s cka]# kubectl run -h  [root@k8s cka]# kubectl run sleepy --image=busybox -- sleep 3600  pod/sleepy created [root@k8s cka]# kubectl get pods NAME                         READY   STATUS    RESTARTS   AGE firstnginx-d8679d567-249g9   0/1     Pending   0          14h firstnginx-d8679d567-66c4s   0/1     Pending   0          14h firstnginx-d8679d567-72qbd   0/1     Pending   0          14h sleepy                       0/1     Pending   0          3m14s testpod                      0/1     Pending   0          14h web-0                        0/1     Pending   0          133m | 
--  [dash dash space] is the easy way to pass a command (sleep 3600) that sholud be started by a pod.
What can you do if you want to initialize something before the main application is started ? You can run an init container.
Using !nit Containers
- If preparation is required before running the main container, use an init container
- Init containers run to completion, and once completed the main container can be started
- Use init containers in any case where preliminary setup is required
Consider such an init container temmplate:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | [root@k8s cka]# cat initme.yaml apiVersion: v1 kind: Pod metadata:   name: init-demo spec:   containers:   - name: nginx     image: nginx     ports:     - containerPort: 80   # These containers are run during pod initialization   initContainers:   - name: install     image: busybox     command:     - sleep     - "30" [root@k8s cka]# kubectl apply -f initme.yaml pod/init-demo created [root@k8s cka]# kubectl get pods NAME                         READY   STATUS    RESTARTS   AGE firstnginx-d8679d567-249g9   0/1     Pending   0          15h firstnginx-d8679d567-66c4s   0/1     Pending   0          15h firstnginx-d8679d567-72qbd   0/1     Pending   0          15h init-demo                    0/1     Pending   0          20s sleepy                       0/1     Pending   0          47m testpod                      0/1     Pending   0          15h web-0                        0/1     Pending   0          177m | 
Scaling Applications
- kubectl scale is used to manually scale Deployment, ReplicaSet, or StatefulSet
- kubectl scale deployment myapp --replicas=3
 
- Alternatively, HorizontalPodAutoscaler can be used
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [root@k8s cka]# kubectl scale -h [root@k8s cka]# kubectl scale --replicas=4 deployment/firstnginx deployment.apps/firstnginx scaled [root@k8s cka]# kubectl get deploy NAME         READY   UP-TO-DATE   AVAILABLE   AGE firstnginx   0/4     4            0           17h [root@k8s cni]# kubectl get all --selector app=firstnginx NAME                             READY   STATUS    RESTARTS   AGE pod/firstnginx-d8679d567-249g9   1/1     Running   0          24h pod/firstnginx-d8679d567-66c4s   1/1     Running   0          24h pod/firstnginx-d8679d567-72qbd   1/1     Running   0          24h pod/firstnginx-d8679d567-rhhlz   1/1     Running   0          7h54m NAME                         READY   UP-TO-DATE   AVAILABLE   AGE deployment.apps/firstnginx   4/4     4            4           24h NAME                                   DESIRED   CURRENT   READY   AGE replicaset.apps/firstnginx-d8679d567   4         4         4       24h | 
Multi-container Pods
- As a Pod should be created for each specific task, running single-container Pods is the standard
- In some cases, an additional container is needed to modify or present data generated by the main container
- Specific use cases are defined:
- Sidecar: provides additional functionality to the main container
- Ambassador: is used as a proxy to connect containers externally
- Adapter: is used to standardize or normalize main container output
 
Multi-container Storage
- In a multi-container Pod, Pod Volumes are often used as shared storage
- The Pod Volume may use PersistentVolumeClaim (PVC) to refer to a PersistentVolume, but may also directly refer to the required storage
- By using shared storage, the main container can write to it, and the helper Pod will pick up information written to the shared storage
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | [root@k8s cka]# cat sidecarlog.yaml apiVersion: v1 kind: Pod metadata:   name: two-containers spec:   volumes:   - name: shared-data     emptyDir: {}   containers:   - name: nginx-container     image: nginx     volumeMounts:     - name: shared-data       mountPath: /usr/share/nginx/html   - name: busybox-container     image: busybox     volumeMounts:     - name: shared-data       mountPath: /messages     command: ["/bin/sh"]     args: ["-c", "echo hello from the cluster > /messages/index.html && sleep 600" ] [root@k8s cka]# kubectl apply -f sidecarlog.yaml pod/two-containers created [root@k8s cni]# kubectl exec -it two-containers -c nginx-container -- cat /usr/share/nginx/html/index.html hello from the cluster | 
Lab: Running a DaemonSet
- Create a DaemonSet with the name nginxdaemon.
- Ensure it runs an Nginx Pod on every worker node.
| 1 2 | [root@k8s cni]# kubectl create deployment deploydaemon --image=nginx --dry-run=client -o yaml > deploydaemon.yaml [root@k8s cni]# vim deploydaemon.yaml | 
Edit deploydaemon.yaml. Change kind and delete replicas and strategy lines.
| 1 2 3 4 5 6 7 8 9 | [root@k8s cni]# kubectl apply -f deploydaemon.yaml daemonset.apps/deploydaemon created [root@k8s cni]# kubectl get all --selector app=deploydaemon NAME                     READY   STATUS    RESTARTS   AGE pod/deploydaemon-zzllp   1/1     Running   0          28s NAME                          DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE daemonset.apps/deploydaemon   1         1         1       1            1           <none>          28s | 

