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:
- Part 1. Cross-Cutting Aspects
- Part 2. Pods and Jobs.
- Part 3. Configuration and Volumes.
- Part 4. Deployments, Services and Networking.
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