CKAD Exam Preparation 2/4 - Pods and Jobs

▶️ Introduction

This part is devoted to Pods and Jobs as key primitives of the CKAD Exam Curriculum. To learn more about the CKAD Exam please read this overview.

About this Series

During this blog series I summarize the main “study hooks” in order to be successful with your exam, as I was. The series is composed by the following articles:

All the examples have been developed using minikube on macOS Catalina with VirtualBox.

⚙️ Running Pods

📘   https://kubernetes.io/docs/concepts/workloads/Pods/

To general formula for running a K8s Pod is:

kubectl run <Pod_NAME> --image=<IMAGE> --port=<PORT> --labels=<LABELS> --env=<ENV> -- <COMMAND>

📌  The <COMMAND> has to be the literal command as you would type it on a command line. After the -- only the command line shall appear

📌  <PORT> is purely informative.

An example of the above is the following:

kubectl run b1 --image=busybox --port=3456 --labels='app=my-app' --env='var1=val1' -- sh -c 'sleep 3600'

📌  busybox is one of your best allies when it comes to creating testing/dummy Pods. Another option is alpine.

📌  By default a Pod’s container will always be restarted by K8s once it dies.

We can easily execute a process inside the former Pod, for instance we can check environment variables:

kubectl exec b1 -it -- env

Running a “casual”, temporal Pod inside a Cluster:

kubectl run tm-pod -it --image=busybox --restart=Never --rm=true -- sh

📌  -it allows to attach your container to the local console.

📌  run is for running Pods and exec is for executing commands on Pods (or, more precisely, containers pertaining to Pods)

If you need to customize a Pod manifest you can start with a YAML boilerplate, edit it and finally apply it. The --dry-run=client and -o=yaml are the key options when it comes to creating a boilerplate, for instance:

kubectl run b2 --image=busybox --env='var1=val1' --dry-run=client -o=yaml -- sh -c 'sleep 3600' > b2.yaml

will generate a b2.yaml file containing:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: b2
  name: b2
spec:
  containers:
  - args:
    - sh
    - -c
    - sleep 3600
    env:
    - name: var1
      value: val1
    image: busybox
    name: b2
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

then you can edit the file b2.yaml adding the custom directives you may need and finally

kubectl apply -f b2.yaml

To check the logs of a Pod

kubectl logs b2 

📌  If a Pod executes more than one container you can always select the target container with -c <CONTAINER_NAME>

📌  Use --previous to get logs of a previous execution. -f can be used to stream logs.

🧰 Pod manifest examples

Hereby you will find some Pod manifest examples highlighting different features related to Pods.

Simple Pod

apiVersion: v1
kind: Pod
metadata:
  namespace: jmcf 
  labels:
   run: my-pod
  name: my-pod
spec:
 containers:
 - image: busybox
   name: my-pod
   command: ["sh", "-c"]
   args: ["sleep 3600"]
 dnsPolicy: ClusterFirst
 restartPolicy: Always

Pod with a custom Service Account

📌  A Service Account allows service within a Namespace to call the K8s API Server.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 serviceAccountName: sca1
 containers:
 - image: busybox
   name: my-pod
   command: ["sh", "-c"]
   args: ["sleep 3600"]
 dnsPolicy: ClusterFirst
 restartPolicy: Always

Pod with liveness probe

📌  There are thresholds to consider the process as dead.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 containers:
 - image: nginx
   ports:
     - name: p1
       containerPort: 80
   name: my-pod
   livenessProbe:
     initialDelaySeconds: 3
     httpGet:
       port: "p1"
       path: "/"
 dnsPolicy: ClusterFirst
 restartPolicy: Always

Pod with readiness probe

📌  There are thresholds to consider the process as ready.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 containers:
 - image: nginx
   ports:
     - name: p1
       containerPort: 80
   name: my-pod
   readinessProbe:
     initialDelaySeconds: 3
     httpGet:
       port: "p1"
       path: "/"
 dnsPolicy: ClusterFirst
 restartPolicy: Always

Pod with security context

