CKA — Mock Exam Part 2

11 minute read

Mock Exam Instructions

Welcome to Mock Exam Part 2. This is your second set of CKA-style questions.

Rules:

  • 8 questions — try to complete all of them in 60 minutes
  • Use only kubernetes.io/docs as a reference
  • Set up your aliases first
  • Switch to the specified context before starting each question
  • Verify your work after each question

Combined with Part 1, these two mock exams cover a full 100% exam worth of questions.

Total points: 50%

Setup:

alias k=kubectl
export do="--dry-run=client -o yaml"
export now="--force --grace-period=0"
source <(kubectl completion bash)
complete -o default -F __start_kubectl k

Start your timer now.


Question 1 (7%) — etcd Restore

Context: kubectl config use-context cluster2

An etcd snapshot has been previously saved at /opt/etcd-backup.db on the controlplane node.

Restore the etcd cluster from this snapshot:

  1. Restore the snapshot to a new data directory /var/lib/etcd-restored
  2. Update the etcd static Pod manifest to use the new data directory
  3. Verify etcd is running with the restored data
Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster2

Step 2: Restore the snapshot

ETCDCTL_API=3 etcdctl snapshot restore /opt/etcd-backup.db \
  --data-dir=/var/lib/etcd-restored

Note: The restore command does NOT require certificates — it works on the snapshot file directly.

Step 3: Update the etcd static Pod manifest

vi /etc/kubernetes/manifests/etcd.yaml

Find the volumes section and locate the etcd data volume:

volumes:
- hostPath:
    path: /var/lib/etcd
    type: DirectoryOrCreate
  name: etcd-data

Change the path:

volumes:
- hostPath:
    path: /var/lib/etcd-restored
    type: DirectoryOrCreate
  name: etcd-data

Save and exit (:wq).

Step 4: Wait for etcd to restart

The kubelet will detect the manifest change and restart the etcd Pod. This may take 30–60 seconds.

# Watch until the Pod is back
watch "k get pods -n kube-system | grep etcd"

or:

# Wait and then check
sleep 30
k get pods -n kube-system | grep etcd

Step 5: Verify etcd is healthy

k get pods -n kube-system
k get nodes
k get all -A

If everything returns data, etcd is working.

Key points:

  • snapshot restore creates a NEW data directory — never restore into the original directory
  • Only the hostPath.path for the etcd-data volume needs to change in the manifest
  • Don’t change the --data-dir flag in the etcd command if it maps to a volume — change the volume’s hostPath instead
  • Be patient — etcd restart can take up to a minute
  • If etcd doesn’t come back, check crictl ps and crictl logs on the controlplane

Question 2 (4%) — Static Pod

Context: kubectl config use-context cluster1

Create a static Pod on the controlplane node with the following specifications:

  • Pod name: static-nginx
  • Image: nginx:1.27
  • Port: 80

The static Pod manifest path on the controlplane is /etc/kubernetes/manifests/.

Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster1

Step 2: Generate the Pod YAML

k run static-nginx --image=nginx:1.27 --port=80 $do > /etc/kubernetes/manifests/static-nginx.yaml

Or create it manually:

cat <<EOF > /etc/kubernetes/manifests/static-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-nginx
  labels:
    run: static-nginx
spec:
  containers:
  - name: static-nginx
    image: nginx:1.27
    ports:
    - containerPort: 80
EOF

Step 3: Verify

The kubelet will automatically detect the manifest and create the Pod. Wait a few seconds, then:

k get pods -A | grep static-nginx

You should see static-nginx-controlplane running. Static Pods have the node name appended.

Key points:

  • Static Pods are managed by the kubelet, not the API server
  • Place the manifest in the staticPodPath directory (check /var/lib/kubelet/config.yaml if unsure)
  • Static Pod names get the node name appended as a suffix
  • You don’t need kubectl apply — the kubelet picks up the manifest automatically
  • To delete a static Pod, remove the manifest file

Question 3 (7%) — Troubleshoot DNS

Context: kubectl config use-context cluster3

Pods in the cluster cannot resolve Service names. DNS resolution is failing.

Diagnose and fix the DNS issue so that Pods can resolve Services again.

Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster3

Step 2: Confirm the DNS issue

k run dns-test --image=busybox:1.36 --rm -it --restart=Never \
  -- nslookup kubernetes.default

If this times out or returns SERVFAIL, DNS is indeed broken.

Step 3: Check CoreDNS Pods

k get pods -n kube-system -l k8s-app=kube-dns

Common findings:

  • Pods in CrashLoopBackOff: Configuration error
  • Pods not running (0 replicas): Deployment scaled down
  • Pods in Pending: Resource issues or scheduling issues

Step 4: Check CoreDNS Deployment

k get deployment coredns -n kube-system
k describe deployment coredns -n kube-system

Step 5: Check CoreDNS logs

k logs -n kube-system -l k8s-app=kube-dns --tail=50

Step 6: Common fixes

If CoreDNS Pods are in CrashLoopBackOff — check the Corefile ConfigMap:

k get configmap coredns -n kube-system -o yaml

