Compare commits
No commits in common. "71f57de121499b9d5c4f5a1496bd57ac27cb43d5" and "99f833db6898b6680031be83c0a09426879fa9a3" have entirely different histories.
71f57de121
...
99f833db68
@ -1,661 +0,0 @@
|
|||||||
# CKA Simulator — Step-by-Step Solutions & Conceptual Guide
|
|
||||||
|
|
||||||
This guide explains how to approach and solve every question in the CKA exam bank. It breaks down the **underlying Kubernetes concepts**, **the step-by-step commands**, **why specific flags are used**, and **how to verify your solution** before submitting.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Domain 1: Cluster Architecture, Installation & Configuration (25%)
|
|
||||||
|
|
||||||
### Q01 · RBAC for a Deployment Bot
|
|
||||||
- **Concept**: Role-Based Access Control (RBAC) separates permissions using `Roles` (namespaced) and `ClusterRoles` (cluster-wide), bound to users/ServiceAccounts via `RoleBindings` or `ClusterRoleBindings`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create the ServiceAccount in the target namespace:
|
|
||||||
```bash
|
|
||||||
k -n rbac-app create sa deploy-bot
|
|
||||||
```
|
|
||||||
2. Create the Role with explicit verbs on `deployments.apps`:
|
|
||||||
```bash
|
|
||||||
k -n rbac-app create role deploy-manager \
|
|
||||||
--verb=get,list,watch,create,update,patch,delete \
|
|
||||||
--resource=deployments.apps
|
|
||||||
```
|
|
||||||
3. Bind the Role to the ServiceAccount:
|
|
||||||
```bash
|
|
||||||
k -n rbac-app create rolebinding deploy-bot-binding \
|
|
||||||
--role=deploy-manager \
|
|
||||||
--serviceaccount=rbac-app:deploy-bot
|
|
||||||
```
|
|
||||||
4. **Verify** using `kubectl auth can-i`:
|
|
||||||
```bash
|
|
||||||
k auth can-i create deployments -n rbac-app --as=system:serviceaccount:rbac-app:deploy-bot # yes
|
|
||||||
k auth can-i delete pods -n rbac-app --as=system:serviceaccount:rbac-app:deploy-bot # no
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q02 · etcd Snapshot
|
|
||||||
- **Concept**: etcd stores all cluster state. Snapshots require TLS certificates (CA cert, server cert, server key) and `ETCDCTL_API=3`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. SSH / access the control plane node (`podman exec -it <control-plane> bash` or `ssh <control-plane>`).
|
|
||||||
2. Run `etcdctl snapshot save` pointing to the etcd TLS certificates:
|
|
||||||
```bash
|
|
||||||
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
|
|
||||||
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
|
|
||||||
--cert=/etc/kubernetes/pki/etcd/server.crt \
|
|
||||||
--key=/etc/kubernetes/pki/etcd/server.key \
|
|
||||||
snapshot save /opt/cka/etcd-snapshot.db
|
|
||||||
```
|
|
||||||
3. **Verify** snapshot integrity:
|
|
||||||
```bash
|
|
||||||
etcdutl snapshot status /opt/cka/etcd-snapshot.db -w table
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q03 · Kustomize Overlay
|
|
||||||
- **Concept**: Kustomize allows environment-specific customization (like `prod` overlays) without templating YAMLs.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create the overlay folder: `mkdir -p workspace/q03/overlays/prod && cd workspace/q03/overlays/prod`
|
|
||||||
2. Create `namespace.yaml` for `store-prod`.
|
|
||||||
3. Write `kustomization.yaml` referencing `../../base`, adding namespace, labels, and replica count.
|
|
||||||
4. Apply via `kubectl apply -k .`
|
|
||||||
5. **Verify**: `k -n store-prod get deploy,pod -l env=prod`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q04 · CustomResourceDefinition (CRD) & Custom Resource
|
|
||||||
- **Concept**: CRDs extend Kubernetes APIs with custom objects. Schema validation uses OpenAPI v3.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create the `CustomResourceDefinition` YAML specifying `group`, `names`, `scope: Namespaced`, and `openAPIV3Schema`.
|
|
||||||
2. Apply the CRD: `k apply -f crd.yaml`.
|
|
||||||
3. Instantiate the Custom Resource (`Backup` named `nightly`).
|
|
||||||
4. **Verify**: `k get bk` or `k get backups`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q05 · Static Pod Creation
|
|
||||||
- **Concept**: Static pods are managed directly by the Kubelet on a specific node without API server scheduling. Kubelet creates a mirror pod in the API server.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. SSH / access the target node.
|
|
||||||
2. Check static pod path: `grep staticPodPath /var/lib/kubelet/config.yaml` (usually `/etc/kubernetes/manifests`).
|
|
||||||
3. Create the pod manifest in that directory: `/etc/kubernetes/manifests/static-web.yaml`.
|
|
||||||
4. **Verify**: Kubelet creates the mirror pod within seconds: `k get pods -o wide | grep static-web`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q06 · Certificate Expiry & User CSR
|
|
||||||
- **Concept**: Kubeadm manages control-plane certs. Kubernetes CSR API (`certificates.k8s.io/v1`) signs and approves client certificates.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. On the control plane: `kubeadm certs check-expiration | grep '^apiserver ' > apiserver-expiry.txt`.
|
|
||||||
2. Generate user private key and CSR with OpenSSL:
|
|
||||||
```bash
|
|
||||||
openssl genrsa -out jane.key 2048
|
|
||||||
openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr
|
|
||||||
```
|
|
||||||
3. Submit `CertificateSigningRequest` with `base64 -w0 jane.csr` and signer `kubernetes.io/kube-apiserver-client`.
|
|
||||||
4. Approve: `k certificate approve jane`.
|
|
||||||
5. Extract certificate: `k get csr jane -o jsonpath='{.status.certificate}' | base64 -d > jane.crt`.
|
|
||||||
6. Create RoleBinding binding user `jane` to ClusterRole `view`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q33 · Aggregated ClusterRole
|
|
||||||
- **Concept**: Aggregated ClusterRoles automatically aggregate permissions from other ClusterRoles that have matching labels defined in `aggregationRule.clusterRoleSelectors`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create the parent ClusterRole with `aggregationRule: {clusterRoleSelectors: [{matchLabels: {rbac.example.com/aggregate-to-parent: "true"}}]}` and `rules: []`.
|
|
||||||
2. Create sub-ClusterRoles (`pods`, `services`) with required verbs.
|
|
||||||
3. Label the sub-ClusterRoles to trigger aggregation.
|
|
||||||
4. Bind the parent ClusterRole to the user via `ClusterRoleBinding`.
|
|
||||||
5. **Verify**: `k get clusterrole <name> -o yaml` (rules will be automatically populated).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q34 · Discover CRD Field with `kubectl explain`
|
|
||||||
- **Concept**: `kubectl explain <resource>.<field> --recursive` inspects the live schema for any built-in or custom resource.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Inspect CRD schema: `k explain <kind>.spec --recursive`.
|
|
||||||
2. Check field description: `k explain <kind>.spec.tuning.<field>`.
|
|
||||||
3. Write path to file: `echo spec.tuning.<field> > path.txt`.
|
|
||||||
4. Create sample custom resource with that field configured.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q40 · Kubeadm Join Token Creation
|
|
||||||
- **Concept**: `kubeadm token create` generates authentication bootstrap tokens used by new nodes to join the cluster.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. On control plane: `kubeadm token create --ttl 1h --print-join-command`.
|
|
||||||
2. Save output to `join.sh`.
|
|
||||||
3. Verify with `kubeadm token list`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q41 · Component Versions Inspection
|
|
||||||
- **Concept**: Control-plane components run as static pods; Kubelet runs as a systemd service.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Server version: `k version`.
|
|
||||||
2. Node versions: `k get node <node> -o jsonpath='{.status.nodeInfo.kubeletVersion} {.status.nodeInfo.containerRuntimeVersion}'`.
|
|
||||||
3. etcd version: `k -n kube-system get pod -l component=etcd -o jsonpath='{.items[0].spec.containers[0].image}'`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q101 · [HARD] RBAC Cleanup & Granular Privileges
|
|
||||||
- **Concept**: Principle of least privilege. Remove over-privileged admin bindings before granting surgical access.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Find and delete over-granting binding: `k delete clusterrolebinding <sa>-legacy-admin`.
|
|
||||||
2. Create Role for deployments & replicasets: `k -n <ns> create role <sa>-role --verb=get,list,watch,patch --resource=deployments.apps,replicasets.apps`.
|
|
||||||
3. Add `pods/exec` create permissions to the Role.
|
|
||||||
4. Create ClusterRole with `list` on `nodes` and bind it.
|
|
||||||
5. **Verify**: `k auth can-i --list -n <ns> --as=system:serviceaccount:<ns>:<sa>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q102 · [HARD] etcd Restore
|
|
||||||
- **Concept**: Restoring etcd from a snapshot requires stopping etcd, restoring data to a new data directory, and updating the etcd static pod hostPath volume.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. On control plane, restore snapshot: `etcdutl snapshot restore /opt/cka/backup.db --data-dir /var/lib/etcd-restore`.
|
|
||||||
2. Edit `/etc/kubernetes/manifests/etcd.yaml`:
|
|
||||||
- Update the `etcd-data` hostPath volume path from `/var/lib/etcd` to `/var/lib/etcd-restore`.
|
|
||||||
3. Wait for Kubelet to restart etcd and the API server to reconnect (`watch crictl ps`).
|
|
||||||
4. **Verify**: `k get cm <restored-cm>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q103 · [HARD] Static Pod on Relocated Path
|
|
||||||
- **Concept**: Some nodes may have custom `staticPodPath` configured in `/var/lib/kubelet/config.yaml` instead of `/etc/kubernetes/manifests`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Find target node: `k get nodes -l tier=edge`.
|
|
||||||
2. On node, inspect `grep staticPodPath /var/lib/kubelet/config.yaml`.
|
|
||||||
3. Write pod manifest directly into that specific directory.
|
|
||||||
4. **Verify**: `k get pods -n <ns> -o wide`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q104 · [HARD] Self-Contained Kubeconfig Creation
|
|
||||||
- **Concept**: A portable kubeconfig embeds all certificates using `--embed-certs` rather than referencing file paths.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Issue & approve CSR for the user; save `user.crt` and `user.key`.
|
|
||||||
2. Extract cluster CA: `k config view --minify --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt`.
|
|
||||||
3. Set cluster, credentials, context, and current-context with `--kubeconfig=<file> --embed-certs`.
|
|
||||||
4. **Verify**: `kubectl --kubeconfig=<file> get pods`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q122 · [HARD] Renew Control Plane Certificates
|
|
||||||
- **Concept**: `kubeadm certs renew all` generates new certificates for all control-plane components. Static pods must be bounced to reload the new certs.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. On control plane: `kubeadm certs renew all`.
|
|
||||||
2. Bounce static pods: `mv /etc/kubernetes/manifests/*.yaml /tmp/ && sleep 20 && mv /tmp/*.yaml /etc/kubernetes/manifests/`.
|
|
||||||
3. Update admin config: `cp /etc/kubernetes/admin.conf ~/.kube/config`.
|
|
||||||
4. **Verify**: `kubeadm certs check-expiration`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Domain 2: Workloads & Scheduling (15%)
|
|
||||||
|
|
||||||
### Q07 · Rolling Update Strategy & Scaling
|
|
||||||
- **Concept**: `maxSurge` controls how many extra pods can be created during updates; `maxUnavailable` controls how many pods can be unavailable.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Update image: `k -n apps set image deploy web-frontend nginx=nginx:1.27`.
|
|
||||||
2. Scale replicas: `k -n apps scale deploy web-frontend --replicas=4`.
|
|
||||||
3. Patch strategy: `k -n apps patch deploy web-frontend -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'`.
|
|
||||||
4. **Verify**: `k -n apps rollout status deploy web-frontend`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q08 · Sidecar Container & Shared Volume
|
|
||||||
- **Concept**: Multi-container pods share volumes (`emptyDir`) and localhost network namespace.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Define Pod with an `emptyDir` volume mounted at `/var/log` in both the app container and sidecar container.
|
|
||||||
2. App writes logs to `/var/log/app.log`; sidecar runs `tail -f /var/log/app.log`.
|
|
||||||
3. **Verify**: `k -n apps logs log-shipper -c shipper`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q09 · Node Affinity & Resource Requests/Limits
|
|
||||||
- **Concept**: `nodeAffinity` matches node labels (`requiredDuringSchedulingIgnoredDuringExecution`). Resource `requests` are used for scheduling; `limits` enforce runtime boundaries.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create deployment template: `k -n apps create deploy cache --image=redis:7-alpine --replicas=2 $do > cache.yaml`.
|
|
||||||
2. Add `affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution` with `matchExpressions: [{key: disktype, operator: In, values: [ssd]}]`.
|
|
||||||
3. Add `resources: {requests: {cpu: 100m, memory: 64Mi}, limits: {cpu: 200m, memory: 128Mi}}`.
|
|
||||||
4. Apply and verify scheduling: `k -n apps get pods -o wide`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q10 · ConfigMap & Secret Consumption
|
|
||||||
- **Concept**: ConfigMaps and Secrets can be mounted as files via volumes, loaded as individual environment variables (`valueFrom`), or loaded entirely (`envFrom`).
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create ConfigMap: `k -n apps create cm app-config --from-literal=APP_MODE=production --from-literal=LOG_LEVEL=debug`.
|
|
||||||
2. Create Secret: `k -n apps create secret generic db-creds --from-literal=username=admin --from-literal='password=S3cr3t!'`.
|
|
||||||
3. In Pod spec: mount ConfigMap as volume at `/etc/config`, inject `APP_MODE` from CM, and use `envFrom` for `db-creds`.
|
|
||||||
4. **Verify**: `k -n apps exec config-pod -- ls /etc/config` and `k -n apps exec config-pod -- env`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q23 · Metrics Server Repair & HPA
|
|
||||||
- **Concept**: Metrics Server provides CPU/memory metrics for `kubectl top` and Horizontal Pod Autoscalers. In non-production/kind environments, `--kubelet-insecure-tls` is required.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Edit metrics-server deployment: `k -n kube-system edit deploy metrics-server`. Add `--kubelet-insecure-tls` under container args.
|
|
||||||
2. Verify: `k top nodes`.
|
|
||||||
3. Create HPA: `k -n <ns> autoscale deploy <dep> --min=2 --max=<max> --cpu-percent=<cpu>`.
|
|
||||||
4. **Verify**: `k -n <ns> get hpa`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q24 · PriorityClass
|
|
||||||
- **Concept**: PriorityClasses determine pod preemption and scheduling order. Higher value = higher priority.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create `PriorityClass` with `value: <integer>` and `globalDefault: false`.
|
|
||||||
2. Assign `priorityClassName: <name>` in the Deployment pod template spec.
|
|
||||||
3. **Verify**: `k -n <ns> rollout status deploy <dep>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q25 · Taints & Tolerations
|
|
||||||
- **Concept**: Taints repel pods (`NoSchedule`, `PreferNoSchedule`, `NoExecute`). Pods require matching tolerations to be scheduled on tainted nodes.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Taint node: `k taint node <node> key=val:NoSchedule`.
|
|
||||||
2. Add toleration to deployment pod spec:
|
|
||||||
```yaml
|
|
||||||
tolerations:
|
|
||||||
- key: "key"
|
|
||||||
operator: "Equal"
|
|
||||||
value: "val"
|
|
||||||
effect: "NoSchedule"
|
|
||||||
```
|
|
||||||
3. Set `nodeSelector: {kubernetes.io/hostname: <node>}`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q26 · DaemonSet on All Nodes (including Control Plane)
|
|
||||||
- **Concept**: DaemonSets ensure all (or some) nodes run a copy of a pod. To run on control-plane nodes, they must tolerate control-plane taints (`node-role.kubernetes.io/control-plane:NoSchedule`).
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Write `DaemonSet` YAML with toleration:
|
|
||||||
```yaml
|
|
||||||
tolerations:
|
|
||||||
- key: node-role.kubernetes.io/control-plane
|
|
||||||
operator: Exists
|
|
||||||
effect: NoSchedule
|
|
||||||
```
|
|
||||||
2. Apply and verify: `k -n <ns> get pods -o wide -l app=<ds>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q27 · CronJob Configuration
|
|
||||||
- **Concept**: CronJobs create Jobs on a time schedule (`schedule: "*/5 * * * *"`). `concurrencyPolicy: Forbid` ensures jobs do not overlap.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create CronJob: `k -n <ns> create cj <name> --image=busybox:1.36 --schedule='*/5 * * * *' $do -- date > cj.yaml`.
|
|
||||||
2. Add `concurrencyPolicy: Forbid`, `successfulJobsHistoryLimit: 3`, `failedJobsHistoryLimit: 1`, `activeDeadlineSeconds: 30`.
|
|
||||||
3. Trigger manual execution: `k -n <ns> create job <name>-manual --from=cronjob/<name>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q28 · Init Containers
|
|
||||||
- **Concept**: Init containers run to completion sequentially before application containers start.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Define `initContainers` block in Pod spec with shared volume mount (`/work`).
|
|
||||||
2. Init container writes content (`echo 'hello' > /work/index.html`).
|
|
||||||
3. Main container mounts shared volume at `/usr/share/nginx/html`.
|
|
||||||
4. **Verify**: `k -n <ns> exec <pod> -c web -- cat /usr/share/nginx/html/index.html`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q29 · Immutable ConfigMap Rotation
|
|
||||||
- **Concept**: Setting `immutable: true` on ConfigMaps prevents accidental updates and reduces API server load. To update apps, create a new ConfigMap (`v2`) and update Deployment volume references.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create `cm-v2` with `immutable: true`.
|
|
||||||
2. Patch Deployment volume to point to `cm-v2`.
|
|
||||||
3. Monitor rolling update: `k -n <ns> rollout status deploy <dep>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q31 · Parallel Job
|
|
||||||
- **Concept**: Batch Jobs manage batch tasks. `completions` is total tasks to complete; `parallelism` is max concurrent pods.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Define Job with `completions: 4`, `parallelism: 2`, `backoffLimit: 2`, `restartPolicy: Never`.
|
|
||||||
2. Apply and wait: `k -n <ns> wait --for=condition=complete job/<name> --timeout=120s`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q32 · ResourceQuota & LimitRange
|
|
||||||
- **Concept**: `ResourceQuota` enforces aggregate consumption limits in a namespace; `LimitRange` enforces default/min/max resource requests and limits on individual containers.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create quota: `k -n <ns> create quota <name> --hard=pods=4,requests.cpu=1,limits.memory=2Gi`.
|
|
||||||
2. Create LimitRange with `default`, `defaultRequest`, `min`, `max`.
|
|
||||||
3. **Verify**: `k -n <ns> describe quota` and `k -n <ns> describe limitrange`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q107 · [HARD] Anti-Affinity under Strict Quota
|
|
||||||
- **Concept**: Spread replicas across distinct nodes using `podAntiAffinity` while ensuring total resource requests do not breach namespace quotas.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Inspect quota: `k -n <ns> get quota`.
|
|
||||||
2. Configure deployment with `podAntiAffinity` on `topologyKey: kubernetes.io/hostname`.
|
|
||||||
3. Size container requests to fit within namespace quota.
|
|
||||||
4. **Verify**: Each pod runs on a distinct node (`k get pods -o wide`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q108 · [HARD] Rollback Revision and Pause Rollout
|
|
||||||
- **Concept**: `rollout undo --to-revision=N` rolls back to a specific ReplicaSet revision. `rollout pause` halts further container rollout.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Inspect history: `k -n <ns> rollout history deploy <dep>`.
|
|
||||||
2. Undo: `k -n <ns> rollout undo deploy <dep> --to-revision=<rev>`.
|
|
||||||
3. Pause: `k -n <ns> rollout pause deploy <dep>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q109 · [HARD] Native Sidecar Container (K8s 1.28+)
|
|
||||||
- **Concept**: Native sidecar containers are defined in `initContainers` with `restartPolicy: Always`. They start before app containers and keep running throughout the pod's life.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Define initContainer with `restartPolicy: Always` and readiness probe.
|
|
||||||
2. Define main container with liveness probe.
|
|
||||||
3. **Verify**: `k -n <ns> get pod <pod>` shows all containers running.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Domain 3: Services & Networking (20%)
|
|
||||||
|
|
||||||
### Q11 · NetworkPolicy (Ingress Restriction)
|
|
||||||
- **Concept**: By default, pods accept traffic from any source. A `NetworkPolicy` selects target pods with `podSelector` and restricts traffic using ingress/egress rules.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create NetworkPolicy in namespace:
|
|
||||||
```yaml
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: NetworkPolicy
|
|
||||||
metadata: {name: allow-frontend, namespace: marketing}
|
|
||||||
spec:
|
|
||||||
podSelector: {matchLabels: {app: api}}
|
|
||||||
policyTypes: [Ingress]
|
|
||||||
ingress:
|
|
||||||
- from:
|
|
||||||
- podSelector: {matchLabels: {role: frontend}}
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 80
|
|
||||||
```
|
|
||||||
2. **Verify**: `k -n marketing describe netpol allow-frontend`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q12 · NodePort Service
|
|
||||||
- **Concept**: `NodePort` exposes the Service on each Node's IP at a static port (default range: 30000–32767).
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Expose deployment: `k -n web expose deploy web-server --type=NodePort --port=80 --target-port=80 $do > svc.yaml`.
|
|
||||||
2. Edit `spec.ports[0].nodePort: 30080`.
|
|
||||||
3. Apply and verify: `k -n web get svc web-server` and `k -n web get endpoints web-server`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q13 · Ingress Routing
|
|
||||||
- **Concept**: Ingress manages external HTTP/HTTPS routing to cluster Services based on hostname and URI path prefixes.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create Ingress imperatively:
|
|
||||||
```bash
|
|
||||||
k -n web create ingress web-ingress \
|
|
||||||
--class=nginx \
|
|
||||||
--rule="order.example.com/api*=order-service:8080"
|
|
||||||
```
|
|
||||||
2. **Verify**: `k -n web describe ingress web-ingress`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q14 · Cluster DNS Querying
|
|
||||||
- **Concept**: Services get DNS names in the format `<service>.<ns>.svc.cluster.local`. Pods with IP `a.b.c.d` get `<a-b-c-d>.<ns>.pod.cluster.local`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Run transient debug pod: `k run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup web-service.apps.svc.cluster.local`.
|
|
||||||
2. Query pod IP record: `nslookup 10-244-1-5.apps.pod.cluster.local`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q22 · Gateway API Configuration
|
|
||||||
- **Concept**: Gateway API is the modern successor to Ingress. `GatewayClass` defines the controller, `Gateway` defines listeners/ports, and `HTTPRoute` defines routing rules.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create `GatewayClass` (`controllerName: example.com/gw`).
|
|
||||||
2. Create `Gateway` with HTTP listener on port 80.
|
|
||||||
3. Create `HTTPRoute` attaching to the Gateway with path prefix matching `/api` forwarding to target service.
|
|
||||||
4. **Verify**: `k -n <ns> get gateway,httproute`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q30 · Named Ports in Service
|
|
||||||
- **Concept**: Services can map `targetPort` to the `name` of a container port rather than a numeric port.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. In Pod/Deployment container: `ports: [{name: http-web, containerPort: 80}]`.
|
|
||||||
2. In Service: `ports: [{name: http, port: 8080, targetPort: http-web}]`.
|
|
||||||
3. **Verify**: `k -n <ns> get endpoints <svc>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q42 · Egress-Only NetworkPolicy
|
|
||||||
- **Concept**: Egress NetworkPolicies restrict outbound traffic from pods to specific IP CIDR blocks or namespaces.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Define policy with `policyTypes: [Egress]`.
|
|
||||||
2. Add `egress: [{to: [{ipBlock: {cidr: 192.168.1.0/24}}], ports: [{port: 443}]}]`.
|
|
||||||
3. **Verify**: `k -n <ns> describe netpol <name>`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q43 · Headless & ExternalName Services
|
|
||||||
- **Concept**: `clusterIP: None` (Headless) returns direct Pod IPs on DNS queries. `type: ExternalName` returns a CNAME pointing to an external domain.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create Headless Service: `spec.clusterIP: None`, `spec.selector: {app: db}`.
|
|
||||||
2. Create ExternalName Service: `spec.type: ExternalName`, `spec.externalName: api.partner.com`.
|
|
||||||
3. **Verify**: `k -n <ns> get svc`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q111 · [HARD] Zero-Trust Namespace Network Policies
|
|
||||||
- **Concept**: Zero-trust architecture starts with default-deny on both ingress and egress, then explicitly allows DNS to kube-system (port 53 UDP/TCP) and tiered service-to-service communication.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create `default-deny` for both `[Ingress, Egress]`.
|
|
||||||
2. Create `allow-dns` egress policy to `kube-system` on port 53.
|
|
||||||
3. Create `api-ingress` from external labeled namespace.
|
|
||||||
4. Create `api-to-db` egress and `db-ingress` policies on port 5432.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q112 · [HARD] TLS Ingress with Certificate & Fingerprint
|
|
||||||
- **Concept**: Secure Ingress with TLS Secrets containing `tls.crt` and `tls.key`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Generate TLS key and self-signed certificate:
|
|
||||||
```bash
|
|
||||||
openssl req -x509 -newkey rsa:2048 -nodes -days 365 -subj "/CN=<host>" -keyout tls.key -out tls.crt
|
|
||||||
```
|
|
||||||
2. Create TLS secret: `k -n <ns> create secret tls <name>-tls --cert=tls.crt --key=tls.key`.
|
|
||||||
3. Create Ingress with `tls: [{hosts: [<host>], secretName: <name>-tls}]` and annotation `nginx.ingress.kubernetes.io/ssl-redirect: "true"`.
|
|
||||||
4. Extract fingerprint: `openssl x509 -in tls.crt -noout -fingerprint -sha256 | cut -d= -f2 > fp.txt`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q114 · [HARD] Multi-Fault CoreDNS Repair
|
|
||||||
- **Concept**: CoreDNS failures can be caused by blocking NetworkPolicies in `kube-system`, deployment scaled to 0, or bad upstream forwarding in `Corefile`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Check and delete rogue NetworkPolicy in `kube-system`: `k -n kube-system delete netpol maintenance-lockdown`.
|
|
||||||
2. Scale CoreDNS back to 2 replicas: `k -n kube-system scale deploy coredns --replicas=2`.
|
|
||||||
3. Fix `Corefile` in ConfigMap: `k -n kube-system edit cm coredns` (change `forward . <bad-ip>` to `forward . /etc/resolv.conf`).
|
|
||||||
4. Restart: `k -n kube-system rollout restart deploy coredns`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Domain 4: Storage (10%)
|
|
||||||
|
|
||||||
### Q15 · PersistentVolume, PVC & Pod Mounting
|
|
||||||
- **Concept**: `PersistentVolume` (PV) represents physical cluster storage; `PersistentVolumeClaim` (PVC) is a request for storage by a user.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create PV: `capacity: 1Gi`, `accessModes: [ReadWriteOnce]`, `persistentVolumeReclaimPolicy: Retain`, `storageClassName: manual`, `hostPath: {path: /mnt/data}`.
|
|
||||||
2. Create PVC: `storageClassName: manual`, `requests.storage: 500Mi`.
|
|
||||||
3. Create Pod referencing PVC under `spec.volumes` and mounting it in the container.
|
|
||||||
4. **Verify**: `k get pv` and `k -n storage get pvc` show `Bound`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q16 · Default StorageClass & Dynamic Provisioning
|
|
||||||
- **Concept**: When a PVC omits `storageClassName`, Kubernetes provisions storage using the StorageClass annotated with `storageclass.kubernetes.io/is-default-class: "true"`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create new StorageClass with annotation `storageclass.kubernetes.io/is-default-class: "true"`.
|
|
||||||
2. Remove default annotation from old StorageClass: `k annotate sc <old-sc> storageclass.kubernetes.io/is-default-class-`.
|
|
||||||
3. Create PVC without `storageClassName` and attach to Pod.
|
|
||||||
4. **Verify**: Storage is dynamically created and bound upon pod scheduling (`WaitForFirstConsumer`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q35 · Re-bind Released PersistentVolume
|
|
||||||
- **Concept**: When a PVC is deleted, a PV with `Retain` policy enters `Released` status because `spec.claimRef` still points to the old PVC UID. Removing `spec.claimRef` returns it to `Available`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Remove old claimRef: `k patch pv <pv> --type=json -p='[{"op":"remove","path":"/spec/claimRef"}]'`.
|
|
||||||
2. Verify PV is now `Available`: `k get pv <pv>`.
|
|
||||||
3. Create new PVC specifying `volumeName: <pv>`.
|
|
||||||
4. **Verify**: Status transitions back to `Bound`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q36 · Data Persistence Across Pod Restarts
|
|
||||||
- **Concept**: Data written to a PersistentVolume survives pod termination and rescheduling.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create PVC.
|
|
||||||
2. Run writer Pod (`restartPolicy: Never`) writing data to `/data/marker.txt`.
|
|
||||||
3. Wait for writer Pod completion: `k wait --for=jsonpath='{.status.phase}'=Succeeded pod/writer`.
|
|
||||||
4. Run reader Pod mounting the same PVC.
|
|
||||||
5. Verify data: `k exec reader -- cat /data/marker.txt`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q115 · [HARD] Local PV with Node Affinity Matching
|
|
||||||
- **Concept**: Local PersistentVolumes use `nodeAffinity` to bind to specific worker nodes. Pods mounting the claim are automatically scheduled onto the matching node.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Inspect PV labels and node affinity: `k get pv -l media --show-labels`.
|
|
||||||
2. Create PVC matching `selector: {matchLabels: {media: nvme}}`.
|
|
||||||
3. Create Pod mounting the PVC.
|
|
||||||
4. **Verify**: Pod schedules onto the node containing the NVMe disk without explicit `nodeSelector`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q116 · [HARD] Data Migration Between PVCs via Batch Job
|
|
||||||
- **Concept**: Migrating data between volumes requires running a migration Job mounting both the old claim (`src`) and new claim (`dst`).
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Create `new-data` PVC.
|
|
||||||
2. Run batch Job mounting `old-data` to `/src` and `new-data` to `/dst` running `cp -a /src/. /dst/`.
|
|
||||||
3. Wait for Job completion: `k wait --for=condition=complete job/migrate`.
|
|
||||||
4. Update application Pod to reference `new-data` claim.
|
|
||||||
5. Delete `old-data` claim (PV remains retained).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Domain 5: Troubleshooting (30%)
|
|
||||||
|
|
||||||
### Q17 · Node `NotReady` (Kubelet Inactive)
|
|
||||||
- **Concept**: A node reports `NotReady` when the local `kubelet` service is stopped, disabled, or failing to communicate with the API server.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Identify broken node: `k get nodes`.
|
|
||||||
2. SSH / access the node: `podman exec -it <node> bash` or `ssh <node>`.
|
|
||||||
3. Check status: `systemctl status kubelet`.
|
|
||||||
4. Enable & start service: `systemctl enable --now kubelet`.
|
|
||||||
5. Check logs: `journalctl -u kubelet -f --no-pager`.
|
|
||||||
6. **Verify**: Node transitions to `Ready`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q18 · Deployment & Service Misconfiguration
|
|
||||||
- **Concept**: Pods in `ImagePullBackOff` have invalid image names/tags; Services without endpoints have mismatched `spec.selector` or incorrect `targetPort`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Fix Deployment image: `k -n troubleshoot set image deploy payments payments=nginx:1.27`.
|
|
||||||
2. Inspect Service: `k -n troubleshoot get svc payments -o yaml`.
|
|
||||||
3. Fix typo in selector (`app: payments`) and match `targetPort: 80` to container port.
|
|
||||||
4. **Verify**: `k -n troubleshoot get endpointslices -l kubernetes.io/service-name=payments`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q19 · `CrashLoopBackOff` Diagnosis
|
|
||||||
- **Concept**: `CrashLoopBackOff` means the application process started and crashed. Check previous logs with `--previous`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Read crash logs: `k -n troubleshoot logs crasher --previous | tee crasher.log`.
|
|
||||||
2. Locate missing configuration: `FATAL: required environment variable DB_HOST is not set`.
|
|
||||||
3. Export pod YAML, add `env: [{name: DB_HOST, value: db.internal}]`, strip metadata, delete and recreate pod.
|
|
||||||
4. **Verify**: Pod reaches `Running` status and stays healthy.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q20 · Control Plane Component Failure (Kube-Scheduler Static Pod)
|
|
||||||
- **Concept**: Static pods for `kube-apiserver`, `kube-scheduler`, `kube-controller-manager`, and `etcd` live in `/etc/kubernetes/manifests/`. Kubelet watches and restarts them automatically.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Check failing control-plane pod: `k -n kube-system get pods`.
|
|
||||||
2. Check logs: `k -n kube-system logs kube-scheduler-<cp>`.
|
|
||||||
3. SSH into control plane.
|
|
||||||
4. Edit `/etc/kubernetes/manifests/kube-scheduler.yaml`: fix binary command typo (`kube-schedulerr` -> `kube-scheduler`).
|
|
||||||
5. **Verify**: Kubelet reloads the manifest and pods schedule normally.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q37 · CoreDNS Misconfiguration (Invalid Forwarding)
|
|
||||||
- **Concept**: CoreDNS forwards external queries based on the `Corefile` in ConfigMap `coredns` in `kube-system`.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Inspect logs: `k -n kube-system logs -l k8s-app=kube-dns`.
|
|
||||||
2. Edit ConfigMap: `k -n kube-system edit cm coredns`.
|
|
||||||
3. Replace invalid forward IP (`10.255.255.1`) with `/etc/resolv.conf`.
|
|
||||||
4. Restart deployment: `k -n kube-system rollout restart deploy coredns`.
|
|
||||||
5. **Verify**: `k run test-dns --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q38 · Controller-Manager Crash (Invalid Flag)
|
|
||||||
- **Concept**: Invalid CLI flags in static pod manifests prevent components from starting.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Check logs: `k -n kube-system logs kube-controller-manager-<cp>`.
|
|
||||||
2. Error: `invalid flag: --leader-elect-lease-duration=1s`.
|
|
||||||
3. On control plane, edit `/etc/kubernetes/manifests/kube-controller-manager.yaml` and remove the bad flag.
|
|
||||||
4. **Verify**: Controller manager restarts and reconciles deployments and ReplicaSets.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q39 · Kubelet Configuration Drift (Certificate Path)
|
|
||||||
- **Concept**: Kubelet configuration lives at `/var/lib/kubelet/config.yaml`. Broken certificate paths prevent Kubelet from starting.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. On broken worker, inspect `journalctl -u kubelet -n 30 --no-pager`.
|
|
||||||
2. Error: `open /etc/kubernetes/pki/ca-bundle.crt: no such file or directory`.
|
|
||||||
3. In `/var/lib/kubelet/config.yaml`, fix `clientCAFile: /etc/kubernetes/pki/ca.crt`.
|
|
||||||
4. Restart kubelet: `systemctl restart kubelet && systemctl enable kubelet`.
|
|
||||||
5. **Verify**: `systemctl status kubelet` is active.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q117 · [HARD] Multi-Fault Worker Node (Dead Kubelet + Wrong CA + Cordoned)
|
|
||||||
- **Concept**: Killer.sh-style compound failures combine multiple root causes on a single node.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Check node in cluster: `k get node <node>` (shows `NotReady,SchedulingDisabled`).
|
|
||||||
2. Access node, check `systemctl status kubelet`.
|
|
||||||
3. Fix wrong CA file in `/var/lib/kubelet/config.yaml` (`ca-wrong.crt` -> `ca.crt`).
|
|
||||||
4. Start kubelet: `systemctl enable --now kubelet`.
|
|
||||||
5. Exit node back to main terminal.
|
|
||||||
6. **Uncordon** the node: `k uncordon <node>`.
|
|
||||||
7. **Verify**: Test scheduling on the node (`k run test-pod --overrides='{"spec":{"nodeName":"<node>"}}'`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q118 · [HARD] Two Control Plane Components Down Simultaneously
|
|
||||||
- **Concept**: Both `kube-scheduler` and `kube-controller-manager` fail for different reasons (command typo + broken image tag).
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. On control plane, fix `kube-scheduler.yaml` command typo (`kube-schedulerr` -> `kube-scheduler`).
|
|
||||||
2. Fix `kube-controller-manager.yaml` image tag (remove broken suffix).
|
|
||||||
3. **Verify**: `k -n kube-system get pods -w` shows all control-plane pods Running.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q119 · [HARD] Application Failure (4 Compound Reasons)
|
|
||||||
- **Concept**: Quota exhaustion + image tag typo + missing Secret key + broken Service selector.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Patch namespace quota hard limit from 1 to 3 pods.
|
|
||||||
2. Fix deployment image tag to `nginx:1.27`.
|
|
||||||
3. Add missing key to Secret `DATABASE_URL` and restart rollout.
|
|
||||||
4. Fix Service selector and targetPort.
|
|
||||||
5. **Verify**: All 3 deployment replicas are Available with active endpoints.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Q120 · [HARD] Extract Cluster Networking Facts
|
|
||||||
- **Concept**: Extract network configuration directly from control-plane manifests and ConfigMaps.
|
|
||||||
- **Step-by-Step**:
|
|
||||||
1. Pod CIDR: `k -n kube-system get pod -l component=kube-controller-manager -o yaml | grep cluster-cidr`.
|
|
||||||
2. Service CIDR: `k -n kube-system get pod -l component=kube-apiserver -o yaml | grep service-cluster-ip-range`.
|
|
||||||
3. CoreDNS ClusterIP: `k -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}'`.
|
|
||||||
4. Kube-proxy mode: `k -n kube-system get cm kube-proxy -o yaml | grep '^ *mode:'` (default: `iptables`).
|
|
||||||
5. Node PodCIDR: `k get node <node> -o jsonpath='{.spec.podCIDR}'`.
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user