📌  A Security Context allows to set up a UID and GID under which the container process will execute.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 containers:
 - image: busybox
   name: my-pod
   command: ["sh", "-c"]
   args: ["while true; do echo \"hello!\"; sleep 5; done"]
   securityContext:
     runAsUser:  101
     runAsGroup: 101
 dnsPolicy: ClusterFirst
 restartPolicy: Always

Pod with resource declaration

📌  If a Namespace defines quotas then resource declaration is mandatory.

apiVersion: v1
kind: Pod
metadata:
 labels:
   run: my-pod
 name: my-pod
 namespace: jmcf
spec:
 containers:
 - image: nginx
   ports:
     - name: p1
       containerPort: 80
   name: my-pod
   resources:
     limits:
       cpu: 200m
       memory: 50Mi
     requests:
       cpu: 50m
       memory: 10Mi
 dnsPolicy: ClusterFirst
 restartPolicy: Always

Pod with main and Sidecar containers

📌  There are other multi-container patterns such as Ambassador or Adapter.

kind: Pod
apiVersion: v1
metadata:
 name: pod-with-sidecar
 namespace: jmcf
spec:
   volumes:
     - name: shared-logs
       emptyDir: {}
   containers:
     # Main application container
     - name: app-container
     # Simple application: write the current date every 5s.
       image: alpine
       command: ["/bin/sh"]
       args: ["-c", "while true; do date >> /var/log/app.txt; sleep 5; done"]
     # Mount the pod's shared log file into the app container.
       volumeMounts:
         - name: shared-logs
           mountPath: /var/log
     # Sidecar container
     - name: sidecar-container
     # Simple sidecar: display log files using nginx.
       image: nginx:1.7.9
       ports:
         - containerPort: 80
       # Mount the pod's shared log file into the sidecar
       # container. In this case, nginx will serve the files
       # in this directory.
       volumeMounts:
         - name: shared-logs
           mountPath: /usr/share/nginx/html

⏰ Jobs

📘   https://kubernetes.io/docs/tasks/job/

Create a Job

kubectl create job j1 --image=alpine --restart=OnFailure -- date

List Jobs

kubectl get jobs

A Job YAML manifest

apiVersion: batch/v1
kind: Job
metadata:
 name: my-job
 namespace: jmcf
spec:
 parallelism: 2
 completions: 4
 activeDeadlineSeconds: 15
 template:
   metadata:
   spec:
     containers:
     - image: busybox
       name: my-job
       command: ["sh", "-c"]
       args: ["date"]
       resources: {}
     restartPolicy: OnFailure

📌  Jobs are based on a templated Pod.

📌  Parallelism and deadlines allow to have finer control of Job execution.

📌  The Pod used to incarnate your Job will remain unless you set ttlSecondsAfterFinished.

Cron Jobs

Create a Cron Job scheduled once per minute

kubectl create cronjob cj1 --image=alpine --schedule="*/1 * * * *" --restart=OnFailure  -- date

📌  A Cron Job schedule is in Cron format, see https://en.wikipedia.org/wiki/Cron.

List Cron Jobs

kubectl get cronjobs

A Cron Job YAML manifest

apiVersion: batch/v1beta1
kind: CronJob
metadata:
 name: my-cjob
 namespace: jmcf
spec:
 jobTemplate:
   metadata:
     name: my-cjob
   spec:
     template:
       metadata:
         labels:
          app: my-cjob
       spec:
         containers:
         - image: busybox
           name: my-cjob
           command: ["sh", "-c"]
           args: ["date"]
         restartPolicy: OnFailure
 schedule: '*/1 * * * *'

📌  Cron Jobs are based on a templated Job.

You can list the Pods launched to incarnate and execute your (Cron) Job:

kubectl get pods -n jmcf --show-labels --selector='app=my-cjob'
NAME                       READY   STATUS              RESTARTS   AGE    LABELS
my-cjob-1592928720-rgvsd   0/1     Completed           0          3m3s   app=my-cjob

📌  For Cron Jobs there is a limit in the number of Pods kept in history as per the successfulJobsHistoryLimit parameter.

You can inspect logs of a (Cron) Job by showing logs of an incarnating Pod:

kubectl logs -n jmcf my-cjob-1592928720-rgvsd

⏭️ Next in this series

Configuration and Volumes

🗒️ Feedback