📘 Day 30:全真模拟考试

🎯 今日目标

在严格限时的模拟比赛环境中,完成一套完整试卷,检验 30 天学习成果。


📋 考试规则

项目 要求
考试时长 180 分钟(3 小时)
总分 100 分
及格线 60 分
优秀线 85 分
环境 3 节点 K8s 集群(已就绪)
允许参考 个人速查手册(不允许联网搜索)
提交物 所有 YAML 文件 + 验证命令输出

📝 全真模拟试卷

第一部分:集群运维(15 分,25 分钟)

题目 1.1(5 分)

1
2
3
4
5
# 检查集群健康状态,输出以下信息:
# - 所有节点状态和版本
# - 所有命名空间中非 Running 的 Pod
# - 证书到期时间
# - 最近 10 条 Warning 事件

题目 1.2(5 分)

1
2
# 对 etcd 做一次快照备份,验证快照完整性
# 将备份文件存放到 /opt/etcd-backup/exam-$(date +%Y%m%d).db

题目 1.3(5 分)

1
2
3
4
# 现有节点 k8s-node1 需要维护,执行:
# - cordon 使其不再调度新 Pod
# - drain 排空已有 Pod
# - 维护完成后 uncordon 恢复

第二部分:工作负载编排(25 分,45 分钟)

题目 2.1:StatefulSet 数据库集群(10 分)

1
2
3
4
5
6
7
8
部署一个 redis 集群:
- 名称:redis-cluster
- 副本数:3
- 镜像:redis:7-alpine
- 启动命令:redis-server --appendonly yes --cluster-enabled yes
- Headless Service:redis-cluster-svc(端口 6379)
- 每个 Pod 独立 PVC:200Mi
- 限制资源:cpu 200m, memory 256Mi

题目 2.2:Deployment 应用层(10 分)

1
2
3
4
5
6
7
8
9
部署一个 API 服务:
- 名称:api-gateway
- 镜像:nginx:alpine
- 副本数:4
- 滚动策略:maxSurge=1, maxUnavailable=0
- livenessProbe: HTTP GET /health, 端口 80
- readinessProbe: HTTP GET /ready, 端口 80
- 资源:requests cpu=100m mem=128Mi, limits cpu=300m mem=256Mi
- Service:ClusterIP,端口 80

题目 2.3:DaemonSet + CronJob(5 分)

1
2
3
4
- DaemonSet node-monitor:busybox:1.36,每 10 秒输出主机名和时间
- CronJob cluster-health:每 5 分钟检查一次 DNS 解析,
命令:nslookup kubernetes.default
保留最近 3 个成功的 Job

第三部分:网络配置(20 分,35 分钟)

题目 3.1:Ingress 路由(10 分)

1
2
3
4
5
创建 Ingress 规则,路由到题 2.2 的 api-gateway:
- 域名:api.exam.com
- /v1/stats → api-gateway:80/stats
- /v1/health → api-gateway:80/health
- 配置 rewrite-target 注解

题目 3.2:NetworkPolicy 网络隔离(10 分)

1
2
3
4
5
6
在 exam 命名空间中创建网络策略:
- 默认拒绝所有入站流量
- 允许 Ingress Controller → api-gateway:80
- 允许 api-gateway → redis-cluster:6379
- 允许所有 Pod → CoreDNS(kube-system, UDP 53)
- 验证:从 api-gateway 可访问 redis,从外部 Pod 不可访问

第四部分:存储与配置(20 分,35 分钟)

题目 4.1:配置管理(8 分)

1
2
3
4
5
6
7
- ConfigMap api-config:
LOG_LEVEL=info, MAX_CONNECTIONS=500, TIMEOUT=30s
- Secret api-secret:
DB_PASSWORD=exam-secret-2024, JWT_KEY=jwt-key-exam
- 修改 Deployment api-gateway:
envFrom 注入 api-config
valueFrom 注入 DB_PASSWORD → DATABASE_PASSWORD

题目 4.2:持久存储(12 分)

1
2
3
4
5
6
- 创建 hostPath PV pv-exam(容器 2Gi, RWO, /data/exam-pv)
- 创建 PVC pvc-exam(请求 1Gi, RWO)
- 验证 PV 和 PVC 绑定
- 创建一个 Pod exam-storage 挂载 pvc-exam 到 /data:
写入验证文件 /data/exam.txt
删除 Pod 后验证文件仍存在

第五部分:安全与调度(20 分,40 分钟)

题目 5.1:RBAC(10 分)

1
2
3
4
5
6
7
8
9
创建以下权限体系:
- SA exam-admin:对 exam 命名空间全部权限
- SA exam-developer:对 Pod/Deploy/Service/ConfigMap
有 get/list/watch/create/update 权限
对 Secret 和 Pod/log 只有 get/list 权限
- SA exam-viewer:只读所有(get/list/watch)

每个 SA 创建对应的 Role + RoleBinding
验证:kubectl auth can-i --as=... 测试权限差异

题目 5.2:安全与调度(10 分)

1
2
3
4
5
6
7
8
修改 Deployment api-gateway 添加:
- SecurityContext:
runAsUser: 1001, runAsNonRoot: true
allowPrivilegeEscalation: false
drop ALL capabilities
- PodAntiAffinity: 同 app=api-gateway 的 Pod 不在同一节点
- topologySpreadConstraints: 按 hostname 均匀分布
- HPA: CPU 60%, min 2, max 8

📊 评分卡

部分 题目 满分 得分
第一部分 集群运维 15
第二部分 工作负载编排 25
第三部分 网络配置 20
第四部分 存储与配置 20
第五部分 安全与调度 20
总分 100

🏁 考后总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
得分:____ / 100
用时:____ 分钟

各模块得分情况:
- 集群运维:____ / 15
- 工作负载编排:____ / 25
- 网络配置:____ / 20
- 存储与配置:____ / 20
- 安全与调度:____ / 20

失分分析:
1. 最薄弱的模块:________
2. 主要失分原因:________
3. 时间分配是否合理?________

赛后提升计划:
1. ________
2. ________
3. ________

🎉 30 天学习完成

恭喜完成 Kubernetes 容器云 30 天学习计划!

1
2
3
4
5
6
7
8
9
回顾这段旅程:
- M1: 从零搭建了 K8s 集群
- M2: 掌握了 5 种工作负载
- M3: 构建了完整网络架构
- M4: 管理了配置和持久存储
- M5: 实现了智能调度和资源管理
- M6: 加固了集群安全
- M7: 建立了监控和排错体系
- M8: 完成了 8 套赛题模拟

后续建议

  1. 赛前 3 天:每天做一套限时模拟,保持手感
  2. 赛前 1 天:回顾速查手册,不再学新内容
  3. 比赛当天:先通读全部题目,按难度分配时间
  4. 长期:考取 CKA/CKAD 认证,深入源码和 Operator 开发

📋 命令速查

全真模拟考试必备命令速查(赛前 5 分钟默背):

命令 功能 注解
k=kubectl 设置别名 节省 80% 键盘输入量
kubectl create deploy <name> --image=<img> --dry-run=client -o yaml > deploy.yaml YAML 速成 赛题 80% YAML 用此方式生成再修改
kubectl expose deploy <name> --port=<p> --target-port=<tp> --dry-run=client -o yaml Service YAML 速成 不要手写 Service YAML
kubectl apply -f <file>.yaml 部署 优先 apply 而非 create
kubectl get all -n <ns> 全面审计 确认所有资源已创建
kubectl describe pod <pod> | tail -20 秒看 Event 排错第一步
kubectl logs <pod> --tail=20 日志速看 验证应用输出
kubectl exec <pod> -- curl -s http://<svc>:<port> 功能验证 证明服务可达
kubectl create cm <name> --from-literal=k=v --dry-run=client -o yaml ConfigMap 速成 配置注入
kubectl create secret generic <name> --from-literal=k=v --dry-run=client -o yaml Secret 速成 密钥注入
kubectl scale deploy <name> --replicas=<n> 副本调整 注意赛题要求的具体数量
kubectl rollout undo deploy/<name> 回滚 操作失误首选回滚
kubectl get events --field-selector type=Warning Warning 事件 致命错误秒定位
!khistory | grep kubectl 复用历史命令 一条命令多个任务使用

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:kubectl 速查表(打印版) https://kubernetes.io/docs/reference/kubectl/quick-reference/
Kubernetes 官方:命令参考 https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands
kubectl 最佳实践 https://kubernetes.io/docs/reference/kubectl/conventions/

📘 Day 22:Prometheus 监控体系

🎯 今日目标

  • 用 Helm 部署 kube-prometheus-stack
  • 理解 Prometheus 数据采集链路
  • 查看集群核心指标(CPU/Memory/Network)
  • 自定义告警规则

🧠 理论精讲(30 分钟)

Prometheus Stack 架构

1
2
3
4
5
6
7
8
9
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ Prometheus │←──│ ServiceMon. │ │ Grafana │
│ (采集+存储) │ │ (动态目标) │ │ (可视化) │
└──────┬───────┘ └──────────────┘ └──────────────┘

├──→ AlertManager(告警路由)

└──→ node_exporter(节点指标)
kube-state-metrics(K8s 对象指标)

核心指标速查

指标 含义
container_cpu_usage_seconds_total CPU 累计使用
container_memory_working_set_bytes 内存使用量
kube_pod_status_phase Pod 状态
kube_deployment_status_replicas_ready Deploy 就绪副本
node_filesystem_avail_bytes 节点磁盘可用

🔧 动手实操(120 分钟)

