# Certified Kubernetes Application Developer(CKAD) Certification.
###### tags: `CKAD,k8s,certifications,developer, kubernetes`
## Background
###
I recently passed the CKAD exam on my first try with a score of 91 and I thought I'd share my experience preparing for the exam as well as resources,tips and tricks that helped me on my journey.
The exam is a 2 hour practical exam with 19 questions. You'll need to score atleast 66 to pass.
Read more information here: https://www.cncf.io/certification/ckad/
### What is CKAD
The people at [CNCF](https://www.cncf.io/certification/ckad/) have described CKAD as an which certifies that users can design, build, configure, and expose cloud native applications for Kubernetes.
Read all about this from the horse's :horse: mouth --> https://www.cncf.io/certification/ckad/
### Why CKAD(something you would tell a recruiter?)
As one of the highest velocity projects in the history of open source, Kubernetes use is exploding. The Cloud Native Computing Foundation is committed to growing the community of Kubernetes-knowledgeable application developers, thereby enabling continued growth across the broad set of organizations using the technology
A Certified Kubernetes Application Developer can define application resources and use core primitives to build, monitor, and troubleshoot scalable applications and tools in Kubernetes.
-- The People @ [CLOUD NATIVE COMPUTING FOUNDATION](https://www.cncf.io/certification/ckad/)
### Why CKAD(in my own words)?
I love container tech and I've been collecting information about kubernetes from different sources(books, articles) and I just felt like it was time to sort of formalize my understanding of kubernetes in a way that relates to my current job as a developer.
### Is it worth it?
Absolutely :100:
From the devops point of view, you'll learn a lot about kubernetes. You won't be lost in space the next time you sit with those guys who keep talking about deployments, configmaps, secrets and all the "buzz" words flying about :+1:
From a developer's point of view, you'll begin to think differently about how you write applications. The whole micro-service thing will begin to make a lot more sense to you.
You'll begin to visualize how easy deploying and managing applications can be. The downside is maybe that you'll begin to hate that "GIANT MONOLITH" you've been working with(even more).
## Foundation
### OCI-Compliant Container Runtime
> I know, the word is so fancy, think docker :whale:
Basically you need to be comfortable with An OCI-Compliant Container Runtime, such as Docker or cri-o. The whole thing builds on containers so it's pretty important to have a good grasp of OCI-Compliant Container Runtime.
This shouldn't be difficult as there are lots of material online.
Some materials for starting out
- [Udemy](https://www.udemy.com/courses/search/?src=ukw&q=Docker): select whatever course suits you best.
- [Learn docker in a month of lunches](https://www.manning.com/books/learn-docker-in-a-month-of-lunches?query=Docker) - A great resource
### Kubernetes
If you've never worked with kubernetes, you might want to invest sometime in learning the basics before you even start preparing for the exam.
The exam is not one of those multi choice question exams where you can just memorize information and sort of play by ear. It's a practical hands on exam where you'll be given some clusters and you'll have to solve issues that you'll face in real life situation.
You'll have access to [kubernetes docs](https://kubernetes.io/docs/home/) but it won't help you if you don't know what you're looking for.
There's also loads of `yaml` files to be configured, edited or generated so you'll need to be able look at such a file and be able to tell what's going on
```yaml=
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
volumes:
- name: html
emptyDir: {}
initContainers:
- image: busybox
name: init-con
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
command:
- /bin/sh
- -c
- "echo 'Welcome to CKAD!' > /usr/share/nginx/html/index.html"
containers:
- env:
- name: hello
value: world
image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
```
> a typical manifest file. Can you tell what it does?
Some of my favourite resources to get started are
- [Learn kubernetes in a month of lunches](https://https://www.manning.com/books/learn-kubernetes-in-a-month-of-lunches?query=Kubernetes)
- [Kubernetes in action](https://www.manning.com/books/kubernetes-in-action-second-edition?query=Kubernetes)
- Containarize your applications(or use already existing ones) and play around with kubernetes yourself. Inject database credentials using secrets? Inject logging configuration using configmaps? Inject environment variables into your app through kubernetes? Deploy your application through kubernetes? Expose them through services and or ingress? In my experience, working with an application you've designed yourself sometimes allows you get your hands dirty in a way that teaches you how to navigate kubernetes in a very meaningful way.
Kubernetes is one of those things that feels hard when you're on the outside of it, but once you get in, it really is something :100:
## Prep
So I'll assume that you now know what the `yaml` shown above does.
> the file is an nginx pod definition that creates an `initContainer` which creates an index.html file and places it in a volume which will be used by the main container `nginx` to run.
- When I decided to do this exam, I started off with this [Certified Kubernetes Application Developer (CKAD) course](https://learning.oreilly.com/videos/certified-kubernetes-application/9780136677628/). I liked the teacher's approach and illustrations. It was great way to get my feet wet for the exam. While it's a good resource and covers a number of topics, I didn't it was enough for me so I decided to look further.
- The next prep I did was through the [KUBERNETES FOR DEVELOPERS (LFD259)](https://training.linuxfoundation.org/training/kubernetes-for-developers/) from Linux Foundation. Also a good resource, some labs to do and good focus on the exam. I like the fact that a larger part of it was documentation as I could just read on a mobile device instead of having to sit by the computer. The downside of this course is probably the price. You'll have to part with $299 :sweat: unless you get a deal. Look around the internet for deals and you might be lucky. If you buy this course and the exam you'll pay around $575 which is $100 cheaper. I was lucky to get both the exam and the course for the price of the exam.
- Next prep I did was [Kubernetes Certified Application Developer (CKAD) with Tests](https://www.udemy.com/course/certified-kubernetes-application-developer/) and I think this is where I sort of started zoning in on the exam itself. This course has excellent test/labs which test your skill on various topics, the teacher is articulate and recaps topics in a simple and meaningful way and there are a few mock exams as well. When you go through this course including regular labs, lightning labs and mock exams, you'll start feeling confident and probably start thinking about booking a date for the exam.
> :information_source: At this point you should probably pay for the exam and try to book a date. This is important for the next prep step. When you pay, you get access to an amazing resource from [killer.sh](https://killer.sh/) which will play a vital role in your preparation.
- Next prep I did I was activating my first [killer.sh](https://killer.sh/) session. The people at [killer.sh](https://killer.sh/) have developed a replica of the exam environment with 'tougher' questions. Each session runs for about 36 hours and you can re-run a session as many times as you like within the 36 hour window. You get 2 of these sessions and the questions are the same in both sessions. My tip will be to activate your first session exactly 36 hours to your exam and try to do as many simulations as possible before your exam. You remember that confidence you built up from the previous prep? Chances are after your first simulation, it will be deflated because you'll discover how short 2 hours are for the exam. But it gets better with each simulation you do. Remember at this point, you're not learning kubernetes, you're just practicing your skills and learning how to work quickly in the environment.
## During the Exam
Whoop! You've gone through the hastle of learning and prepping and the exam is here.
- Allow the last 2 hours before the exam for some slow paced repetition. Practice these [questions](https://github.com/lucassha/CKAD-resources) just to remind yourself of some basic stuff. You don't want to get worked up just before the exam.
- Run the system checklist and make sure you've installed the plugin called `innovative exams screensharing` on your browser. I used chrome.
- You can start the exam 15 minutes before the time. It's good you start as early as possible because there are some checks the proctor does before he releases the exam. Clear out your desk. Yep! Really clear out your desk. My proctor even asked me to take off my headphones. I had to show him the room via webcam(you might want to clean a bit..you don't want some random person on the internet telling their friends and family how dirty your room is). Close all applications except your web browser(obviously).
- When you start the exam, invest about 2 minutes in setting up your aliases. This will save you a lot of time in typing and searching through namespaces. You don't even have to commit the code to memory, it's on the [kubernetes docs page](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/) and you are allowed to access it during the exam.
```bash=
sudo -i
apt-get install bash-completion
echo 'source <(kubectl completion bash)' >>~/.bashrc
kubectl completion bash >/etc/bash_completion.d/kubectl
echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -F __start_kubectl k' >>~/.bashrc
## You'll have to commit the last line to memory sorry :)
source .bashrc
```
> Doing this will give you access to completion which will save you time. I'll talk more about this in the next section.
- Go through the questions and solve those with the highest weight first. You want to get those in the bag as early as possible. As I've mentioned earlier, time is quite short and chances are you might not answer all questions so one of the best ways to give yourself a better chance is to answer those questions with the higher weights.
- Copy `kubectl config use-context <context-name>` provided for every question. By doing this, you guarantee that you're on the right cluster/context needed to solve the question correctly.
- If you're generating `yaml` files then it's smart to save them according to the question `e.g question 1 --> 1.yaml`. If the question has several subtasks, then save them according to the tasks `e.g question 1, subtask a,b,c --> 1a.yaml, 1b.yaml,1c.yaml`
- If you're not sure of a question or couldn't get it to work first try, just flag it and move on. Be conscious of the weight of the question too. You don't want to spend 4 minutes trying to solve a question with 2% weight when you've a few 4% weights that you could solve. When you've solved others, you can come back and give it a try.
- Read through the questions and in your mind ask yourself about the namespace. It's so easy to glance through the question and then miss the namespace.
- Above all, be calm! You've worked so hard so you've got this in the bag :100:
## Tips & Tricks
### To Vim or not to Vim(that is the question)
You'll be editing a lot of yaml files and kubernetes uses Vim editor by default when you choose to edit an existing resource. Learning how to use this editor is a good skill to have, not just for the exam but in linux(life).
There are loads of ways to make using Vim easier but the only thing I did was to turn on syntax.
```bash=
echo 'syntax on' > .vimrc
```
This helped me see indentations better. I used the good ol' copy and paste and aligned by hand. I'm sure there are easier ways to do this but this is what worked for me.
`i` to edit
`dd` to delete a line
`<no of lines to delete> dd` to delete a number of lines.
Really this is all i needed for the exam.
### Copy names directly from the questions
No need to type the names of pods, images etc.. just copy it from the question. It's faster and there's zero risk of making a silly mistake.
### kubectl get all -n namespace -o wide --show-labels
You'll be doing a lot of kubectl get for various namespaces. Make it a habit of adding -o wide and --show-labels. You get more information like the ip addresses incase you need to create a temporary pod to check connections. You can just copy and paste the ip address.
This is what `k get all -n default` looks like
```bash=
NAME READY STATUS RESTARTS AGE
pod/mysql-77599ccd6d-plk6v 1/1 Running 0 112s
pod/nginx 1/1 Running 0 2m27s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6h2m
service/mysql-service LoadBalancer 10.97.152.166 localhost 3306:30262/TCP 6h1m
service/nginx ClusterIP 10.101.161.25 <none> 80/TCP 177m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/mysql 1/1 1 1 6h1m
NAME DESIRED CURRENT READY AGE
replicaset.apps/mysql-77599ccd6d 1 1 1 6h1m
```
This is what `kubectl get all -n default -o wide --show-labels` looks like
```bash=
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
pod/mysql-77599ccd6d-plk6v 1/1 Running 0 2m51s 10.1.2.169 docker-desktop <none> <none> app=mysql,pod-template-hash=77599ccd6d
pod/nginx 1/1 Running 0 3m26s 10.1.2.168 docker-desktop <none> <none> run=nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR LABELS
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6h3m <none> component=apiserver,provider=kubernetes
service/mysql-service LoadBalancer 10.97.152.166 localhost 3306:30262/TCP 6h2m app=mysql <none>
service/nginx ClusterIP 10.101.161.25 <none> 80/TCP 178m run=nginx <none>
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR LABELS
deployment.apps/mysql 1/1 1 1 6h2m database mysql app=mysql <none>
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR LABELS
replicaset.apps/mysql-77599ccd6d 1 1 1 6h2m database mysql app=mysql,pod-template-hash=77599ccd6d app=mysql,pod-template-hash=77599ccd6d
```
You get so much information that can be helpful at a go.
### Imperative commands
In the exam, there are `yaml` files flying all over the place. You'll be creating, editing and doing all sorts with these files.
Resist the urge to write these files from scratch. You'll make mistakes and you probably will answer like 3 questions before time is up. Luckily kubernetes gives you the option of using imperative commands to generate these files.
Take this file below for example
```yaml=
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- env:
- name: hello
value: world
image: nginx
name: nginx
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
```
Writing this out by hand seems painful. Going back and forth to the kubernetes docs to find a pod definition will cost you time. Your best option is using the kubectl tool to generate this file and then you can edit it if needed.
> by running `kubectl run nginx --image=nginx --port=80 --dry-run=client --env=hello=world --restart=Never -o yaml --expose` ,I could easily generate the file above.
https://kubernetes.io/docs/reference/kubectl/cheatsheet/ has a list of commands that you should familiarize yourself with.
### volumes and volumeMounts
Mounting volumes into pods come up a lot and while you can copy syntax from https://kubernetes.io/docs/concepts/storage/volumes/ and edit it as you'd like, it does take some time. So you'd want to commit this to memory
```yaml=
# this goes in the spec section
volumes:
- name: <name>
emptyDir: {}
- name: <name>
configMap:
name: <config-map name>
- name: <name>
secret:
secretName: <secretName>
- name: <name>
persistentVolumeClaim:
claimName: <claim-name>
```
```yaml=
# this goes in the containers section
volumeMounts:
- mountPath: <path name>
name: <volume-name>
```
> can you mount volumes from configmaps, secrets, persistentVolumes or a volume that exists only for the duration of the pod without looking at the docs?
### initContainers
You might need to create init-containers from time to time. A quick way to do this would be to use the imperative command to generate the `yaml` file and then just copy the `containers` section and rename to `initContainers`
```yaml=
containers:
- env:
- name: hello
value: world
image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
```
then copy the block above and rename `containers` to `initContainers` and update the image details
```yaml=
initContainers: # changed from containers to initContainers
- image: busybox # from nginx to busybox
name: init-con # from nginx to busybox
volumeMounts: # same volume mount from the containers section.
- name: html
mountPath: /usr/share/nginx/html
command: # added new commands to the initContainers
- /bin/sh
- -c
- "echo 'Welcome to CKAD!' > /usr/share/nginx/html/index.html"
```
### Scaffold envFrom & valueFrom
Occasionally you'll also have to add some environment variable from a configmap or secret. To do this, you'll have to write out the env section in your `yaml` file.
A simple way to speed up things is to create an empty `env` column with the `kubectl run` command
```bash=
kubectl run nginx --image=nginx --port=80 --dry-run=client --env=name-of-env="" --restart=Never -o yaml
```
The result will be
```yaml=
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- env:
- name: name-of-env # notice the env section was created without a value. You can input the value/valueFrom that you need
image: nginx
name: nginx
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
```
You can just change the values according to what you need for `envFrom`
```yaml=
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- envFrom: ## changed env to envFrom
- configMapRef: ## changed name to configMapRef (could also be secretRef)
name: <config-name>
image: nginx
name: nginx
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
```
You can just change the values according to what you need for `valueFrom`
```yaml=
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- env: # same as before
- name: name-of-env #
valueFrom: # valueFrom section is what we've added
configMapKeyRef: # could also be secretKeyRef
name: config-map-name
key: config-map-key
image: nginx
name: nginx
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
```
### Creating secrets
Use the imperative command to generate your secrets. Remember that there are about 3 types of secrets you can create. Can you guess them?
> :heart: spoiler --> `docker-registry,generic,tls`
My guess is you'll be using the `generic` most of the time during the exam so don't forget to add it in your command
`kubectl create secret generic secret-name --from-literal=var1=val1 -o yaml > <secret-file-name.yaml>`
### Creating configmaps
Use the imperative command to generate configmaps. If the values to add into the configmap exceed 2, then you might save time copying them into an .env file and then generate from that env file.
Lets assume you've been asked to create a configmap which will be used for a mysql pod using these values
```
MYSQL_ROOT_PASSWORD=root-pass
MYSQL_DATABASE=db
MYSQL_USER=user
MYSQL_PASSWORD=pass
```
Instead of repeating `--from-literal` several times, you can use run `vi file.env` to create and edit a new file called `file.env`. Then copy and paste the values into this file. You can verify the content of file using
```bash=
❯ cat file.env
MYSQL_ROOT_PASSWORD=root-pass
MYSQL_DATABASE=db
MYSQL_USER=user
MYSQL_PASSWORD=pass
## Now you can just generate the configmap using the --from-env-file
k create cm cm --from-env-file=file.env --dry-run=client -o yaml
```
The result should be
```yaml=
apiVersion: v1
data:
MYSQL_DATABASE: db
MYSQL_PASSWORD: pass
MYSQL_ROOT_PASSWORD: root-pass
MYSQL_USER: user
kind: ConfigMap
metadata:
creationTimestamp: null
name: cm
```
### Kubernetes.io/docs is your friend
Get used to the docs page. There's a lot of already made `yaml` or parts of it that you can refer to or copy. Looking for a ready made `persistentVolume` manifest file? Just search for it and you'll find a few definitions that you can just copy
Some of the things you'll need most from the docs are
- NetworkPolicy
- PersistentVolume
- PersistentVolumeClaim
- LivenessProbes && ReadinessProbes
just to mention a few..
### kubectl explain <resource> --recursive
The truth is you can find all documentation you need within the kubectl tool. Using the `explain` can give you descriptions about the resource in question.
Adding the `--recursive` flag will recursively go through all the resources under the main resource.
> tip add the `|` less pipe so as not to clutter your terminal
lets see an example
Assume you're asked to add a capability "CAP_SYS_TIME" to a pod. Instead of heading to the docs to look for this, you could just run `kubectl explain pod.spec.containers.securityContext.capabilities --recursive | less`
you'll get this output
```bash=
KIND: Pod
VERSION: v1
RESOURCE: capabilities <Object>
DESCRIPTION:
The capabilities to add/drop when running containers. Defaults to the
default set of capabilities granted by the container runtime.
Adds and removes POSIX capabilities from running containers.
FIELDS:
add <[]string>
drop <[]string>
(END)
```
voila !! you can just add this to the pod as needed
```yaml=
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- env:
- name: ENV_NAME
valueFrom:
configMapKeyRef:
name: config-map-name
key: config-map-key
image: nginx
securityContext:
capabilities:
add: ["CAP_SYS_TIME"] # capability added.
name: nginx
ports:
- containerPort: 80
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}
```
A big thanks to the amazing people at
- KodeKloud Training,Mumshad Mannambeth
https://www.udemy.com/course/certified-kubernetes-application-developer/
- killer.sh https://killer.sh
- Manning https://manning.com
- oreilly.com & Sander van Vugt https://learning.oreilly.com/videos/certified-kubernetes-application/9780136677628
- Linux Foundation https://training.linuxfoundation.org/
- Shannon Lucassha https://github.com/lucassha/CKAD-resources
That's all I have for now. Feel free to comment and add more tips and tricks
:rocket: Goodluck