@xdev-asia/xdev-knowledge-mcp 1.0.44 → 1.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/series/luyen-thi/luyen-thi-cka/chapters/01-cluster-architecture/lessons/01-kien-truc-cka-kubeadm.md +133 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/01-cluster-architecture/lessons/02-cluster-upgrade-kubeadm.md +147 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/01-cluster-architecture/lessons/03-rbac-cka.md +152 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/02-workloads-scheduling/lessons/04-deployments-daemonsets-statefulsets.md +186 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/02-workloads-scheduling/lessons/05-scheduling-taints-affinity.md +163 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/03-services-networking/lessons/06-services-endpoints-coredns.md +145 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/03-services-networking/lessons/07-ingress-networkpolicies-cni.md +172 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/04-storage/lessons/08-persistent-volumes-storageclass.md +159 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/05-troubleshooting/lessons/09-etcd-backup-restore.md +149 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/05-troubleshooting/lessons/10-troubleshooting-nodes.md +153 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/05-troubleshooting/lessons/11-troubleshooting-workloads.md +146 -0
- package/content/series/luyen-thi/luyen-thi-cka/chapters/05-troubleshooting/lessons/12-troubleshooting-networking-exam.md +170 -0
- package/content/series/luyen-thi/luyen-thi-cka/index.md +1 -1
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/01-app-design-build/lessons/01-multi-container-pods.md +146 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/01-app-design-build/lessons/02-jobs-cronjobs-resources.md +174 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/02-app-deployment/lessons/03-rolling-updates-rollbacks.md +148 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/02-app-deployment/lessons/04-helm-kustomize.md +181 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/03-app-observability/lessons/05-probes-logging-debugging.md +183 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/04-app-environment-config/lessons/06-configmaps-secrets.md +182 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/04-app-environment-config/lessons/07-securitycontext-pod-security.md +168 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/04-app-environment-config/lessons/08-resources-qos.md +168 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/05-services-networking/lessons/09-services-ingress.md +182 -0
- package/content/series/luyen-thi/luyen-thi-ckad/chapters/05-services-networking/lessons/10-networkpolicies-exam-strategy.md +236 -0
- package/content/series/luyen-thi/luyen-thi-ckad/index.md +1 -1
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/01-kubernetes-fundamentals/lessons/01-kien-truc-kubernetes.md +137 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/01-kubernetes-fundamentals/lessons/02-pods-workloads-controllers.md +142 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/01-kubernetes-fundamentals/lessons/03-services-networking-storage.md +155 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/01-kubernetes-fundamentals/lessons/04-rbac-security.md +137 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/02-container-orchestration/lessons/05-container-runtimes-oci.md +137 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/02-container-orchestration/lessons/06-orchestration-patterns.md +147 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/03-cloud-native-architecture/lessons/07-cloud-native-architecture.md +143 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/04-observability-delivery/lessons/08-observability.md +143 -0
- package/content/series/luyen-thi/luyen-thi-kcna/chapters/04-observability-delivery/lessons/09-helm-gitops-cicd.md +162 -0
- package/content/series/luyen-thi/luyen-thi-kcna/index.md +1 -1
- package/data/quizzes.json +1059 -0
- package/package.json +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ckad-d3-l05
|
|
3
|
+
title: 'Bài 5: Probes, Logging & Debugging'
|
|
4
|
+
slug: 05-probes-logging-debugging
|
|
5
|
+
description: >-
|
|
6
|
+
Liveness, Readiness và Startup Probes với các probe types (httpGet, tcpSocket,
|
|
7
|
+
exec). Kubectl logs, exec, debug và port-forward cho CKAD troubleshooting.
|
|
8
|
+
duration_minutes: 55
|
|
9
|
+
is_free: true
|
|
10
|
+
video_url: null
|
|
11
|
+
sort_order: 5
|
|
12
|
+
section_title: "Domain 3: Application Observability and Maintenance (15%)"
|
|
13
|
+
course:
|
|
14
|
+
id: lt-ckad-series-001
|
|
15
|
+
title: 'Luyện thi CKAD — Certified Kubernetes Application Developer'
|
|
16
|
+
slug: luyen-thi-ckad
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<img src="/storage/uploads/2026/04/k8s-cert-ckad-bai5-probes.png" alt="Liveness, Readiness và Startup Probes — timeline và probe methods" style="max-width: 800px; width: 100%; border-radius: 12px;" />
|
|
20
|
+
|
|
21
|
+
<h2 id="probe-types">1. Ba loại Probe</h2>
|
|
22
|
+
|
|
23
|
+
<table>
|
|
24
|
+
<thead><tr><th>Probe</th><th>Mục đích</th><th>Khi fail?</th></tr></thead>
|
|
25
|
+
<tbody>
|
|
26
|
+
<tr><td><strong>Liveness</strong></td><td>Container có còn "sống" không?</td><td>Container bị restart</td></tr>
|
|
27
|
+
<tr><td><strong>Readiness</strong></td><td>Container có sẵn sàng nhận traffic?</td><td>Removed from Service endpoints (không restart)</td></tr>
|
|
28
|
+
<tr><td><strong>Startup</strong></td><td>App đã khởi động xong chưa?</td><td>Container bị restart (dùng trước liveness check)</td></tr>
|
|
29
|
+
</tbody>
|
|
30
|
+
</table>
|
|
31
|
+
|
|
32
|
+
<pre><code class="language-text">Probe execution timeline:
|
|
33
|
+
|
|
34
|
+
Container starts
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
startupProbe checks (periodically)
|
|
38
|
+
│ success
|
|
39
|
+
▼
|
|
40
|
+
Both livenessProbe & readinessProbe run in parallel
|
|
41
|
+
│ │
|
|
42
|
+
▼ fail ▼ fail
|
|
43
|
+
Container restart Removed from Service
|
|
44
|
+
(pod still running)</code></pre>
|
|
45
|
+
|
|
46
|
+
<h2 id="probe-methods">2. Probe Methods (httpGet, tcpSocket, exec)</h2>
|
|
47
|
+
|
|
48
|
+
<pre><code class="language-text">livenessProbe:
|
|
49
|
+
httpGet: # HTTP GET — success if status 200-399
|
|
50
|
+
path: /healthz
|
|
51
|
+
port: 8080
|
|
52
|
+
httpHeaders:
|
|
53
|
+
- name: Custom-Header
|
|
54
|
+
value: Awesome
|
|
55
|
+
initialDelaySeconds: 15 # Wait before first probe
|
|
56
|
+
periodSeconds: 20 # How often to probe
|
|
57
|
+
timeoutSeconds: 5 # Timeout per probe
|
|
58
|
+
failureThreshold: 3 # Fail count before action
|
|
59
|
+
successThreshold: 1 # Success count to pass
|
|
60
|
+
|
|
61
|
+
readinessProbe:
|
|
62
|
+
tcpSocket: # TCP connection — success if port open
|
|
63
|
+
port: 3306
|
|
64
|
+
initialDelaySeconds: 5
|
|
65
|
+
periodSeconds: 10
|
|
66
|
+
|
|
67
|
+
startupProbe:
|
|
68
|
+
exec: # Run command in container — success if exit 0
|
|
69
|
+
command:
|
|
70
|
+
- cat
|
|
71
|
+
- /tmp/healthy
|
|
72
|
+
failureThreshold: 30 # Allows 30*10s = 5 min to start
|
|
73
|
+
periodSeconds: 10</code></pre>
|
|
74
|
+
|
|
75
|
+
<table>
|
|
76
|
+
<thead><tr><th>Field</th><th>Default</th><th>Ý nghĩa</th></tr></thead>
|
|
77
|
+
<tbody>
|
|
78
|
+
<tr><td><code>initialDelaySeconds</code></td><td>0</td><td>Chờ trước khi probe đầu tiên</td></tr>
|
|
79
|
+
<tr><td><code>periodSeconds</code></td><td>10</td><td>Interval giữa các probes</td></tr>
|
|
80
|
+
<tr><td><code>timeoutSeconds</code></td><td>1</td><td>Timeout của mỗi probe</td></tr>
|
|
81
|
+
<tr><td><code>failureThreshold</code></td><td>3</td><td>Fail bao nhiêu lần thì action</td></tr>
|
|
82
|
+
<tr><td><code>successThreshold</code></td><td>1</td><td>Pass bao nhiêu lần để "healthy"</td></tr>
|
|
83
|
+
</tbody>
|
|
84
|
+
</table>
|
|
85
|
+
|
|
86
|
+
<blockquote><p><strong>Exam tip:</strong> <strong>Startup probe</strong> dùng khi app khởi động lâu (ví dụ: legacy app cần 2 phút load). Set <code>failureThreshold * periodSeconds</code> >= thời gian startup tối đa. Liveness probe không chạy cho đến khi startup probe pass.</p></blockquote>
|
|
87
|
+
|
|
88
|
+
<h2 id="logging">3. Logging & kubectl logs</h2>
|
|
89
|
+
|
|
90
|
+
<pre><code class="language-text"># Xem logs của pod
|
|
91
|
+
kubectl logs podname
|
|
92
|
+
|
|
93
|
+
# Follow logs (tail -f)
|
|
94
|
+
kubectl logs -f podname
|
|
95
|
+
|
|
96
|
+
# Previous container (nếu bị crash)
|
|
97
|
+
kubectl logs podname --previous
|
|
98
|
+
|
|
99
|
+
# Logs của specific container trong multi-container pod
|
|
100
|
+
kubectl logs podname -c container-name
|
|
101
|
+
|
|
102
|
+
# Logs với timestamp
|
|
103
|
+
kubectl logs podname --timestamps
|
|
104
|
+
|
|
105
|
+
# Tail N lines
|
|
106
|
+
kubectl logs podname --tail=100</code></pre>
|
|
107
|
+
|
|
108
|
+
<h2 id="debugging">4. Debugging Commands</h2>
|
|
109
|
+
|
|
110
|
+
<pre><code class="language-text"># Exec vào container
|
|
111
|
+
kubectl exec -it podname -- /bin/bash
|
|
112
|
+
kubectl exec -it podname -c container-name -- sh
|
|
113
|
+
|
|
114
|
+
# Port forward để test service locally
|
|
115
|
+
kubectl port-forward pod/podname 8080:80
|
|
116
|
+
kubectl port-forward service/myservice 8080:80
|
|
117
|
+
|
|
118
|
+
# Ephemeral debug container (khi container không có shell)
|
|
119
|
+
kubectl debug -it podname --image=busybox --target=container-name
|
|
120
|
+
|
|
121
|
+
# Debug a node
|
|
122
|
+
kubectl debug node/worker-1 -it --image=ubuntu
|
|
123
|
+
|
|
124
|
+
# Copy files
|
|
125
|
+
kubectl cp podname:/app/logs/error.log ./error.log
|
|
126
|
+
kubectl cp ./config.yaml podname:/app/config.yaml</code></pre>
|
|
127
|
+
|
|
128
|
+
<blockquote><p><strong>Exam tip:</strong> Khi pod không có shell (distroless image), dùng <code>kubectl debug</code> với ephemeral container. Trong CKAD exam, <code>kubectl exec</code> + <code>kubectl logs</code> là 2 commands debugging quan trọng nhất. Luôn check logs trước khi exec.</p></blockquote>
|
|
129
|
+
|
|
130
|
+
<h2 id="pod-states">5. Common Pod States & Debug</h2>
|
|
131
|
+
|
|
132
|
+
<table>
|
|
133
|
+
<thead><tr><th>Pod State</th><th>Nguyên nhân thường gặp</th><th>Debug command</th></tr></thead>
|
|
134
|
+
<tbody>
|
|
135
|
+
<tr><td><code>CrashLoopBackOff</code></td><td>App crash, bad command, missing config</td><td><code>kubectl logs --previous</code></td></tr>
|
|
136
|
+
<tr><td><code>ImagePullBackOff</code></td><td>Wrong image name, registry auth failure</td><td><code>kubectl describe pod</code></td></tr>
|
|
137
|
+
<tr><td><code>Pending</code></td><td>Insufficient resources, unschedulable</td><td><code>kubectl describe pod</code> → Events</td></tr>
|
|
138
|
+
<tr><td><code>OOMKilled</code></td><td>Memory limit exceeded</td><td><code>kubectl describe pod</code> → Last State</td></tr>
|
|
139
|
+
<tr><td><code>Error</code></td><td>Init container failed, bad entrypoint</td><td><code>kubectl logs -c init-c</code></td></tr>
|
|
140
|
+
</tbody>
|
|
141
|
+
</table>
|
|
142
|
+
|
|
143
|
+
<h2 id="cheatsheet">6. Cheat Sheet</h2>
|
|
144
|
+
|
|
145
|
+
<table>
|
|
146
|
+
<thead><tr><th>Task</th><th>Command</th></tr></thead>
|
|
147
|
+
<tbody>
|
|
148
|
+
<tr><td>Check pod sức khỏe</td><td><code>kubectl describe pod <name></code></td></tr>
|
|
149
|
+
<tr><td>Xem logs crash</td><td><code>kubectl logs <pod> --previous</code></td></tr>
|
|
150
|
+
<tr><td>Shell vào container</td><td><code>kubectl exec -it <pod> -- sh</code></td></tr>
|
|
151
|
+
<tr><td>Test service connectivity</td><td><code>kubectl port-forward svc/<name> 8080:80</code></td></tr>
|
|
152
|
+
<tr><td>Debug distroless container</td><td><code>kubectl debug -it <pod> --image=busybox</code></td></tr>
|
|
153
|
+
</tbody>
|
|
154
|
+
</table>
|
|
155
|
+
|
|
156
|
+
<h2 id="practice">7. Practice Questions</h2>
|
|
157
|
+
|
|
158
|
+
<p><strong>Q1:</strong> A Pod's readinessProbe fails, but the livenessProbe passes. What happens to the Pod?</p>
|
|
159
|
+
<ul>
|
|
160
|
+
<li>A) The Pod is restarted</li>
|
|
161
|
+
<li>B) The Pod is deleted</li>
|
|
162
|
+
<li>C) The Pod remains running but is removed from the Service's endpoint list ✓</li>
|
|
163
|
+
<li>D) The Pod is marked as Failed</li>
|
|
164
|
+
</ul>
|
|
165
|
+
<p><em>Explanation: Readiness probe failure does NOT restart the container. It only removes the Pod from the Service endpoints so no new traffic is routed to it. The Pod keeps running. When the readiness probe passes again, the Pod is re-added to the endpoints.</em></p>
|
|
166
|
+
|
|
167
|
+
<p><strong>Q2:</strong> An application takes 3 minutes to start. Without a startupProbe, the livenessProbe with failureThreshold: 3 and periodSeconds: 10 would kill the container before it finishes starting. How should you configure a startupProbe to allow up to 5 minutes for startup?</p>
|
|
168
|
+
<ul>
|
|
169
|
+
<li>A) startupProbe with failureThreshold: 5 and periodSeconds: 60</li>
|
|
170
|
+
<li>B) startupProbe with failureThreshold: 30 and periodSeconds: 10 ✓</li>
|
|
171
|
+
<li>C) startupProbe with failureThreshold: 300 and periodSeconds: 1</li>
|
|
172
|
+
<li>D) startupProbe with initialDelaySeconds: 300</li>
|
|
173
|
+
</ul>
|
|
174
|
+
<p><em>Explanation: failureThreshold × periodSeconds = maximum startup time. 30 × 10s = 300s = 5 minutes. During this window, the liveness probe is disabled. Once the startup probe succeeds, both liveness and readiness probes activate.</em></p>
|
|
175
|
+
|
|
176
|
+
<p><strong>Q3:</strong> You need to debug a running Pod that uses a distroless container image (no shell available). How do you get a shell for debugging?</p>
|
|
177
|
+
<ul>
|
|
178
|
+
<li>A) kubectl exec -it podname -- /bin/sh</li>
|
|
179
|
+
<li>B) kubectl attach podname -it</li>
|
|
180
|
+
<li>C) kubectl debug -it podname --image=busybox --target=app ✓</li>
|
|
181
|
+
<li>D) kubectl run debug --image=busybox --attach</li>
|
|
182
|
+
</ul>
|
|
183
|
+
<p><em>Explanation: kubectl debug with an ephemeral container injects a debug container (busybox) into the running Pod with access to the same process namespace. The --target flag shares the process namespace with the specified container. This works even when the main container has no shell.</em></p>
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ckad-d4-l06
|
|
3
|
+
title: 'Bài 6: ConfigMaps & Secrets'
|
|
4
|
+
slug: 06-configmaps-secrets
|
|
5
|
+
description: >-
|
|
6
|
+
Tạo và consume ConfigMaps và Secrets trong Kubernetes. Inject qua env vars
|
|
7
|
+
(envFrom, valueFrom) và volume mounts. Secret types và security considerations.
|
|
8
|
+
duration_minutes: 55
|
|
9
|
+
is_free: true
|
|
10
|
+
video_url: null
|
|
11
|
+
sort_order: 6
|
|
12
|
+
section_title: "Domain 4: Application Environment, Configuration and Security (25%)"
|
|
13
|
+
course:
|
|
14
|
+
id: lt-ckad-series-001
|
|
15
|
+
title: 'Luyện thi CKAD — Certified Kubernetes Application Developer'
|
|
16
|
+
slug: luyen-thi-ckad
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<img src="/storage/uploads/2026/04/k8s-cert-ckad-bai6-configmap-secret.png" alt="ConfigMap và Secret injection — envFrom, valueFrom và Volume Mount" style="max-width: 800px; width: 100%; border-radius: 12px;" />
|
|
20
|
+
|
|
21
|
+
<h2 id="configmap">1. ConfigMap</h2>
|
|
22
|
+
|
|
23
|
+
<p>ConfigMap lưu trữ cặp key-value non-sensitive. Không được mã hóa (plain text).</p>
|
|
24
|
+
|
|
25
|
+
<pre><code class="language-text"># Tạo ConfigMap — Imperative
|
|
26
|
+
kubectl create configmap app-config \
|
|
27
|
+
--from-literal=DB_HOST=mysql \
|
|
28
|
+
--from-literal=DB_PORT=3306
|
|
29
|
+
|
|
30
|
+
kubectl create configmap app-config --from-file=config.properties
|
|
31
|
+
kubectl create configmap app-config --from-env-file=.env
|
|
32
|
+
|
|
33
|
+
# Declarative YAML
|
|
34
|
+
apiVersion: v1
|
|
35
|
+
kind: ConfigMap
|
|
36
|
+
metadata:
|
|
37
|
+
name: app-config
|
|
38
|
+
data:
|
|
39
|
+
DB_HOST: mysql
|
|
40
|
+
DB_PORT: "3306"
|
|
41
|
+
config.properties: |
|
|
42
|
+
server.port=8080
|
|
43
|
+
debug=false</code></pre>
|
|
44
|
+
|
|
45
|
+
<h2 id="secret">2. Secret</h2>
|
|
46
|
+
|
|
47
|
+
<p>Secret lưu trữ sensitive data. Được <strong>base64 encode</strong> (không phải mã hóa — encode thôi!).</p>
|
|
48
|
+
|
|
49
|
+
<pre><code class="language-text"># Tạo Secret — Imperative
|
|
50
|
+
kubectl create secret generic db-secret \
|
|
51
|
+
--from-literal=username=admin \
|
|
52
|
+
--from-literal=password=mypassword
|
|
53
|
+
|
|
54
|
+
kubectl create secret generic db-secret --from-file=credentials.txt
|
|
55
|
+
|
|
56
|
+
# Declarative (base64 encode trước)
|
|
57
|
+
echo -n 'admin' | base64 # YWRtaW4=
|
|
58
|
+
echo -n 'mypassword' | base64 # bXlwYXNzd29yZA==
|
|
59
|
+
|
|
60
|
+
apiVersion: v1
|
|
61
|
+
kind: Secret
|
|
62
|
+
metadata:
|
|
63
|
+
name: db-secret
|
|
64
|
+
type: Opaque
|
|
65
|
+
data:
|
|
66
|
+
username: YWRtaW4=
|
|
67
|
+
password: bXlwYXNzd29yZA==</code></pre>
|
|
68
|
+
|
|
69
|
+
<table>
|
|
70
|
+
<thead><tr><th>Secret Type</th><th>Mục đích</th></tr></thead>
|
|
71
|
+
<tbody>
|
|
72
|
+
<tr><td><code>Opaque</code></td><td>Generic — default type, any key-value data</td></tr>
|
|
73
|
+
<tr><td><code>kubernetes.io/dockerconfigjson</code></td><td>Docker registry credentials</td></tr>
|
|
74
|
+
<tr><td><code>kubernetes.io/tls</code></td><td>TLS certificate và private key</td></tr>
|
|
75
|
+
<tr><td><code>kubernetes.io/service-account-token</code></td><td>ServiceAccount token (auto-created)</td></tr>
|
|
76
|
+
</tbody>
|
|
77
|
+
</table>
|
|
78
|
+
|
|
79
|
+
<blockquote><p><strong>Exam tip:</strong> Secret data là <strong>base64 encoded, không encrypted</strong>. Bất kỳ ai có quyền đọc Secret đều có thể decode: <code>echo 'YWRtaW4=' | base64 -d</code>. Để encrypt at rest, phải enable EncryptionConfiguration — nhưng CKAD không test điều này. Exam thường test: tạo secret và inject vào Pod.</p></blockquote>
|
|
80
|
+
|
|
81
|
+
<h2 id="inject-env">3. Inject qua Environment Variables</h2>
|
|
82
|
+
|
|
83
|
+
<pre><code class="language-text">spec:
|
|
84
|
+
containers:
|
|
85
|
+
- name: app
|
|
86
|
+
image: myapp
|
|
87
|
+
|
|
88
|
+
# Method 1: envFrom — load ALL keys as env vars
|
|
89
|
+
envFrom:
|
|
90
|
+
- configMapRef:
|
|
91
|
+
name: app-config
|
|
92
|
+
- secretRef:
|
|
93
|
+
name: db-secret
|
|
94
|
+
|
|
95
|
+
# Method 2: valueFrom — load SPECIFIC key
|
|
96
|
+
env:
|
|
97
|
+
- name: DATABASE_HOST
|
|
98
|
+
valueFrom:
|
|
99
|
+
configMapKeyRef:
|
|
100
|
+
name: app-config
|
|
101
|
+
key: DB_HOST
|
|
102
|
+
- name: DB_PASSWORD
|
|
103
|
+
valueFrom:
|
|
104
|
+
secretKeyRef:
|
|
105
|
+
name: db-secret
|
|
106
|
+
key: password</code></pre>
|
|
107
|
+
|
|
108
|
+
<h2 id="inject-volume">4. Inject qua Volume Mount</h2>
|
|
109
|
+
|
|
110
|
+
<pre><code class="language-text">spec:
|
|
111
|
+
volumes:
|
|
112
|
+
- name: config-vol
|
|
113
|
+
configMap:
|
|
114
|
+
name: app-config
|
|
115
|
+
- name: secret-vol
|
|
116
|
+
secret:
|
|
117
|
+
secretName: db-secret
|
|
118
|
+
|
|
119
|
+
containers:
|
|
120
|
+
- name: app
|
|
121
|
+
image: myapp
|
|
122
|
+
volumeMounts:
|
|
123
|
+
- name: config-vol
|
|
124
|
+
mountPath: /etc/config # Each key becomes a file
|
|
125
|
+
- name: secret-vol
|
|
126
|
+
mountPath: /etc/secrets
|
|
127
|
+
readOnly: true
|
|
128
|
+
|
|
129
|
+
# Result: /etc/config/DB_HOST contains "mysql"
|
|
130
|
+
# /etc/secrets/password contains "mypassword" (decoded)</code></pre>
|
|
131
|
+
|
|
132
|
+
<table>
|
|
133
|
+
<thead><tr><th>Method</th><th>Khi dùng</th><th>Auto-update khi CM/Secret đổi?</th></tr></thead>
|
|
134
|
+
<tbody>
|
|
135
|
+
<tr><td><code>envFrom</code> / <code>env.valueFrom</code></td><td>App đọc env vars</td><td>Không (cần restart pod)</td></tr>
|
|
136
|
+
<tr><td>Volume mount</td><td>App đọc từ files, hoặc cần auto-reload</td><td>Có (sau ~1-2 phút)</td></tr>
|
|
137
|
+
</tbody>
|
|
138
|
+
</table>
|
|
139
|
+
|
|
140
|
+
<blockquote><p><strong>Exam tip:</strong> Volume-mounted ConfigMaps/Secrets tự động update khi nguồn thay đổi (sau kubelet sync period ~1 phút). Env vars phải restart pod mới reflect changes. CKAD thường test cả 2 methods — nắm rõ syntax của <code>configMapKeyRef</code> vs <code>configMapRef</code> (có chữ "Key" thì là single key).</p></blockquote>
|
|
141
|
+
|
|
142
|
+
<h2 id="cheatsheet">5. Cheat Sheet</h2>
|
|
143
|
+
|
|
144
|
+
<table>
|
|
145
|
+
<thead><tr><th>Task</th><th>Command / YAML</th></tr></thead>
|
|
146
|
+
<tbody>
|
|
147
|
+
<tr><td>Tạo CM từ literals</td><td><code>kubectl create cm name --from-literal=k=v</code></td></tr>
|
|
148
|
+
<tr><td>Tạo Secret từ literals</td><td><code>kubectl create secret generic n --from-literal=k=v</code></td></tr>
|
|
149
|
+
<tr><td>Load all CM keys as env</td><td><code>envFrom: - configMapRef: name: ...</code></td></tr>
|
|
150
|
+
<tr><td>Load specific key</td><td><code>env: - valueFrom: configMapKeyRef: ...</code></td></tr>
|
|
151
|
+
<tr><td>Mount as files</td><td><code>volumes: configMap/secret + volumeMounts</code></td></tr>
|
|
152
|
+
</tbody>
|
|
153
|
+
</table>
|
|
154
|
+
|
|
155
|
+
<h2 id="practice">6. Practice Questions</h2>
|
|
156
|
+
|
|
157
|
+
<p><strong>Q1:</strong> A Pod needs to consume ALL key-value pairs from a ConfigMap named "app-settings" as environment variables. Which configuration is correct?</p>
|
|
158
|
+
<ul>
|
|
159
|
+
<li>A) <code>env: - name: APP_SETTINGS valueFrom: configMapRef: name: app-settings</code></li>
|
|
160
|
+
<li>B) <code>envFrom: - configMapRef: name: app-settings</code> ✓</li>
|
|
161
|
+
<li>C) <code>volumes: - configMap: name: app-settings</code></li>
|
|
162
|
+
<li>D) <code>env: - configMapKeyRef: name: app-settings key: "*"</code></li>
|
|
163
|
+
</ul>
|
|
164
|
+
<p><em>Explanation: envFrom with configMapRef loads ALL keys from the ConfigMap as environment variables. env with valueFrom configMapKeyRef only loads ONE specific key. The wildcard "*" syntax does not exist.</em></p>
|
|
165
|
+
|
|
166
|
+
<p><strong>Q2:</strong> You create a Secret with the command: kubectl create secret generic mysecret --from-literal=password=secret123. How is the data stored in etcd?</p>
|
|
167
|
+
<ul>
|
|
168
|
+
<li>A) Plain text: "password=secret123"</li>
|
|
169
|
+
<li>B) AES-256 encrypted</li>
|
|
170
|
+
<li>C) Base64 encoded ✓</li>
|
|
171
|
+
<li>D) SHA-256 hashed</li>
|
|
172
|
+
</ul>
|
|
173
|
+
<p><em>Explanation: By default, Kubernetes stores Secret data as base64 encoded values in etcd — NOT encrypted. Base64 is encoding, not encryption. Anyone with etcd access can decode it. To encrypt at rest, an EncryptionConfiguration must be configured separately.</em></p>
|
|
174
|
+
|
|
175
|
+
<p><strong>Q3:</strong> A ConfigMap is updated with new values. A Pod is using this ConfigMap mounted as a volume at /etc/config. What happens?</p>
|
|
176
|
+
<ul>
|
|
177
|
+
<li>A) The Pod fails immediately because the config changed</li>
|
|
178
|
+
<li>B) The files in /etc/config are automatically updated (with a short delay) ✓</li>
|
|
179
|
+
<li>C) Nothing happens — the Pod must be restarted to see changes</li>
|
|
180
|
+
<li>D) The Pod is restarted automatically by Kubernetes</li>
|
|
181
|
+
</ul>
|
|
182
|
+
<p><em>Explanation: Volume-mounted ConfigMaps are automatically updated when the ConfigMap changes, after the kubelet sync period (typically ~1 minute). Environment variables from ConfigMaps do NOT auto-update — the Pod must be recreated. Applications that watch for file changes can react to these updates without restarts.</em></p>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ckad-d4-l07
|
|
3
|
+
title: 'Bài 7: SecurityContext, Capabilities & ServiceAccounts'
|
|
4
|
+
slug: 07-securitycontext-pod-security
|
|
5
|
+
description: >-
|
|
6
|
+
SecurityContext cho Pod và Container: runAsUser, runAsNonRoot, readOnlyRootFilesystem,
|
|
7
|
+
Linux capabilities. ServiceAccount binding và automountServiceAccountToken.
|
|
8
|
+
duration_minutes: 55
|
|
9
|
+
is_free: true
|
|
10
|
+
video_url: null
|
|
11
|
+
sort_order: 7
|
|
12
|
+
section_title: "Domain 4: Application Environment, Configuration and Security (25%)"
|
|
13
|
+
course:
|
|
14
|
+
id: lt-ckad-series-001
|
|
15
|
+
title: 'Luyện thi CKAD — Certified Kubernetes Application Developer'
|
|
16
|
+
slug: luyen-thi-ckad
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<img src="/storage/uploads/2026/04/k8s-cert-ckad-bai7-security-context.png" alt="SecurityContext — Pod-level vs Container-level, Linux capabilities" style="max-width: 800px; width: 100%; border-radius: 12px;" />
|
|
20
|
+
|
|
21
|
+
<h2 id="securitycontext">1. SecurityContext</h2>
|
|
22
|
+
|
|
23
|
+
<p>SecurityContext định nghĩa các privilege và access control settings cho Pod hoặc Container.</p>
|
|
24
|
+
|
|
25
|
+
<pre><code class="language-text">apiVersion: v1
|
|
26
|
+
kind: Pod
|
|
27
|
+
spec:
|
|
28
|
+
securityContext: # Pod-level: applies to ALL containers
|
|
29
|
+
runAsUser: 1000 # UID để chạy containers
|
|
30
|
+
runAsGroup: 3000 # GID primary group
|
|
31
|
+
fsGroup: 2000 # GID cho mounted volumes
|
|
32
|
+
runAsNonRoot: true # Reject containers that run as root
|
|
33
|
+
|
|
34
|
+
containers:
|
|
35
|
+
- name: app
|
|
36
|
+
image: myapp
|
|
37
|
+
securityContext: # Container-level: overrides pod-level
|
|
38
|
+
allowPrivilegeEscalation: false
|
|
39
|
+
readOnlyRootFilesystem: true # Filesystem là read-only
|
|
40
|
+
capabilities:
|
|
41
|
+
add: ["NET_BIND_SERVICE"] # Add capability
|
|
42
|
+
drop: ["ALL"] # Drop all, add only what needed</code></pre>
|
|
43
|
+
|
|
44
|
+
<table>
|
|
45
|
+
<thead><tr><th>Setting</th><th>Level</th><th>Tác dụng</th></tr></thead>
|
|
46
|
+
<tbody>
|
|
47
|
+
<tr><td><code>runAsUser</code></td><td>Pod/Container</td><td>Chạy process với UID cụ thể</td></tr>
|
|
48
|
+
<tr><td><code>runAsNonRoot</code></td><td>Pod/Container</td><td>Từ chối chạy nếu UID = 0 (root)</td></tr>
|
|
49
|
+
<tr><td><code>readOnlyRootFilesystem</code></td><td>Container</td><td>Mount root filesystem read-only</td></tr>
|
|
50
|
+
<tr><td><code>allowPrivilegeEscalation</code></td><td>Container</td><td>Chặn privilege escalation (sudo etc.)</td></tr>
|
|
51
|
+
<tr><td><code>privileged</code></td><td>Container</td><td>Run as privileged (như root trên host)</td></tr>
|
|
52
|
+
<tr><td><code>fsGroup</code></td><td>Pod</td><td>GID cho volume files (shared volume access)</td></tr>
|
|
53
|
+
</tbody>
|
|
54
|
+
</table>
|
|
55
|
+
|
|
56
|
+
<blockquote><p><strong>Exam tip:</strong> Container-level <code>securityContext</code> <strong>override</strong> Pod-level settings. Nếu Pod có <code>runAsUser: 1000</code> và container có <code>runAsUser: 2000</code>, container đó chạy với UID 2000. Hay bị test: verify user bằng <code>kubectl exec pod -- id</code> hoặc <code>whoami</code>.</p></blockquote>
|
|
57
|
+
|
|
58
|
+
<h2 id="capabilities">2. Linux Capabilities</h2>
|
|
59
|
+
|
|
60
|
+
<pre><code class="language-text">Capabilities cho phép grant specific privileges mà không cần full root.
|
|
61
|
+
|
|
62
|
+
# Ví dụ thường gặp:
|
|
63
|
+
NET_BIND_SERVICE — Bind to port < 1024 (e.g., port 80)
|
|
64
|
+
NET_ADMIN — Network administration (ifconfig etc.)
|
|
65
|
+
SYS_TIME — Modify system clock
|
|
66
|
+
CHOWN — Change file ownership
|
|
67
|
+
SETUID/SETGID — Change user/group ID
|
|
68
|
+
|
|
69
|
+
securityContext:
|
|
70
|
+
capabilities:
|
|
71
|
+
drop: ["ALL"] # Best practice: drop all first
|
|
72
|
+
add: ["NET_BIND_SERVICE"] # Then re-add only what's needed</code></pre>
|
|
73
|
+
|
|
74
|
+
<h2 id="serviceaccount">3. ServiceAccounts</h2>
|
|
75
|
+
|
|
76
|
+
<p>Pod dùng <strong>ServiceAccount</strong> để authenticate với Kubernetes API.</p>
|
|
77
|
+
|
|
78
|
+
<pre><code class="language-text"># Tạo ServiceAccount
|
|
79
|
+
kubectl create serviceaccount my-sa
|
|
80
|
+
|
|
81
|
+
# Bind vào Role
|
|
82
|
+
kubectl create rolebinding my-binding \
|
|
83
|
+
--role=pod-reader \
|
|
84
|
+
--serviceaccount=default:my-sa
|
|
85
|
+
|
|
86
|
+
# Assign SA cho Pod
|
|
87
|
+
spec:
|
|
88
|
+
serviceAccountName: my-sa # Use specific SA
|
|
89
|
+
automountServiceAccountToken: false # Don't auto-mount SA token
|
|
90
|
+
|
|
91
|
+
# Mặc định: default SA được mount tại /var/run/secrets/kubernetes.io/serviceaccount/
|
|
92
|
+
# Token file có thể gọi K8s API từ container</code></pre>
|
|
93
|
+
|
|
94
|
+
<table>
|
|
95
|
+
<thead><tr><th>Concept</th><th>Mô tả</th></tr></thead>
|
|
96
|
+
<tbody>
|
|
97
|
+
<tr><td>Default SA</td><td>Mỗi namespace có sẵn <code>default</code> SA (ít quyền)</td></tr>
|
|
98
|
+
<tr><td>Token mount</td><td>Token tự động mount vào pod nếu không tắt</td></tr>
|
|
99
|
+
<tr><td><code>automountServiceAccountToken: false</code></td><td>Tắt việc mount token (best security practice)</td></tr>
|
|
100
|
+
</tbody>
|
|
101
|
+
</table>
|
|
102
|
+
|
|
103
|
+
<blockquote><p><strong>Exam tip:</strong> Khi Pod cần gọi Kubernetes API (ví dụ: operator pattern), cần ServiceAccount có quyền phù hợp. Nếu Pod không cần gọi API, best practice là set <code>automountServiceAccountToken: false</code>. CKAD thường test: tạo SA, bind role, và set SA trong Pod spec.</p></blockquote>
|
|
104
|
+
|
|
105
|
+
<h2 id="readonly-volume">4. readOnlyRootFilesystem với emptyDir</h2>
|
|
106
|
+
|
|
107
|
+
<pre><code class="language-text"># Khi dùng readOnlyRootFilesystem: true, app KHÔNG ghi vào root FS.
|
|
108
|
+
# Nhưng app vẫn cần ghi temp files → dùng emptyDir volume:
|
|
109
|
+
|
|
110
|
+
spec:
|
|
111
|
+
containers:
|
|
112
|
+
- name: app
|
|
113
|
+
image: myapp
|
|
114
|
+
securityContext:
|
|
115
|
+
readOnlyRootFilesystem: true
|
|
116
|
+
volumeMounts:
|
|
117
|
+
- name: tmp
|
|
118
|
+
mountPath: /tmp # App ghi temp files vào đây
|
|
119
|
+
- name: cache
|
|
120
|
+
mountPath: /app/cache
|
|
121
|
+
volumes:
|
|
122
|
+
- name: tmp
|
|
123
|
+
emptyDir: {}
|
|
124
|
+
- name: cache
|
|
125
|
+
emptyDir: {}</code></pre>
|
|
126
|
+
|
|
127
|
+
<h2 id="cheatsheet">5. Cheat Sheet</h2>
|
|
128
|
+
|
|
129
|
+
<table>
|
|
130
|
+
<thead><tr><th>Task</th><th>YAML / Command</th></tr></thead>
|
|
131
|
+
<tbody>
|
|
132
|
+
<tr><td>Run container as non-root</td><td><code>securityContext: runAsNonRoot: true</code></td></tr>
|
|
133
|
+
<tr><td>Run với specific UID</td><td><code>securityContext: runAsUser: 1000</code></td></tr>
|
|
134
|
+
<tr><td>Read-only filesystem</td><td><code>securityContext: readOnlyRootFilesystem: true</code></td></tr>
|
|
135
|
+
<tr><td>Drop all capabilities</td><td><code>capabilities: drop: ["ALL"]</code></td></tr>
|
|
136
|
+
<tr><td>Gán ServiceAccount</td><td><code>spec: serviceAccountName: my-sa</code></td></tr>
|
|
137
|
+
<tr><td>Verify user trong container</td><td><code>kubectl exec pod -- whoami</code></td></tr>
|
|
138
|
+
</tbody>
|
|
139
|
+
</table>
|
|
140
|
+
|
|
141
|
+
<h2 id="practice">6. Practice Questions</h2>
|
|
142
|
+
|
|
143
|
+
<p><strong>Q1:</strong> A Pod spec has securityContext.runAsUser: 1000 at the Pod level. One container within the Pod has securityContext.runAsUser: 2000. What UID does that container run with?</p>
|
|
144
|
+
<ul>
|
|
145
|
+
<li>A) 0 (root, because Pod-level overrides)</li>
|
|
146
|
+
<li>B) 1000 (Pod-level takes priority)</li>
|
|
147
|
+
<li>C) 2000 (Container-level overrides Pod-level) ✓</li>
|
|
148
|
+
<li>D) Both UIDs simultaneously</li>
|
|
149
|
+
</ul>
|
|
150
|
+
<p><em>Explanation: Container-level securityContext settings override Pod-level settings. The container runs with UID 2000. Other containers in the same Pod without a container-level securityContext.runAsUser would inherit the Pod-level UID of 1000.</em></p>
|
|
151
|
+
|
|
152
|
+
<p><strong>Q2:</strong> An application container needs to bind to port 80 (a privileged port below 1024) but should NOT run as root. How do you configure this?</p>
|
|
153
|
+
<ul>
|
|
154
|
+
<li>A) Set securityContext.privileged: true</li>
|
|
155
|
+
<li>B) Set securityContext.runAsUser: 0</li>
|
|
156
|
+
<li>C) Add NET_BIND_SERVICE capability while dropping ALL others ✓</li>
|
|
157
|
+
<li>D) Use a NodePort Service instead of port 80</li>
|
|
158
|
+
</ul>
|
|
159
|
+
<p><em>Explanation: Linux capabilities allow granular privilege grants. NET_BIND_SERVICE allows binding to ports below 1024 without full root. Best practice is to drop ALL capabilities first, then add only what's needed: capabilities: { drop: ["ALL"], add: ["NET_BIND_SERVICE"] }.</em></p>
|
|
160
|
+
|
|
161
|
+
<p><strong>Q3:</strong> A Pod is running with readOnlyRootFilesystem: true, but the application tries to write to /tmp and fails. What is the best solution?</p>
|
|
162
|
+
<ul>
|
|
163
|
+
<li>A) Remove readOnlyRootFilesystem: true</li>
|
|
164
|
+
<li>B) Set securityContext.privileged: true</li>
|
|
165
|
+
<li>C) Mount an emptyDir volume at /tmp ✓</li>
|
|
166
|
+
<li>D) Use a ConfigMap mounted at /tmp</li>
|
|
167
|
+
</ul>
|
|
168
|
+
<p><em>Explanation: readOnlyRootFilesystem prevents writes to the container's filesystem, but emptyDir volumes are separate writable mounts. By mounting an emptyDir at /tmp, the application can write temp files there while the root filesystem remains read-only — maintaining the security benefit.</em></p>
|