Look for typos in the Corefile. Common errors:

  • Typo in kubernetes cluster.local line
  • Missing or extra braces
  • Wrong plugin names

Fix the ConfigMap:

k edit configmap coredns -n kube-system

Then restart the Pods:

k rollout restart deployment coredns -n kube-system

If CoreDNS Deployment has 0 replicas:

k scale deployment coredns -n kube-system --replicas=2

If CoreDNS Pods are Pending (scheduling issue):

k describe pod <coredns-pod-name> -n kube-system

Check events for scheduling errors (taints, resource constraints).

Step 7: Verify the fix

k run dns-test2 --image=busybox:1.36 --rm -it --restart=Never \
  -- nslookup kubernetes.default

Should return the Service IP for kubernetes.default.

Key points:

  • CoreDNS Pods have the label k8s-app=kube-dns
  • Check Pods → Deployment → ConfigMap → Logs, in that order
  • The Corefile is stored in the coredns ConfigMap in kube-system
  • After fixing the ConfigMap, restart the Deployment to pick up changes

Question 4 (7%) — Ingress with TLS

Context: kubectl config use-context cluster2

An Ingress controller is already running in the cluster. Create an Ingress resource named app-ingress in namespace web with the following rules:

  • Host: app.example.com
  • Path /shop (Prefix) → Service shop-svc on port 80
  • Path /api (Prefix) → Service api-svc on port 80
  • TLS enabled using Secret app-tls for host app.example.com

Assume the Services shop-svc and api-svc already exist in namespace web.

Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster2

Step 2: Create the Ingress YAML

cat <<EOF | k apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: web
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
    - app.example.com
    secretName: app-tls
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /shop
        pathType: Prefix
        backend:
          service:
            name: shop-svc
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-svc
            port:
              number: 80
EOF

Step 3: Verify

k get ingress app-ingress -n web
k describe ingress app-ingress -n web

Check that:

  • Both paths are listed with correct backends
  • TLS section shows the host and secret name
  • No errors in the events

Key points:

  • API version is networking.k8s.io/v1 (not v1beta1)
  • pathType is required — use Prefix or Exact
  • TLS secret must contain tls.crt and tls.key keys
  • The tls.hosts must match the rules.host
  • You can find a complete Ingress YAML example in the kubernetes.io docs by searching “ingress”

Question 5 (4%) — DaemonSet with Toleration

Context: kubectl config use-context cluster1

Create a DaemonSet named log-collector in namespace kube-system with:

  • Image: fluentd:v1.16
  • The DaemonSet must run on ALL nodes, including the control plane node
  • Add the necessary toleration for control plane nodes
Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster1

Step 2: Create the DaemonSet YAML

There’s no imperative command to create a DaemonSet, so create it from YAML:

cat <<EOF | k apply -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: log-collector
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: log-collector
  template:
    metadata:
      labels:
        app: log-collector
    spec:
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluentd:v1.16
EOF

Alternative: Tolerate everything (runs on any tainted node):

tolerations:
- operator: Exists

Step 3: Verify

k get daemonset log-collector -n kube-system
k get pods -n kube-system -l app=log-collector -o wide

Verify that Pods are running on ALL nodes, including the control plane.

Key points:

  • Control plane nodes typically have the taint node-role.kubernetes.io/control-plane:NoSchedule
  • Without the toleration, DaemonSet Pods won’t schedule on the control plane
  • operator: Exists without a value matches any value for that taint key
  • You can use k describe node controlplane | grep Taint to find exact taint keys

Question 6 (7%) — Troubleshoot Broken Scheduler

Context: kubectl config use-context cluster3

New Pods are being created but remain in Pending state. All nodes are Ready. Investigate and fix the issue.

Hint: A control plane component may not be running.

Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster3

Step 2: Confirm the issue

k get pods -A

Look for Pending Pods. Also check if all control plane components are running:

k get pods -n kube-system

If kube-scheduler Pod is not running or in CrashLoopBackOff, the scheduler is broken.

Step 3: Check the scheduler Pod

k describe pod kube-scheduler-controlplane -n kube-system

Look at events for error messages.

Step 4: Check the static Pod manifest

The scheduler is a static Pod. Its manifest is at:

cat /etc/kubernetes/manifests/kube-scheduler.yaml

Step 5: Find the error

Common issues:

  • Typo in the binary name (e.g., kube-schedulerr instead of kube-scheduler)
  • Wrong --kubeconfig path
  • Wrong port or bind address

Look at the command or spec.containers[0].command section:

grep -A5 "command" /etc/kubernetes/manifests/kube-scheduler.yaml

Step 6: Fix the manifest

vi /etc/kubernetes/manifests/kube-scheduler.yaml

Fix the typo or misconfiguration. For example, if the command says:

command:
- kube-schedulerr  # typo — extra 'r'

Fix it to:

command:
- kube-scheduler

Save and exit (:wq).

Step 7: Wait for the scheduler to restart

# The kubelet will restart the static Pod automatically
sleep 20
k get pods -n kube-system | grep scheduler

Step 8: Verify Pending Pods are now scheduled

k get pods -A