练习 22.1:安装 kube-prometheus-stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 添加 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 2. 安装(使用 NodePort 暴露 Grafana)
helm install monitoring prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set grafana.service.type=NodePort \
--set grafana.service.nodePort=30300 \
--set prometheus.service.type=NodePort \
--set prometheus.service.nodePort=30900 \
--set alertmanager.service.type=NodePort \
--set alertmanager.service.nodePort=30903

# 3. 等待所有 Pod 就绪
kubectl get pods -n monitoring -w
# prometheus-xxx, grafana-xxx, alertmanager-xxx, operator-xxx, node-exporter-xxx, kube-state-metrics-xxx

# 4. 查看 Service
kubectl get svc -n monitoring

练习 22.2:访问 Prometheus 和 Grafana

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 获取 Grafana 登录密码
kubectl get secret -n monitoring monitoring-grafana \
-o jsonpath='{.data.admin-password}' | base64 -d
echo

# 2. 访问 Grafana(NodePort 30300)
echo "Grafana: http://<任意节点IP>:30300"
echo "User: admin"
echo "Password: <上面获取的密码>"

# 3. 访问 Prometheus(NodePort 30900)
echo "Prometheus: http://<任意节点IP>:30900"

# 4. 在 Prometheus 中查询一些指标:
# - up(所有目标状态)
# - kube_node_info(节点信息)
# - container_memory_usage_bytes(容器内存)

练习 22.3:ServiceMonitor 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 1. 部署一个带 metrics 的应用
kubectl create ns app-metrics

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: metrics-app
namespace: app-metrics
labels:
app: metrics-app
spec:
replicas: 2
selector:
matchLabels:
app: metrics-app
template:
metadata:
labels:
app: metrics-app
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9113"
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
- name: exporter
image: nginx/nginx-prometheus-exporter:0.11
args:
- -nginx.scrape-uri=http://localhost/nginx_status
ports:
- containerPort: 9113
name: metrics
EOF

kubectl expose deploy metrics-app -n app-metrics --port=9113 --name=metrics-app-svc

# 2. 创建 ServiceMonitor
cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: metrics-app-monitor
namespace: monitoring
spec:
selector:
matchLabels:
app: metrics-app
namespaceSelector:
matchNames:
- app-metrics
endpoints:
- port: metrics
interval: 30s
EOF

# 3. 在 Prometheus Targets 中验证新目标已出现
kubectl port-forward -n monitoring svc/monitoring-prometheus 9090:9090 &
# 浏览器打开 http://localhost:9090/targets

练习 22.4:自定义告警规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 创建 PrometheusRule
cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: custom-alerts
namespace: monitoring
spec:
groups:
- name: pod-alerts
rules:
- alert: HighPodRestarts
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ \$labels.pod }} has high restart rate"
description: "Pod {{ \$labels.pod }} in {{ \$labels.namespace }} restarted {{ \$value }} times in 15min"

- alert: PodCrashLooping
expr: kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} > 0
for: 2m
labels:
severity: critical
annotations:
summary: "Pod {{ \$labels.pod }} is crash looping"
EOF

# 验证规则
kubectl get PrometheusRule -n monitoring
kubectl describe PrometheusRule custom-alerts -n monitoring

🐛 排错练习(30 分钟)

场景:Prometheus 无法采集指标

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 检查 ServiceMonitor 是否创建
kubectl get servicemonitor -A

# 2. 检查 Prometheus 配置是否已加载
kubectl port-forward -n monitoring svc/monitoring-prometheus 9090:9090
# 访问 http://localhost:9090/config 查看 scrape_configs

# 3. 检查 Target 状态
# http://localhost:9090/targets

# 4. 标签是否匹配
kubectl get servicemonitor <name> -o yaml | grep -A10 selector
kubectl get svc <name> -o yaml | grep -A5 labels

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 40 分钟

题目:监控体系部署与配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
【操作要求】

1. 使用 Helm 部署 kube-prometheus-stack 到 monitoring 命名空间
- Grafana NodePort 30300
- Prometheus NodePort 30900

2. 部署示例应用:
- Deployment demo-app(nginx:alpine,2 副本)
- 暴露 80 和 metrics 端口

3. 配置 ServiceMonitor 采集 demo-app 的指标

4. 自定义 PrometheusRule:
- 规则 1:Pod 重启次数 > 3(15分钟内)
- 规则 2:Deployment 副本不达期望数超过 5 分钟

5. 在 Grafana 中:
- 导入 Node Exporter Full 仪表盘(ID: 1860)
- 查看集群 CPU/内存/磁盘使用情况
- 截图保存

6. 验证:
- Prometheus Targets 中包含 demo-app
- 自定义告警规则生效

【评分标准】
- Prometheus Stack 部署成功(25 分)
- ServiceMonitor 正确配置(20 分)
- PrometheusRule 正确(20 分)
- Grafana 仪表盘可用(20 分)
- 整体验证(15 分)

📋 命令速查

命令 功能 注解
kubectl get --raw /metrics 查看 apiserver 指标 原生 Prometheus 格式,kube-state-metrics 的补充
kubectl top nodes 查看节点实时资源用量 依赖 metrics-server
kubectl top pods -A --sort-by=cpu 按 CPU 排序 Pod 用量 --sort-by=memory 可改为内存排序
kubectl port-forward -n monitoring svc/prometheus-server 9090:80 本地访问 Prometheus UI 无 Ingress 时的快捷方式
kubectl port-forward -n monitoring svc/grafana 3000:80 本地访问 Grafana 默认账密 admin/admin
kubectl -n monitoring logs -l app=prometheus --tail=50 查看 Prometheus 日志 Prometheus 启动失败时首要排查
kubectl -n monitoring exec -it prometheus-<pod> -- promtool query instant http://localhost:9090 'up' Prometheus CLI 查询 promtool 是 Prometheus 自带的调试工具
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts 添加 Prometheus Helm 仓库 kube-prometheus-stack 包含 Prometheus+Grafana+AlertManager+NodeExporter
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace 一键安装监控全家桶 Helm Chart 安装是最快捷的方式
kubectl get servicemonitor -A 列出 ServiceMonitor Prometheus Operator CRD,定义采集目标
kubectl get prometheusrules -A 列出告警规则 Prometheus Operator CRD
kubectl get alertmanager -A 列出 AlertManager 实例 Prometheus Operator CRD
kubectl -n kube-system logs -l k8s-app=metrics-server --tail=20 查看 metrics-server 日志 kubectl top 不可用时的排错入口

📚 参考来源

来源 链接 / 说明
Prometheus 官方文档 https://prometheus.io/docs/
Prometheus Operator 文档 https://prometheus-operator.dev/
kube-prometheus-stack Helm Chart https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack
Grafana 官方文档 https://grafana.com/docs/
Kubernetes 官方:监控 https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/
PromQL 教程 https://prometheus.io/docs/prometheus/latest/querying/basics/

📘 Day 23:日志收集与 EFK

🎯 今日目标

  • 理解 K8s 日志层级(容器→节点→集群)
  • 部署 Fluent Bit 做日志采集
  • 掌握 kubectl logs 的全部选项
  • 能排查日志丢失/配置错误

🧠 理论精讲(30 分钟)

K8s 日志三层架构

