The below details my approach when studying for the CKAD.
On-line Training
Below are the sites I used to prepare.
Udemy
Kubernetes Certified Application Developer (CKAD) with Tests
O’Reilly Learning Site
Certified Kubernetes Application Developer (CKAD)
Free Practice Exam by Liptan Biswas
I did not use A Cloud Guru for the CKAD mainly because they are out of date. The Kubernetes version is 1.12.
Reviewing the Labs in A Cloud Guru and understanding the solution using the kubectl imperative method for the current test version is well worth the effort.
A Cloud Guru
Certified Kubernetes Application Developer (CKAD)
Practice servers
- Udemy uses KataKoda
- Liptan Biswas’ Practice Exam uses KataKoda
The nodes for KataKoda have tmux and bash_completion already installed. I found it helpful using both tmux and bash_completion when taking the practice exams but I did not use them during the real exam. I was not comfortable running the below prior to the exam.
apt update
apt install tmux bash_completion
tmux
It is really nice to have a split terminal with kubectl explain cronjob.spec --recursive |less
up are the right pane and the yaml in vim on the left.
One annoyance with using tmux in KataKoda is that copying text does not work within the shell window. So if I wanted to copy
concurrencyPolicy
from the right pane to the left, I had to type it out. The right click copy menu is not available within tmux.
bash_completion
Bash Completion is very helpful when learning especially with all of the –options. Below is the output when typing k create cronjob --
and then hit the tab key twice.
Use this reference to configure bash_completion
Practice, Practice, Practice
The way I studied is to take the lightning labs and practice tests over and over and over again. The goal is to NOT memorize the questions or answers but to get ‘muscle memory’ on creating yaml files using either the imperative method or copying them from Kubernetes Documentation.
Tips
One File with multiple blocks
Several of the practice question had the need to create a PV, a PV and PVC or a PV, PVC and a Pod. It takes seconds to copy these yaml blocks. This link has all three yamls. Simply copy each and paste into a single yaml file with ---
separating each block as below;
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage
persistentVolumeClaim:
claimName: task-pv-claim
containers:
- name: task-pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: task-pv-storage
Then simply change the names, paths or image as the question states.
Multiple container pods
When creating a pod with multiple containers, one method I found helpful was to use the imperative method twice, targeting the same file.
Say we are asked to create a pod with 2 image using the below specifications
- Pod Name = my-2-containers
- Image 1 Name = myBb
- Image 1 image = busybox
- Image 1 command = sleep 3600
- Image 2 Name = myAl
- Image 2 image = alpine
- Image 2 command = sleep 5600
Run the below;
kubectl run my-2-containers --image=busybox --dry-run=client -o yaml --command -- sleep 3600 > my-2-pods.yaml
kubectl run myAl --image=alpine --dry-run=client -o yaml --command -- sleep 5600 >> my-2-pods.yaml
Notice the >>
in the second command. This appends the output of the dry run to the file.
The output is as below;
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-2-containers
name: my-2-containers
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
name: my-2-containers
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: myAl
name: myAl
spec:
containers:
- command:
- sleep
- "5600"
image: alpine
name: myAl
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Next, change the name of the first container and delete the appropriate lines as below;
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-2-containers
name: my-2-containers
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
name: myBb
- command:
- sleep
- "5600"
image: alpine
name: myAl
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Commands
Compare the below;
kubectl create job hello --image=busybox --dry-run=client -o yaml -- sleep 3600
apiVersion: batch/v1
kind: Job
metadata:
creationTimestamp: null
name: hello
spec:
template:
metadata:
creationTimestamp: null
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
name: hello
resources: {}
restartPolicy: Never
status: {}
kubectl run my-2-containers --image=busybox --dry-run=client -o yaml -- sleep 3600
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-2-containers
name: my-2-containers
spec:
containers:
- args:
- sleep
- "3600"
image: busybox
name: my-2-containers
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Adding the --command
option changes the output
kubectl run my-2-containers --image=busybox --dry-run=client -o yaml --command -- sleep 3600
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: my-2-containers
name: my-2-containers
spec:
containers:
- command:
- sleep
- "3600"
image: busybox
name: my-2-containers
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
The execution between
- command:
- sleep
- "3600"
and
- args:
- sleep
- "3600"
Appears identical but I would lean for insuring command:
syntax.