{"id":48930812,"url":"https://github.com/theplatformlab/CKA-Certified-Kubernetes-Administrator","last_synced_at":"2026-05-03T11:01:11.497Z","repository":{"id":300237109,"uuid":"995941609","full_name":"theplatformlab/CKA-Certified-Kubernetes-Administrator","owner":"theplatformlab","description":"CKA Certification Exam Guide 2026 — study notes, practice questions, kubectl cheat sheet, exam tips, and full Kubernetes v1.35 syllabus breakdown. Covers etcd backup, RBAC, kubeadm, Gateway API, NetworkPolicy, troubleshooting, and killer.sh prep. Scored 89%.","archived":false,"fork":false,"pushed_at":"2026-04-21T08:54:13.000Z","size":446,"stargazers_count":206,"open_issues_count":3,"forks_count":56,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-21T10:47:01.357Z","etag":null,"topics":["certification","certified-kubernetes-administrator","cka","cka-certification","cka-cheat-sheet","cka-exam","cka-exercises","cka-practice-questions","devops","exam","gateway-api","helm","k8s","k8s-cluster","killer-sh","kube","kubernetes","kubernetes-troubleshooting"],"latest_commit_sha":null,"homepage":"https://techwithmohamed.com/blog/cka-exam-study/","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theplatformlab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-04T08:21:49.000Z","updated_at":"2026-04-21T09:25:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"622ff926-d623-4600-a2a4-16b8f1460606","html_url":"https://github.com/theplatformlab/CKA-Certified-Kubernetes-Administrator","commit_stats":null,"previous_names":["techwithmohamed/cka-certified-kubernetes-administrator","theplatformlab/cka-certified-kubernetes-administrator"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/theplatformlab/CKA-Certified-Kubernetes-Administrator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theplatformlab%2FCKA-Certified-Kubernetes-Administrator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theplatformlab%2FCKA-Certified-Kubernetes-Administrator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theplatformlab%2FCKA-Certified-Kubernetes-Administrator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theplatformlab%2FCKA-Certified-Kubernetes-Administrator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theplatformlab","download_url":"https://codeload.github.com/theplatformlab/CKA-Certified-Kubernetes-Administrator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theplatformlab%2FCKA-Certified-Kubernetes-Administrator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32566444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["certification","certified-kubernetes-administrator","cka","cka-certification","cka-cheat-sheet","cka-exam","cka-exercises","cka-practice-questions","devops","exam","gateway-api","helm","k8s","k8s-cluster","killer-sh","kube","kubernetes","kubernetes-troubleshooting"],"created_at":"2026-04-17T09:00:29.535Z","updated_at":"2026-05-03T11:01:11.466Z","avatar_url":"https://github.com/theplatformlab.png","language":"Shell","funding_links":[],"categories":["Shell"],"sub_categories":[],"readme":"[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n[![YAML Validation](https://github.com/theplatformlab/CKA-Certified-Kubernetes-Administrator/actions/workflows/validate.yml/badge.svg)](https://github.com/theplatformlab/CKA-Certified-Kubernetes-Administrator/actions/workflows/validate.yml)\n[![CKA](https://img.shields.io/badge/CKA-Certified%202026-success)]()\n[![Kubernetes](https://img.shields.io/badge/Kubernetes-v1.35-326CE5?logo=kubernetes\u0026logoColor=white)](https://kubernetes.io/)\n[![Exercises](https://img.shields.io/badge/Exercises-22-blue)](exercises/)\n[![Skeletons](https://img.shields.io/badge/YAML%20Skeletons-23-blue)](skeletons/)\n[![Mock Exams](https://img.shields.io/badge/Mock%20Exams-2-success)](mock-exams/)\n[![GitHub stars](https://img.shields.io/github/stars/theplatformlab/CKA-Certified-Kubernetes-Administrator?style=social)](https://github.com/theplatformlab/CKA-Certified-Kubernetes-Administrator)\n\n**Disclaimer:** This is a study and practice resource. It contains practice exercises and training materials designed to help prepare for the CKA exam. It does not include, share, or reproduce actual CKA exam questions. All exercises are independently designed training scenarios. See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for policies.\n\n\u003e My CKA study notes, practice questions, and kubectl cheat sheet. Kubernetes v1.35. I scored 89% — this is everything I used to prepare.\n\n# CKA Certification Guide 2026 — How I Passed with 89%\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/cka.png\" alt=\"CKA Certification Exam 2026 — Certified Kubernetes Administrator Study Guide with Practice Questions, Cheat Sheet, and Exam Tips\"\u003e\n\u003c/p\u003e\n\nI took the CKA in March 2026 and scored 89%. Writing this while it's fresh — partly because I was frustrated with how many outdated guides are still floating around (dockershim references in 2026, come on) and partly because organizing my notes helped me retain what I learned.\n\nThe [CKA](https://www.cncf.io/certification/cka/) is a hands-on, terminal-based exam. 2 hours, roughly 17-25 tasks, no multiple choice. I prepped for about 4 weeks. This repo has my notes, the commands I actually used, YAML I wrote from memory, and the mistakes I made along the way.\n\n\u003e Blog version of these notes: [Pass the CKA Certification Exam](https://techwithmohamed.com/blog/cka-exam-study-guide/)\n\nIf this was useful, a star helps others find it.\n\n---\n\n## Quick Start (\u003c 4 Weeks to Exam)\n\nIf you're time-pressured, here's the fast track:\n\n1. **Run the setup script** — get your aliases and vim config right from day one: [scripts/exam-setup.sh](scripts/exam-setup.sh)\n2. **Do the exercises** — work through the [22 hands-on exercises](exercises/) in order. Each one targets a specific CKA domain.\n3. **Use YAML templates** — reference [TEMPLATES.md](TEMPLATES.md) for all skeleton YAML. Copy, paste, modify.\n4. **Do the mock exam** — practice under exam conditions with timed scenarios.\n5. **Do killer.sh twice** — once 2 weeks out, once 3 days before. See [killer.sh vs the Real Exam](#killersh-vs-the-real-cka-exam).\n6. **Read the exam day strategy** — the [two-pass approach](#exam-day-strategy--time-allocation) saved time on exam day.\n\n---\n\n## Repo Structure\n\n```\nCKA-Certified-Kubernetes-Administrator/\n├── README.md                          # This guide (you're here)\n├── exercises/                         # 22 hands-on labs\n│   ├── 01-pod-basics/\n│   ├── 02-multi-container-pod/\n│   ├── 03-configmap-secret/\n│   ├── 04-rbac/\n│   ├── 05-networkpolicy/\n│   ├── 06-deployment-rollout/\n│   ├── 07-statefulset/\n│   ├── 08-node-drain-cordon/\n│   ├── 09-kubeadm-upgrade/\n│   ├── 10-static-pod/\n│   ├── 11-troubleshoot-cluster/\n│   ├── 12-storage-pv-pvc/\n│   ├── 13-helm-install-upgrade/\n│   ├── 14-kustomize-overlays/\n│   ├── 15-gateway-api/\n│   ├── 16-hpa/\n│   ├── 17-kubectl-debug/\n│   ├── 18-cri-dockerd-setup/\n│   ├── 19-ingress-classic/\n│   ├── 20-pod-security-standards/\n│   ├── 21-jobs-cronjobs/\n│   └── 22-priorityclass/\n├── TEMPLATES.md                       # All YAML templates in collapsible format\n├── skeletons/                         # 23 YAML template files (see TEMPLATES.md)\n├── mock-exams/                        # Full practice exams (15 questions, 2 hours each)\n│   ├── MOCK-EXAM-01.md               # Practice questions\n│   ├── MOCK-EXAM-01-SOLUTIONS.md    # Complete solutions and explanations\n│   ├── MOCK-EXAM-02.md\n│   └── MOCK-EXAM-02-SOLUTIONS.md\n├── cheatsheet/\n│   └── cka-cheatsheet.md              # One-page printable reference\n├── troubleshooting/\n│   └── README.md                      # Symptom-based lookup playbook\n├── scripts/\n│   ├── exam-setup.sh                 # Aliases, vim config, bash completion\n│   └── validate-local.sh             # Local YAML validation (run before pushing)\n├── .github/\n│   ├── workflows/validate.yml         # CI — YAML lint on every push\n│   ├── ISSUE_TEMPLATE/                # Bug, content request, exam feedback\n│   │   └── config.yml                 # Discussions link for questions\n│   └── PULL_REQUEST_TEMPLATE.md\n├── CHANGELOG.md\n├── CODE_OF_CONDUCT.md\n├── CONTRIBUTING.md\n├── SECURITY.md\n└── LICENSE\n```\n\n---\n\n## Table of Contents\n\n### Start Here — Practice Your Skills\n- [CKA Syllabus Breakdown (v1.35)](#cka-syllabus-breakdown-v135)\n  - [Domain 1 — Storage (10%)](#domain-1--storage-10)\n  - [Domain 2 — Troubleshooting (30%)](#domain-2--troubleshooting-30)\n  - [Domain 3 — Workloads \u0026 Scheduling (15%)](#domain-3--workloads--scheduling-15)\n  - [Domain 4 — Cluster Architecture, Installation \u0026 Configuration (25%)](#domain-4--cluster-architecture-installation--configuration-25)\n  - [Domain 5 — Services \u0026 Networking (20%)](#domain-5--services--networking-20)\n- [CKA Domain Weight Distribution](#cka-domain-weight-distribution)\n- [Practice Scenarios with Full Solutions](#practice-scenarios-with-full-solutions)\n- [Mock Exams — Final Preparation](#mock-exams--final-preparation)\n- [Study Progress Tracker](#study-progress-tracker)\n\n### Exam Preparation Strategy\n- [The Exam Environment (PSI Remote Desktop)](#the-exam-environment-psi-remote-desktop)\n- [First 60 Seconds — Aliases, vim, bash](#first-60-seconds--aliases-vim-bash)\n- [Imperative Commands Quick Reference](#imperative-commands-quick-reference)\n- [YAML Templates Quick Reference](#yaml-skeletons--write-these-from-memory)\n- [Exam Day Strategy — Time Allocation](#exam-day-strategy--time-allocation)\n- [Mistakes That Will Fail You on the CKA](#mistakes-that-will-fail-you-on-the-cka)\n- [Vim Keys I Actually Used on Exam Day](#vim-keys-i-actually-used-on-exam-day)\n- [Troubleshooting Decision Flowchart](#troubleshooting-decision-flowchart)\n- [CKA Exam Day Checklist](#cka-exam-day-checklist)\n\n### Study Resources \u0026 Planning\n- [kubectl Cheat Sheet for CKA](#kubectl-cheat-sheet-for-cka)\n- [Docs Pages I Actually Used During the Exam](#docs-pages-i-actually-used-during-the-exam)\n- [CKA Study Plan (4-5 Weeks)](#cka-study-plan-4-5-weeks)\n- [Study Resources for CKA 2026](#study-resources-for-cka-2026)\n- [killer.sh vs the Real CKA Exam](#killersh-vs-the-real-cka-exam)\n\n### Exam Details \u0026 Reference\n- [CKA Exam Details — Cost, Duration, Passing Score, Format](#cka-exam-details--cost-duration-passing-score-format-march-2026)\n- [How Much Does the CKA Exam Cost?](#how-much-does-the-cka-exam-cost)\n- [CKA vs CKAD vs CKS — Which One Should You Take?](#cka-vs-ckad-vs-cks--which-one-should-you-take)\n- [CKA vs CKAD vs CKS Scope Architecture Diagram](#cka-vs-ckad-vs-cks-scope-architecture-diagram)\n- [What Changed in Kubernetes v1.35 for CKA](#what-changed-in-kubernetes-v135-for-cka)\n- [Before You Book the CKA Exam](#before-you-book-the-cka-exam)\n- [CKA FAQ — Common Questions](#cka-faq--common-questions)\n\n### Final Thoughts\n- [Final Words](#final-words)\n\n---\n\n## CKA Exam Details — Cost, Duration, Passing Score, Format (March 2026)\n\n| **CKA Exam Details**               | **Information**                                                                                                                                     |\n|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|\n| **Exam Type**                      | Performance-based (live terminal — NOT multiple choice)                                                                                             |\n| **Exam Duration**                  | 2 hours                                                                                                                                             |\n| **Passing Score**                  | 66%                                                                                                                                                 |\n| **Kubernetes Version**             | v1.35                                                                                                                                               |\n| **Number of Questions**            | ~17-25 tasks (varies per session)                                                                                                                   |\n| **Exam Cost**                      | $445 USD (includes one free retake)                                                                                                                 |\n| **Certificate Validity**           | 2 years                                                                                                                                             |\n| **Exam Delivery**                  | PSI Secure Browser (remote proctored)                                                                                                               |\n| **Allowed Resources**              | kubernetes.io/docs, kubernetes.io/blog, github.com/kubernetes — open in exam browser                                                                |\n| **Domains Covered**               | 5 domains: Storage, Troubleshooting, Workloads \u0026 Scheduling, Cluster Architecture, Services \u0026 Networking                                            |\n| **Exam Language**                  | English, Japanese, Simplified Chinese                                                                                                                |\n| **OS in Exam**                     | Ubuntu Linux terminal                                                                                                                                |\n\nImportant: the passing score is 66%, not 75% like some older guides say. They lowered it. Still not easy though — 2 hours goes fast when you're troubleshooting a broken kubelet under pressure.\n\n[Back to top](#table-of-contents)\n\n---\n\n## How Much Does the CKA Exam Cost?\n\nThe CKA costs **$445 USD** as of March 2026. That includes:\n\n- One exam attempt\n- One free retake (if you fail)\n- Two killer.sh simulator sessions (24 hours each)\n- Access to a self-paced training course\n\nDiscount tips:\n- The CNCF runs sales on Black Friday and KubeCon weeks — I've seen 30-40% off\n- Linux Foundation bundles (CKA + CKAD) sometimes drop to ~$500 total\n- Check if your employer has a training budget — most do for certs\n- Student discounts exist through the Linux Foundation\n\nDon't pay full price if you can wait for a sale. I paid around $300 during a KubeCon promo.\n\n---\n\n## CKA vs CKAD vs CKS — Which One Should You Take?\n\n| | **CKA** | **CKAD** | **CKS** |\n|---|---|---|---|\n| **Focus** | Cluster administration | Application development | Security |\n| **Who it's for** | SREs, platform engineers, admins | Developers deploying to K8s | Security engineers, senior admins |\n| **Difficulty** | Hard — the troubleshooting and etcd questions are brutal under time pressure | Medium — if you already deploy to K8s, most of this is familiar | Hardest of the three — Falco and AppArmor syntax is miserable to memorize |\n| **Duration** | 2 hours | 2 hours | 2 hours |\n| **Passing Score** | 66% | 66% | 67% |\n| **Cost** | $445 | $445 | $445 |\n| **Prerequisites** | None | None | Must hold active CKA |\n| **Key Topics** | etcd, kubeadm, RBAC, troubleshooting, networking | Pods, Deployments, Jobs, probes, volumes | Falco, AppArmor, OPA, Network Policies, audit |\n| **Questions** | ~17-25 | ~15-20 | ~15-20 |\n| **My honest take** | Start here if you manage clusters. The etcd and kubeadm skills don't exist anywhere else. | Easier than CKA but less impressive on a resume. | Skip unless your job requires it — the ROI is lower. |\n\nMy take: if you're doing any kind of cluster administration, start with CKA. If you're purely a dev who deploys apps, CKAD first. CKS requires an active CKA, so you can't skip it.\n\nThere's about 40% overlap between CKA and CKAD (pods, deployments, services, configmaps, secrets). If you pass one, the other is easier. I did CKA first because troubleshooting and etcd backup are harder to learn on your own.\n\n---\n\n## CKA vs CKAD vs CKS Scope Architecture Diagram\n\n```mermaid\ngraph TB\n    subgraph CKA[\"CKA — Cluster Administration\"]\n        style CKA fill:#326CE5,color:#fff\n        A1[etcd backup/restore]\n        A2[kubeadm install/upgrade]\n        A3[RBAC — Roles, ClusterRoles]\n        A4[Node management — drain, cordon]\n        A5[Troubleshooting — kubelet, kube-proxy, CoreDNS]\n        A6[Cluster networking — CNI, Services]\n        A7[Storage — PV, PVC, StorageClass]\n    end\n    \n    subgraph CKAD[\"CKAD — Application Development\"]\n        style CKAD fill:#00A86B,color:#fff\n        B1[Multi-container pods — sidecars, init]\n        B2[Jobs, CronJobs]\n        B3[Probes — liveness, readiness, startup]\n        B4[Helm charts]\n        B5[Custom Resource Definitions]\n        B6[Blue/green, canary deployments]\n    end\n    \n    subgraph SHARED[\"Shared (~40% overlap)\"]\n        style SHARED fill:#FF8C00,color:#fff\n        S1[Pods, Deployments, Services]\n        S2[ConfigMaps, Secrets]\n        S3[NetworkPolicies]\n        S4[Ingress / Gateway API]\n        S5[Labels, selectors, annotations]\n        S6[Resource requests/limits]\n    end\n    \n    subgraph CKS[\"CKS — Security\"]\n        style CKS fill:#DC143C,color:#fff\n        C1[Falco runtime security]\n        C2[AppArmor / Seccomp profiles]\n        C3[OPA Gatekeeper]\n        C4[Audit logging]\n        C5[Image scanning — Trivy]\n        C6[Pod Security Standards]\n        C7[Supply chain security]\n    end\n    \n    CKA --\u003e|\"~40% overlap\"| CKAD\n    CKA --\u003e|\"required for\"| CKS\n```\n\n---\n\n## What Changed in Kubernetes v1.35 for CKA\n\nIf you're studying from a guide written for v1.29 or v1.30, some of it is wrong. I found this out the hard way — half my bookmarked blog posts had outdated sidecar syntax and still referenced `--record` on rollouts. Here's what actually changed that matters for the CKA:\n\n| Feature | Status in v1.35 | CKA Impact |\n|---|---|---|\n| **Sidecar containers (native)** | GA | Init containers with `restartPolicy: Always` run as sidecars. You'll see this on the exam. |\n| **In-place pod vertical scaling** | Beta | Can resize CPU/memory without restarting. I spent 30 minutes learning this and it wasn't on my exam. Know it exists, move on. |\n| **Gateway API** | GA (v1.2+) | Replacing Ingress long-term. I got a question on this. Know how to create a Gateway and an HTTPRoute that points to a backend service. |\n| **cgroup v2** | Default | All nodes use cgroup v2 now. Affects resource monitoring and limits. |\n| **kubectl debug** | GA | `k debug node/\u003cname\u003e` and `k debug pod/\u003cname\u003e` — useful for troubleshooting tasks. |\n| **ValidatingAdmissionPolicy** | GA | CEL-based admission without webhooks. I didn't get this on my exam but it's in the curriculum now. Worth 15 minutes of study. |\n| **Pod Scheduling Readiness** | GA | Pods can wait in scheduling gates. Not likely to show up on the exam. |\n| **CSI migration complete** | Done | In-tree volume plugins fully migrated. StorageClass provisioners are all CSI now. |\n\nThe big ones for exam prep: native sidecars and Gateway API. If your study material doesn't cover these, it's outdated.\n\n---\n\n## Before You Book the CKA Exam\n\nChecklist I wish someone had given me before I started booking:\n\n1. **Can you set up a cluster from scratch with kubeadm?** I couldn't the first time I tried. Took me 3 attempts before I could do it without the docs open. Do it at least twice before booking.\n2. **Can you do an etcd backup and restore?** This is almost guaranteed to show up. I practiced this 10+ times. The cert flags need to be muscle memory, not something you look up.\n3. **Are you comfortable with RBAC?** Role vs ClusterRole, RoleBinding vs ClusterRoleBinding, ServiceAccounts — I fumbled the `--as=system:serviceaccount:ns:name` syntax for weeks before it clicked.\n4. **Can you troubleshoot a NotReady node?** SSH in, check kubelet, check certificates, check networking. This is 30% of the score and it's the section where most people lose the most time.\n5. **Do you have a cluster to practice on?** kind or minikube on your laptop, or Killercoda/KodeKloud online. You cannot pass this exam by reading — you have to break things.\n6. **Have you done killer.sh at least once?** The real exam is easier, but killer.sh builds speed and confidence. My first killer.sh score was terrible. That's normal.\n7. **Is your ID ready?** Government-issued ID, matching your CNCF account name. Check this before exam day.\n\n---\n\n## The Exam Environment (PSI Remote Desktop)\n\nThe exam runs in a PSI Secure Browser — a remote Ubuntu desktop. Things that surprised me:\n\n**Copy/Paste:**\n- `Ctrl+Shift+C` / `Ctrl+Shift+V` in the terminal\n- Right-click paste works sometimes, sometimes it doesn't\n- The built-in notepad uses normal `Ctrl+C` / `Ctrl+V`\n- Practice these shortcuts. I wasted 2 minutes fumbling with paste in the first question.\n\n**Terminal quirks:**\n- There's a small delay on every keystroke — maybe 50-100ms. It adds up.\n- Tab completion works but feels laggy.\n- You can open multiple terminal tabs. I used two: one for the task, one for verification.\n- The file browser is basic. Stick to command line.\n\n**Browser:**\n- One extra tab allowed for kubernetes.io documentation\n- Bookmarks are not available — you'll type URLs manually\n- The search on kubernetes.io is your best friend. Use it instead of navigating.\n\n**General:**\n- Webcam and mic are on the entire time\n- Clear your desk — nothing on it except your computer\n- No second monitor\n- No headphones/earphones\n- Water bottle is fine (clear, no label)\n- Bathroom breaks are allowed but the timer doesn't pause\n\n---\n\n## Important: Exam Environment vs Practice\n\n**In the exam, each question is a fresh SSH connection.** Any aliases or configuration you set up will NOT carry over to the next question. Do not waste exam time on setup.\n\n**Only reliable shortcut:** The `k` alias for `kubectl` is pre-configured on every machine.\n\n**For practice on your laptop,** you can use the setup script in [`scripts/exam-setup.sh`](scripts/exam-setup.sh) to speed up drilling. But practice without these shortcuts 1-2 weeks before the exam to build command muscle memory. You need to know:\n- `kubectl run ...`\n- `kubectl create ...`\n- `--dry-run=client -o yaml` (type it out, not an alias)\n- `--force --grace-period=0` (type it out, not an alias)\n\nMemorize the commands. That beats any alias on test day.\n\n---\n\n## Imperative Commands Quick Reference\n\n**Why imperative?** Under exam pressure (2 hours, ~17 tasks), typing YAML is slow. The CKA expects speed. Most questions can be solved faster with imperative commands than writing manifests. Declarative is for when you need complex control or for learning.\n\n**Speed tip:** Memorize these command patterns. On test day, `kubectl run`, `kubectl create`, and `kubectl expose` will be your fastest friends.\n\n### Fast Pod Creation (15-30 seconds vs 2 minutes for YAML)\n\n```bash\n# Basic pod\nk run nginx --image=nginx:1.27\n\n# Pod with port exposed\nk run nginx --image=nginx:1.27 --port=80\n\n# Pod with labels\nk run nginx --image=nginx:1.27 --labels=app=web,tier=frontend\n\n# Pod with resource limits\nk run nginx --image=nginx:1.27 --limits=cpu=200m,memory=512Mi\n\n# Pod with environment variables\nk run nginx --image=nginx:1.27 --env=LOG_LEVEL=debug --env=APP_ENV=prod\n\n# Pod with command override\nk run nginx --image=nginx:1.27 -- sh -c \"echo 'Hello' \u0026\u0026 sleep 3600\"\n\n# Pod with multiple containers (init + app)\nk run myapp --image=myapp:1.0 --overrides='{\"spec\":{\"initContainers\":[{\"name\":\"init\",\"image\":\"busybox\",\"command\":[\"wget\",\"-O\",\"/data/file\",\"http://example.com\"]}],\"containers\":[{\"name\":\"myapp\",\"image\":\"myapp:1.0\",\"volumeMounts\":[{\"name\":\"data\",\"mountPath\":\"/data\"}]}],\"volumes\":[{\"name\":\"data\",\"emptyDir\":{}}]}}'\n\n# Generate YAML without running (for review/editing)\nk run nginx --image=nginx:1.27 $do \u003e pod.yaml\n```\n\n**Exam pattern:** Use `$do` flag to generate YAML, review it, then apply. Saves you from memorizing exact YAML structure.\n\n### Deployment Creation \u0026 Management (Most exam questions)\n\n```bash\n# Basic deployment\nk create deployment webapp --image=nginx:1.27\n\n# Deployment with replicas\nk create deployment webapp --image=nginx:1.27 --replicas=3\n\n# Deployment with resource requests\nk create deployment webapp --image=nginx:1.27 --replicas=3 --dry-run=client -o yaml | \\\n  sed 's/resources: {}/resources:\\n              requests:\\n                cpu: 100m\\n                memory: 128Mi/' \u003e deploy.yaml\n\n# Scale deployment\nk scale deployment webapp --replicas=5\n\n# Update image (rolling update)\nk set image deployment/webapp nginx=nginx:1.28 --record\n\n# Check rollout status\nk rollout status deployment/webapp\n\n# View rollout history\nk rollout history deployment/webapp\n\n# Rollback to previous version\nk rollout undo deployment/webapp\n\n# Rollback to specific revision\nk rollout undo deployment/webapp --to-revision=2\n\n# Pause rollout (for manual canary)\nk rollout pause deployment/webapp\n\n# Resume rollout\nk rollout resume deployment/webapp\n\n# Generate deployment YAML\nk create deployment webapp --image=nginx:1.27 --dry-run=client -o yaml \u003e deploy.yaml\n```\n\n**Exam tip:** Rollout commands appear on almost every CKA exam. Practice `rollout undo` and `rollout history` until they're muscle memory.\n\n### Service Exposure (ClusterIP, NodePort, LoadBalancer)\n\n```bash\n# Expose deployment as ClusterIP (default, internal only)\nk expose deployment webapp --port=80 --target-port=8080\n\n# Expose as NodePort (accessible on all nodes)\nk expose deployment webapp --port=80 --target-port=8080 --type=NodePort\n\n# Expose as LoadBalancer (cloud-only)\nk expose deployment webapp --port=80 --target-port=8080 --type=LoadBalancer\n\n# Get service external IP (NodePort/LoadBalancer)\nk get svc -w\n\n# Expose a pod directly (not recommended but does work)\nk expose pod nginx --port=80 --name=web-svc\n\n# Create service without deploying (generate YAML)\nk create service clusterip web --tcp=80:8080 $do \u003e svc.yaml\nk create service nodeport web --tcp=80:8080 $do \u003e svc.yaml\n\n# Edit service after creation\nk edit svc webapp\n\n# Port forward for testing (like accessing the pod locally)\nk port-forward svc/webapp 8080:80\n```\n\n**Exam pattern:** Most questions ask: \"Expose deployment X on port Y.\" Use `k expose deployment X --port=Y --target-port=\u003capp-port\u003e`.\n\n### RBAC — Roles, ServiceAccounts, RoleBindings (25% of exam)\n\n```bash\n# Create ServiceAccount\nk create sa my-app -n prod\n\n# Create Role (allow specific verbs on specific resources)\nk create role pod-reader --verb=get,list,watch --resource=pods -n prod\nk create role pod-deleter --verb=get,list,delete --resource=pods -n prod\n\n# Create RoleBinding (bind role to user/sa)\nk create rolebinding read-pods --role=pod-reader --serviceaccount=prod:my-app -n prod\n\n# ClusterRole (cross-namespace)\nk create clusterrole node-reader --verb=get,list --resource=nodes\n\n# ClusterRoleBinding\nk create clusterrolebinding read-nodes --clusterrole=node-reader --serviceaccount=prod:my-app\n\n# Check if user/SA has permission\nk auth can-i list pods -n prod --as=system:serviceaccount:prod:my-app\n\n# Check your own permissions\nk auth can-i list pods -n prod\n\n# View role details\nk get role pod-reader -n prod -o yaml\nk get rolebinding read-pods -n prod -o yaml\n\n# Edit role to add/remove permissions\nk edit role pod-reader -n prod\n```\n\n**Exam tip:** RBAC questions usually involve creating SA + Role + RoleBinding, then testing with `k auth can-i`. Practice the syntax until you don't have to think.\n\n### ConfigMaps \u0026 Secrets (Application config)\n\n```bash\n# ConfigMap from literal values\nk create configmap app-config --from-literal=LOG_LEVEL=debug --from-literal=DB_HOST=postgres.prod\n\n# ConfigMap from file\nk create configmap app-config --from-file=config.properties\n\n# ConfigMap from directory\nk create configmap app-config --from-file=./configs/\n\n# Secret from literal\nk create secret generic db-secret --from-literal=username=admin --from-literal=password=secret123\n\n# Secret from file\nk create secret generic tls-secret --from-file=tls.crt=cert.pem --from-file=tls.key=key.pem\n\n# Docker registry secret (for pulling private images)\nk create secret docker-registry dockerhub --docker-server=docker.io --docker-username=myuser --docker-password=mypass\n\n# View secret (NOT decrypted)\nk get secret db-secret -o yaml\n\n# Describe configmap\nk describe cm app-config\n```\n\n**Exam pattern:** When a question mentions \"app needs config from file,\" use `k create configmap $name --from-file`.\n\n### Node Management (Maintenance, upgrades, troubleshooting)\n\n```bash\n# Cordon node (mark unschedulable, don't evict existing pods)\nk cordon node-1\n\n# Drain node (evict all pods before maintenance)\nk drain node-1 --ignore-daemonsets --delete-emptydir-data\n\n# Uncordon node (resume scheduling)\nk uncordon node-1\n\n# Label a node\nk label nodes node-1 disk=ssd\nk label nodes node-1 disk=ssd --overwrite  # update existing\n\n# Taint a node (prevent pods from scheduling)\nk taint nodes node-1 key=value:NoSchedule\nk taint nodes node-1 key=value:NoExecute   # evict existing pods\n\n# Remove taint\nk taint nodes node-1 key-\n\n# Get node info (CPU, memory, conditions)\nk describe node node-1\n\n# Check node status\nk get nodes -o wide\n```\n\n**Exam pattern:** \"Prepare node for maintenance\" = `k drain`. \"Node is full\" = label it and use nodeSelector. \"Node needs maintenance\" = `k cordon` + `k drain`.\n\n### Debugging \u0026 Troubleshooting (30% of exam — learn this well)\n\n```bash\n# Get pod logs (follow in real-time)\nk logs pod-name\nk logs pod-name -f\nk logs pod-name --tail=50\n\n# Logs from previous crashed pod\nk logs pod-name --previous\n\n# Logs from all containers in pod\nk logs pod-name --all-containers\n\n# Logs from specific container in multi-container pod\nk logs pod-name -c container-name\n\n# Short-lived troubleshooting — exec into pod\nk exec -it pod-name -- /bin/bash\nk exec -it pod-name -c container-name -- /bin/bash\n\n# One-off command in pod\nk exec pod-name -- curl http://localhost:8080\n\n# Describe pod (events, conditions, resource usage)\nk describe pod pod-name\n\n# Describe everything about a resource\nk describe node node-1\n\n# Watch events in real-time\nk get events -w\n\n# Get specific event from a namespace\nk get events -n prod --sort-by='.lastTimestamp'\n\n# Port forward to debug (useful when service isn't working)\nk port-forward pod-name 8080:8080\nk port-forward svc/service-name 8080:8080\n\n# Copy files from pod to local (for log inspection)\nk cp pod-name:/var/log/app.log ./app.log\n\n# Check resource metrics (requires metrics-server)\nk top nodes\nk top pods -n prod\n```\n\n**Exam tip:** 30% of exam is \"troubleshoot why this isn't working.\" `k describe` and `k logs` are your debugging weapons. Learn to read the error messages.\n\n### Resource Quotas \u0026 Limits (Cluster resource management)\n\n```bash\n# Create LimitRange (per-pod limits)\nk create limitrange cpu-limit --max=2 --min=100m --type=Pod\n\n# Resource quota (per-namespace total limits)\nk create quota my-quota --hard=requests.cpu=10,limits.cpu=20,requests.memory=100Gi,pods=100\n\n# Check current usage\nk describe resourcequota my-quota -n prod\nk describe limitrange cpu-limit -n prod\n```\n\n**Exam pattern:** Usually appears as \"create resource quota so namespace doesn't exceed X CPU.\"\n\n### Shortcuts \u0026 Pro Tips (Save 5-10 minutes per exam)\n\n```bash\n# Dry-run + output to file (review before applying)\nk create deployment app --image=app:1.0 $do \u003e deploy.yaml\nk apply -f deploy.yaml\n\n# Delete resources fast\nk delete pod pod-name $now          # force deletion\nk delete pods --all -n prod --now   # delete all pods in namespace\nk delete deployment webapp -n prod  # cascade delete (pods too)\n\n# Get resources in all namespaces\nk get pods -A\nk get pods --all-namespaces\n\n# Get in custom columns (useful for spotting issues)\nk get pods -o wide                  # show node, IP, etc.\nk get pods -o custom-columns=NAME:.metadata.name,IMAGE:.spec.containers[0].image\n\n# JSONPath queries (find pods by image)\nk get pods -o jsonpath='{.items[*].metadata.name}' | xargs -I{} echo {}\n\n# Get yaml for an existing resource then copy it\nk get deployment webapp -o yaml \u003e webapp-backup.yaml\nk apply -f webapp-backup.yaml\n\n# Edit resource live\nk edit deployment webapp\n\n# Patch resource (update specific field)\nk patch deployment webapp -p '{\"spec\":{\"replicas\":5}}'\n```\n\n---\n\n## Docs Pages I Actually Used During the Exam\n\nYou can access kubernetes.io during the exam. These are the pages I remember opening — there were probably others I clicked through but these are the ones I went back to:\n\n| Topic | Page |\n|---|---|\n| kubectl cheat sheet | https://kubernetes.io/docs/reference/kubectl/cheatsheet/ |\n| etcd backup/restore | https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/ |\n| kubeadm upgrade | https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/ |\n| RBAC | https://kubernetes.io/docs/reference/access-authn-authz/rbac/ |\n| NetworkPolicy | https://kubernetes.io/docs/concepts/services-networking/network-policies/ |\n| PV / PVC | https://kubernetes.io/docs/concepts/storage/persistent-volumes/ |\n| Static pods | https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/ |\n| Taints and tolerations | https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ |\n| Debug services | https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/ |\n| CoreDNS | https://kubernetes.io/docs/tasks/administer-cluster/coredns/ |\n| Ingress | https://kubernetes.io/docs/concepts/services-networking/ingress/ |\n| Gateway API | https://kubernetes.io/docs/concepts/services-networking/gateway/ |\n| Drain a node | https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/ |\n\nTip: use the search bar on kubernetes.io. Don't waste time clicking through navigation menus.\n\n---\n\n## kubectl Cheat Sheet for CKA\n\nThese are the commands I used most during the exam. All using the aliases from the setup section.\n\n### Context and Namespace\n\n```bash\n# Switch context (DO THIS BEFORE EVERY QUESTION)\nk config use-context \u003ccontext-name\u003e\n\n# Set default namespace\nkn \u003cnamespace\u003e\n\n# Check current context\nk config current-context\n```\n\n### Pods\n\n```bash\n# Create a pod\nk run nginx --image=nginx:1.27\n\n# Create pod YAML without running it\nk run nginx --image=nginx:1.27 $do \u003e pod.yaml\n\n# Pod with labels\nk run nginx --image=nginx:1.27 --labels=app=web,tier=frontend\n\n# Pod with port\nk run nginx --image=nginx:1.27 --port=80\n\n# Get pods with extra info\nk get pods -o wide\nk get pods --show-labels\nk get pods -l app=web\n\n# Delete pod fast\nk delete pod nginx $now\n```\n\n### Deployments\n\n```bash\n# Create deployment\nk create deployment webapp --image=nginx:1.27 --replicas=3\n\n# Generate YAML\nk create deployment webapp --image=nginx:1.27 --replicas=3 $do \u003e deploy.yaml\n\n# Scale\nk scale deployment webapp --replicas=5\n\n# Update image\nk set image deployment/webapp nginx=nginx:1.28\n\n# Rollout commands\nk rollout status deployment/webapp\nk rollout history deployment/webapp\nk rollout undo deployment/webapp\nk rollout undo deployment/webapp --to-revision=2\n```\n\n### Services\n\n```bash\n# Expose a deployment\nk expose deployment webapp --port=80 --target-port=80 --type=ClusterIP\nk expose deployment webapp --port=80 --target-port=80 --type=NodePort\n\n# Expose a pod\nk expose pod nginx --port=80 --name=nginx-svc\n\n# Generate service YAML\nk create service clusterip my-svc --tcp=80:80 $do \u003e svc.yaml\n```\n\n### RBAC\n\n```bash\n# Create ServiceAccount\nk create sa my-sa -n my-ns\n\n# Create Role\nk create role pod-reader --verb=get,list,watch --resource=pods -n my-ns\n\n# Create RoleBinding\nk create rolebinding read-pods --role=pod-reader --serviceaccount=my-ns:my-sa -n my-ns\n\n# Create ClusterRole\nk create clusterrole node-reader --verb=get,list --resource=nodes\n\n# Create ClusterRoleBinding\nk create clusterrolebinding read-nodes --clusterrole=node-reader --serviceaccount=my-ns:my-sa\n\n# Check permissions\nk auth can-i list pods -n my-ns --as=system:serviceaccount:my-ns:my-sa\n```\n\n### Node Management\n\n```bash\n# Cordon (mark unschedulable)\nk cordon \u003cnode-name\u003e\n\n# Drain (evict pods)\nk drain \u003cnode-name\u003e --ignore-daemonsets --delete-emptydir-data\n\n# Uncordon\nk uncordon \u003cnode-name\u003e\n\n# Label a node\nk label node \u003cnode-name\u003e disk=ssd\n\n# Taint a node\nk taint nodes \u003cnode-name\u003e key=value:NoSchedule\n\n# Remove a taint\nk taint nodes \u003cnode-name\u003e key=value:NoSchedule-\n```\n\n### etcd\n\n```bash\n# Snapshot\nETCDCTL_API=3 etcdctl snapshot save /tmp/etcd-backup.db \\\n  --endpoints=https://127.0.0.1:2379 \\\n  --cacert=/etc/kubernetes/pki/etcd/ca.crt \\\n  --cert=/etc/kubernetes/pki/etcd/server.crt \\\n  --key=/etc/kubernetes/pki/etcd/server.key\n\n# Verify snapshot\nETCDCTL_API=3 etcdctl snapshot status /tmp/etcd-backup.db --write-table\n\n# Restore\nETCDCTL_API=3 etcdctl snapshot restore /tmp/etcd-backup.db \\\n  --data-dir=/var/lib/etcd-restored\n```\n\n### Troubleshooting\n\n```bash\n# Node issues\nk get nodes\nk describe node \u003cnode-name\u003e\nssh \u003cnode\u003e -- sudo systemctl status kubelet\nssh \u003cnode\u003e -- sudo journalctl -u kubelet --no-pager | tail -30\n\n# Pod issues\nk describe pod \u003cpod-name\u003e\nk logs \u003cpod-name\u003e\nk logs \u003cpod-name\u003e -c \u003ccontainer-name\u003e\nk logs \u003cpod-name\u003e --previous\n\n# Service/endpoint issues\nk get endpoints \u003cservice-name\u003e\nk get svc\nk describe svc \u003cservice-name\u003e\n\n# DNS\nk run test-dns --image=busybox:1.36 --rm -it -- nslookup kubernetes\nk get pods -n kube-system -l k8s-app=kube-dns\n\n# Debug node\nk debug node/\u003cnode-name\u003e -it --image=busybox:1.36\n```\n\n### Quick YAML Generation\n\n```bash\n# Pod\nk run nginx --image=nginx:1.27 $do \u003e pod.yaml\n\n# Deployment\nk create deployment webapp --image=nginx:1.27 $do \u003e deploy.yaml\n\n# Service\nk expose deployment webapp --port=80 $do \u003e svc.yaml\n\n# Job\nk create job my-job --image=busybox:1.36 -- sh -c \"echo done\" $do \u003e job.yaml\n\n# CronJob\nk create cronjob my-cron --image=busybox:1.36 --schedule=\"*/5 * * * *\" -- sh -c \"echo tick\" $do \u003e cron.yaml\n\n# ConfigMap\nk create configmap my-cm --from-literal=key=value $do \u003e cm.yaml\n\n# Secret\nk create secret generic my-secret --from-literal=pass=s3cret $do \u003e secret.yaml\n```\n\n[Back to top](#table-of-contents)\n\n---\n\n## CKA Syllabus Breakdown (v1.35)\n\n### Domain 1 — Storage (10%)\n\n10% of the score. Sounds small, but the questions are straightforward if you understand PV/PVC binding. I almost skipped this in my study plan and then it showed up as one of the easiest points on the exam. The main trap: `storageClassName` has to match exactly between PV and PVC, and \"exactly\" includes the case where one side has it set and the other doesn't.\n\n\u003e See also: [Exercise 12 — Storage](exercises/12-storage-pv-pvc/) | Skeletons: [pv.yaml](skeletons/pv.yaml), [pvc.yaml](skeletons/pvc.yaml), [storageclass.yaml](skeletons/storageclass.yaml)\n\n#### 1.1 — Understand Storage Classes and Persistent Volumes\n\nStorage on Kubernetes is simple in concept but annoying in practice. A **PV** is the actual storage (think: the hard drive). A **PVC** is a request for that storage (think: \"I need 2Gi of disk\"). A **StorageClass** tells Kubernetes how to dynamically create PVs when a PVC asks for one.\n\nWhat actually matters for the exam:\n- PV is cluster-scoped (no namespace). PVC is namespace-scoped. I mixed these up and created a PVC in the wrong namespace — it bound fine but the pod couldn't see it.\n- PVC binds to a PV when: capacity \u003e= request, accessModes match, AND storageClassName matches. If any one of these is off, the PVC sits in `Pending` forever with no helpful error message.\n- The `storageClassName` trap is real. `manual` ≠ `Manual` ≠ empty string. Triple-check it.\n\n```yaml\n# PV — cluster-scoped\napiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: my-pv\nspec:\n  capacity:\n    storage: 5Gi\n  accessModes:\n  - ReadWriteOnce\n  persistentVolumeReclaimPolicy: Retain\n  storageClassName: manual\n  hostPath:\n    path: /data/my-pv\n```\n\n```yaml\n# PVC — namespace-scoped\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: my-pvc\n  namespace: default\nspec:\n  accessModes:\n  - ReadWriteOnce\n  resources:\n    requests:\n      storage: 2Gi\n  storageClassName: manual\n```\n\n```yaml\n# Pod using PVC\napiVersion: v1\nkind: Pod\nmetadata:\n  name: storage-pod\nspec:\n  containers:\n  - name: app\n    image: nginx:1.27\n    volumeMounts:\n    - name: data\n      mountPath: /usr/share/nginx/html\n  volumes:\n  - name: data\n    persistentVolumeClaim:\n      claimName: my-pvc\n```\n\n#### 1.2 — Understand Volume Mode, Access Modes, and Reclaim Policies\n\n**Access Modes** — you need to know the four-letter abbreviations:\n\n| Mode | Short | What it actually means |\n|---|---|---|\n| ReadWriteOnce | RWO | One node can mount read-write. This is what you'll use 90% of the time. |\n| ReadOnlyMany | ROX | Many nodes can mount read-only. Rarely comes up on the exam. |\n| ReadWriteMany | RWX | Many nodes can mount read-write. Doesn't work with hostPath — I tried. |\n| ReadWriteOncePod | RWOP | Only one pod can mount read-write. New in v1.29+, might show up. |\n\n**Reclaim Policies** — know the difference or you'll lose data:\n\n| Policy | What actually happens |\n|---|---|\n| Retain | PV survives PVC deletion. Data is safe but you have to manually clean up the PV before it can be reused. |\n| Delete | PV and the underlying storage get nuked. This is the default for most cloud StorageClasses. Be careful. |\n| Recycle | Deprecated. Don't use it, don't memorize it. |\n\n**Volume Modes:**\n- `Filesystem` (default) — mounted as a directory\n- `Block` — raw block device, no filesystem\n\nOn the exam: know the difference between Retain and Delete. If the question says \"data should persist after PVC deletion,\" use Retain.\n\n#### 1.3 — Configure Applications with Persistent Storage\n\nThe pattern is always the same:\n1. Create PV (or let StorageClass provision it dynamically)\n2. Create PVC referencing the StorageClass\n3. Mount PVC in the Pod spec\n\n```yaml\n# StorageClass for dynamic provisioning\napiVersion: storage.k8s.io/v1\nkind: StorageClass\nmetadata:\n  name: fast\nprovisioner: kubernetes.io/no-provisioner\nreclaimPolicy: Retain\nvolumeBindingMode: WaitForFirstConsumer\n```\n\n`WaitForFirstConsumer` delays binding until a pod actually needs the volume. This avoids scheduling issues where the PV is on node A but the pod lands on node B.\n\nCommon gotcha: forgetting `storageClassName`. If the PVC has `storageClassName: \"\"` (empty string), it only binds to PVs with no StorageClass. If you set `storageClassName: manual`, the PV must also have `storageClassName: manual`.\n\n---\n\n### Domain 2 — Troubleshooting (30%)\n\nThis is the biggest domain — 30% of the score. You'll get several questions asking you to fix broken things. This is where I lost the most time in early practice because I had no system. I'd randomly check pods, then nodes, then pods again. Once I built a consistent troubleshooting order (nodes → kubelet → control plane pods → describe → logs → endpoints), my accuracy went way up.\n\n\u003e See also: [Exercise 11 — Troubleshoot Cluster](exercises/11-troubleshoot-cluster/) | [Troubleshooting Decision Flowchart](#troubleshooting-decision-flowchart)\n\n#### 2.1 — Evaluate Cluster and Node Logging\n\nWhere to find logs:\n\n| Component | Log Location |\n|---|---|\n| kubelet | `journalctl -u kubelet` (systemd service) |\n| kube-apiserver | `/var/log/kube-apiserver.log` or `k logs -n kube-system kube-apiserver-\u003cnode\u003e` |\n| kube-scheduler | `k logs -n kube-system kube-scheduler-\u003cnode\u003e` |\n| kube-controller-manager | `k logs -n kube-system kube-controller-manager-\u003cnode\u003e` |\n| etcd | `k logs -n kube-system etcd-\u003cnode\u003e` |\n| Container runtime | `journalctl -u containerd` |\n\nControl plane components run as static pods (in `/etc/kubernetes/manifests/`), so you can check their logs with `k logs`. But kubelet is a systemd service — use `journalctl`.\n\n```bash\n# Check node status\nk get nodes\nk describe node \u003cnode-name\u003e\n\n# Check kubelet on a node\nssh \u003cnode\u003e\nsudo systemctl status kubelet\nsudo journalctl -u kubelet --no-pager | tail -50\n\n# Check control plane pods\nk get pods -n kube-system\n```\n\n#### 2.2 — Monitor Applications\n\nHonestly, monitoring during the exam boils down to two things: `k describe` and `k get events`. I never used `k top` during my actual exam — metrics-server wasn't available on every cluster. But know it exists in case they ask.\n\n```bash\n# These two are 90% of exam monitoring\nk describe pod \u003cpod-name\u003e       # events section at bottom tells you everything\nk get events --sort-by='.lastTimestamp'\n\n# The rest — know them, probably won't need them\nk get pods\nk top pods\nk top nodes\nk get events -n \u003cnamespace\u003e --field-selector reason=Failed\n```\n\n#### 2.3 — Container stdout/stderr Logs\n\nThe two you'll actually use on the exam: `k logs \u003cpod\u003e` and `k logs \u003cpod\u003e --previous`. That's it. The rest are nice-to-know but I never needed `-f` or `--tail` under exam time pressure.\n\n```bash\n# The essentials\nk logs \u003cpod-name\u003e\nk logs \u003cpod-name\u003e --previous                 # crashed container — you'll use this a lot\nk logs \u003cpod-name\u003e -c \u003ccontainer-name\u003e         # multi-container pods\n\n# Rarely needed on the exam but useful\nk logs \u003cpod-name\u003e -f\nk logs \u003cpod-name\u003e --tail=50\nk logs -l app=web --all-containers\n```\n\n#### 2.4 — Troubleshoot Application Failure\n\nThe exam gives you a broken pod and you figure out why. After enough practice, you develop a reflex based on the status:\n\n**Pending** — this is the most common one on the exam. 9 times out of 10 it's one of these:\n- PVC not bound → `k get pvc` — check storageClassName matches\n- Taint with no toleration → `k describe node` and look at Taints\n- No resources available → `k describe pod` events will say \"Insufficient cpu\"\n- Node selector or affinity doesn't match any node → check labels\n- I once spent 5 minutes on a Pending pod that just needed a namespace with a ResourceQuota increased\n\n**CrashLoopBackOff** — the container starts and dies immediately:\n- `k logs \u003cpod\u003e --previous` first. Always. The error is usually obvious.\n- Wrong command/entrypoint is the sneaky one — I got tricked by `[\"sh\", \"-c\"]` vs `[\"sh -c\"]` once\n- Missing config (env vars, configmaps, secrets)\n\n**ImagePullBackOff** — almost always a typo in the image name. Seriously. Check the image string character by character.\n- Private registry without imagePullSecrets is the other cause, but rare on the exam\n\n**Error / Failed:**\n- `k describe pod` events section → `k logs` → you'll find it\n\n```bash\n# Systematic pod debugging\nk get pod \u003cpod\u003e -o wide          # which node? what IP?\nk describe pod \u003cpod\u003e             # events, conditions\nk logs \u003cpod\u003e                     # app logs\nk logs \u003cpod\u003e --previous          # if it crashed\nk exec \u003cpod\u003e -- cat /etc/resolv.conf   # DNS config\nk exec \u003cpod\u003e -- env                     # env vars loaded?\n```\n\n#### 2.5 — Troubleshoot Cluster Component Failure\n\nWhen the whole cluster is broken:\n\n```bash\n# 1. Are nodes ready?\nk get nodes\n\n# 2. Are control plane pods running?\nk get pods -n kube-system\n\n# 3. Is kubelet running on the node?\nssh \u003cnode\u003e\nsudo systemctl status kubelet\nsudo systemctl restart kubelet    # try restarting\n\n# 4. Check kubelet logs\nsudo journalctl -u kubelet --no-pager | tail -50\n\n# 5. Are certificates expired?\nsudo kubeadm certs check-expiration\n\n# 6. Is etcd healthy?\nETCDCTL_API=3 etcdctl endpoint health \\\n  --endpoints=https://127.0.0.1:2379 \\\n  --cacert=/etc/kubernetes/pki/etcd/ca.crt \\\n  --cert=/etc/kubernetes/pki/etcd/server.crt \\\n  --key=/etc/kubernetes/pki/etcd/server.key\n\n# 7. Check static pod manifests\nls /etc/kubernetes/manifests/\n# Should have: etcd.yaml, kube-apiserver.yaml, kube-controller-manager.yaml, kube-scheduler.yaml\n```\n\nCommon causes:\n- kubelet not running → `systemctl start kubelet`\n- Wrong static pod manifest → fix YAML in `/etc/kubernetes/manifests/`\n- Certificates expired → `kubeadm certs renew all`\n- etcd data directory wrong → check `--data-dir` in etcd manifest\n- kube-apiserver flag wrong → check manifest, fix, wait for restart\n\n#### 2.6 — Troubleshoot Networking\n\nService not reachable? Work through this:\n\n```bash\n# 1. Does the service exist and have the right selector?\nk get svc \u003cservice\u003e\nk describe svc \u003cservice\u003e\n\n# 2. Does the service have endpoints?\nk get endpoints \u003cservice\u003e\n# If empty: selector doesn't match any running pod\n\n# 3. Is the pod actually running on the target port?\nk exec \u003cpod\u003e -- wget -qO- localhost:\u003cport\u003e\n\n# 4. DNS working?\nk run test-dns --image=busybox:1.36 --rm -it -- nslookup \u003cservice-name\u003e\n\n# 5. Is kube-proxy running?\nk get pods -n kube-system -l k8s-app=kube-proxy\n\n# 6. NetworkPolicy blocking traffic?\nk get networkpolicy -n \u003cnamespace\u003e\n```\n\nThe most common networking issues on the exam:\n- Service selector doesn't match pod labels (typo in labels)\n- Service targetPort doesn't match container port\n- NetworkPolicy denying traffic (remember: any policy = deny by default for that pod)\n- CoreDNS down or misconfigured\n- kube-proxy not running on a node\n\n---\n\n### Domain 3 — Workloads \u0026 Scheduling (15%)\n\n15% of the score. Deployments, rolling updates, ConfigMaps, Secrets, static pods, scheduling constraints. I found this the most comfortable domain because it's what you do day-to-day. The gotcha: static pods. I kept trying to delete them with kubectl and wondering why they came back. Once you understand kubelet manages them directly, it clicks.\n\n\u003e See also: [Exercise 01 — Pod Basics](exercises/01-pod-basics/) | [Exercise 06 — Deployment Rollout](exercises/06-deployment-rollout/) | [Exercise 10 — Static Pod](exercises/10-static-pod/)\n\n#### 3.1 — Understand Deployments and How to Perform Rolling Updates and Rollbacks\n\nDeployments manage ReplicaSets, which manage Pods. When you update the image, Kubernetes creates a new ReplicaSet and gradually shifts pods over. The thing that tripped me up: the container name in `k set image` is the container name from the pod spec, not the deployment name. I kept writing `k set image deployment/webapp webapp=nginx:1.27` when the container was actually called `nginx`. Wasted 3 minutes every time.\n\n```bash\n# Create\nk create deployment webapp --image=nginx:1.26 --replicas=3\n\n# Update image (triggers rolling update)\nk set image deployment/webapp nginx=nginx:1.27\n\n# Watch the rollout\nk rollout status deployment/webapp\n\n# Check history\nk rollout history deployment/webapp\n\n# Rollback to previous\nk rollout undo deployment/webapp\n\n# Rollback to specific revision\nk rollout undo deployment/webapp --to-revision=2\n```\n\nRolling update strategy options:\n\n```yaml\nspec:\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 1        # max pods above desired count during update\n      maxUnavailable: 0   # max pods that can be unavailable during update\n```\n\n- `maxSurge: 1, maxUnavailable: 0` = zero-downtime (one extra pod at a time)\n- `maxSurge: 0, maxUnavailable: 1` = no extra pods, one goes down at a time\n- `Recreate` strategy = kill all old pods first, then create new ones (causes downtime)\n\n#### 3.2 — Use ConfigMaps and Secrets to Configure Applications\n\nConfigMaps hold non-sensitive config. Secrets hold sensitive data (base64-encoded, not encrypted by default).\n\n```bash\n# Create ConfigMap\nk create configmap app-config \\\n  --from-literal=APP_MODE=production \\\n  --from-literal=LOG_LEVEL=info\n\n# Create Secret\nk create secret generic db-creds \\\n  --from-literal=DB_USER=admin \\\n  --from-literal=DB_PASS=changeme\n\n# From file\nk create configmap nginx-conf --from-file=nginx.conf\n```\n\nThree ways to inject into a pod:\n\n**1. Environment variables (all keys):**\n```yaml\nenvFrom:\n- configMapRef:\n    name: app-config\n- secretRef:\n    name: db-creds\n```\n\n**2. Single key as env var:**\n```yaml\nenv:\n- name: DATABASE_USER\n  valueFrom:\n    secretKeyRef:\n      name: db-creds\n      key: DB_USER\n```\n\n**3. Mounted as files:**\n```yaml\nvolumeMounts:\n- name: config-vol\n  mountPath: /etc/config\nvolumes:\n- name: config-vol\n  configMap:\n    name: app-config\n```\n\nGotcha: if you mount a ConfigMap as a volume at a directory, it replaces the entire directory. Use `subPath` to mount a single file without replacing the directory.\n\n#### 3.3 — Know How to Scale Applications\n\n```bash\n# Manual scaling\nk scale deployment webapp --replicas=5\n\n# Autoscaling (HPA — not heavily tested on CKA but know it exists)\nk autoscale deployment webapp --min=2 --max=10 --cpu-percent=80\n```\n\n#### 3.4 — Self-Healing Workloads: Deployments, DaemonSets, StatefulSets\n\nThe one you'll actually use on the exam is **Deployment**. It manages ReplicaSets, which manage Pods. You create Deployments, you scale them, you update them, you roll them back. That's 90% of this topic.\n\n- **ReplicaSet**: Keeps N pods running. You almost never create these directly — Deployments create them for you.\n- **Deployment**: This is the workhorse. Rolling updates, rollbacks, scaling. Know this cold.\n- **DaemonSet**: One pod per node. Logging agents, monitoring. Comes up occasionally on the exam. The YAML is basically a Deployment without `replicas`.\n- **StatefulSet**: Stable network identity and persistent storage. Barely on the CKA — know it exists, maybe know the headless service pattern, don't spend hours on it.\n\n```yaml\n# DaemonSet — runs on every node\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: log-agent\nspec:\n  selector:\n    matchLabels:\n      app: log-agent\n  template:\n    metadata:\n      labels:\n        app: log-agent\n    spec:\n      tolerations:\n      - key: node-role.kubernetes.io/control-plane\n        operator: Exists\n        effect: NoSchedule\n      containers:\n      - name: agent\n        image: fluentd:v1.17\n        volumeMounts:\n        - name: varlog\n          mountPath: /var/log\n      volumes:\n      - name: varlog\n        hostPath:\n          path: /var/log\n```\n\n#### 3.5 — Understand How Resource Limits Can Affect Pod Scheduling\n\n```yaml\nresources:\n  requests:\n    memory: \"64Mi\"    # scheduler uses this to find a node\n    cpu: \"250m\"       # 250 millicores = 0.25 CPU\n  limits:\n    memory: \"128Mi\"   # OOMKilled if exceeded\n    cpu: \"500m\"       # throttled if exceeded\n```\n\n- **Requests** = what the scheduler looks at when placing the pod. If no node has enough, pod stays Pending.\n- **Limits** = ceiling. Memory over limit = OOMKilled. CPU over limit = throttled.\n- If you set limits without requests, requests default to limits.\n- LimitRange sets defaults and constraints for a namespace. ResourceQuota caps total usage.\n\n#### 3.6 — Awareness of Manifest Management and Common Templating Tools\n\nOn the CKA, you mostly write raw YAML. But know these exist:\n- **Kustomize**: `k apply -k \u003cdir\u003e` — built into kubectl, overlays and patches\n- **Helm**: package manager for Kubernetes — CKA may ask you to install a chart\n- **kubectl $do**: generate YAML with `--dry-run=client -o yaml` and edit it\n\n#### 3.7 — Schedule Pods on Specific Nodes\n\n**nodeSelector** (simplest):\n```yaml\nspec:\n  nodeSelector:\n    disk: ssd\n```\n\n**Node affinity** (more flexible):\n```yaml\nspec:\n  affinity:\n    nodeAffinity:\n      requiredDuringSchedulingIgnoredDuringExecution:\n        nodeSelectorTerms:\n        - matchExpressions:\n          - key: disk\n            operator: In\n            values:\n            - ssd\n```\n\n**Taints and tolerations:**\n\nTaints go on nodes. Tolerations go on pods.\n\n```bash\n# Taint a node\nk taint nodes node1 gpu=true:NoSchedule\n\n# Remove taint\nk taint nodes node1 gpu=true:NoSchedule-\n```\n\n```yaml\n# Pod toleration\nspec:\n  tolerations:\n  - key: \"gpu\"\n    operator: \"Equal\"\n    value: \"true\"\n    effect: \"NoSchedule\"\n```\n\nEffects:\n- `NoSchedule` — don't schedule new pods (existing stay)\n- `PreferNoSchedule` — try to avoid, but not strict\n- `NoExecute` — evict existing pods too\n\n#### 3.8 — Static Pods\n\nStatic pods are managed by the kubelet directly, not the API server. The kubelet watches a directory (usually `/etc/kubernetes/manifests/`) and creates pods from any YAML files it finds there.\n\n```bash\n# Find the static pod path\ncat /var/lib/kubelet/config.yaml | grep staticPodPath\n# Usually: /etc/kubernetes/manifests\n\n# Create a static pod\nsudo tee /etc/kubernetes/manifests/static-web.yaml \u003c\u003cEOF\napiVersion: v1\nkind: Pod\nmetadata:\n  name: static-web\nspec:\n  containers:\n  - name: web\n    image: nginx:1.27\n    ports:\n    - containerPort: 80\nEOF\n```\n\nStatic pods show up in `kubectl get pods` with the node name appended (e.g., `static-web-node1`). You can't delete them via kubectl — the kubelet recreates them. To remove: delete the manifest file.\n\nControl plane components (kube-apiserver, kube-scheduler, kube-controller-manager, etcd) are all static pods.\n\n---\n\n### Domain 4 — Cluster Architecture, Installation \u0026 Configuration (25%)\n\n25% of the score. This is the domain that separates CKA from CKAD — etcd, kubeadm, RBAC. If you're coming from CKAD, this is all new and it's where I spent the most study time. etcd backup/restore alone took me a week to get reliable. The `--as=system:serviceaccount:ns:name` syntax for testing RBAC was another thing I had to drill until it was automatic.\n\n\u003e See also: [Exercise 04 — RBAC](exercises/04-rbac/) | [Exercise 09 — kubeadm Upgrade](exercises/09-kubeadm-upgrade/) | [Exercise 18 — CRI-dockerd Setup](exercises/18-cri-dockerd-setup/)\n\n#### 4.1 — Manage Role-Based Access Control (RBAC)\n\nRBAC has four objects:\n\n| Object | Scope | Binds to |\n|---|---|---|\n| Role | Namespace | RoleBinding |\n| ClusterRole | Cluster-wide | ClusterRoleBinding or RoleBinding |\n| RoleBinding | Namespace | Role or ClusterRole |\n| ClusterRoleBinding | Cluster-wide | ClusterRole |\n\n```bash\n# Create Role (namespace-scoped permissions)\nk create role pod-reader \\\n  --verb=get,list,watch \\\n  --resource=pods \\\n  -n dev\n\n# Create RoleBinding\nk create rolebinding read-pods \\\n  --role=pod-reader \\\n  --serviceaccount=dev:my-sa \\\n  -n dev\n\n# Create ClusterRole (cluster-wide permissions)\nk create clusterrole node-reader \\\n  --verb=get,list \\\n  --resource=nodes\n\n# Create ClusterRoleBinding\nk create clusterrolebinding read-nodes \\\n  --clusterrole=node-reader \\\n  --serviceaccount=dev:my-sa\n\n# Test permissions\nk auth can-i list pods -n dev --as=system:serviceaccount:dev:my-sa\nk auth can-i list nodes --as=system:serviceaccount:dev:my-sa\n```\n\nTricky bit: a ClusterRole bound with a RoleBinding only grants access in that namespace. A ClusterRole bound with a ClusterRoleBinding grants access cluster-wide. Same ClusterRole, different scope depending on the binding type.\n\n#### 4.2 — Use Kubeadm to Install a Basic Cluster\n\nThe kubeadm workflow:\n\n```bash\n# On control plane node:\nsudo kubeadm init --pod-network-cidr=10.244.0.0/16\n\n# Set up kubeconfig\nmkdir -p $HOME/.kube\nsudo cp /etc/kubernetes/admin.conf $HOME/.kube/config\nsudo chown $(id -u):$(id -g) $HOME/.kube/config\n\n# Install CNI (e.g., Calico)\nk apply -f https://docs.projectcalico.org/manifests/calico.yaml\n\n# On worker nodes:\nsudo kubeadm join \u003ccontrol-plane-ip\u003e:6443 --token \u003ctoken\u003e --discovery-token-ca-cert-hash sha256:\u003chash\u003e\n```\n\nIf you lost the join command:\n```bash\nkubeadm token create --print-join-command\n```\n\n#### 4.3 — Manage a Highly Available Kubernetes Cluster\n\nYou won't set up HA from scratch on the exam, but they want you to understand the two topologies:\n\n- **Stacked etcd**: etcd on the same nodes as the control plane. This is what everyone uses. Simpler, good enough for most setups.\n- **External etcd**: etcd on dedicated nodes. I've never set this up. I doubt you will either. Just know it exists and that it's \"more resilient\" because etcd failures don't take down the control plane node.\n- Multiple control plane nodes behind a load balancer — the LB endpoint goes in `kubeadm init --control-plane-endpoint=\u003clb\u003e:6443`\n\nI spent maybe 10 minutes on this topic. Read it, understood the difference, moved on. It wasn't worth more time than that.\n\n#### 4.4 — Provision Underlying Infrastructure to Deploy a Kubernetes Cluster\n\nkubeadm handles most of this. You won't provision VMs on the exam, but you might need to fix a node that was set up wrong. The things that break:\n- containerd not installed or not running — `systemctl status containerd`\n- Swap still enabled — `swapoff -a` (I always forget this on fresh VMs)\n- Missing kernel modules: `br_netfilter` and `overlay`. Load them with `modprobe`.\n- sysctl params — `net.bridge.bridge-nf-call-iptables = 1`. I can never remember the exact param name, I just grep the docs page.\n\n#### 4.5 — Perform a Version Upgrade on a Kubernetes Cluster Using Kubeadm\n\nThis is a classic CKA question. The sequence matters:\n\n**Control plane node:**\n```bash\n# 1. Update kubeadm\nsudo apt-mark unhold kubeadm\nsudo apt-get update \u0026\u0026 sudo apt-get install -y kubeadm=1.35.0-1.1\nsudo apt-mark hold kubeadm\n\n# 2. Plan\nsudo kubeadm upgrade plan\n\n# 3. Apply\nsudo kubeadm upgrade apply v1.35.0\n\n# 4. Update kubelet + kubectl\nsudo apt-mark unhold kubelet kubectl\nsudo apt-get install -y kubelet=1.35.0-1.1 kubectl=1.35.0-1.1\nsudo apt-mark hold kubelet kubectl\n\n# 5. Restart kubelet\nsudo systemctl daemon-reload\nsudo systemctl restart kubelet\n```\n\n**Worker node:**\n```bash\n# 1. From control plane: drain the worker\nk drain worker-1 --ignore-daemonsets --delete-emptydir-data\n\n# 2. SSH to worker, update packages\nsudo apt-mark unhold kubeadm kubelet kubectl\nsudo apt-get update\nsudo apt-get install -y kubeadm=1.35.0-1.1 kubelet=1.35.0-1.1 kubectl=1.35.0-1.1\nsudo apt-mark hold kubeadm kubelet kubectl\n\n# 3. Upgrade node\nsudo kubeadm upgrade node\n\n# 4. Restart kubelet\nsudo systemctl daemon-reload\nsudo systemctl restart kubelet\n\n# 5. From control plane: uncordon\nk uncordon worker-1\n```\n\nKey difference: control plane uses `kubeadm upgrade apply`, worker uses `kubeadm upgrade node`.\n\n#### 4.6 — Implement etcd Backup and Restore\n\nThis shows up on almost every CKA exam. Memorize this.\n\n**Backup:**\n```bash\nETCDCTL_API=3 etcdctl snapshot save /tmp/etcd-backup.db \\\n  --endpoints=https://127.0.0.1:2379 \\\n  --cacert=/etc/kubernetes/pki/etcd/ca.crt \\\n  --cert=/etc/kubernetes/pki/etcd/server.crt \\\n  --key=/etc/kubernetes/pki/etcd/server.key\n```\n\nWhere to find the cert paths: `cat /etc/kubernetes/manifests/etcd.yaml` and look for `--cert-file`, `--key-file`, `--trusted-ca-file`.\n\n**Verify:**\n```bash\nETCDCTL_API=3 etcdctl snapshot status /tmp/etcd-backup.db --write-table\n```\n\n**Restore:**\n```bash\n# 1. Restore to a new directory\nETCDCTL_API=3 etcdctl snapshot restore /tmp/etcd-backup.db \\\n  --data-dir=/var/lib/etcd-restored\n\n# 2. Update etcd manifest to use the restored directory\nsudo vi /etc/kubernetes/manifests/etcd.yaml\n# Change --data-dir=/var/lib/etcd → --data-dir=/var/lib/etcd-restored\n# Change hostPath path: /var/lib/etcd → /var/lib/etcd-restored\n\n# 3. Wait for etcd to restart (it's a static pod)\n# kubectl may be unresponsive for 30-60 seconds — that's normal\n```\n\nThe three flags you need every time: `--cacert`, `--cert`, `--key`. I wrote them on the notepad in the exam environment before starting.\n\n---\n\n### Domain 5 — Services \u0026 Networking (20%)\n\n20% of the score. Services, Ingress, Gateway API, NetworkPolicy, DNS. NetworkPolicy is where I lost the most points in practice exams — the AND vs OR selector behavior is unintuitive, and forgetting DNS egress is a silent killer.\n\n\u003e See also: [Exercise 05 — NetworkPolicy](exercises/05-networkpolicy/) | Skeletons: [service.yaml](skeletons/service.yaml), [ingress.yaml](skeletons/ingress.yaml), [networkpolicy.yaml](skeletons/networkpolicy.yaml)\n\n#### 5.1 — Understand Host Networking Configuration on the Cluster Nodes\n\nNetworking in Kubernetes \"just works\" if your CNI is installed correctly. Don't overthink the model — pods get IPs, pods can talk to each other, nodes can talk to pods. The CNI plugin (Calico, Flannel, Cilium) makes it happen. That's really all you need to know conceptually.\n\nWhat actually matters for the exam: knowing where to look when it doesn't work.\n\n```bash\n# Check what CNI is installed\nls /etc/cni/net.d/\ncat /etc/cni/net.d/10-calico.conflist\n\n# Check pod CIDR\nk cluster-info dump | grep -m 1 cluster-cidr\n```\n\n#### 5.2 — Understand Connectivity Between Pods\n\nSame-node pods talk through a bridge, cross-node pods go through the CNI overlay. You don't need to know the internals — but you need to test connectivity when something breaks.\n\n```bash\n# Test pod-to-pod connectivity\nk exec pod-a -- wget -qO- --timeout=2 http://\u003cpod-b-ip\u003e\n\n# Check pod IPs\nk get pods -o wide\n```\n\n#### 5.3 — Understand ClusterIP, NodePort, LoadBalancer Service Types\n\n| Type | How it works | When to use |\n|---|---|---|\n| **ClusterIP** | Internal cluster IP only | Internal services (default) |\n| **NodePort** | ClusterIP + port on every node (30000-32767) | Dev/testing, direct node access |\n| **LoadBalancer** | NodePort + cloud LB | Production with cloud provider |\n| **ExternalName** | CNAME to external DNS | Pointing to external services |\n\n```bash\n# ClusterIP (default)\nk expose deployment webapp --port=80 --target-port=8080\n\n# NodePort (random port in 30000-32767)\nk expose deployment webapp --port=80 --target-port=8080 --type=NodePort\n\n# Specific NodePort — generate YAML, edit nodePort, then apply\nk expose deployment webapp --port=80 --target-port=8080 --type=NodePort $do \u003e svc.yaml\n# edit svc.yaml → set spec.ports[0].nodePort: 30080\nk apply -f svc.yaml\n```\n\nThe most important thing: `port` is what clients use to reach the service. `targetPort` is the port the container listens on. `nodePort` is the port on the node (NodePort/LoadBalancer only).\n\n#### 5.4 — Understand How to Use Ingress Controllers and Ingress Resources\n\nIngress gives you HTTP/HTTPS routing to services based on hostname or path.\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: my-ingress\n  annotations:\n    nginx.ingress.kubernetes.io/rewrite-target: /\nspec:\n  ingressClassName: nginx\n  rules:\n  - host: myapp.example.com\n    http:\n      paths:\n      - path: /\n        pathType: Prefix\n        backend:\n          service:\n            name: my-service\n            port:\n              number: 80\n```\n\nRequirements:\n- An Ingress Controller must be installed (e.g., nginx-ingress). The Ingress resource alone does nothing.\n- `ingressClassName` is required in v1.35 — the old annotation `kubernetes.io/ingress.class` still works but is deprecated.\n\n#### 5.5 — Know How to Use and Configure CoreDNS\n\nCoreDNS is the cluster DNS server (replaced kube-dns a long time ago). It runs as a Deployment in `kube-system`.\n\n```bash\n# Check CoreDNS pods\nk get pods -n kube-system -l k8s-app=kube-dns\n\n# Check CoreDNS config\nk get configmap coredns -n kube-system -o yaml\n\n# Test DNS resolution\nk run test-dns --image=busybox:1.36 --rm -it -- nslookup kubernetes.default.svc.cluster.local\n```\n\nDNS naming:\n- Service: `\u003cservice\u003e.\u003cnamespace\u003e.svc.cluster.local`\n- Pod: `\u003cpod-ip-dashed\u003e.\u003cnamespace\u003e.pod.cluster.local`\n\nIf DNS doesn't work:\n1. Is CoreDNS running? `k get pods -n kube-system -l k8s-app=kube-dns`\n2. Does kube-dns service have endpoints? `k get endpoints kube-dns -n kube-system`\n3. Is the Corefile correct? `k get cm coredns -n kube-system -o yaml`\n4. Can the pod reach the DNS service? `k exec \u003cpod\u003e -- cat /etc/resolv.conf`\n\n#### 5.6 — Choose an Appropriate Container Network Interface Plugin\n\nThe CKA won't ask you to write a CNI plugin. Just use Calico. It supports NetworkPolicy, it's the most common, and it's what most training platforms use. The exam doesn't care which CNI is installed.\n\nOther CNIs exist — Flannel is simpler but doesn't support NetworkPolicy (dealbreaker for the exam), Cilium is the fancy eBPF one everyone's talking about, Weave still works. But if a question says \"install a CNI plugin,\" just apply the Calico manifest and move on.\n\nInstall is one `kubectl apply -f \u003curl\u003e`. The CNI must be installed before worker nodes join — pods stay Pending without it.\n\n#### 5.7 — Understand NetworkPolicy\n\nNetworkPolicy controls pod-to-pod traffic. Once you apply any NetworkPolicy to a pod, all traffic not explicitly allowed is denied for that pod.\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: api-policy\n  namespace: production\nspec:\n  podSelector:\n    matchLabels:\n      app: api\n  policyTypes:\n  - Ingress\n  - Egress\n  ingress:\n  - from:\n    - podSelector:\n        matchLabels:\n          app: frontend\n    ports:\n    - protocol: TCP\n      port: 8080\n  egress:\n  - to:\n    - podSelector:\n        matchLabels:\n          app: database\n    ports:\n    - protocol: TCP\n      port: 5432\n  # Always allow DNS\n  - to: []\n    ports:\n    - protocol: UDP\n      port: 53\n```\n\nCritical gotcha on the exam: if you add an Egress policy, you must also allow DNS (UDP 53). Otherwise the pod can't resolve any service names and everything looks broken even though the policy is \"correct.\"\n\nAnother gotcha: `from` with multiple selectors in one rule = AND. Multiple rules = OR.\n\n```yaml\n# AND — must match BOTH namespace AND pod label\ningress:\n- from:\n  - namespaceSelector:\n      matchLabels:\n        env: prod\n    podSelector:\n      matchLabels:\n        app: frontend\n\n# OR — matches namespace OR pod label\ningress:\n- from:\n  - namespaceSelector:\n      matchLabels:\n        env: prod\n- from:\n  - podSelector:\n      matchLabels:\n        app: frontend\n```\n\nThis difference tripped me up during practice. Read the indentation carefully.\n\n#### 5.8 — Gateway API (v1.35)\n\nGateway API is the successor to Ingress. It's GA in v1.35 and may appear on the CKA.\n\n```yaml\napiVersion: gateway.networking.k8s.io/v1\nkind: Gateway\nmetadata:\n  name: my-gateway\nspec:\n  gatewayClassName: istio\n  listeners:\n  - name: http\n    protocol: HTTP\n    port: 80\n    allowedRoutes:\n      namespaces:\n        from: Same\n---\napiVersion: gateway.networking.k8s.io/v1\nkind: HTTPRoute\nmetadata:\n  name: my-route\nspec:\n  parentRefs:\n  - name: my-gateway\n  hostnames:\n  - \"myapp.example.com\"\n  rules:\n  - matches:\n    - path:\n        type: PathPrefix\n        value: /api\n    backendRefs:\n    - name: api-service\n      port: 80\n```\n\nKey differences from Ingress:\n- Gateway = infrastructure resource (managed by platform team)\n- HTTPRoute = application routing (managed by app team)\n- More features: header matching, traffic splitting, request mirroring\n- Supports TCP, UDP, gRPC — not just HTTP\n\n---\n\n## CKA Domain Weight Distribution\n\n```mermaid\npie title CKA Exam Domain Weights\n    \"Troubleshooting (30%)\" : 30\n    \"Cluster Architecture (25%)\" : 25\n    \"Services \u0026 Networking (20%)\" : 20\n    \"Workloads \u0026 Scheduling (15%)\" : 15\n    \"Storage (10%)\" : 10\n```\n\nWhere to focus: Troubleshooting + Cluster Architecture = 55% of the exam. If you nail these two, you only need a few more points to pass.\n\n---\n\n## Exam Day Strategy — Time Allocation\n\nI used a two-pass approach in practice and it changed everything. Before this, I'd get stuck on a hard question for 12 minutes and run out of time for easy ones worth the same points.\n\n**Pass 1 (first 80 minutes):** Do all questions in order. If a question looks like it'll take more than 8 minutes, flag it and move on. Don't get emotionally invested in any single question.\n\n**Pass 2 (last 40 minutes):** Go back to flagged questions. You now know exactly how much time you have. The pressure feels different when you've already banked easy points.\n\nTime estimates by question type:\n\n| Question Type | Typical Time | Notes |\n|---|---|---|\n| Create a pod/deployment | 2-3 min | Use `$do` to generate YAML |\n| Create RBAC resources | 3-5 min | Know the imperative commands |\n| NetworkPolicy | 5-8 min | Always allow DNS egress |\n| etcd backup/restore | 8-10 min | Know the cert paths |\n| kubeadm upgrade | 8-10 min | Follow the sequence exactly |\n| Troubleshoot broken node | 5-8 min | Check kubelet first |\n| Troubleshoot networking | 5-8 min | Check endpoints, selectors |\n| PV/PVC/StorageClass | 4-6 min | Match accessModes and storageClassName |\n| DaemonSet | 3-4 min | Copy from docs fast |\n| Ingress/Gateway | 4-6 min | Know ingressClassName |\n| Static pod | 3-4 min | Write manifest directly to /etc/kubernetes/manifests/ |\n| Node drain/cordon | 2-3 min | --ignore-daemonsets --delete-emptydir-data |\n\nTotal available: 120 minutes. Budget ~100 minutes for questions, 20 minutes buffer for context switching, copy/paste fumbling, and double-checking.\n\n[Back to top](#table-of-contents)\n\n---\n\n## Mistakes That Will Fail You on the CKA\n\nEvery single one of these cost me points during practice exams. I'm not listing hypothetical risks — these are things I actually did wrong and had to learn from.\n\n### 1. Forgetting to switch context\n\nEvery question says \"use context k8s-xxx.\" I missed this twice during one practice exam — answered two questions perfectly on the wrong cluster. Zero points for both. Now I read the context line first, switch, then read the actual question.\n\n```bash\n# ALWAYS do this first\nk config use-context \u003ccontext-name\u003e\n```\n\n### 2. Wrong namespace\n\nI created a perfect Deployment in `default` when the question said `production`. The YAML was right, the containers were right, everything worked — but the grader checks the namespace. Zero points. Now I run `kn \u003cnamespace\u003e` as the FIRST command for every question.\n\n```bash\n# Set namespace for the question\nkn \u003cnamespace\u003e\n# Or use -n on every command\nk get pods -n production\n```\n\n### 3. YAML indentation errors\n\nI had one tab character hidden in a YAML file. `kubectl apply` gave a cryptic parsing error and I spent 4 minutes hunting for the problem. Set up vim with `expandtab` so tabs become spaces. One wrong indent level = broken YAML = zero points.\n\n```bash\n# This should already be in your .vimrc\nset expandtab\nset tabstop=2\nset shiftwidth=2\n```\n\n### 4. etcd restore — forgetting to update hostPath\n\nThis one burned me on my second practice exam. I restored etcd to `/var/lib/etcd-restored` and updated the `--data-dir` flag. Cluster came back but all my previous resources were gone. Turns out the `hostPath.path` in the volume section was still pointing to the old `/var/lib/etcd`. The etcd process and the volume mount have to agree, or you're reading from the wrong directory.\n\n```yaml\n# Must update BOTH:\n# 1. --data-dir=/var/lib/etcd-restored\n# 2. volumes[].hostPath.path: /var/lib/etcd-restored\n```\n\n### 5. Drain without --ignore-daemonsets\n\n`k drain` fails if DaemonSet pods exist and you don't pass the flag. Don't waste time reading the error — just always use:\n\n```bash\nk drain \u003cnode\u003e --ignore-daemonsets --delete-emptydir-data\n```\n\n### 6. NetworkPolicy without DNS egress\n\nI have made this mistake more times than I want to admit. You write what looks like a perfect NetworkPolicy egress rule, test connectivity, and it fails. You rewrite the rule. Still fails. You check selectors. Still right. The pod can't resolve service names because you didn't allow UDP 53. I now write the DNS egress block FIRST before writing any other egress rules.\n\n```yaml\n# Always include this in egress rules\n- to: []\n  ports:\n  - protocol: UDP\n    port: 53\n```\n\n### 7. Not verifying your work\n\nI finished a question early once and moved on feeling confident. Turns out the pod was in `ImagePullBackOff` because I had a typo in the image name. Would have caught it in 5 seconds if I'd checked `k get pod`. Now I verify every single resource I create before moving on.\n\n```bash\nk get pod \u003cname\u003e -n \u003cns\u003e        # Is it Running?\nk get svc \u003cname\u003e -n \u003cns\u003e        # Does service exist?\nk get endpoints \u003cname\u003e -n \u003cns\u003e  # Does service have endpoints?\n```\n\n### 8. Wasting time on hard questions first\n\nA 3-point question and a 7-point question get the same time if you're stuck. Do the easy ones first.\n\n### 9. Forgetting the ServiceAccount in RBAC\n\nThe question says \"create a ServiceAccount and give it access.\" I jumped straight to creating the Role and RoleBinding, then ran `k auth can-i` and it returned \"no\" for everything. Spent 6 minutes thinking my Role was wrong before realizing the ServiceAccount didn't exist. The RoleBinding referenced a SA that wasn't there. Create the SA first.\n\n```bash\n# Create SA first\nk create sa my-sa -n my-ns\n\n# Then bind it\nk create rolebinding my-binding \\\n  --role=my-role \\\n  --serviceaccount=my-ns:my-sa \\\n  -n my-ns\n```\n\n---\n\n## Vim Keys I Actually Used on Exam Day\n\nNot a vim guide, just the handful I kept hitting. If you already know vim, skip this.\n\nVim has two modes that matter here: **insert mode** (where you type text like a normal editor) and **normal mode** (where keys are commands). You start in normal mode. Press `i` to enter insert mode, press `Esc` to get back to normal mode. Almost every shortcut below is a normal mode command, so hit `Esc` first if you are not sure where you are.\n\n**Opening and saving**\n\n- `vim \u003cfile\u003e` to open the file. You start in normal mode.\n- `i` (normal mode) to enter insert mode and start typing.\n- `Esc` to leave insert mode and go back to normal mode.\n- `:wq` (normal mode) to save and quit. `:q!` to bail without saving.\n\n**Moving around (normal mode)**\n\n- `A` jumps to the end of the current line and drops you into insert mode. Handy for adding to an existing line without arrow-keying across.\n- `$` moves the cursor to the end of the line without entering insert mode.\n- `0` moves to the start of the line.\n- `gg` jumps to the top of the file, `G` jumps to the bottom.\n- `H` top of the visible screen, `M` middle, `L` bottom.\n- `/word` then `Enter` to search, `n` for the next match.\n\n**Editing (normal mode)**\n\n- `\u003cnumber\u003edd` deletes that many lines. `5dd` wipes 5 lines at once. I used this constantly to clean up the junk that `kubectl ... --dry-run=client -o yaml` adds (status blocks, creationTimestamp, empty resources).\n- `dd` on its own deletes the current line.\n- `u` to undo.\n\nThat is all I needed. Anything fancier I would look up, but on exam day I never had to.\n\n[Back to top](#table-of-contents)\n\n\n---\n\n## Troubleshooting Decision Flowchart\n\nUse this when something is broken and you don't know where to start.\n\n```mermaid\nflowchart TD\n    START[Something is broken] --\u003e NODES{Are all nodes Ready?}\n    \n    NODES --\u003e|No| NODE_DEBUG[Node is NotReady]\n    NODE_DEBUG --\u003e KUBELET{Is kubelet running?}\n    KUBELET --\u003e|No| KUBELET_FIX[systemctl start kubelet\u003cbr/\u003eCheck journalctl -u kubelet]\n    KUBELET --\u003e|Yes| CERTS{Are certs expired?}\n    CERTS --\u003e|Yes| CERT_FIX[kubeadm certs renew all]\n    CERTS --\u003e|No| NET{Node networking OK?}\n    NET --\u003e|No| NET_FIX[Check CNI plugin\u003cbr/\u003eCheck kube-proxy]\n    \n    NODES --\u003e|Yes| PODS{Is the pod running?}\n    \n    PODS --\u003e|Pending| PENDING[Check describe pod events]\n    PENDING --\u003e PEND_RES{Resource issue?}\n    PEND_RES --\u003e|Yes| RES_FIX[Scale down or add nodes]\n    PEND_RES --\u003e|No| PEND_SCHED{Scheduling issue?}\n    PEND_SCHED --\u003e|Taint| TAINT_FIX[Add toleration or remove taint]\n    PEND_SCHED --\u003e|Selector| SEL_FIX[Fix nodeSelector or affinity]\n    PEND_SCHED --\u003e|PVC| PVC_FIX[Fix PVC — check storageClass, accessMode]\n    \n    PODS --\u003e|CrashLoop| CRASH[Check logs --previous]\n    CRASH --\u003e CRASH_CMD{Wrong command?}\n    CRASH_CMD --\u003e|Yes| CMD_FIX[Fix command/args]\n    CRASH_CMD --\u003e|No| CRASH_CFG{Missing config?}\n    CRASH_CFG --\u003e|Yes| CFG_FIX[Fix ConfigMap/Secret/env]\n    CRASH_CFG --\u003e|No| APP_FIX[Application bug — check logs]\n    \n    PODS --\u003e|ImagePull| IMAGE[Check image name/tag]\n    IMAGE --\u003e IMG_SECRET{Private registry?}\n    IMG_SECRET --\u003e|Yes| SECRET_FIX[Add imagePullSecrets]\n    IMG_SECRET --\u003e|No| IMG_FIX[Fix image name or check network]\n    \n    PODS --\u003e|Running| SVC{Can you reach the service?}\n    SVC --\u003e|No| EP{Service has endpoints?}\n    EP --\u003e|No| LABEL_FIX[Fix selector — labels don't match]\n    EP --\u003e|Yes| PORT{targetPort matches container?}\n    PORT --\u003e|No| PORT_FIX[Fix targetPort]\n    PORT --\u003e|Yes| DNS{DNS resolving?}\n    DNS --\u003e|No| DNS_FIX[Check CoreDNS pods + configmap]\n    DNS --\u003e|Yes| NETPOL{NetworkPolicy blocking?}\n    NETPOL --\u003e|Yes| NP_FIX[Fix NetworkPolicy rules]\n    \n    PODS --\u003e|Running| ETCD{etcd healthy?}\n    ETCD --\u003e|No| ETCD_FIX[Check etcd pod\u003cbr/\u003eRestore from backup if needed]\n```\n\n---\n\n## Practice Scenarios with Full Solutions\n\nThese are longer, multi-step scenarios that mimic real exam questions.\n\n### Scenario 1: etcd Backup and Restore\n\n**Task:** Back up etcd to `/opt/etcd-backup.db`, then restore it to verify the backup works.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# Find cert paths\ngrep -E \"cert-file|key-file|trusted-ca-file\" /etc/kubernetes/manifests/etcd.yaml\n\n# Backup\nETCDCTL_API=3 etcdctl snapshot save /opt/etcd-backup.db \\\n  --endpoints=https://127.0.0.1:2379 \\\n  --cacert=/etc/kubernetes/pki/etcd/ca.crt \\\n  --cert=/etc/kubernetes/pki/etcd/server.crt \\\n  --key=/etc/kubernetes/pki/etcd/server.key\n\n# Verify\nETCDCTL_API=3 etcdctl snapshot status /opt/etcd-backup.db --write-table\n\n# Restore\nETCDCTL_API=3 etcdctl snapshot restore /opt/etcd-backup.db \\\n  --data-dir=/var/lib/etcd-restored\n\n# Update etcd manifest\nsudo sed -i 's|/var/lib/etcd|/var/lib/etcd-restored|g' /etc/kubernetes/manifests/etcd.yaml\n\n# Wait for etcd to restart\nsleep 30\nk get nodes\n```\n\n\u003c/details\u003e\n\n### Scenario 2: RBAC — ServiceAccount with Limited Access\n\n**Task:** In namespace `dev`, create a ServiceAccount `deploy-bot` that can only create and list Deployments. Verify it cannot delete pods.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\nk create ns dev\nk create sa deploy-bot -n dev\nk create role deploy-manager -n dev \\\n  --verb=create,list,get \\\n  --resource=deployments\nk create rolebinding deploy-bot-binding -n dev \\\n  --role=deploy-manager \\\n  --serviceaccount=dev:deploy-bot\n\n# Verify\nk auth can-i create deployments -n dev --as=system:serviceaccount:dev:deploy-bot\n# yes\nk auth can-i delete pods -n dev --as=system:serviceaccount:dev:deploy-bot\n# no\n```\n\n\u003c/details\u003e\n\n### Scenario 3: Node Drain and Maintenance\n\n**Task:** Drain node `worker-2` for maintenance, verify pods are rescheduled, then bring it back.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# Check current state\nk get pods -o wide | grep worker-2\n\n# Drain\nk drain worker-2 --ignore-daemonsets --delete-emptydir-data\n\n# Verify node is cordoned\nk get nodes\n# worker-2 should show SchedulingDisabled\n\n# Verify pods moved\nk get pods -o wide\n# No non-DaemonSet pods on worker-2\n\n# Simulate maintenance (wait)\n# ...\n\n# Bring back\nk uncordon worker-2\nk get nodes\n# worker-2 should be Ready\n```\n\n\u003c/details\u003e\n\n### Scenario 4: kubeadm Upgrade\n\n**Task:** Upgrade the control plane from v1.34.x to v1.35.0.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# 1. Upgrade kubeadm\nsudo apt-mark unhold kubeadm\nsudo apt-get update\nsudo apt-get install -y kubeadm=1.35.0-1.1\nsudo apt-mark hold kubeadm\n\n# 2. Plan\nsudo kubeadm upgrade plan\n\n# 3. Apply\nsudo kubeadm upgrade apply v1.35.0\n\n# 4. Upgrade kubelet + kubectl\nsudo apt-mark unhold kubelet kubectl\nsudo apt-get install -y kubelet=1.35.0-1.1 kubectl=1.35.0-1.1\nsudo apt-mark hold kubelet kubectl\n\n# 5. Restart\nsudo systemctl daemon-reload\nsudo systemctl restart kubelet\n\n# 6. Verify\nk get nodes\n```\n\n\u003c/details\u003e\n\n### Scenario 5: Troubleshoot — Pod Can't Reach Service\n\n**Task:** Pod `client` can't reach service `backend-svc` on port 80. Find and fix the issue.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# 1. Check service exists\nk get svc backend-svc\n\n# 2. Check endpoints\nk get endpoints backend-svc\n# If empty: selector doesn't match!\n\n# 3. Check service selector\nk describe svc backend-svc | grep Selector\n# e.g., Selector: app=backend\n\n# 4. Check pod labels\nk get pods --show-labels | grep backend\n# e.g., labels are app=back (typo!)\n\n# 5. Fix pod labels\nk label pod \u003cbackend-pod\u003e app=backend --overwrite\n\n# 6. Or fix service selector\nk edit svc backend-svc\n# Change selector to match actual pod labels\n\n# 7. Verify\nk get endpoints backend-svc\n# Should now show pod IPs\nk exec client -- wget -qO- --timeout=2 http://backend-svc\n```\n\n\u003c/details\u003e\n\n### Scenario 6: NetworkPolicy — Isolate Database\n\n**Task:** Create a NetworkPolicy that allows only pods with label `role=api` to reach pods with label `role=db` on port 5432. Block all other ingress to the database.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: db-isolation\nspec:\n  podSelector:\n    matchLabels:\n      role: db\n  policyTypes:\n  - Ingress\n  ingress:\n  - from:\n    - podSelector:\n        matchLabels:\n          role: api\n    ports:\n    - protocol: TCP\n      port: 5432\n```\n\n```bash\nk apply -f db-policy.yaml\n\n# Test — api pod should work\nk exec api-pod -- nc -zv \u003cdb-pod-ip\u003e 5432\n\n# Test — other pod should fail\nk exec other-pod -- nc -zv \u003cdb-pod-ip\u003e 5432\n```\n\n\u003c/details\u003e\n\n### Scenario 7: PV/PVC — Persistent Storage\n\n**Task:** Create a PV using hostPath `/data/logs` (1Gi, ReadWriteOnce, Retain), a PVC requesting 500Mi, and mount it in a pod at `/var/log/app`.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: log-pv\nspec:\n  capacity:\n    storage: 1Gi\n  accessModes:\n  - ReadWriteOnce\n  persistentVolumeReclaimPolicy: Retain\n  storageClassName: manual\n  hostPath:\n    path: /data/logs\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: log-pvc\nspec:\n  accessModes:\n  - ReadWriteOnce\n  resources:\n    requests:\n      storage: 500Mi\n  storageClassName: manual\n---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: log-pod\nspec:\n  containers:\n  - name: app\n    image: busybox:1.36\n    command: [\"sh\", \"-c\", \"while true; do echo $(date) \u003e\u003e /var/log/app/app.log; sleep 5; done\"]\n    volumeMounts:\n    - name: log-vol\n      mountPath: /var/log/app\n  volumes:\n  - name: log-vol\n    persistentVolumeClaim:\n      claimName: log-pvc\n```\n\n```bash\nk apply -f storage.yaml\nk get pv log-pv\nk get pvc log-pvc\nk exec log-pod -- cat /var/log/app/app.log\n```\n\n\u003c/details\u003e\n\n### Scenario 8: Sidecar Container — Log Streaming\n\n**Task:** Create a pod with a main container writing logs to `/var/log/app.log` and a sidecar (native v1.35 style) streaming that file to stdout.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: sidecar-logging\nspec:\n  initContainers:\n  - name: log-streamer\n    image: busybox:1.36\n    restartPolicy: Always\n    command: [\"sh\", \"-c\", \"tail -f /var/log/app.log\"]\n    volumeMounts:\n    - name: log-vol\n      mountPath: /var/log\n  containers:\n  - name: app\n    image: busybox:1.36\n    command: [\"sh\", \"-c\", \"while true; do echo \\\"$(date) app running\\\" \u003e\u003e /var/log/app.log; sleep 3; done\"]\n    volumeMounts:\n    - name: log-vol\n      mountPath: /var/log\n  volumes:\n  - name: log-vol\n    emptyDir: {}\n```\n\n```bash\nk apply -f sidecar.yaml\nk logs sidecar-logging -c log-streamer\n```\n\n\u003c/details\u003e\n\n---\n\n## Practice Questions with Answers (Mock Exam)\n\n17 questions weighted to match the real exam. Switch context before each one.\n\n---\n\n### Question 1 [4%] [Cluster Architecture] Easy\n\n`kubectl config use-context k8s-cluster1`\n\nCreate a ServiceAccount named `monitoring-sa` in namespace `monitoring`. Create a ClusterRole named `pod-viewer` that can `get`, `list`, `watch` pods in all namespaces. Bind the ClusterRole to the ServiceAccount.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\nk create ns monitoring\nk create sa monitoring-sa -n monitoring\nk create clusterrole pod-viewer --verb=get,list,watch --resource=pods\nk create clusterrolebinding pod-viewer-binding \\\n  --clusterrole=pod-viewer \\\n  --serviceaccount=monitoring:monitoring-sa\n\n# Verify\nk auth can-i list pods -A --as=system:serviceaccount:monitoring:monitoring-sa\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 2 [5%] [Cluster Architecture] Medium\n\n`kubectl config use-context k8s-cluster1`\n\nBack up etcd to `/opt/etcd-snapshot.db`. The etcd server is running on the control plane node at `https://127.0.0.1:2379`.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# Find cert paths\ncat /etc/kubernetes/manifests/etcd.yaml | grep -E \"cert-file|key-file|trusted-ca\"\n\nETCDCTL_API=3 etcdctl snapshot save /opt/etcd-snapshot.db \\\n  --endpoints=https://127.0.0.1:2379 \\\n  --cacert=/etc/kubernetes/pki/etcd/ca.crt \\\n  --cert=/etc/kubernetes/pki/etcd/server.crt \\\n  --key=/etc/kubernetes/pki/etcd/server.key\n\n# Verify\nETCDCTL_API=3 etcdctl snapshot status /opt/etcd-snapshot.db --write-table\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 3 [3%] [Workloads \u0026 Scheduling] Easy\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a Deployment named `web-app` in namespace `production` with 3 replicas using image `nginx:1.27`. Expose it as a ClusterIP service named `web-svc` on port 80.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\nk create ns production\nk create deployment web-app -n production --image=nginx:1.27 --replicas=3\nk expose deployment web-app -n production --port=80 --target-port=80 --name=web-svc\nk get deploy,svc -n production\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 4 [5%] [Troubleshooting] Medium\n\n`kubectl config use-context k8s-cluster1`\n\nNode `worker-1` is showing `NotReady`. Investigate and fix the issue so the node becomes `Ready`.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# Check node status\nk describe node worker-1 | grep -A5 Conditions\n\n# SSH to the node\nssh worker-1\n\n# Check kubelet\nsudo systemctl status kubelet\n# If inactive/dead:\nsudo systemctl start kubelet\nsudo systemctl enable kubelet\n\n# If it's a config issue, check logs\nsudo journalctl -u kubelet --no-pager | tail -30\n\n# Common fixes:\n# - Wrong --kubeconfig path\n# - Certificate issue → check /var/lib/kubelet/config.yaml\n# - Swap enabled → sudo swapoff -a\n\n# Verify from control plane\nk get nodes\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 5 [4%] [Services \u0026 Networking] Medium\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a NetworkPolicy named `restrict-ingress` in namespace `production` that:\n- Applies to pods with label `app=database`\n- Allows ingress only from pods with label `app=backend` on TCP port 3306\n- Allows DNS egress (UDP 53)\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: restrict-ingress\n  namespace: production\nspec:\n  podSelector:\n    matchLabels:\n      app: database\n  policyTypes:\n  - Ingress\n  - Egress\n  ingress:\n  - from:\n    - podSelector:\n        matchLabels:\n          app: backend\n    ports:\n    - protocol: TCP\n      port: 3306\n  egress:\n  - to: []\n    ports:\n    - protocol: UDP\n      port: 53\n```\n\n```bash\nk apply -f netpol.yaml\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 6 [5%] [Cluster Architecture] Medium\n\n`kubectl config use-context k8s-cluster1`\n\nUpgrade the control plane node from Kubernetes v1.34.4 to v1.35.0 using kubeadm.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\nsudo apt-mark unhold kubeadm\nsudo apt-get update\nsudo apt-get install -y kubeadm=1.35.0-1.1\nsudo apt-mark hold kubeadm\n\nsudo kubeadm upgrade plan\nsudo kubeadm upgrade apply v1.35.0\n\nsudo apt-mark unhold kubelet kubectl\nsudo apt-get install -y kubelet=1.35.0-1.1 kubectl=1.35.0-1.1\nsudo apt-mark hold kubelet kubectl\n\nsudo systemctl daemon-reload\nsudo systemctl restart kubelet\n\nk get nodes\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 7 [3%] [Storage] Easy\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a PersistentVolume named `data-pv` with 2Gi capacity, ReadWriteOnce access, hostPath `/data/volumes/pv1`, and storageClassName `manual`. Create a PersistentVolumeClaim named `data-pvc` in namespace `storage-test` requesting 1Gi.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: data-pv\nspec:\n  capacity:\n    storage: 2Gi\n  accessModes:\n  - ReadWriteOnce\n  storageClassName: manual\n  hostPath:\n    path: /data/volumes/pv1\n---\napiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: data-pvc\n  namespace: storage-test\nspec:\n  accessModes:\n  - ReadWriteOnce\n  resources:\n    requests:\n      storage: 1Gi\n  storageClassName: manual\n```\n\n```bash\nk create ns storage-test\nk apply -f storage.yaml\nk get pv data-pv\nk get pvc data-pvc -n storage-test\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 8 [5%] [Troubleshooting] Hard\n\n`kubectl config use-context k8s-cluster1`\n\nService `frontend-svc` in namespace `web` has no endpoints. Pods with label `app=frontend` are running. Find and fix the issue.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# Check service\nk describe svc frontend-svc -n web | grep Selector\n# e.g., Selector: app=front (typo)\n\n# Check pod labels\nk get pods -n web --show-labels\n# Labels show: app=frontend\n\n# Fix: edit service selector\nk edit svc frontend-svc -n web\n# Change selector from app=front to app=frontend\n# Or:\nk patch svc frontend-svc -n web -p '{\"spec\":{\"selector\":{\"app\":\"frontend\"}}}'\n\n# Verify\nk get endpoints frontend-svc -n web\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 9 [4%] [Workloads \u0026 Scheduling] Medium\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a DaemonSet named `log-collector` in namespace `kube-system` using image `fluentd:v1.17`. It should run on all nodes including the control plane.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: log-collector\n  namespace: kube-system\nspec:\n  selector:\n    matchLabels:\n      app: log-collector\n  template:\n    metadata:\n      labels:\n        app: log-collector\n    spec:\n      tolerations:\n      - key: node-role.kubernetes.io/control-plane\n        operator: Exists\n        effect: NoSchedule\n      containers:\n      - name: fluentd\n        image: fluentd:v1.17\n```\n\n```bash\nk apply -f ds.yaml\nk get ds log-collector -n kube-system\nk get pods -n kube-system -l app=log-collector -o wide\n# Should run on ALL nodes\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 10 [5%] [Troubleshooting] Hard\n\n`kubectl config use-context k8s-cluster1`\n\nDNS resolution is not working in the cluster. Pods cannot resolve service names. Find and fix the issue.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# 1. Check CoreDNS pods\nk get pods -n kube-system -l k8s-app=kube-dns\n# Are they running? CrashLoopBackOff?\n\n# 2. If not running, check logs\nk logs -n kube-system -l k8s-app=kube-dns\n\n# 3. Check CoreDNS ConfigMap\nk get cm coredns -n kube-system -o yaml\n# Look for syntax errors in Corefile\n\n# 4. Check kube-dns service\nk get svc kube-dns -n kube-system\nk get endpoints kube-dns -n kube-system\n\n# 5. Common fixes:\n# - Corefile syntax error → fix ConfigMap, pods restart automatically\n# - CoreDNS pods not scheduled → check tolerations\n# - kube-dns service missing → recreate it\n\n# 6. Test\nk run test-dns --image=busybox:1.36 --rm -it -- nslookup kubernetes.default.svc.cluster.local\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 11 [3%] [Workloads \u0026 Scheduling] Easy\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a static pod named `static-nginx` on node `worker-1` using image `nginx:1.27` with port 80.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# SSH to worker-1\nssh worker-1\n\n# Create manifest\nsudo tee /etc/kubernetes/manifests/static-nginx.yaml \u003c\u003cEOF\napiVersion: v1\nkind: Pod\nmetadata:\n  name: static-nginx\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.27\n    ports:\n    - containerPort: 80\nEOF\n\n# Back on control plane\nk get pods | grep static-nginx\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 12 [4%] [Cluster Architecture] Medium\n\n`kubectl config use-context k8s-cluster1`\n\nDrain node `worker-2` for maintenance. Make sure no pods are disrupted unexpectedly. After a simulated maintenance window, make the node schedulable again.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\nk drain worker-2 --ignore-daemonsets --delete-emptydir-data\n\n# Verify\nk get nodes\n# worker-2: SchedulingDisabled\nk get pods -o wide | grep worker-2\n# Only DaemonSet pods\n\n# After maintenance\nk uncordon worker-2\nk get nodes\n# worker-2: Ready\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 13 [5%] [Services \u0026 Networking] Medium\n\n`kubectl config use-context k8s-cluster2`\n\nCreate an Ingress resource named `app-ingress` in namespace `web` that routes:\n- `app.example.com/api` to service `api-svc` on port 8080\n- `app.example.com/web` to service `web-svc` on port 80\n\nUse ingressClassName `nginx`.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: app-ingress\n  namespace: web\n  annotations:\n    nginx.ingress.kubernetes.io/rewrite-target: /\nspec:\n  ingressClassName: nginx\n  rules:\n  - host: app.example.com\n    http:\n      paths:\n      - path: /api\n        pathType: Prefix\n        backend:\n          service:\n            name: api-svc\n            port:\n              number: 8080\n      - path: /web\n        pathType: Prefix\n        backend:\n          service:\n            name: web-svc\n            port:\n              number: 80\n```\n\n```bash\nk apply -f ingress.yaml\nk get ingress app-ingress -n web\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 14 [5%] [Troubleshooting] Hard\n\n`kubectl config use-context k8s-cluster1`\n\nA pod named `failing-app` in namespace `debug` is in `CrashLoopBackOff`. Investigate and fix it.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# 1. Check events\nk describe pod failing-app -n debug\n\n# 2. Check logs (previous container)\nk logs failing-app -n debug --previous\n\n# 3. Common issues:\n# a) Wrong command → fix command/args\n# b) Missing ConfigMap/Secret → create the missing resource\n# c) Wrong env var reference → fix valueFrom\n# d) Application crash → fix the image or config\n\n# 4. Fix based on what you find, e.g.:\nk edit pod failing-app -n debug\n# Or delete and recreate with fixes:\nk get pod failing-app -n debug -o yaml \u003e fix.yaml\n# Edit fix.yaml\nk delete pod failing-app -n debug $now\nk apply -f fix.yaml\n\n# 5. Verify\nk get pod failing-app -n debug\n# Should be Running\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 15 [4%] [Storage] Medium\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a pod named `volume-pod` in namespace `storage-test` that mounts the existing PVC `data-pvc` at `/data`. Write the string \"exam-test\" to `/data/test.txt`. Verify the file exists.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: volume-pod\n  namespace: storage-test\nspec:\n  containers:\n  - name: app\n    image: busybox:1.36\n    command: [\"sh\", \"-c\", \"echo 'exam-test' \u003e /data/test.txt \u0026\u0026 sleep 3600\"]\n    volumeMounts:\n    - name: data\n      mountPath: /data\n  volumes:\n  - name: data\n    persistentVolumeClaim:\n      claimName: data-pvc\n```\n\n```bash\nk apply -f pod.yaml\nk exec volume-pod -n storage-test -- cat /data/test.txt\n# Should output: exam-test\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 16 [5%] [Cluster Architecture] Hard\n\n`kubectl config use-context k8s-cluster1`\n\nRestore etcd from the snapshot at `/opt/etcd-snapshot.db`. Restore it to data directory `/var/lib/etcd-from-backup`.\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```bash\n# Restore\nETCDCTL_API=3 etcdctl snapshot restore /opt/etcd-snapshot.db \\\n  --data-dir=/var/lib/etcd-from-backup\n\n# Update etcd manifest\nsudo vi /etc/kubernetes/manifests/etcd.yaml\n# 1. Change --data-dir=/var/lib/etcd to --data-dir=/var/lib/etcd-from-backup\n# 2. In volumes section, change hostPath path from /var/lib/etcd to /var/lib/etcd-from-backup\n\n# Wait for etcd to restart\n# kubectl may hang for 30-60s — that's normal\nsleep 30\nk get nodes\n```\n\n\u003c/details\u003e\n\n---\n\n### Question 17 [4%] [Workloads \u0026 Scheduling] Medium\n\n`kubectl config use-context k8s-cluster2`\n\nCreate a pod named `restricted-pod` in namespace `secure` that:\n- Uses image `nginx:1.27`\n- Runs as user ID 1000\n- Has a read-only root filesystem\n- Drops all capabilities\n- Does not allow privilege escalation\n\n\u003cdetails\u003e\n\u003csummary\u003eSolution\u003c/summary\u003e\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: restricted-pod\n  namespace: secure\nspec:\n  securityContext:\n    runAsUser: 1000\n  containers:\n  - name: nginx\n    image: nginx:1.27\n    securityContext:\n      readOnlyRootFilesystem: true\n      allowPrivilegeEscalation: false\n      capabilities:\n        drop:\n        - ALL\n```\n\n```bash\nk create ns secure\nk apply -f restricted.yaml\nk get pod restricted-pod -n secure\n```\n\n\u003c/details\u003e\n\n---\n\n### Score Card\n\nCopy this table into a text file. After completing the mock, mark each question as full credit, partial, or missed. Add up your weighted score.\n\n| # | Domain | Weight | Difficulty | Result | Points |\n|---|---|---|---|---|---|\n| 1 | Cluster Architecture | 4% | Easy | ___ | /4 |\n| 2 | Cluster Architecture | 5% | Medium | ___ | /5 |\n| 3 | Workloads \u0026 Scheduling | 3% | Easy | ___ | /3 |\n| 4 | Troubleshooting | 5% | Medium | ___ | /5 |\n| 5 | Services \u0026 Networking | 4% | Medium | ___ | /4 |\n| 6 | Cluster Architecture | 5% | Medium | ___ | /5 |\n| 7 | Storage | 3% | Easy | ___ | /3 |\n| 8 | Troubleshooting | 5% | Hard | ___ | /5 |\n| 9 | Workloads \u0026 Scheduling | 4% | Medium | ___ | /4 |\n| 10 | Troubleshooting | 5% | Hard | ___ | /5 |\n| 11 | Workloads \u0026 Scheduling | 3% | Easy | ___ | /3 |\n| 12 | Cluster Architecture | 4% | Medium | ___ | /4 |\n| 13 | Services \u0026 Networking | 5% | Medium | ___ | /5 |\n| 14 | Troubleshooting | 5% | Hard | ___ | /5 |\n| 15 | Storage | 4% | Medium | ___ | /4 |\n| 16 | Cluster Architecture | 5% | Hard | ___ | /5 |\n| 17 | Workloads \u0026 Scheduling | 4% | Medium | ___ | /4 |\n| | **Total** | **73%** | | | **___/73** |\n\n**How to score:** Full credit = full weight. Partial (got the right idea but missed a flag or namespace) = half weight. Wrong or skipped = 0. Real exam uses partial scoring too, so this is realistic.\n\n**Passing threshold:** 66% of 73 = **48 points**. If you're below 48, re-study the domains where you dropped points and redo those questions in a week.\n\n**Timing target:** Set a timer for 2 hours. If you finish early, note how much time you had left — that buffer is your safety margin on exam day.\n\n**Domain breakdown of this mock:**\n\n| Domain | Questions | Total Weight | Your Score |\n|---|---|---|---|\n| Cluster Architecture | 1, 2, 6, 12, 16 | 23% | ___/23 |\n| Troubleshooting | 4, 8, 10, 14 | 20% | ___/20 |\n| Services \u0026 Networking | 5, 13 | 9% | ___/9 |\n| Workloads \u0026 Scheduling | 3, 9, 11, 17 | 14% | ___/14 |\n| Storage | 7, 15 | 7% | ___/7 |\n\nIf any domain is below 50%, that's where your next study session should focus.\n\n[Back to top](#table-of-contents)\n\n---\n\n## Study Resources for CKA 2026\n\nWhat I actually used, in order of usefulness:\n\n### Free\n\n| Resource | What I actually used it for |\n|---|---|\n| [Kubernetes Official Docs](https://kubernetes.io/docs/) | The only site allowed during the exam. I spent a week getting fast at searching it. Bookmark the Tasks section — that's where etcd backup and kubeadm upgrade live. |\n| [Kubernetes Tasks](https://kubernetes.io/docs/tasks/) | This saved me on the etcd question during the actual exam. I had the steps bookmarked and copied the cert paths directly. Don't skip this. |\n| [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) | I had this open in a tab during the exam. The jsonpath examples alone are wo","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheplatformlab%2FCKA-Certified-Kubernetes-Administrator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheplatformlab%2FCKA-Certified-Kubernetes-Administrator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheplatformlab%2FCKA-Certified-Kubernetes-Administrator/lists"}