1
2
3
4
5
6
7
8
9
10
11
┌──────────────────────────────────────┐
│ Pod (Container stdout/stderr) │ ← 容器日志
│ ↓ 写入 │
│ Node (/var/log/containers/*.log) │ ← 节点层
│ ↓ 采集 │
│ Log Agent (DaemonSet: Fluent Bit) │ ← 采集层
│ ↓ 转发 │
│ Elasticsearch / Loki / Kafka │ ← 存储层
│ ↓ 查询 │
│ Kibana / Grafana │ ← 展示层
└──────────────────────────────────────┘

kubectl logs 进阶

选项 用途
--tail=N 最后 N 行
-f / --follow 实时跟踪
--since=5m 最近 5 分钟
--previous / -p 上一次容器的日志
--timestamps 显示时间戳
--all-containers 所有容器日志

🔧 动手实操(120 分钟)

练习 23.1:kubectl logs 精通

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 1. 创建一个多容器 Pod
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: log-demo
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- |
i=0
while true; do
echo "[$(date +%T)][INFO] Application log entry $i"
i=$((i+1))
sleep 2
done
- name: sidecar
image: busybox:1.36
command:
- sh
- -c
- |
while true; do
echo "[$(date +%T)][DEBUG] Sidecar heartbeat"
sleep 5
done
EOF

# 2. 基础操作
kubectl logs log-demo -c app --tail=5
kubectl logs log-demo -c sidecar --tail=3

# 3. 跟踪日志
kubectl logs log-demo -c app -f &
# Ctrl+C 停止

# 4. 查看所有容器日志
kubectl logs log-demo --all-containers --tail=10

# 5. 查看最近 2 分钟的日志
kubectl logs log-demo -c app --since=2m

# 6. 带时间戳
kubectl logs log-demo -c app --timestamps --tail=5

# 7. 查看上一次崩溃的日志
# 先模拟崩溃
kubectl run crash-log --image=busybox:1.36 --restart=Always -- /bin/sh -c 'echo "About to crash"; exit 1'

sleep 15
kubectl logs crash-log --previous
# 输出:About to crash

# 清理
kubectl delete pod log-demo crash-log

练习 23.2:使用 stern 多 Pod 日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# stern 是比 kubectl logs 更好的多 Pod 日志工具

# 1. 安装 stern
# macOS
brew install stern
# Linux
wget https://github.com/stern/stern/releases/download/v1.28.0/stern_1.28.0_linux_amd64.tar.gz
tar -xzf stern_1.28.0_linux_amd64.tar.gz
sudo mv stern /usr/local/bin/

# 2. 创建多副本 Deployment
kubectl create deploy stern-test --image=busybox:1.36 --replicas=3 -- \
sh -c 'while true; do echo "[$(hostname)] log at $(date +%T)"; sleep 3; done'

# 3. stern 同时查看所有副本日志
stern stern-test --tail=5
# 按颜色区分不同 Pod!

# 4. 按标签过滤
stern -l app=stern-test --since=1m

# 5. 清理
kubectl delete deploy stern-test

练习 23.3:Fluent Bit 日志采集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 添加 Fluent Helm 仓库
helm repo add fluent https://fluent.github.io/helm-charts
helm repo update

# 2. 安装 Fluent Bit
helm install fluent-bit fluent/fluent-bit \
--namespace logging \
--create-namespace \
--set config.outputs.es.enabled=false \
--set config.outputs.stdout.enabled=true \
--set config.outputs.stdout.match="*"

# 3. 查看 Fluent Bit Pod
kubectl get pods -n logging
# fluent-bit-xxx(每个节点一个)

# 4. 查看 Fluent Bit 采集的日志(输出到 stdout)
kubectl logs -n logging -l app.kubernetes.io/name=fluent-bit --tail=20

# 5. 验证日志 Pipeline
kubectl get daemonset -n logging
kubectl get configmap -n logging
kubectl describe configmap -n logging fluent-bit

练习 23.4:节点日志直接查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 所有容器的 stdout/stderr 日志在这里
# SSH 到任一 worker 节点:
ls /var/log/containers/
# 每个容器一个 symlink → /var/log/pods/

# 2. 容器日志存储格式
ls /var/log/pods/
# <namespace>_<pod-name>_<pod-uid>/<container-name>/

# 3. 查看 kubelet 日志
sudo journalctl -u kubelet -n 50 --no-pager

# 4. 查看容器运行时日志(containerd)
sudo journalctl -u containerd -n 30 --no-pager

# 5. 查看内核日志
sudo dmesg | tail -30

🐛 排错练习(30 分钟)

场景 1:Pod 日志为空

1
2
3
4
5
6
7
# 可能原因:
# 1. 应用没有输出到 stdout/stderr(写到了文件)
kubectl exec <pod> -- ls /var/log/app/

# 2. 日志被轮转删除了
# 3. 应用还没开始输出
kubectl logs <pod> -f

场景 2:日志过多导致磁盘满

1
2
3
4
5
6
7
8
9
# 检查节点磁盘
df -h

# 检查容器日志大小
du -sh /var/log/containers/* | sort -rh | head -10

# 配置 kubelet 日志轮转(/var/lib/kubelet/config.yaml)
# containerLogMaxSize: "10Mi"
# containerLogMaxFiles: 5

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 35 分钟

题目:日志采集与排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
【操作要求】

1. 部署一个会持续产生日志的应用:
- Deployment log-gen(3 副本,busybox:1.36)
- 每 2 秒输出一条带时间戳的日志
- 日志格式:[LEVEL][TIMESTAMP] message

2. 日志操作:
a. 查看某个 Pod 最后 20 条日志
b. 实时跟踪所有 log-gen Pod 的日志(用 stern 或 kubectl logs -f)
c. 查看最近 3 分钟的日志
d. 模拟 Pod 崩溃,用 --previous 查看崩溃前日志

3. 部署 Fluent Bit(DaemonSet 方式):
- 输出到 stdout(便于验证)
- 在 Fluent Bit 日志中确认采集到了 log-gen 的日志

4. 排查:
- 某个 Pod 日志为空的原因(模拟:应用写日志到文件而非 stdout)
- 给出解决方案

【评分标准】
- log-gen 部署正确(15 分)
- kubectl logs 操作全对(25 分)
- Fluent Bit 部署 + 验证(30 分)
- 排查分析(30 分)

📋 命令速查

命令 功能 注解
kubectl logs <pod> -f 实时跟踪 Pod 日志 等于 tail -f
kubectl logs <pod> --tail=100 最后 100 行日志 避免刷屏
kubectl logs <pod> --since=10m 最近 10 分钟日志 时间范围过滤
kubectl logs <pod> --timestamps 带时间戳的日志 确定日志时间线
kubectl logs <pod> -c <container> 指定容器日志 多容器 Pod 必须用 -c
kubectl logs <pod> --all-containers=true 所有容器日志 一次性查看 Pod 所有容器
kubectl logs <pod> --previous 查看上一次崩溃的日志 容器重启后的历史日志
kubectl logs -l app=<name> --tail=20 按标签批量查看日志 -l 按标签选择器筛选
kubectl logs -l app=<name> --all-containers=true --since=5m 批量日志 + 时间过滤 组合选项精确定位
kubectl stern "app-*" -n <ns> 多 Pod 日志流(stern 工具) 比 kubectl logs -l 更好用的实时多 Pod 日志,需安装 stern
journalctl -u kubelet -f 实时查看 kubelet 节点日志 Pod 启停失败根因在 kubelet
journalctl -u containerd -f 实时查看 containerd 日志 容器运行时层面排错
crictl logs <container-id> 容器日志(CRI 层) kubectl logs 不可用时的备选
kubectl -n logging logs -l app=fluentd --tail=50 查看 Fluentd 日志 日志收集管道故障排查
kubectl -n logging logs -l app=elasticsearch --tail=50 查看 Elasticsearch 日志 ES 集群健康状态检查

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:日志架构 https://kubernetes.io/docs/concepts/cluster-administration/logging/
Kubernetes 官方:kubectl logs https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#logs
stern 多 Pod 日志工具 https://github.com/stern/stern
Fluentd Kubernetes 集成 https://docs.fluentd.org/container-deployment/kubernetes
Elasticsearch 官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
EFK Stack 部署指南 https://github.com/fluent/fluentd-kubernetes-daemonset (Fluentd DaemonSet 官方示例,原 kubernetes/cluster/addons 已在 v1.24+ 移除)

📘 Day 24:故障排查实战

🎯 今日目标

  • 掌握 K8s 系统化排错方法
  • 能独立排查 Pod CrashLoopBackOff
  • 能排查 Service 不通的问题
  • 能排查 PVC 绑定失败

🧠 理论精讲(30 分钟)

系统化排错方法论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
第 1 层:现象确认
kubectl get pods/nodes/svc → 确认哪个对象出问题?

第 2 层:事件查看
kubectl describe <resource> → 看 Events 区域

第 3 层:日志分析
kubectl logs <pod> → 应用日志
journalctl -u kubelet → 系统日志

第 4 层:深入诊断
kubectl exec → 进容器验证
网络: nc / curl / nslookup
存储: ls / df / mount

第 5 层:关联分析
有没有 NetworkPolicy?RBAC?ResourceQuota?

常见故障速查表

现象 可能原因 排查命令
Pending 资源不足/调度失败 describe pod
CrashLoopBackOff 应用崩溃/OOM logs --previous
ImagePullBackOff 镜像问题 describe pod
ContainerCreating 卷挂载/CNI describe pod
NotReady (Node) kubelet 问题 describe node
Service 不通 selector/label get endpoints
PVC Pending 无 PV/SC describe pvc

🔧 动手实操(120 分钟)

练习 24.1:Pod 故障排查

故障 1:CrashLoopBackOff

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 创建故意崩溃的 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: crash-demo
spec:
containers:
- name: bad-app
image: busybox:1.36
command: ["sh", "-c", "echo 'Starting...'; sleep 5; exit 1"]
EOF

# 排查流程:
# Step 1: 确认状态
kubectl get pod crash-demo
# STATUS: CrashLoopBackOff

# Step 2: 查看事件
kubectl describe pod crash-demo | grep -A15 Events
# Back-off restarting failed container

# Step 3: 查看日志(包括上一次)
kubectl logs crash-demo
kubectl logs crash-demo --previous
# Starting...

# Step 4: 结论 → 应用启动后 exit 1,需要检查应用配置
kubectl delete pod crash-demo

故障 2:OOMKilled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: oom-demo
spec:
containers:
- name: mem-hog
image: busybox:1.36
command:
- sh
- -c
- dd if=/dev/zero of=/dev/shm/big bs=50M count=10
resources:
limits:
memory: "100Mi"
EOF

# 排查
kubectl get pod oom-demo -w
# STATUS: OOMKilled

kubectl describe pod oom-demo | grep -A5 "State"
# Reason: OOMKilled

kubectl delete pod oom-demo

故障 3:ImagePullBackOff

1
2
3
4
5
6
7
8
9
kubectl run bad-image --image=notexist/image:v99 --restart=Never

kubectl get pod bad-image
# STATUS: ImagePullBackOff / ErrImagePull

kubectl describe pod bad-image | grep -A5 Events
# Failed to pull image: notexist/image:v99

kubectl delete pod bad-image

练习 24.2:Service 网络故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 模拟 Service 不通的场景
# 1. 创建后端但故意 label 不匹配
kubectl create deploy web-backend --image=nginx:alpine --replicas=2

# 2. 创建 Service 选错误的 label
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: bad-svc
spec:
selector:
app: non-existent # 故意不匹配!
ports:
- port: 80
EOF

# 3. 排查
kubectl get endpoints bad-svc
# ENDPOINTS: <none> ← 问题!

kubectl get pod -l app=web-backend --show-labels
# 实际 label: app=web-backend

kubectl get svc bad-svc -o jsonpath='{.spec.selector}'
# {"app":"non-existent"}

# 4. 修复
kubectl patch svc bad-svc -p '{"spec":{"selector":{"app":"web-backend"}}}'
kubectl get endpoints bad-svc
# 现在有 ENDPOINTS 了

# 5. 清理
kubectl delete deploy web-backend
kubectl delete svc bad-svc

练习 24.3:节点故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 模拟节点问题
# 1. 对节点加污点使其不可调度
kubectl taint node k8s-node1 test=block:NoSchedule

# 2. 创建 Pod 观察现象
kubectl run taint-test --image=nginx:alpine
kubectl get pod taint-test -o wide
# 被调度到其他节点

# 3. 如果所有节点都有污点?
kubectl describe pod taint-test | grep -A10 Events
# Warning FailedScheduling ...

# 4. 排查节点状况
kubectl describe node k8s-node1 | grep -A10 Taints
kubectl describe node k8s-node1 | grep -A5 Conditions
# MemoryPressure / DiskPressure / PIDPressure

# 5. 恢复
kubectl taint node k8s-node1 test-
kubectl delete pod taint-test

练习 24.4:存储故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 模拟 PVC 无法绑定
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: impossible-pvc
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 100Ti # 不可能有的容量
EOF

# 排查
kubectl get pvc impossible-pvc
# STATUS: Pending

kubectl describe pvc impossible-pvc | grep -A5 Events
# "no persistent volumes available..."

kubectl get pv
# 没有 100Ti 的 PV

kubectl delete pvc impossible-pvc

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 40 分钟

题目:故障诊断竞赛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
【场景】以下每个故障独立出现。针对每个故障写出:
1. 排查步骤(具体命令)
2. 可能的根因
3. 解决方案

【故障列表】

故障 1:Pod 状态 CrashLoopBackOff
- Deployment web-app 的 Pod 不断重启
- kubectl get pod 显示 RESTARTS=15

故障 2:Service 无法访问
- 集群内 curl http://api-svc 超时
- kubectl get svc api-svc 正常
- kubectl get endpoints api-svc 为空

故障 3:PVC 一直 Pending
- 创建了 PVC,状态始终 Pending
- kubectl get sc 为空

故障 4:Pod 调度失败
- 4 节点集群,创建 10 副本 Deployment
- 第 8 个 Pod 一直是 Pending
- kubectl describe pod 显示 Insufficient cpu

故障 5:DNS 解析失败
- Pod 内 nslookup kubernetes.default 返回 server can't find
- CoreDNS Pod Running 正常

【评分标准】
- 每个故障:排查步骤(10分) + 根因分析(5分) + 解决方案(5分)
- 总计 100 分

📋 命令速查

命令 功能 注解
kubectl describe pod <pod> | tail -30 Pod Events(排错第一入口) 80% 的故障在 Events 里能找到原因
kubectl get events --sort-by=.lastTimestamp 按时间排序所有事件 全局视角查看集群正在发生什么
kubectl get events -w 实时监听事件 操作时开一个终端 watch,观察连锁反应
kubectl get events --field-selector type=Warning 只看 Warning 事件 过滤 Normal 噪音,聚焦异常
kubectl get pods --field-selector=status.phase=Pending 筛选 Pending Pod 调度失败/镜像拉取失败
kubectl get pods --field-selector=status.phase=Failed 筛选 Failed Pod CrashLoopBackOff/Error/Completed(exit≠0)
kubectl get pods --field-selector=status.phase!=Running 筛选非 Running Pod 一次性找出所有问题 Pod
kubectl describe pod <pod> | grep -A 5 "State:|Ready:|Restart" 容器状态摘要 快速确认容器是 Waiting/Running/Terminated
kubectl describe pod <pod> | grep -B 2 "Exit Code" 查看容器退出码 Exit Code 137=OOMKilled, 143=SIGTERM, 1=应用错误
kubectl logs <pod> --previous 上一次崩溃的日志 CrashLoopBackOff 时当前容器可能还没产生日志
kubectl -n kube-system logs kube-apiserver-<node> apiserver 日志 集群入口故障,大量 5xx/超时根因在此
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50 CoreDNS 日志 DNS 解析超时/失败时的排查
kubectl -n kube-system logs -l k8s-app=calico-node --tail=50 CNI 日志 网络不通、Pod IP 分配失败
journalctl -u kubelet --since "5 min ago" --no-pager kubelet 近 5 分钟日志 无需 --no-pager 短输出更易读
kubectl cluster-info dump | grep -i "error|failed" | head -30 集群诊断 + 错误过滤 输出所有组件的日志摘要
kubectl get componentstatuses 控制平面组件健康 1.19+ 建议用 --raw='/readyz?verbose'
kubectl get nodes -o json | jq '.items[] | {name:.metadata.name, conditions:.status.conditions}' 节点 Conditions JSON 输出 结构化的节点健康状况

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:排错指南 https://kubernetes.io/docs/tasks/debug/
Kubernetes 官方:排错 Pod https://kubernetes.io/docs/tasks/debug/debug-application/debug-pods/
Kubernetes 官方:排错 Service https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/
Kubernetes 官方:排错集群 https://kubernetes.io/docs/tasks/debug/debug-cluster/
Kubernetes 官方:节点健康监控 https://kubernetes.io/docs/tasks/debug/debug-cluster/kubectl-node-summary/
kubectl 排错速查表 https://kubernetes.io/docs/reference/kubectl/quick-reference/

📘 Day 25:赛题模拟 1-3

🎯 今日目标

完成 3 套基础赛题,每套限时 70 分钟,覆盖集群管理 + 工作负载 + 配置管理。


🏆 赛题 1:集群初始化与基础部署(70 分钟)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
【场景】你作为运维工程师,需要在一台新服务器上初始化 K8s 并部署基础应用。

【操作要求】(总分 100 分)

Part A: 集群初始化(30 分)
1. 安装 containerd、kubeadm、kubelet、kubectl
2. 初始化单节点集群:
- kubeadm init --pod-network-cidr=10.244.0.0/16
- 镜像源 registry.aliyuncs.com/google_containers
3. 安装 Flannel CNI:
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
4. 配置 kubectl 自动补全 + k 别名
5. 验证:kubectl get nodes 显示 Ready

Part B: 工作负载部署(30 分)
6. 创建 Deployment nginx-deploy:
- 镜像 nginx:1.25-alpine
- 4 副本
- 资源:requests cpu=100m mem=128Mi, limits cpu=300m mem=256Mi
- livenessProbe: HTTP GET /, 端口 80
- readinessProbe: HTTP GET /, 端口 80
7. 暴露为 NodePort Service,映射到 31080
8. 创建命名空间 dev,在其中创建 Deployment dev-api(nginx:alpine, 2 副本)

Part C: 配置管理(20 分)
9. 创建 ConfigMap app-config,包含 DB_HOST=mysql、DB_PORT=3306
10. 创建 Secret db-secret,包含 DB_PASSWORD=secret123
11. 创建 Pod config-pod(busybox:1.36),用 envFrom 注入 app-config

Part D: 基础运维(20 分)
12. 将 nginx-deploy 从 4 副本缩容到 2 副本
13. 对 nginx-deploy 执行滚动更新到 nginx:1.26-alpine
14. 回滚 nginx-deploy 到最初版本
15. 输出 nginx-deploy 的 rollout history

【验证清单】
□ kubectl get nodes → Ready
□ 所有 Pod Running
□ curl <NodeIP>:31080 → nginx
□ kubectl get endpoints nginx-deploy → 有 Endpoint
□ kubectl rollout history deploy/nginx-deploy → 有记录

🏆 赛题 2:多工作负载编排(70 分钟)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
【场景】为一个微服务系统部署三层架构。

【操作要求】(总分 100 分)

Part A: StatefulSet 数据层(30 分)
1. 创建 Headless Service data-svc(端口 6379)
2. 创建 StatefulSet data-cluster(3 副本):
- 镜像 redis:7-alpine
- 命令 redis-server --appendonly yes
- volumeClaimTemplates: 100Mi
3. 验证 PVC 自动创建:data-cluster-data-0/1/2

Part B: Deployment 应用层(25 分)
4. 创建 Deployment api-service(3 副本,nginx:alpine)
5. 配置滚动更新策略:maxSurge=1, maxUnavailable=0
6. 配置 livenessProbe(HTTP GET /, 端口 80)
7. 配置 readinessProbe(同上)
8. 创建 ClusterIP Service api-svc

Part C: DaemonSet + CronJob(25 分)
9. 创建 DaemonSet node-logger(busybox:1.36):
- 每 10 秒输出节点主机名 + 时间
- 使用 hostPath 写日志到 /tmp/node-logs
10. 创建 CronJob data-backup:
- 每 30 分钟执行一次
- 命令:echo "backup $(date)"
- 保留最近 3 个成功的 Job 历史

Part D: 运维操作(20 分)
11. 对 api-service 执行滚动更新并记录 change-cause
12. 缩容 data-cluster 到 2,再扩容回 3
13. 验证 StatefulSet Pod 顺序(2→1 终止,1→2 启动)
14. 手动触发一次 data-backup CronJob

【验证清单】
□ data-cluster-0/1/2 Running, 独立 PVC
□ api-service 3/3 Ready
□ 每节点一个 node-logger Pod
□ kubectl get cj data-backup → SCHEDULE 正确

🏆 赛题 3:配置与存储综合(70 分钟)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
【场景】为 Web 应用配置完整的配置管理和持久存储方案。

【操作要求】(总分 100 分)

Part A: 配置管理(25 分)
1. 创建 ConfigMap web-config(namespace: web-app):
- APP_ENV=staging
- APP_DEBUG=true
- MAX_UPLOAD_SIZE=10M
2. 创建 Secret web-secret(namespace: web-app):
- DB_PASSWORD=webpass123
- REDIS_PASSWORD=redispwd
- JWT_SECRET=jwt-secret-key

Part B: 存储配置(35 分)
3. 创建 hostPath 类型的 PV(pv-static,1Gi,RWO)
4. 创建 PVC(pvc-web-data,请求 500Mi)
5. 验证 PV 和 PVC 绑定

Part C: 应用部署(25 分)
6. 创建 Deployment web-server(3 副本,nginx:alpine):
- envFrom 注入 web-config
- valueFrom 注入 DB_PASSWORD(命名 DATABASE_PASSWORD)
- 挂载 PVC 到 /usr/share/nginx/html/data
- SecurityContext: runAsUser=1000
7. 创建 NodePort Service,映射到 32080

Part D: 验证(15 分)
8. 向 PVC 挂载目录写入测试文件
9. 删除 Pod 后验证 PVC 数据仍在
10. 验证环境变量注入正确

【验证清单】
□ ConfigMap 和 Secret 存在
□ PV/PVC Bound
□ Pod 环境变量包含 APP_ENV、DATABASE_PASSWORD
□ /usr/share/nginx/html/data 可读写
□ NodePort 可访问

📋 命令速查

该赛题涉及的核心命令速查:

命令 功能 注解
kubectl create deploy <name> --image=<image> --replicas=<n> 创建指定副本数的 Deployment 赛题最常见的开局操作
kubectl expose deploy <name> --port=<p> --target-port=<tp> 为 Deployment 创建 Service 赛题中”暴露服务”的标配命令
kubectl set image deploy/<name> <container>=<new-image> 滚动更新镜像 赛题中”更新应用”操作
kubectl rollout undo deploy/<name> 回滚 Deployment 赛题中”回滚到上一版本”
kubectl scale deploy <name> --replicas=<n> 扩缩副本 赛题中”调整副本数”
kubectl create cm <name> --from-literal=<k>=<v> 创建 ConfigMap 赛题中”创建配置”
kubectl create secret generic <name> --from-literal=<k>=<v> 创建 Secret 赛题中”创建密钥”
kubectl get pods -o wide 查看 Pod 分布 确认 Pod 是否按要求调度到指定节点
kubectl describe pod <pod> | grep -A 5 Conditions 查看 Pod 条件 验证 Pod 是否 Ready
kubectl exec <pod> -- curl -s http://<svc>:<port> 容器内测试服务 验证服务连通性
kubectl logs <pod> --tail=20 查看 Pod 日志 验证应用启动是否正常
kubectl apply -f <file>.yaml 声明式部署 综合赛题一次性部署多个资源

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:kubectl 速查表 https://kubernetes.io/docs/reference/kubectl/quick-reference/
Kubernetes 官方:Deployment https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
Kubernetes 官方:Service https://kubernetes.io/docs/concepts/services-networking/service/
CKA 模拟题 https://killer.sh — 赛题风格最接近的模拟
全国职业院校技能大赛官网 历年容器云赛题规程与样题

📘 Day 26:赛题模拟 4-5

🎯 今日目标

完成 2 套进阶赛题,每套限时 110 分钟,覆盖网络 + 安全 + 调度。


🏆 赛题 4:网络与安全加固(110 分钟)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
【场景】搭建一个安全的微服务系统,实现网络隔离和精细权限控制。

【操作要求】(总分 100 分)

Part A: 三层网络(30 分)
1. 创建命名空间 net-secure
2. 部署三层应用:
a. frontend: Deployment web-ui(nginx:alpine, 2副本)
ClusterIP Service frontend-svc
b. backend: Deployment api-gateway(httpd:alpine, 3副本)
ClusterIP Service backend-svc
c. database: Pod data-db(redis:7-alpine)
ClusterIP Service db-svc

3. 配置 Ingress(NGINX Controller 已安装):
- / → frontend-svc:80
- /api → backend-svc:80
- rewrite-target: /

Part B: NetworkPolicy(30 分)
4. 创建 Deny-All 默认策略(拒绝所有 ingress)
5. 放行规则:
a. Ingress Controller → frontend-svc:80
b. frontend → backend-svc:80
c. backend → db-svc:6379
d. 所有 Pod → CoreDNS(UDP 53)
6. 验证:
- 外部可访问 frontend
- frontend 可访问 backend
- backend 可访问 db
- frontend 不可直接访问 db(跨层访问被阻止)

Part C: RBAC(25 分)
7. 创建 3 个 SA:sa-viewer, sa-dev, sa-admin
8. 创建 Role viewer-role:pods/deployments/svc get/list/watch
9. 创建 Role dev-role:viewer + create/update + pods/log
10. 创建 Role admin-role:全部权限
11. 分别绑定到对应 SA
12. 验证权限差异

Part D: SecurityContext(15 分)
13. 修改 backend Deployment:
- runAsUser: 1001
- runAsNonRoot: true
- allowPrivilegeEscalation: false
- drop ALL capabilities

【验证清单】
□ Ingress 路由正常工作
□ NetworkPolicy 隔离有效
□ RBAC 权限分级正确
□ backend 以非 root 用户运行

🏆 赛题 5:调度与弹性伸缩(110 分钟)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
【场景】设计多租户 K8s 平台的调度和资源管理策略。

【操作要求】(总分 100 分)

Part A: 节点池划分(20 分)
1. 给节点打标签:
- k8s-node1: pool=frontend, zone=az1
- k8s-node2: pool=backend, zone=az1
- k8s-master: pool=management, zone=az1(移除 NoSchedule 污点用于测试)

2. 给 k8s-node1 添加污点 frontend=true:NoSchedule
(只有明确声明 toleration 的 Pod 才能调度到 frontend 节点)

Part B: 调度策略实战(30 分)
3. 创建 Deployment frontend-app(3 副本,nginx:alpine):
- nodeAffinity (required): pool=frontend
- toleration: frontend=true:NoSchedule
- podAntiAffinity (required): 按 hostname 分散

4. 创建 Deployment backend-app(2 副本,httpd:alpine):
- nodeAffinity (required): pool=backend
- topologySpreadConstraints: 按 zone 均匀分布,maxSkew=1

5. 观察调度结果并解释

Part C: 资源管理(30 分)
6. 创建命名空间 project-x
7. ResourceQuota project-x-quota:
- requests.cpu: 2
- requests.memory: 4Gi
- pods: 10
8. LimitRange project-x-limits:
- 默认 requests cpu=100m, mem=128Mi
- 默认 limits cpu=500m, mem=512Mi
- max cpu=1, max mem=1Gi

9. 创建 Deployment quota-test(3 副本):
- requests cpu=600m, mem=256Mi(验证是否能创建成功)
- 如果成功,尝试创建第 2 个相同 Deployment

Part D: HPA(20 分)
10. 创建 Deployment hpa-app(1 副本,nginx:alpine):
- requests cpu=50m
11. 配置 HPA:
- 目标 CPU 50%
- min 1, max 6
12. 生成负载或手动验证 HPA

【验证清单】
□ Pod 按节点标签正确分布
□ frontend-app 有 toleration
□ ResourceQuota 生效
□ HPA 显示目标

📋 命令速查

该赛题涉及的核心命令速查:

命令 功能 注解
kubectl create deploy <name> --image=<image> 创建 Deployment 快速部署应用
kubectl expose deploy <name> --type=NodePort --port=<p> --target-port=<tp> 创建 NodePort Service 外部可通过 节点IP:NodePort 访问
kubectl set image deploy/<name> <container>=<new-image> 更新容器镜像 触发滚动更新
kubectl rollout status deploy/<name> 查看更新进度 等待更新完成
kubectl rollout history deploy/<name> 查看部署历史 确认 revision 数
kubectl rollout undo deploy/<name> 回滚 赛题高频操作
kubectl get pv,pvc 查看存储绑定状态 确认 PVC 是否 Bound
kubectl create cm <name> --from-file=<file> 从文件创建 ConfigMap 配置文件注入
kubectl describe pod <pod> Pod 详情 查看挂载卷/环境变量/事件
kubectl exec <pod> -- cat <path> 验证挂载内容 确认配置是否正确注入
kubectl get events --sort-by=.lastTimestamp 查看集群事件 排错必用

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:kubectl 速查表 https://kubernetes.io/docs/reference/kubectl/quick-reference/
Kubernetes 官方:StatefulSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
Kubernetes 官方:PV/PVC https://kubernetes.io/docs/concepts/storage/persistent-volumes/
CKA 模拟题 https://killer.sh

📘 Day 20:ServiceAccount、镜像安全与 Pod Security

🎯 今日目标

  • 理解 Pod 默认挂载 SA Token 的含义
  • 能用 imagePullSecrets 拉取私有镜像
  • 能用 SecurityContext 控制容器权限
  • 理解 privileged/restricted/baseline 三个级别

🧠 理论精讲(30 分钟)

ServiceAccount 自动挂载

1
2
3
4
5
6
7
8
# 每个 Pod 默认会自动挂载 SA Token:
# /var/run/secrets/kubernetes.io/serviceaccount/token
# /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# /var/run/secrets/kubernetes.io/serviceaccount/namespace

# 可通过以下方式禁用:
spec:
automountServiceAccountToken: false

SecurityContext 可控制的内容

配置 说明
runAsUser / runAsGroup 指定容器运行用户
runAsNonRoot 强制非 root 运行
privileged 特权模式(尽量不用)
allowPrivilegeEscalation 是否允许提权
readOnlyRootFilesystem 根文件系统只读
capabilities Linux Capabilities 管理

Pod Security Standards(PSS)

级别 说明 适用场景
Privileged 无限制 系统组件
Baseline 禁止已知风险(hostPath、特权) 普通应用
Restricted 严格限制(非 root、只读根文件系统) 安全要求高

🔧 动手实操(120 分钟)

练习 20.1:ServiceAccount 与 Token 管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 1. 查看 Pod 默认挂载的 SA Token
kubectl run sa-test --image=busybox:1.36 --restart=Never -- sleep 3600

kubectl exec sa-test -- ls /var/run/secrets/kubernetes.io/serviceaccount/
# ca.crt namespace token

kubectl exec sa-test -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | head -3

# 2. 禁用自动挂载
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: no-sa-mount
spec:
automountServiceAccountToken: false
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
EOF

kubectl exec no-sa-mount -- ls /var/run/secrets/kubernetes.io/serviceaccount/ 2>&1
# ls: /var/run/secrets/kubernetes.io/serviceaccount/: No such file or directory

# 3. 创建自定义 SA
kubectl create sa custom-sa
kubectl run custom-sa-pod --image=busybox:1.36 --restart=Never --overrides='
{
"spec": {
"serviceAccountName": "custom-sa"
}
}' -- sleep 3600

kubectl get pod custom-sa-pod -o jsonpath='{.spec.serviceAccountName}'
# custom-sa

# 清理
kubectl delete pod sa-test no-sa-mount custom-sa-pod
kubectl delete sa custom-sa

练习 20.2:SecurityContext 安全上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 1. 非 root 用户运行
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: non-root-pod
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
containers:
- name: app
image: nginx:alpine
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
EOF

# 验证用户
kubectl exec non-root-pod -- id
# uid=1000 gid=3000

# 2. 设置 ReadOnlyRootFilesystem
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: readonly-pod
spec:
containers:
- name: app
image: nginx:alpine
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: nginx-run
mountPath: /var/run
- name: nginx-cache
mountPath: /var/cache/nginx
volumes:
- name: nginx-run
emptyDir: {}
- name: nginx-cache
emptyDir: {}
EOF

kubectl exec readonly-pod -- touch /test-file
# touch: /test-file: Read-only file system

# 3. Capabilities 管理
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: caps-pod
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
- CHOWN
EOF

# 清理
kubectl delete pod non-root-pod readonly-pod caps-pod

练习 20.3:私有镜像仓库拉取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1. 创建 Docker Registry Secret
kubectl create secret docker-registry private-registry \
--docker-server=registry.cn-hangzhou.aliyuncs.com \
--docker-username=your-username \
--docker-password=your-password \
--docker-email=your@email.com

# 2. 方式一:在 Pod 中指定
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
imagePullSecrets:
- name: private-registry
containers:
- name: app
image: registry.cn-hangzhou.aliyuncs.com/your-ns/your-image:tag
EOF

# 3. 方式二:绑定到 ServiceAccount(推荐)
kubectl patch serviceaccount default -p '{"imagePullSecrets":[{"name":"private-registry"}]}'

# 验证
kubectl get sa default -o yaml | grep -A3 imagePullSecrets

# 4. 清理
kubectl delete pod private-image-pod
kubectl delete secret private-registry
# 恢复默认 SA
kubectl patch serviceaccount default --type=json \
-p='[{"op":"remove","path":"/imagePullSecrets"}]'

🐛 排错练习(30 分钟)

场景 1:镜像拉取失败(ErrImagePull / ImagePullBackOff)

1
2
3
4
5
6
7
8
9
10
11
12
# 排查步骤
# 1. 查看错误详情
kubectl describe pod <pod-name> | grep -A10 Events

# 2. 是否是私有镜像?
# → 检查是否配置了 imagePullSecrets

# 3. Secret 是否正确?
kubectl get secret <secret-name> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | python3 -m json.tool

# 4. 镜像名是否正确?
# registry/namespace/image:tag

场景 2:Pod 启动后立即退出(Permission Denied)

1
2
3
4
5
6
# 可能是因为 SecurityContext 限制了权限
kubectl logs <pod-name>
# Permission denied

# 检查 SecurityContext
kubectl get pod <pod-name> -o yaml | grep -A15 securityContext

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 35 分钟

题目:Pod 安全加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
【操作要求】

1. 创建命名空间 secure-workspace

2. 创建 ServiceAccount secure-runner:
- 禁用默认 Token 自动挂载

3. 创建私有仓库 Secret(模拟):
- Server: registry.internal.com
- Username: ci-bot
- Password: ci-token-123
- 将此 Secret 绑定到 secure-runner SA

4. 创建 Deployment secure-app(2副本,nginx:alpine):
- 使用 secure-runner SA
- SecurityContext:
* runAsUser: 1001
* runAsNonRoot: true
* allowPrivilegeEscalation: false
* readOnlyRootFilesystem: false(nginx 需要写临时文件)
* drop ALL capabilities
- 定义 resources.requests 和 limits
- livenessProbe + readinessProbe

5. 验证:
- SA 没有自动挂载 Token
- 容器以 uid 1001 运行
- 容器不能以 root 运行

【评分标准】
- SA 配置正确(15 分)
- 私有仓库 Secret + 绑定(20 分)
- SecurityContext 配置(35 分)
- 探针配置(15 分)
- 安全验证(15 分)

📋 命令速查

命令 功能 注解
kubectl get sa -A 列出所有 ServiceAccount 每个 NS 有默认 default SA
kubectl describe sa <name> -n <ns> SA 详情 查看关联的 Secrets 和镜像拉取密钥
kubectl create sa <name> -n <ns> 创建 ServiceAccount SA 创建后需绑定 Role 才有权限
kubectl create token <sa> -n <ns> 生成 SA 临时 Token(1.24+) 有时效性,用于外部访问 apiserver
kubectl create token <sa> -n <ns> --duration=1h 指定 Token 有效期 默认 1h,最长 48h
kubectl get pod <pod> -o jsonpath='{.spec.serviceAccountName}' 查看 Pod 使用的 SA 默认使用 default SA
kubectl get podsecurity 查看 Pod Security Admission 配置 1.25+ 替代 PSP,三个等级:privileged/baseline/restricted
kubectl label ns <ns> pod-security.kubernetes.io/enforce=restricted 设置命名空间安全等级 restricted 最严格,禁止特权容器、hostPath 等
kubectl label ns <ns> pod-security.kubernetes.io/warn=baseline 设置安全警告等级 仅警告不拒绝
kubectl get secret | grep <sa>-docker 查找镜像拉取密钥 私有仓库认证
kubectl create secret docker-registry <name> --docker-server=<url> --docker-username=<user> --docker-password=<pass> 创建镜像拉取密钥 在 Pod spec 中通过 imagePullSecrets 引用
kubectl patch sa default -n <ns> -p '{"imagePullSecrets":[{"name":"<secret>"}]}' 给默认 SA 添加镜像拉取密钥 该 NS 所有使用 default SA 的 Pod 自动携带
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}' 列出所有 Pod 的镜像 审计镜像版本
kubectl set image deploy/<name> <container>=<image>@sha256:<digest> 使用镜像摘要更新 比 tag 更安全,防止标签篡改

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:ServiceAccount https://kubernetes.io/docs/concepts/security/service-accounts/
Kubernetes 官方:Pod Security Admission https://kubernetes.io/docs/concepts/security/pod-security-admission/
Kubernetes 官方:Pod 安全标准 https://kubernetes.io/docs/concepts/security/pod-security-standards/
Kubernetes 官方:镜像拉取密钥 https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
Kubernetes 官方:镜像安全最佳实践 https://kubernetes.io/docs/concepts/security/

📘 Day 21:安全综合实战

🎯 今日目标

  • 综合 RBAC + SecurityContext + NetworkPolicy 构建安全体系
  • 完成一次完整的安全审计
  • 排查安全相关故障

🧠 理论精讲(10 分钟)

K8s 安全 4C 模型

1
2
3
4
5
6
Cloud → Cluster → Container → Code
↓ ↓ ↓ ↓
云安全 集群安全 容器安全 应用安全
(防火墙) (RBAC) (Seccomp) (输入校验)
(NetworkPolicy)
(etcd 加密)

🔧 动手实操(150 分钟)

练习 21.1:安全应用全栈部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
kubectl create ns secure-stack

# === 1. ServiceAccount ===
kubectl create sa app-runner -n secure-stack

# === 2. RBAC(最小权限) ===
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role
namespace: secure-stack
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["app-config"] # 只能访问特定 ConfigMap
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-runner-binding
namespace: secure-stack
subjects:
- kind: ServiceAccount
name: app-runner
namespace: secure-stack
roleRef:
kind: Role
name: app-role
apiGroup: rbac.authorization.k8s.io
EOF

# === 3. Deploy + SecurityContext ===
kubectl create configmap app-config -n secure-stack \
--from-literal=ENV=production

cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-web
namespace: secure-stack
spec:
replicas: 2
selector:
matchLabels:
app: secure-web
template:
metadata:
labels:
app: secure-web
spec:
serviceAccountName: app-runner
automountServiceAccountToken: false
securityContext:
runAsUser: 1001
runAsNonRoot: true
fsGroup: 1001
containers:
- name: web
image: nginx:alpine
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /
port: 80
readinessProbe:
httpGet:
path: /
port: 80
EOF

kubectl expose deploy secure-web -n secure-stack --port=80

# === 4. NetworkPolicy ===
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: secure-web-policy
namespace: secure-stack
spec:
podSelector:
matchLabels:
app: secure-web
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 80
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
EOF

# 验证部署
kubectl get all -n secure-stack
kubectl get networkpolicy -n secure-stack

练习 21.2:安全审计检查清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 检查特权容器
kubectl get pods --all-namespaces -o json | \
jq '.items[] | select(.spec.containers[].securityContext.privileged==true) | .metadata.name'

# 2. 检查以 root 运行的容器
kubectl get pods --all-namespaces -o json | \
jq '[.items[] | select(.spec.containers[].securityContext.runAsNonRoot!=true)] | length'

# 3. 检查未设资源限制的 Pod
kubectl get pods --all-namespaces -o json | \
jq '.items[] | select(.spec.containers[].resources.requests==null) | "\(.metadata.namespace)/\(.metadata.name)"'

# 4. 检查过于宽泛的 RBAC 权限
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.subjects[]?.kind=="User" and .subjects[]?.name=="system:anonymous")'

# 5. 检查 Secret 是否明文(检查 etcd 加密状态)
kubectl get --raw=/api/v1/secrets | jq '.items[].type' | sort | uniq -c

# 6. 检查是否有 Pod 挂载了 hostPath
kubectl get pods --all-namespaces -o json | \
jq '.items[] | select(.spec.volumes[]?.hostPath!=null) | "\(.metadata.namespace)/\(.metadata.name)"'

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 40 分钟

题目:安全加固综合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
【初始环境】:已有的 Deployment app-insecure 存在安全问题

【操作要求】

1. 安全审计(找出以下问题):
- 容器以 root 运行
- 没有资源限制
- 使用 default SA(权限过大)
- 没有 NetworkPolicy
- 挂载了 hostPath

2. 安全加固:
a. 创建专用 SA app-secure,禁用 auto-mount
b. 创建最小权限 Role(只读自己的 ConfigMap)
c. SecurityContext:
- runAsUser 1001, runAsNonRoot
- drop ALL capabilities
- readOnlyRootFilesystem(需补充 emptyDir 给 tmp)
d. 添加资源限制
e. 移除 hostPath,改用 emptyDir 或 PVC
f. 创建 NetworkPolicy:
- Ingress:只允许同命名空间 + Ingress Controller
- Egress:只允许 DNS + 同命名空间

3. 验证:
- kubectl auth can-i 验证 SA 权限
- kubectl exec 验证非 root 运行
- 验证 NetworkPolicy 隔离

【评分标准】
- 安全审计发现所有问题(20 分)
- SA + RBAC 配置(20 分)
- SecurityContext 加固(25 分)
- NetworkPolicy 配置(20 分)
- hostPath 移除(15 分)

📋 命令速查

命令 功能 注解
kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa> 列出 SA 的完整权限 RBAC 问题排查第一命令
kubectl get role,rolebinding,clusterrole,clusterrolebinding -A RBAC 全貌 安全审计时快速扫描所有权限配置
kubectl get sa -A 所有命名空间的 SA 检查冗余 SA
kubectl get pods -o json | jq '.items[] | {name:.metadata.name, sa:.spec.serviceAccountName, securityContext:.spec.securityContext, containerSecurity:.spec.containers[].securityContext}' 审计 Pod 安全上下文 jq 一次性提取安全相关字段
kubectl get pods -o json | jq '[.items[] | select(.spec.containers[].securityContext.privileged==true)]' 查找特权容器 安全审计:特权容器可以逃逸
kubectl get pods -o json | jq '[.items[] | select(.spec.hostNetwork==true)]' 查找使用 hostNetwork 的 Pod 可以监听节点网络接口,高风险
kubectl get pods -o json | jq '[.items[] | select(.spec.volumes[]?.hostPath)]' 查找挂载 hostPath 的 Pod 可以访问节点文件系统
kubectl -n kube-system get cm kubeadm-config -o jsonpath='{.data.ClusterConfiguration}' | grep -A 5 "apiServer" 查看 apiserver 启动参数 确认准入控制器和加密配置
kubectl get --raw /apis/authorization.k8s.io/v1/selfsubjectaccessreviews 自检 API 访问 编程式权限检查的 API 版

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:安全总览 https://kubernetes.io/docs/concepts/security/
Kubernetes 官方:安全最佳实践 https://kubernetes.io/docs/concepts/security/security-checklist/
Kubernetes 官方:RBAC 最佳实践 https://kubernetes.io/docs/concepts/security/rbac-good-practices/
NSA/CISA Kubernetes 加固指南 https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF
CIS Kubernetes Benchmark https://www.cisecurity.org/benchmark/kubernetes

📘 Day 15:存储综合实战

🎯 今日目标

  • 为有状态应用配置完整存储方案
  • ConfigMap → Secret → PVC 联动配置
  • 排查存储相关故障

🧠 理论精讲(10 分钟)

存储方案决策树

1
2
3
4
5
6
7
应用需要存储配置?
├── 非敏感配置 → ConfigMap
├── 敏感配置(密码/证书)→ Secret
└── 持久数据?
├── 临时数据(Pod 删了就丢)→ emptyDir
├── 节点级数据(日志)→ hostPath
└── 持久数据(数据库)→ PVC(+StorageClass 动态供给)

🔧 动手实操(150 分钟)

练习 15.1:WordPress 风格有状态应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
kubectl create ns wp-demo

# 1. 配置管理:ConfigMap
kubectl create configmap wp-config -n wp-demo \
--from-literal=WORDPRESS_DB_HOST=mysql-svc \
--from-literal=WORDPRESS_DB_USER=wp_user \
--from-literal=WORDPRESS_DB_NAME=wordpress \
--from-literal=WORDPRESS_DEBUG=false

# 2. 密钥管理:Secret
kubectl create secret generic wp-secrets -n wp-demo \
--from-literal=WORDPRESS_DB_PASSWORD='W0rdPr3ss!' \
--from-literal=MYSQL_ROOT_PASSWORD='R00tP@ss!'

# 3. 数据库 StatefulSet + PVC
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
namespace: wp-demo
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: wp-demo
spec:
serviceName: mysql-svc
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
envFrom:
- secretRef:
name: wp-secrets
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
EOF

# 4. WordPress Deployment
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
namespace: wp-demo
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:6-apache
envFrom:
- configMapRef:
name: wp-config
env:
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: wp-secrets
key: WORDPRESS_DB_PASSWORD
ports:
- containerPort: 80
volumeMounts:
- name: wp-content
mountPath: /var/www/html/wp-content
volumes:
- name: wp-content
persistentVolumeClaim:
claimName: wp-content-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-content-pvc
namespace: wp-demo
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
EOF

# 5. 服务暴露
kubectl expose deploy wordpress -n wp-demo --port=80
kubectl get all -n wp-demo

练习 15.2:存储故障排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 故障 1:PVC 绑定不上
kubectl get pvc -n wp-demo
# 如果 Pending,排查:
kubectl describe pvc wp-content-pvc -n wp-demo
# 看 Events → 是否有可用的 PV 或 StorageClass?

# 故障 2:Pod 无法挂载 Volume
kubectl get pod -n wp-demo
kubectl describe pod <pod-name> -n wp-demo | grep -A5 Events
# 常见错误:mount failed、permission denied

# 故障 3:ConfigMap 环境变量未注入
kubectl exec <pod> -n wp-demo -- env | grep WORDPRESS
# 如果没有 → 检查 envFrom 配置

# 故障 4:Secret 值不正确
kubectl get secret wp-secrets -n wp-demo -o jsonpath='{.data.WORDPRESS_DB_PASSWORD}' | base64 -d
# 验证解码后的值

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 40 分钟

题目:完整应用配置与存储方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
【操作要求】创建命名空间 cms-app

1. ConfigMap cms-config:
- SITE_NAME="My CMS"
- DB_HOST=cms-db-svc
- CACHE_TTL=3600

2. Secret cms-secrets:
- DB_PASS=db-secret-789
- ADMIN_PASS=admin-secret-456
- JWT_SECRET=jwt-key-012

3. StatefulSet cms-db(2 副本,mysql:8.0):
- 使用 cms-secrets 注入数据库密码
- volumeClaimTemplates 创建 2Gi PVC
- Headless Service cms-db-svc

4. Deployment cms-web(3 副本,nginx:alpine):
- 用 envFrom 注入 cms-config
- 用 valueFrom 注入 DB_PASS(命名 DATABASE_PASSWORD)
- 挂载 PVC cms-web-data(1Gi)到 /usr/share/nginx/html/uploads
- livenessProbe + readinessProbe

5. 验证:
- 所有 PVC 绑定成功
- Pod 环境变量包含 SITE_NAME、DB_HOST、DATABASE_PASSWORD
- cms-db-0 和 cms-db-1 有独立 PVC
- 向 cms-web 的 PVC 写入测试文件

【评分标准】
- ConfigMap/Secret 创建正确(15 分)
- StatefulSet + PVC 正确(30 分)
- Deployment 配置注入正确(30 分)
- PVC 独立验证(15 分)
- 验证全面(10 分)

🧹 环境清理

1
kubectl delete ns wp-demo cms-app

📋 命令速查

命令 功能 注解
kubectl get pv,pvc,sc 存储资源一览 同时查看 PV/PVC/SC 的绑定和供给关系
kubectl get pvc -A --field-selector=status.phase=Pending 查找所有未绑定的 PVC Pending 意味着没有匹配的 PV 或 SC 供给失败
kubectl get events --field-selector=reason=ProvisioningFailed 查看供给失败事件 动态供给失败时的排错入口
kubectl exec <pod> -- df -h | grep /mnt 验证存储挂载容量 确认 PV 正确挂载且容量匹配
kubectl exec <pod> -- cat /proc/mounts | grep /mnt 查看挂载详情 文件系统类型、挂载选项
kubectl exec <pod> -- touch /mnt/data/write-test 验证存储可写 ReadWriteOnce 模式下测试写权限
kubectl exec <pod> -- rm /mnt/data/write-test 验证存储可删除 ReadWriteMany 多 Pod 同时操作验证
kubectl diff -f manifest.yaml 预览修改差异 综合实战中修改 YAML 后先 diff 确认变更
kubectl apply --server-side -f manifest.yaml 服务端 Apply 处理大文件或复杂 CRD 时的备选方案

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:存储最佳实践 https://kubernetes.io/docs/concepts/storage/
Kubernetes 官方:调试 PVC https://kubernetes.io/docs/tasks/debug/debug-application/
Kubernetes 官方:StatefulSet 与存储 https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#using-statefulsets
Kubernetes 存储 SIG https://github.com/kubernetes/community/tree/master/sig-storage

📘 Day 16:调度策略与亲和性

🎯 今日目标

  • 用 nodeSelector 做简单节点选择
  • 用 nodeAffinity 做高级节点亲和
  • 用 podAffinity 让 Pod 靠近部署
  • 用 podAntiAffinity 让 Pod 分散部署
  • 用 topologySpreadConstraints 做均匀分布

🧠 理论精讲(30 分钟)

调度策略对比

策略 作用范围 强制/偏好 典型场景
nodeSelector 节点标签 强制 简单:GPU 节点
nodeAffinity 节点标签 可偏好 复杂:优先 SSD,可退而求其次
podAffinity Pod 标签 可偏好 缓存靠近应用
podAntiAffinity Pod 标签 可偏好 高可用:同一服务分散
Taint/Toleration 节点污点 排斥/许可 专用节点
topologySpreadConstraints 拓扑域 强制 跨可用区均匀分布

nodeAffinity 字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬性要求
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
preferredDuringSchedulingIgnoredDuringExecution: # 软性偏好
- weight: 1
preference:
matchExpressions:
- key: disk
operator: In
values:
- ssd

运算符

操作符 含义
In 在列表中
NotIn 不在列表中
Exists 存在此标签
DoesNotExist 不存在此标签
Gt 大于(数值)
Lt 小于(数值)

🔧 动手实操(120 分钟)

练习 16.1:nodeSelector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 1. 给节点打标签
kubectl label node k8s-node1 disk=ssd
kubectl label node k8s-node2 disk=hdd

# 2. 创建用 nodeSelector 的 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: ssd-pod
spec:
nodeSelector:
disk: ssd
containers:
- name: app
image: nginx:alpine
EOF

# 3. 验证调度到 node1
kubectl get pod ssd-pod -o wide
# NODE: k8s-node1

# 4. 尝试调度到不存在的标签
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
nodeSelector:
gpu: nvidia
containers:
- name: app
image: nginx:alpine
EOF

kubectl get pod gpu-pod
# STATUS: Pending(因为没有节点有 gpu=nvidia)

kubectl describe pod gpu-pod | grep -A3 Events
# Warning FailedScheduling 0/3 nodes are available: ...

# 5. 清理
kubectl delete pod ssd-pod gpu-pod

练习 16.2:nodeAffinity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: affinity-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: disk
operator: In
values:
- ssd
- weight: 50
preference:
matchExpressions:
- key: zone
operator: In
values:
- cn-east
containers:
- name: app
image: nginx:alpine
EOF

# 查看调度决策
kubectl get pod affinity-pod -o wide
# 优先调度到 disk=ssd 的节点

# 即使没有 disk=ssd,Pod 仍能调度(因为是 preferred)
kubectl describe pod affinity-pod | grep -A5 "Node Affinity"

kubectl delete pod affinity-pod

练习 16.3:podAffinity(靠近部署)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 场景:缓存 Pod 和 应用 Pod 部署在同一节点
# 1. 创建缓存 Pod
kubectl run cache --image=redis:7-alpine --labels=app=cache

# 2. 创建应用 Pod 靠近缓存
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: app-with-cache
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cache
topologyKey: kubernetes.io/hostname # 同一节点
containers:
- name: app
image: nginx:alpine
EOF

# 3. 验证两个 Pod 在同一节点
kubectl get pod cache app-with-cache -o wide
# 两个 Pod 的 NODE 相同

# 4. 清理
kubectl delete pod cache app-with-cache

练习 16.4:podAntiAffinity(分散部署)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 场景:高可用 Web 服务分散到不同节点
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-ha
spec:
replicas: 3
selector:
matchLabels:
app: web-ha
template:
metadata:
labels:
app: web-ha
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-ha
topologyKey: kubernetes.io/hostname
containers:
- name: web
image: nginx:alpine
EOF

# 验证 Pod 分布在不同节点(3 节点集群刚好每节点 1 个)
kubectl get pod -l app=web-ha -o wide
# 3 个 Pod 应在 3 个不同节点上

# 如果再扩容会怎样?
kubectl scale deploy web-ha --replicas=4
kubectl get pod -l app=web-ha -o wide
# 第 4 个 Pod Pending(没有第 4 个节点来分散)

kubectl describe pod <pending-pod> | grep -A5 Events

# 清理
kubectl delete deploy web-ha

练习 16.5:topologySpreadConstraints

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 按可用区均匀分布
kubectl label node k8s-master topology.kubernetes.io/zone=zone-a --overwrite
kubectl label node k8s-node1 topology.kubernetes.io/zone=zone-a --overwrite
kubectl label node k8s-node2 topology.kubernetes.io/zone=zone-b --overwrite

cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: spread-demo
spec:
replicas: 6
selector:
matchLabels:
app: spread-demo
template:
metadata:
labels:
app: spread-demo
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: spread-demo
containers:
- name: app
image: nginx:alpine
EOF

# 查看分布
kubectl get pod -l app=spread-demo -o wide
# 预期 zone-a 和 zone-b 各 3 个 Pod(maxSkew=1)

# 清理
kubectl delete deploy spread-demo

🐛 排错练习(30 分钟)

场景:Pod 一直 Pending(调度失败)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 排查清单:
# 1. 查看 Pod 事件
kubectl describe pod <pod-name> | grep -A20 Events

# 常见原因:
# - "0/3 nodes are available: 3 node(s) didn't match node selector"
# → nodeSelector 太严格
# - "0/3 nodes are available: 3 node(s) had taint {xxx}, that the pod didn't tolerate"
# → 节点有污点,Pod 没容忍
# - "0/3 nodes are available: 3 Insufficient cpu/memory"
# → 资源不足

# 2. 检查节点资源
kubectl describe node <node-name> | grep -A5 "Allocated resources"

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 35 分钟

题目:高级调度策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
【初始环境】3 节点集群

【操作要求】

1. 标签准备:
- k8s-node1:env=prod, tier=frontend
- k8s-node2:env=prod, tier=backend
- k8s-master:env=prod, tier=management

2. 部署 frontend Deployment(3 副本):
- podAntiAffinity:同 app=frontend 的 Pod 不在同一节点
- nodeAffinity (required):tier=frontend
- 观察 Pod 会不会 Pending(只有 1 个 frontend 节点但有 3 个副本)

3. 部署 backend Deployment(2 副本):
- nodeAffinity (required):tier=backend
- podAffinity (preferred):靠近 app=cache 的 Pod

4. 部署 cache Pod(1 个):
- nodeAffinity (required):tier=backend
- 无其他限制

5. 观察 backend 是否自动调度到与 cache 同一节点

6. 添加 topologySpreadConstraints:backend 按 zone 均匀分布

【评分标准】
- 标签设置正确(10 分)
- frontend 反亲和正确(25 分)
- backend nodeAffinity 正确(20 分)
- podAffinity 靠近 cache 正确(20 分)
- topologySpreadConstraints 正确(15 分)
- 观察分析完整(10 分)

📋 命令速查

命令 功能 注解
kubectl label node <node> key=value 给节点打标签 配合 nodeSelector 使用,Pod 通过 nodeSelector 精确匹配
kubectl label node <node> key- 删除节点标签 标签名后加 -
kubectl get nodes --show-labels 查看节点和标签 确认标签是否打对
kubectl get nodes -l key=value 按标签筛选节点 -l = --selector,快速找到匹配标签的节点
kubectl taint node <node> key=value:NoSchedule 添加污点(硬排斥) 无对应 Toleration 的 Pod 无法调度
kubectl taint node <node> key=value:PreferNoSchedule 添加软污点 尽量不调度,资源不足时仍可调度
kubectl taint node <node> key=value:NoExecute 添加驱逐级污点 已有 Pod 若未容忍也会被驱逐
kubectl taint node <node> key=value:NoSchedule- 移除污点 末尾加 - 删除对应 Taint
kubectl describe node <node> | grep Taints 查看节点污点 排错时确认节点是否有预期外的污点
kubectl cordon <node> 标记节点不可调度 等同于添加 node.kubernetes.io/unschedulable:NoSchedule
kubectl uncordon <node> 恢复节点可调度 取消 cordon 标记
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data 安全驱逐节点上所有 Pod 节点维护必需;DaemonSet Pod 需 –ignore-daemonsets 跳过
kubectl top nodes 查看节点资源用量 调度决策参考;需安装 metrics-server
kubectl get pods -o wide | grep <node> 查看某节点上的所有 Pod 替代 --field-selector=spec.nodeName=<node>

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:调度与驱逐 https://kubernetes.io/docs/concepts/scheduling-eviction/
Kubernetes 官方:节点亲和性 https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
Kubernetes 官方:污点与容忍 https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/
Kubernetes 官方:Pod 拓扑分布约束 https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
Kubernetes 官方:安全驱逐节点 https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/
0%