Previously Pending Pods should now be Running or at least scheduled to a node.

Key points:

  • Pending Pods + all nodes Ready = likely a scheduler issue
  • kube-scheduler is a static Pod in /etc/kubernetes/manifests/
  • Check the manifest for typos — this is a common CKA trick
  • After fixing the manifest, the kubelet automatically restarts the Pod
  • Use crictl ps and crictl logs if kubectl can’t show the Pod

Question 7 (7%) — ServiceAccount & ClusterRole

Context: kubectl config use-context cluster2

Create the following RBAC setup:

  1. Create a ServiceAccount named monitoring-sa in namespace monitoring (create the namespace if needed)
  2. Create a ClusterRole named pod-reader that allows get, list, and watch on Pods cluster-wide
  3. Create a ClusterRoleBinding named monitoring-pod-reader that binds the ClusterRole to the ServiceAccount
  4. Verify the ServiceAccount can list Pods in any namespace
Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster2

Step 2: Create the namespace

k create namespace monitoring

Step 3: Create the ServiceAccount

k create serviceaccount monitoring-sa -n monitoring

Step 4: Create the ClusterRole

k create clusterrole pod-reader \
  --verb=get,list,watch \
  --resource=pods

Step 5: Create the ClusterRoleBinding

k create clusterrolebinding monitoring-pod-reader \
  --clusterrole=pod-reader \
  --serviceaccount=monitoring:monitoring-sa

Note the ServiceAccount format: namespace:name → monitoring:monitoring-sa

Step 6: Verify

# Can list Pods in default namespace
k auth can-i list pods --as=system:serviceaccount:monitoring:monitoring-sa

# Can list Pods in kube-system namespace
k auth can-i list pods --as=system:serviceaccount:monitoring:monitoring-sa -n kube-system

# Can watch Pods cluster-wide
k auth can-i watch pods --as=system:serviceaccount:monitoring:monitoring-sa -A

# Should NOT be able to delete Pods
k auth can-i delete pods --as=system:serviceaccount:monitoring:monitoring-sa

All list/get/watch checks should return yes. The delete check should return no.

Key points:

  • ClusterRole + ClusterRoleBinding = cluster-wide access
  • ClusterRole + RoleBinding = namespaced access (reusable ClusterRole, limited by RoleBinding namespace)
  • ServiceAccount format in --serviceaccount flag: namespace:name
  • ServiceAccount format in --as flag: system:serviceaccount:namespace:name
  • Don’t confuse Role with ClusterRole — Role is namespaced, ClusterRole is cluster-wide

Question 8 (7%) — Resource Sorting with kubectl

Context: kubectl config use-context cluster1

Perform the following tasks:

  1. List all Pods in the kube-system namespace sorted by CPU usage (highest first). Save the output to /opt/cpu-usage.txt
  2. Find the node with the most allocated memory and save just the node name to /opt/high-memory-node.txt
Show Answer

Solution

Step 1: Switch context

kubectl config use-context cluster1

Step 2: Sort Pods by CPU usage

k top pods -n kube-system --sort-by=cpu > /opt/cpu-usage.txt

Or with no headers for cleaner output:

k top pods -n kube-system --sort-by=cpu --no-headers > /opt/cpu-usage.txt

Verify:

cat /opt/cpu-usage.txt

Step 3: Find the node with most allocated memory

k top nodes --sort-by=memory

This shows nodes sorted by memory usage. The first row (after the header) is the node using the most memory.

Extract just the node name:

k top nodes --sort-by=memory --no-headers | head -1 | awk '{print $1}' > /opt/high-memory-node.txt

Verify:

cat /opt/high-memory-node.txt

Key points:

  • kubectl top requires the Metrics Server to be running in the cluster
  • --sort-by=cpu sorts by CPU, --sort-by=memory sorts by memory
  • --no-headers removes the header row (useful when piping to files)
  • Use awk '{print $1}' to extract the first column (name)
  • The > operator writes to file (overwrite). Use >> to append.
  • The exam may ask you to save output in a specific format — read carefully

Scoring

Grade yourself honestly:

Question Points Your Score
Q1 — etcd Restore 7% ___
Q2 — Static Pod 4% ___
Q3 — Troubleshoot DNS 7% ___
Q4 — Ingress with TLS 7% ___
Q5 — DaemonSet + Toleration 4% ___
Q6 — Troubleshoot Scheduler 7% ___
Q7 — ServiceAccount & ClusterRole 7% ___
Q8 — Resource Sorting 7% ___
Total 50% ___

Combined Score (Part 1 + Part 2)

Mock Exam Score
Part 1 ___/50%
Part 2 ___/50%
Total ___/100%

Benchmark:

  • 80–100%: Excellent. You’re ready for the exam.
  • 66–79%: Good — you’d pass, but review weak areas for a safety margin.
  • 50–65%: Close but risky. Practice more and retake the mock exams.
  • Below 50%: Keep studying. Review the chapters for your weakest domains.

Remember: The real exam passing score is 66%. Aim for 80%+ in mock exams to build a comfortable margin.


Next: Final Checklist →

Updated: