A scannable reference of architectural patterns the CKAD exam tests. Read top-to-bottom, or jump to a section.
Application Environment, Configuration, and Security
Create a ConfigMap or generic Secret from command-line key-value pairs.
Use `kubectl create configmap <name> --from-literal=<key>=<value>` or `kubectl create secret generic <name> --from-literal=<key>=<value>`.
Why: `--from-literal` is for direct key-value input. Use the flag multiple times for multiple keys. This is faster than creating a YAML file for simple cases.
Enforce security best practices: prevent running as root, make root filesystem read-only, or specify a user ID.
Use `securityContext` at the Pod or container level. Set `runAsNonRoot: true`, `readOnlyRootFilesystem: true`, and/or `runAsUser: <UID>`.
Why: SecurityContext provides fine-grained, declarative control over container privileges, essential for hardening applications and meeting security policies.
Grant a Pod minimal permissions to access the Kubernetes API.
1. Create a custom `ServiceAccount`. 2. Create a `Role` with only the necessary API permissions (e.g., list pods). 3. Create a `RoleBinding` to link the ServiceAccount and Role. 4. Assign the ServiceAccount to the Pod via `spec.serviceAccountName`.
Why: Follows the principle of least privilege, minimizing the attack surface if a Pod is compromised.
Prevent the automatic mounting of a ServiceAccount token into a Pod that does not need API access.
Set `automountServiceAccountToken: false` in the Pod spec or on the ServiceAccount itself.
Why: Reduces the attack surface by not providing API credentials to containers that do not require them.
Create a Secret for use in TLS termination for an Ingress or other secure service.
Use `kubectl create secret tls <secret-name> --cert=<path/to/cert.pem> --key=<path/to/key.pem>`.
Why: This creates a Secret of the correct type `kubernetes.io/tls` with the standard `tls.crt` and `tls.key` data keys expected by Ingress controllers.
Expose Pod metadata (like name, namespace, labels, or node IP) to a container.
Use the Downward API to project metadata as environment variables or files in a `downwardAPI` volume. Example: `valueFrom: {fieldRef: {fieldPath: metadata.name}}`.
Why: Allows containers to be self-aware without needing to query the Kubernetes API, simplifying configuration and reducing RBAC requirements.
Set default CPU/memory requests and limits for all Pods in a namespace.
Create a `LimitRange` object in the namespace. Define `default` and `defaultRequest` values for resources.
Why: Ensures all Pods have resource constraints, improving scheduling and stability, even if developers forget to specify them. Works in concert with ResourceQuota.
Limit the total amount of resources (CPU, memory, object count) that can be consumed in a namespace.
Create a `ResourceQuota` object. Define hard limits in `spec.hard`, e.g., `requests.cpu: "4"`, `pods: "10"`.
Why: Prevents one namespace or team from consuming all cluster resources, ensuring fair resource allocation.
Application Design and Build
Run prerequisite tasks (e.g., wait for a database, run migrations, pull data) before the main application starts.
Define one or more `initContainers` in the Pod spec. They run sequentially to completion before main app containers start.
Why: Decouples setup logic from the application container and guarantees dependencies are met before the application launches.
Extend a primary application container with helper functionality like logging, monitoring, or proxying.
Add a second container (the sidecar) to the Pod spec. Both containers share resources like network and volumes.
Why: Enhances functionality without modifying the main application code, promoting separation of concerns.
Share a directory for reading/writing between containers in the same Pod.
Define an `emptyDir` volume in the Pod spec and mount it into all required containers.
Why: `emptyDir` provides a simple, ephemeral storage volume that exists for the life of the Pod, perfect for intra-pod data sharing.
Override a container image's default ENTRYPOINT and/or CMD.
In the container spec, use `command` to override ENTRYPOINT and `args` to override CMD. `command: ["/bin/sh"], args: ["-c", "echo hello"]`.
Why: Provides full control over the container startup command from the Pod definition, useful for adapting generic images.
Run a finite task to completion, controlling parallelism and the number of successful completions.
Use a `Job` resource. Set `spec.completions` for the target success count and `spec.parallelism` for the number of concurrent Pods. Use `spec.backoffLimit` to control retries.
Why: Jobs are designed for run-to-completion tasks, unlike long-running Deployments. These settings are key to managing batch workloads.
Schedule a recurring task using cron syntax and control how overlapping jobs are handled.
Use a `CronJob` resource. Define the `spec.schedule` in cron format (e.g., `*/5 * * * *`). Set `spec.concurrencyPolicy` to `Allow`, `Forbid`, or `Replace`.
Why: Automates scheduled tasks. `concurrencyPolicy` is critical for preventing overlapping runs (`Forbid`) or replacing stale ones (`Replace`).
Quickly generate a YAML manifest for a resource without creating it in the cluster.
Use the `--dry-run=client -o yaml` flags with imperative commands. Example: `kubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml`.
Why: Saves time by scaffolding a valid manifest that can be customized and then applied declaratively.
Application Deployment
Update, scale, check status, view history, and rollback a Deployment imperatively.
Use `kubectl set image`, `kubectl scale`, `kubectl rollout status`, `kubectl rollout history`, and `kubectl rollout undo`.
Why: These are the core imperative commands for managing the lifecycle of a deployed application during development and troubleshooting.
Control the speed and safety of a Deployment update to ensure availability.
In `spec.strategy.rollingUpdate`, configure `maxSurge` (how many extra Pods can be created) and `maxUnavailable` (how many can be down).
Why: Balancing `maxSurge` and `maxUnavailable` is key to managing capacity vs. resource usage during updates. For zero downtime, `maxUnavailable` must be less than `replicas`.
Ensure no two versions of an application run simultaneously by terminating all old Pods before creating new ones.
Set `spec.strategy.type: Recreate` in the Deployment.
Why: Guarantees that old and new versions do not coexist, which is necessary for applications that cannot handle two different versions accessing the same data. This strategy incurs downtime.
Make multiple changes to a live Deployment without triggering an intermediate rollout for each change.
Use `kubectl rollout pause deployment/<name>`, apply changes, then `kubectl rollout resume deployment/<name>`.
Why: Consolidates multiple updates into a single rollout event, preventing churn and race conditions.
Limit the number of old ReplicaSets retained for a Deployment to save on etcd storage.
Set `spec.revisionHistoryLimit` to the desired number of revisions to keep (e.g., 3). Default is 10.
Why: Setting a lower limit reduces etcd clutter. Setting to `0` disables rollback capability entirely.
Document the reason for a Deployment update so it appears in the revision history.
Add a `kubernetes.io/change-cause` annotation to the Deployment manifest. Example: `kubectl annotate deployment/nginx kubernetes.io/change-cause="update to 1.20"`.
Why: Provides valuable context when viewing rollout history (`kubectl rollout history`), making it easier to identify which revision to roll back to.
Deploy a new application version alongside the old one and switch traffic instantly with zero downtime.
Use two Deployments (e.g., `app-blue`, `app-green`) with different version labels. A single Service selects the active version via its `selector`. To switch, `kubectl patch service` to update the selector to the new version label.
Why: Provides instant, low-risk releases and immediate rollback capability by simply patching the Service selector back.
Route a small percentage of traffic to a new application version for testing in production.
Use two Deployments (stable, canary) sharing the same selector label. A Service targets both. Control traffic percentage by the ratio of replicas (e.g., 9 stable replicas, 1 canary for 10% traffic).
Why: A simple way to perform canary releases without a service mesh, allowing for controlled, low-risk testing of new features. Traffic distribution is approximate.
Services and Networking
Expose a set of Pods for communication only from within the cluster.
Use a Service with `type: ClusterIP`. This is the default type.
Why: `ClusterIP` provides a stable internal IP address and DNS name for a service, abstracting away individual Pod IPs.
Expose a service on a static port on each nodeβs IP address.
Use a Service with `type: NodePort`. K8s allocates a port from a range (default: 30000-32767).
Why: Useful for development or when an external load balancer is not available. Traffic to `<NodeIP>:<NodePort>` is forwarded to the Service.
Route external HTTP/S traffic to internal services based on hostname or URL path.
Create an `Ingress` resource. Define `rules` for hosts and `http.paths` to map to backend services. Configure TLS with `spec.tls` pointing to a TLS Secret.
Why: Ingress provides L7 routing, consolidating multiple services under one external IP and offloading TLS termination.
Restrict network traffic to and from Pods based on labels, namespaces, or IP blocks.
Create a `NetworkPolicy` targeting Pods with `podSelector`. Define `ingress` and/or `egress` rules to allow specific traffic. By default, applying a policy to a pod denies all traffic not explicitly allowed.
Why: NetworkPolicies are fundamental for network segmentation and implementing a zero-trust security model in Kubernetes.
Block all ingress and egress traffic for all pods in a namespace by default.
Create a NetworkPolicy with an empty `podSelector: {}` and empty ingress/egress rules. Example: `podSelector: {}, policyTypes: [Ingress, Egress]`.
Why: Establishes a secure baseline where all traffic is denied unless explicitly allowed by other, more specific NetworkPolicies.
Provide a stable DNS entry that resolves to all Pod IPs directly, without a virtual IP for load balancing.
Create a Service with `spec.clusterIP: None`.
Why: Essential for stateful applications (like StatefulSets) or peer-to-peer systems that need to discover and communicate with specific Pods directly.
Ensure backend Pods see the original client IP for traffic from a NodePort or LoadBalancer Service.
Set `spec.externalTrafficPolicy: Local` on the Service.
Why: The default (`Cluster`) policy obfuscates the source IP via network address translation. `Local` preserves it but can lead to uneven traffic distribution if Pods are not on all nodes.
Ensure all requests from a specific client are sent to the same Pod.
On the Service, set `spec.sessionAffinity: ClientIP`.
Why: Provides 'sticky sessions', which is necessary for legacy applications that store session state in memory on a specific Pod.
A Pod in one namespace needs to communicate with a Service in another namespace.
Use the expanded DNS name: `<service-name>.<namespace-name>.svc.cluster.local` or the short form `<service-name>.<namespace-name>`.
Why: Simple service names only resolve within the same namespace. Cross-namespace communication requires specifying the target namespace in the DNS query.
Application Observability and Maintenance
Define health checks to manage Pod lifecycle: restart on failure vs. remove from service.
`livenessProbe`: Restarts the container if the probe fails. `readinessProbe`: Removes the Pod from Service endpoints if the probe fails. `startupProbe`: Disables other probes until the container finishes its startup.
Why: Correctly configured probes are essential for application self-healing and achieving zero-downtime deployments.
Diagnose why a Pod is stuck in a non-Running state (e.g., Pending, ContainerCreating, CrashLoopBackOff).
Use `kubectl describe pod <pod-name>`. The `Events` section provides critical clues from the scheduler (resource issues), kubelet (image pull errors), or container runtime.
Why: `describe` is the most important command for understanding Pod lifecycle issues that occur before the application starts or logs anything.
Inspect the logs from a container that has crashed and is now in a restart loop (CrashLoopBackOff).
Use `kubectl logs <pod-name> --previous`.
Why: The `--previous` flag shows the logs from the last terminated instance of the container, which contains the error that caused the crash.
View and follow logs from all Pods matching a label selector in real-time.
Use `kubectl logs -l <label-selector> -f`. Add `--prefix` to see which pod each line came from.
Why: Aggregates logs from a distributed application, providing a unified view of its behavior.
Execute a command or get an interactive shell inside a running container for debugging.
Use `kubectl exec -it <pod-name> -- /bin/sh` (or `/bin/bash`). The `--` separates kubectl flags from the command.
Why: Provides direct access to the container's environment for live debugging, inspecting files, or checking network connectivity.
View the current CPU and memory consumption of a running Pod.
Use `kubectl top pod <pod-name>`. Use `--containers` to see usage for each container in the Pod.
Why: Requires the Metrics Server to be installed. It is essential for identifying resource-hungry applications, memory leaks, or CPU bottlenecks.
Troubleshoot a running container that lacks a shell or debugging tools.
Use `kubectl debug <pod-name> -it --image=busybox --share-processes --copy-to=debug-pod`. This creates a new Pod with a debug container sharing the same process namespace.
Why: `kubectl debug` is the modern way to attach a temporary "ephemeral container" with debugging tools to a running Pod without modifying the original Pod spec.