📘 Day 17:资源限制、QoS 与 HPA

🎯 今日目标

  • 理解 requests ≠ limits 的区别与作用
  • 能判断 Pod 的 QoS 等级
  • 会用 LimitRange 限制默认资源
  • 会用 ResourceQuota 限制命名空间
  • 用 HPA 实现自动伸缩

🧠 理论精讲(30 分钟)

requests vs limits

参数 含义 调度影响
requests 保证分配的资源 调度器按此值找节点
limits 资源使用上限 超出即被 throttle/杀死
1
2
3
4
5
6
7
resources:
requests:
cpu: "200m" # 保证 0.2 核
memory: "256Mi" # 保证 256M 内存
limits:
cpu: "500m" # 最多用 0.5 核
memory: "512Mi" # 最多用 512M 内存

QoS 等级

等级 条件 驱逐优先级
Guaranteed requests == limits(两者都设且相等) 最低
Burstable requests < limits(至少一个容器设了 requests) 中等
BestEffort 未设任何 requests/limits 最高(最先被驱逐)

HPA 公式

1
期望副本数 = ceil(当前副本数 × (当前指标值 / 目标指标值))

🔧 动手实操(120 分钟)

练习 17.1:requests 与 limits

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
# 1. 创建不同资源规格的 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: app
image: nginx:alpine
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "100m"
memory: "128Mi"
---
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:
- name: app
image: nginx:alpine
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
apiVersion: v1
kind: Pod
metadata:
name: beste-ffort-pod
spec:
containers:
- name: app
image: nginx:alpine
EOF

# 2. 查看 QoS 等级
kubectl get pod guaranteed-pod -o jsonpath='{.status.qosClass}'
echo
# Guaranteed

kubectl get pod burstable-pod -o jsonpath='{.status.qosClass}'
echo
# Burstable

kubectl get pod beste-ffort-pod -o jsonpath='{.status.qosClass}'
echo
# BestEffort

# 3. 模拟内存超限(OOMKilled)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: oom-pod
spec:
containers:
- name: mem-eater
image: busybox:1.36
command:
- sh
- -c
- |
# 分配超过 limit 的内存
dd if=/dev/zero of=/dev/shm/bigfile bs=100M count=10
sleep 3600
resources:
limits:
memory: "50Mi"
EOF

kubectl get pod oom-pod -w
# 观察 RESTARTS 递增,Last State: OOMKilled

kubectl describe pod oom-pod | grep -A5 "Last State"

# 4. 清理
kubectl delete pod guaranteed-pod burstable-pod beste-ffort-pod oom-pod

练习 17.2:LimitRange

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
# 创建命名空间
kubectl create ns resource-lab

# 创建 LimitRange
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: resource-lab
spec:
limits:
- type: Container
default:
cpu: "200m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "1"
memory: "1Gi"
min:
cpu: "50m"
memory: "64Mi"
EOF

# 创建不设 resources 的 Pod(自动应用默认值)
kubectl run auto-pod --image=nginx:alpine -n resource-lab

# 验证自动注入
kubectl get pod auto-pod -n resource-lab -o yaml | grep -A8 resources
# 应有默认的 requests 和 limits

# 尝试创建超过 max 的 Pod(应被拒绝)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: over-limit-pod
namespace: resource-lab
spec:
containers:
- name: app
image: nginx:alpine
resources:
limits:
cpu: "2" # 超过 max.cpu=1
memory: "256Mi"
EOF
# Error: [cpu: Invalid value: "2": must be less than or equal to cpu limit]

# 清理
kubectl delete pod auto-pod -n resource-lab

练习 17.3:ResourceQuota

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
# 创建 ResourceQuota
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: resource-lab
spec:
hard:
requests.cpu: "2"
requests.memory: "2Gi"
limits.cpu: "4"
limits.memory: "4Gi"
pods: "10"
persistentvolumeclaims: "5"
services: "5"
EOF

# 查看配额
kubectl describe quota team-quota -n resource-lab

# 创建 Deployment 测试配额消耗
kubectl create deploy quota-test -n resource-lab --image=nginx:alpine --replicas=5

# 查看配额使用情况
kubectl describe quota team-quota -n resource-lab
# 可以看到 Used vs Hard

# 清理
kubectl delete deploy quota-test -n resource-lab

练习 17.4:HPA 自动伸缩

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
# 0. 确保 metrics-server 已安装
kubectl get pods -n kube-system -l k8s-app=metrics-server
# 如果没有,先安装:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 1. 创建 Deployment(带资源请求)
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-demo
spec:
replicas: 1
selector:
matchLabels:
app: hpa-demo
template:
metadata:
labels:
app: hpa-demo
spec:
containers:
- name: web
image: nginx:alpine
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "128Mi"
EOF

kubectl expose deploy hpa-demo --port=80

# 2. 创建 HPA
kubectl autoscale deployment hpa-demo --cpu=50% --min=1 --max=5

# 3. 查看 HPA
kubectl get hpa
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
# hpa-demo Deployment/hpa-demo 0%/50% 1 5 1

kubectl describe hpa hpa-demo

# 4. 生成负载
kubectl run load-generator --image=busybox:1.36 --rm -it --restart=Never -- \
sh -c 'while true; do wget -q -O- http://hpa-demo; done'

# 在另一个终端观察 HPA
kubectl get hpa hpa-demo -w
# 观察 TARGETS 上升,REPLICAS 自动增加

# 5. Ctrl+C 停止负载,观察缩容
kubectl get hpa hpa-demo -w
# REPLICAS 逐渐减少回 1

# 6. 清理
kubectl delete hpa hpa-demo
kubectl delete deploy hpa-demo
kubectl delete svc hpa-demo

🐛 排错练习(30 分钟)

场景 1:HPA 不工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 排查清单
# 1. metrics-server 是否运行?
kubectl get pods -n kube-system -l k8s-app=metrics-server

# 2. 能否获取到指标?
kubectl top pods
kubectl top nodes

# 3. Deployment 是否设置了 resources.requests?
kubectl get deploy <name> -o yaml | grep -A5 resources
# HPA 基于百分比需要 requests 做基准

# 4. HPA 状态
kubectl describe hpa <name>
# 看 Events 区域的错误信息

场景 2:Pod 被驱逐(Evicted)

1
2
3
4
5
6
# 查看被驱逐的 Pod
kubectl get pods --field-selector=status.phase=Failed

# 查看驱逐原因
kubectl describe pod <evicted-pod>
# The node was low on resource: memory.

🏆 赛题模拟(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
34
35
36
【操作要求】

1. 创建命名空间 resource-exam

2. 创建 LimitRange:
- 容器默认 requests:cpu 100m, memory 128Mi
- 容器默认 limits:cpu 500m, memory 256Mi
- max cpu: 1, max memory: 512Mi

3. 创建 ResourceQuota:
- 最多 10 个 Pod
- 总 requests.cpu 不超过 2 核
- 总 requests.memory 不超过 2Gi

4. 创建 Deployment web-api(2 副本,nginx:alpine):
- requests: cpu 100m, memory 128Mi
- limits: cpu 200m, memory 256Mi
- QoS 应为 Guaranteed?(检查:不是,因为 requests ≠ limits)
- 修改使其达到 Guaranteed

5. 配置 HPA:
- 基于 CPU,目标 60%
- min 2, max 8

6. 验证:
- kubectl describe quota -n resource-exam
- kubectl get hpa
- kubectl get pod 确认 QoS 等级

【评分标准】
- LimitRange 正确(15 分)
- ResourceQuota 正确(15 分)
- QoS Guaranteed 实现(20 分)
- HPA 配置正确(20 分)
- 配额验证(15 分)
- 整体正确性(15 分)

📋 命令速查

命令 功能 注解
kubectl set resources deploy/<name> -c=<container> --limits=cpu=200m,memory=256Mi --requests=cpu=100m,memory=128Mi 设置容器资源限制 触发滚动更新重建 Pod
kubectl describe pod <pod> | grep -A 5 "Requests|Limits" 查看 Pod 资源配置 确认 requests/limits 是否生效
kubectl describe node <node> | grep -A 5 "Allocated" 查看节点已分配资源 CPU/Memory 分配比例,判断节点是否过载
kubectl top pods -A --sort-by=cpu 按 CPU 排序 Pod 用量 找出 CPU 消耗最高的 Pod
kubectl top pods -A --sort-by=memory 按内存排序 Pod 用量 找出内存消耗最高的 Pod
kubectl get pod <pod> -o jsonpath='{.status.qosClass}' 查看 Pod QoS 等级 返回 Guaranteed/Burstable/BestEffort
kubectl get limitrange -A 列出所有 LimitRange 命名空间级默认资源限制
kubectl describe limitrange <name> -n <ns> 查看 LimitRange 详情 确认默认 requests/limits 和最大/最小限制
kubectl get resourcequota -A 列出所有 ResourceQuota 命名空间级资源配额
kubectl describe resourcequota <name> -n <ns> 查看 ResourceQuota 详情 已用 vs 硬限制对比
kubectl get hpa 列出水平自动扩缩器 查看当前/目标 CPU/内存使用率
kubectl describe hpa <name> HPA 详情 Metrics 段显示当前值 vs 目标值,Events 显示扩缩事件
kubectl autoscale deploy <name> --min=2 --max=10 --cpu=80% 创建 HPA 基于 CPU 使用率自动扩缩副本数
kubectl delete hpa <name> 删除 HPA 删除后副本数不再自动调整
kubectl get vpa 列出垂直自动扩缩器 需安装 VPA;自动调整 Pod requests/limits
kubectl get events --field-selector=reason=FailedScheduling 查看调度失败事件 资源不足导致 Pending 时的排错入口
kubectl describe pod <pod> | grep -A 10 Events 查看 Pod 调度事件 确认是否因资源不足 Pending

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:资源管理 https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
Kubernetes 官方:QoS 等级 https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/
Kubernetes 官方:LimitRange https://kubernetes.io/docs/concepts/policy/limit-range/
Kubernetes 官方:ResourceQuota https://kubernetes.io/docs/concepts/policy/resource-quotas/
Kubernetes 官方:HPA https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/
Kubernetes 官方:VPA https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler

📘 Day 18:调度综合实战

🎯 今日目标

  • 综合使用调度策略实现多层级部署
  • 设计命名空间级别资源隔离
  • 故障自愈 + 自动伸缩全链路验证

🧠 理论精讲(10 分钟)

多租户资源隔离架构

1
2
3
4
5
6
7
8
9
10
11
12
13
┌────────────────────────────────────────────┐
│ K8s Cluster │
│ │
│ ┌── ns: team-a ──┐ ┌── ns: team-b ──┐ │
│ │ ResourceQuota │ │ ResourceQuota │ │
│ │ CPU: 4, Mem: 8G │ │ CPU: 2, Mem: 4G │ │
│ │ │ │ │ │
│ │ NodeSelector: │ │ NodeSelector: │ │
│ │ node-group=a │ │ node-group=b │ │
│ │ │ │ │ │
│ │ HPA + PDB │ │ HPA + PDB │ │
│ └─────────────────┘ └─────────────────┘ │
└────────────────────────────────────────────┘

🔧 动手实操(150 分钟)

练习 18.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# 1. 准备节点分组
kubectl label node k8s-node1 node-group=team-a
kubectl label node k8s-node2 node-group=team-b

# 2. 创建租户命名空间
kubectl create ns team-a
kubectl create ns team-b

# 3. Team A 的 ResourceQuota + LimitRange
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
requests.cpu: "3"
requests.memory: "4Gi"
pods: "15"
---
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- type: Container
default:
cpu: "200m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
EOF

# 4. Team B 的 ResourceQuota
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-b-quota
namespace: team-b
spec:
hard:
requests.cpu: "2"
requests.memory: "3Gi"
pods: "10"
---
apiVersion: v1
kind: LimitRange
metadata:
name: team-b-limits
namespace: team-b
spec:
limits:
- type: Container
default:
cpu: "150m"
memory: "192Mi"
defaultRequest:
cpu: "75m"
memory: "96Mi"
EOF

# 5. Team A 部署应用到专属节点
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-a
namespace: team-a
spec:
replicas: 3
selector:
matchLabels:
app: app-a
template:
metadata:
labels:
app: app-a
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-group
operator: In
values:
- team-a
containers:
- name: app
image: nginx:alpine
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
EOF

# 6. Team B 部署
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-b
namespace: team-b
spec:
replicas: 2
selector:
matchLabels:
app: app-b
template:
metadata:
labels:
app: app-b
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-group
operator: In
values:
- team-b
containers:
- name: app
image: httpd:alpine
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"
EOF

# 7. 验证配额
kubectl describe quota -n team-a
kubectl describe quota -n team-b

# 8. 验证 Pod 分布
kubectl get pod -n team-a -o wide
kubectl get pod -n team-b -o wide

🏆 赛题模拟(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
36
37
38
39
40
【场景】公司两个团队共享 K8s 集群

【操作要求】

1. 命名空间与标签:
- ns: dev-team, prod-team
- 节点标签 tier: k8s-node1→dev, k8s-node2→prod

2. dev-team 配置:
- ResourceQuota: CPU 2 核, Memory 4Gi, Pod ≤ 10
- LimitRange: 默认 requests cpu 100m/mem 128Mi
- Deployment dev-app: 2 副本, nginx:alpine
* nodeAffinity: tier=dev
* HPA: CPU 50%, min 2, max 5

3. prod-team 配置:
- ResourceQuota: CPU 4 核, Memory 8Gi, Pod ≤ 20
- LimitRange: 默认 requests cpu 200m/mem 256Mi
- Deployment prod-api: 3 副本, httpd:alpine
* nodeAffinity: tier=prod
* podAntiAffinity: 按 hostname 分散
* HPA: CPU 70%, min 3, max 10
- Deployment prod-worker: 2 副本, busybox (sleep 3600)
* nodeAffinity: tier=prod

4. 验证:
- 两个团队配额互不干扰
- dev-app 全部在 dev 节点
- prod 所有 Pod 在 prod 节点
- prod-api 分散在不同节点
- HPA 工作正常

【评分标准】
- 命名空间和标签(10 分)
- ResourceQuota 正确(15 分)
- LimitRange 正确(10 分)
- nodeAffinity 正确(20 分)
- podAntiAffinity 正确(15 分)
- HPA 配置(20 分)
- 整体隔离验证(10 分)

📋 命令速查

命令 功能 注解
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints[*].key 自定义列查看节点污点 快速扫描所有节点的污点分布
kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,NODE-SELECTOR:.spec.nodeSelector 自定义列查看 Pod 调度策略 同时显示 Pod 所在节点和 nodeSelector
kubectl get pods -o wide --field-selector=spec.nodeName=<node> 筛选某节点上的 Pod 迁移 Pod 前确认受影响范围
kubectl get pods -o wide --field-selector=status.phase=Pending 筛选 Pending Pod 快速定位未调度的 Pod
kubectl describe pod <pod> | grep -A 5 "Node-Selectors|Tolerations|Affinity" 查看 Pod 调度要求 综合排错时快速了解 Pod 的调度偏好
kubectl patch deploy <name> -p '{"spec":{"template":{"spec":{"nodeSelector":{"key":"value"}}}}}' 给 Deployment 添加 nodeSelector 触发滚动更新将 Pod 迁移到匹配节点
kubectl patch deploy <name> -p '{"spec":{"template":{"spec":{"tolerations":[{"key":"key","operator":"Equal","value":"value","effect":"NoSchedule"}]}}}}' 给 Deployment 添加 Toleration 允许 Pod 调度到有对应污点的节点
kubectl get events --sort-by=.metadata.creationTimestamp | tail -20 最近 20 条事件 综合实战中快速了解集群正在发生什么

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:调度器 https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/
Kubernetes 官方:高级调度 https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/
Kubernetes 官方:Pod 优先级与抢占 https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/
Kubernetes 官方:资源管理 https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/

📘 Day 19:RBAC 权限控制

🎯 今日目标

  • 理解 RBAC 的四个核心概念
  • 能创建 Role 并绑定到用户/ServiceAccount
  • 会用 kubectl auth can-i 验证权限
  • 能排查 Permission Denied 错误

🧠 理论精讲(30 分钟)

RBAC 四要素

1
2
3
4
5
Subject(谁)────→ RoleBinding ────→ Role(能做什么)
│ │
├── User ├── apiGroups
├── Group ├── resources
└── ServiceAccount └── verbs
概念 作用域 说明
Role 命名空间 定义在某 NS 下的权限
ClusterRole 集群 定义集群级别权限
RoleBinding 命名空间 将 Role 绑定到主体
ClusterRoleBinding 集群 将 ClusterRole 绑定到主体

常见 verbs

verb 含义 对应 kubectl
get 查看单个资源 kubectl get pod xxx
list 列出资源 kubectl get pods
watch 监听资源变化 kubectl get pods -w
create 创建 kubectl create/apply
update 修改 kubectl edit/patch
delete 删除 kubectl delete

🔧 动手实操(120 分钟)

练习 19.1:创建 ServiceAccount + Role + RoleBinding

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
# 1. 创建命名空间
kubectl create ns rbac-demo

# 2. 创建 ServiceAccount
kubectl create sa developer -n rbac-demo

# 3. 创建 Role(只读 Pod)
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: rbac-demo
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
EOF

# 4. 创建 RoleBinding
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-pod-reader
namespace: rbac-demo
subjects:
- kind: ServiceAccount
name: developer
namespace: rbac-demo
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
EOF

# 5. 验证权限
kubectl auth can-i get pods -n rbac-demo --as=system:serviceaccount:rbac-demo:developer
# yes

kubectl auth can-i create pods -n rbac-demo --as=system:serviceaccount:rbac-demo:developer
# no

kubectl auth can-i delete pods -n rbac-demo --as=system:serviceaccount:rbac-demo:developer
# no

# 6. 查看完整权限列表
kubectl auth can-i --list -n rbac-demo --as=system:serviceaccount:rbac-demo:developer

练习 19.2:ClusterRole + ClusterRoleBinding

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
# 1. 创建 ClusterRole(集群级别只读)
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-viewer
rules:
- apiGroups: [""]
resources: ["nodes", "namespaces", "pods", "services"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
EOF

# 2. 创建 ServiceAccount
kubectl create sa auditor -n rbac-demo

# 3. ClusterRoleBinding
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: auditor-viewer
subjects:
- kind: ServiceAccount
name: auditor
namespace: rbac-demo
roleRef:
kind: ClusterRole
name: cluster-viewer
apiGroup: rbac.authorization.k8s.io
EOF

# 4. 验证集群级别权限
kubectl auth can-i get nodes --as=system:serviceaccount:rbac-demo:auditor
# yes

kubectl auth can-i get deploy --as=system:serviceaccount:rbac-demo:auditor
# yes

kubectl auth can-i delete nodes --as=system:serviceaccount:rbac-demo:auditor
# no

练习 19.3:创建受限 kubeconfig

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
# 1. 获取 SA 的 Token
kubectl create token developer -n rbac-demo
# 复制输出的 token

# 或者创建长期有效的 token(K8s 1.24+)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: developer-token
namespace: rbac-demo
annotations:
kubernetes.io/service-account.name: developer
type: kubernetes.io/service-account-token
EOF

# 获取 token
TOKEN=$(kubectl get secret developer-token -n rbac-demo -o jsonpath='{.data.token}' | base64 -d)

# 2. 获取集群 CA 证书
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > /tmp/ca.crt

# 3. 构建 kubeconfig
kubectl config set-cluster dev-cluster \
--server=$(kubectl config view --raw -o jsonpath='{.clusters[0].cluster.server}') \
--certificate-authority=/tmp/ca.crt \
--embed-certs=true \
--kubeconfig=/tmp/dev-kubeconfig

kubectl config set-credentials developer \
--token=$TOKEN \
--kubeconfig=/tmp/dev-kubeconfig

kubectl config set-context dev-context \
--cluster=dev-cluster \
--namespace=rbac-demo \
--user=developer \
--kubeconfig=/tmp/dev-kubeconfig

kubectl config use-context dev-context --kubeconfig=/tmp/dev-kubeconfig

# 4. 使用受限配置测试
kubectl get pods -n rbac-demo --kubeconfig=/tmp/dev-kubeconfig
# 正常返回

kubectl create deploy test --image=nginx:alpine -n rbac-demo --kubeconfig=/tmp/dev-kubeconfig
# Error: forbidden

练习 19.4:实战 RBAC 排错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 排查 "cannot get resource" 错误
# 错误信息:Error from server (Forbidden): pods is forbidden

# 排查步骤
kubectl auth can-i get pods --as=<user>
# 如果是 no → 检查 Role/RoleBinding
kubectl get rolebinding -A | grep <user>
kubectl describe rolebinding <name> -n <ns>

# 2. 查看某个 SA 拥有的所有权限
kubectl auth can-i --list --as=system:serviceaccount:rbac-demo:developer -n rbac-demo

# 3. 检查是否有 ClusterRoleBinding(影响更大)
kubectl get clusterrolebinding -o wide | grep <user>

🐛 排错练习(30 分钟)

常见 RBAC 错误排查

1
2
3
4
5
6
7
8
9
# 错误 1: "User system:serviceaccount:default:default cannot create resource"
# → 默认 SA 没有写权限,需要创建专用的 SA + RoleBinding

# 错误 2: "cannot list resource in API group"
# → Role 中 apiGroups 配置错误,检查资源属于哪个 API 组
kubectl api-resources | grep <resource>

# 错误 3: "cannot get resource at cluster scope"
# → 命名空间级别的 Role 不能访问集群级别资源,需用 ClusterRole

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 35 分钟

题目:RBAC 权限体系设计

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. 创建命名空间 project-alpha

2. 创建 3 个 ServiceAccount:
- sa-admin(管理员)
- sa-developer(开发者)
- sa-viewer(只读用户)

3. 创建 3 个 Role / ClusterRole:
a. Role admin-role(namespace: project-alpha):
- 对 pods/deployments/services/configmaps/secrets 有全部权限
b. Role developer-role(namespace: project-alpha):
- 对 pods/deployments/services/configmaps 有 get/list/watch/create/update
- 对 pods/log 有 get/list
- 对 secrets 无权限
c. ClusterRole viewer-role:
- 对所有命名空间的 pods/deployments/services 有 get/list/watch

4. 绑定:
- sa-admin → admin-role(RoleBinding)
- sa-developer → developer-role(RoleBinding)
- sa-viewer → viewer-role(ClusterRoleBinding)

5. 验证:
- sa-admin 能创建/删除 Pod
- sa-developer 能创建 Pod 但不能查看 Secret
- sa-viewer 能查看所有命名空间的 Pod
- sa-viewer 无法创建 Pod

【评分标准】
- SA 创建(10 分)
- Role/ClusterRole 权限粒度正确(40 分)
- RoleBinding/ClusterRoleBinding 正确(20 分)
- 权限验证(30 分)

📋 命令速查

命令 功能 注解
kubectl auth can-i create pods 检查当前用户权限 快速验证 RBAC 配置是否正确
kubectl auth can-i create pods --as=system:serviceaccount:<ns>:<sa> 检查某 SA 权限 调试 ServiceAccount 权限问题
kubectl auth can-i --list 列出当前用户所有权限 输出完整的资源-动词矩阵
kubectl auth can-i '*' '*' 检查是否集群管理员 返回 yes 说明拥有全部权限
kubectl get role -A 列出所有 Role 命名空间级权限
kubectl get rolebinding -A 列出所有 RoleBinding 查看 Role 与用户/SA 的绑定关系
kubectl get clusterrole 列出 ClusterRole 集群级权限(节点、PV、StorageClass 等)
kubectl get clusterrolebinding 列出 ClusterRoleBinding 集群级绑定关系
kubectl describe role <name> -n <ns> Role 详情 查看具体允许的资源和动词
kubectl describe clusterrole <name> ClusterRole 详情 系统自带 cluster-admin/edit/view 的权限范围
kubectl create role <name> --verb=get,list,watch --resource=pods -n <ns> 快速创建 Role --verb--resource 即可,无需手写 YAML
kubectl create rolebinding <name> --role=<role> --user=<user> -n <ns> 绑定 Role 到用户 给真实用户授权
kubectl create rolebinding <name> --role=<role> --serviceaccount=<ns>:<sa> -n <ns> 绑定 Role 到 SA 给 ServiceAccount 授权
kubectl create clusterrole <name> --verb=get,list --resource=nodes 创建 ClusterRole 集群级资源必须用 ClusterRole
kubectl create clusterrolebinding <name> --clusterrole=<role> --serviceaccount=<ns>:<sa> 绑定 ClusterRole 到 SA 跨命名空间的 SA 绑定集群级权限
kubectl get sa -A 列出所有 ServiceAccount 每个 NS 有默认 default SA
kubectl get secrets | grep <sa>-token 查找 SA 的 Token Secret 1.24+ 不再自动创建,需手动 kubectl create token <sa>
kubectl create token <sa> -n <ns> 创建 SA 临时 Token 1.24+ 推荐方式,有时效性

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:RBAC https://kubernetes.io/docs/reference/access-authn-authz/rbac/
Kubernetes 官方:鉴权概述 https://kubernetes.io/docs/reference/access-authn-authz/authorization/
Kubernetes 官方:使用 RBAC 授权 https://kubernetes.io/docs/reference/access-authn-authz/rbac/#command-line-utilities
Kubernetes 官方:ServiceAccount https://kubernetes.io/docs/concepts/security/service-accounts/
Kubernetes 官方:kubectl auth can-i https://kubernetes.io/docs/reference/access-authn-authz/authorization/#checking-api-access

📘 Day 11:网络综合实战

🎯 今日目标

  • 在一个场景中综合使用 Service + Ingress + NetworkPolicy
  • 排查跨命名空间的网络故障
  • 构建生产级网络架构

🧠 理论精讲(10 分钟)

网络排错方法论

1
2
3
4
5
6
1. 确认 Pod 是否 Running?
2. 确认 Pod 是否有 IP?
3. 确认 Service 是否有 Endpoint?
4. 确认 DNS 是否解析正常?
5. 确认 NetworkPolicy 是否阻止?
6. 从近到远逐层测试

常用网络调试命令

1
2
3
4
5
6
7
8
9
10
11
# DNS 测试
nslookup <service-name>

# TCP 连通性测试
nc -zv <host> <port>

# HTTP 测试
wget -q -O- http://<host>:<port>

# 网络包抓取(需额外工具)
tcpdump -i any port <port>

🔧 动手实操(150 分钟)

练习 11.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
# 创建独立命名空间
kubectl create ns net-lab

# === 第 1 层:Ingress 入口层 ===
kubectl create deploy frontend -n net-lab --image=nginx:alpine --replicas=2
kubectl expose deploy frontend -n net-lab --port=80 --name=frontend-svc

# === 第 2 层:API 中间层 ===
kubectl create deploy api -n net-lab --image=httpd:alpine --replicas=3
kubectl expose deploy api -n net-lab --port=80 --name=api-svc

# === 第 3 层:数据层 ===
kubectl run db -n net-lab --image=redis:7-alpine --port=6379
kubectl expose pod db -n net-lab --port=6379 --name=db-svc

# === Ingress 规则 ===
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: net-lab-ingress
namespace: net-lab
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: lab.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80
EOF

# 验证:curl -H "Host: lab.example.com" <NodeIP>:30080/

练习 11.2:跨命名空间网络通信验证

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 从 net-lab 中的 Pod 访问其他命名空间的服务
kubectl run jumpbox -n net-lab --image=busybox:1.36 -- sleep 3600

# 测试 DNS 解析
kubectl exec jumpbox -n net-lab -- nslookup db-svc.net-lab.svc.cluster.local

# 测试 db
kubectl exec jumpbox -n net-lab -- nc -zv db-svc.net-lab 6379

# 2. 从 default 命名空间访问 net-lab
kubectl run cross-test --image=busybox:1.36 --rm -it --restart=Never -- \
wget -q -O- http://api-svc.net-lab

练习 11.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 故障 1:Service selector 不匹配
# 创建一个选错标签的 Service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: broken-svc
namespace: net-lab
spec:
selector:
app: non-existent # 故意选一个不存在的标签!
ports:
- port: 80
EOF

kubectl get endpoints broken-svc -n net-lab
# ENDPOINTS: <none>(问题就在这里!)

kubectl describe svc broken-svc -n net-lab

# 修复:修改 selector
kubectl patch svc broken-svc -n net-lab --type=merge -p '{"spec":{"selector":{"app":"api"}}}'
kubectl get endpoints broken-svc -n net-lab
# 现在有 Endpoint 了

# 故障 2:NetworkPolicy 阻断排查
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: isolate-db
namespace: net-lab
spec:
podSelector:
matchLabels:
run: db
policyTypes:
- Ingress
# 空 Ingress = 拒绝所有
EOF

# 测试 db 连接
kubectl exec jumpbox -n net-lab -- nc -zv db-svc 6379 -w 5
# 超时!(被 NetworkPolicy 阻断)

# 排查
kubectl get networkpolicy -n net-lab
kubectl describe networkpolicy isolate-db -n net-lab

# 修复
kubectl delete networkpolicy isolate-db -n net-lab

# 故障 3:DNS 无法解析
# 检查 CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 如果 Pod 不 Running → 检查日志

🏆 赛题模拟(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
36
37
38
39
【场景】搭建一个博客系统的网络层,包含前端、API、数据库三个层次。

【操作要求】

1. 创建命名空间 blog-system

2. 部署三层应用:
a. 前端层:Deployment blog-frontend(nginx:alpine,2 副本)
ClusterIP Service frontend-svc
b. API 层:Deployment blog-api(httpd:alpine,3 副本)
ClusterIP Service api-svc
c. 数据库层:Pod blog-db(redis:7-alpine)
ClusterIP Service db-svc

3. 配置 Ingress:
- 对外暴露 30080,域名 blog.example.com
- / → frontend-svc
- /api → api-svc
- 配置 rewrite-target: /

4. 配置 NetworkPolicy:
a. 默认拒绝所有入站流量
b. frontend-svc → api-svc:80
c. api-svc → db-svc:6379
d. Ingress Controller(ingress-nginx 命名空间)→ frontend-svc:80
e. 所有 Pod → CoreDNS UDP 53

5. 验证:
- 外部可访问 http://<NodeIP>:30080/ 和 /api
- 前端能访问 API
- API 能访问 DB
- 前端**不能**直接访问 DB
- DB **不能**访问外网

【评分标准】
- 三层应用正确部署(20 分)
- Ingress 路由正确(20 分)
- NetworkPolicy 隔离正确(40 分)
- 验证全面(20 分)

📋 命令速查

命令 功能 注解
kubectl get svc,endpoints,ingress,networkpolicy 网络资源一览 逗号分隔多资源类型,快速审计网络配置
kubectl run net-test --image=nicolaka/netshoot --rm -it -- /bin/bash 网络调试 Pod netshoot 包含 curl/wget/nslookup/dig/nc/iperf 等全套工具
kubectl exec <pod> -- curl -s -o /dev/null -w "%{http_code}" http://<svc>:<port> 测试 HTTP 状态码 仅输出 HTTP code,脚本化健康检查
kubectl exec <pod> -- dig +short <svc>.<ns>.svc.cluster.local DNS 解析(dig 方式) 比 nslookup 更详细,可分析 DNS 查询链
kubectl exec <pod> -- traceroute <target-ip> 追踪 Pod 网络路径 理解数据包从 Pod 到目标的跳数
kubectl exec <pod> -- iperf3 -c <target-ip> 网络带宽测试 性能基准,评估 CNI 插件吞吐
kubectl get pods -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName 自定义列查看 Pod 信息 同时显示 Pod 名、IP、所在节点
kubectl label ns <ns> pod-security.kubernetes.io/enforce=restricted 设置命名空间 Pod 安全等级 综合实战中验证安全策略与网络策略协同
kubectl auth can-i create networkpolicies --as=system:serviceaccount:<ns>:<sa> 验证 SA 是否有创建 NetworkPolicy 权限 RBAC + NetworkPolicy 联合排错

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:服务、负载均衡与网络 https://kubernetes.io/docs/concepts/services-networking/
Kubernetes 官方:网络策略演练 https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/
netshoot 网络调试镜像 https://github.com/nicolaka/netshoot
Calico 网络策略示例 https://docs.tigera.io/calico/latest/network-policy/get-started/
Kubernetes 网络模型 https://kubernetes.io/docs/concepts/services-networking/#the-kubernetes-network-model

📘 Day 12:ConfigMap 与 Secret

🎯 今日目标

  • 会用 4 种方式创建 ConfigMap
  • 会用 envFrom、valueFrom、volume 三种方式消费配置
  • 能创建 Opaque、TLS、dockerconfigjson 三种 Secret
  • 理解 ConfigMap 挂载为 Volume 时的热更新
  • 掌握 immutable ConfigMap/Secret

🧠 理论精讲(30 分钟)

ConfigMap vs Secret

特性 ConfigMap Secret
用途 非敏感配置 敏感数据(密码、证书)
存储 明文(etcd 中) Base64 编码(可配合加密)
大小限制 1 MiB 1 MiB
热更新 ✅(Volume 挂载模式) ✅(Volume 挂载模式)

消费配置的三种方式

1
2
3
1. envFrom    — 整个 ConfigMap/Secret 作为环境变量
2. valueFrom — 从 ConfigMap/Secret 读取单个 key 作为环境变量
3. volume — 将 ConfigMap/Secret 挂载为文件

Secret 类型

类型 用途 数据 key 要求
Opaque 通用密钥(默认) 任意
kubernetes.io/tls TLS 证书 tls.crttls.key
kubernetes.io/dockerconfigjson 镜像拉取凭证 .dockerconfigjson
kubernetes.io/basic-auth 基础认证 usernamepassword

🔧 动手实操(120 分钟)

练习 12.1:ConfigMap 四种创建方式

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
# 方式 1:--from-literal(字面值)
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=APP_DEBUG=false \
--from-literal=LOG_LEVEL=info

# 查看内容
kubectl get cm app-config -o yaml

# 方式 2:--from-file(从文件)
mkdir -p /tmp/cm-demo
cat > /tmp/cm-demo/nginx.conf <<EOF
server {
listen 80;
server_name localhost;
}
EOF
cat > /tmp/cm-demo/app.properties <<EOF
app.name=myapp
app.version=1.0.0
EOF

kubectl create configmap file-config --from-file=/tmp/cm-demo/

# 查看
kubectl describe cm file-config
# 两个 key:nginx.conf 和 app.properties

# 方式 3:--from-file 指定 key 名
kubectl create configmap nginx-conf --from-file=nginx.conf=/tmp/cm-demo/nginx.conf

# 方式 4:--from-env-file(从环境变量文件)
cat > /tmp/cm-demo/env.list <<EOF
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
EOF

kubectl create configmap db-env --from-env-file=/tmp/cm-demo/env.list

# 查看所有
kubectl get cm

练习 12.2:ConfigMap 三种使用方式

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
# === 方式 1:envFrom(整批注入) ===
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: cm-envfrom
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo DB_HOST=\$DB_HOST DB_PORT=\$DB_PORT; sleep 3600"]
envFrom:
- configMapRef:
name: db-env
EOF

kubectl logs cm-envfrom
# 输出:DB_HOST=localhost DB_PORT=5432

# === 方式 2:valueFrom(单个引用) ===
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: cm-valuefrom
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo ENV=\$APP_ENV DEBUG=\$APP_DEBUG; sleep 3600"]
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: APP_DEBUG
valueFrom:
configMapKeyRef:
name: app-config
key: APP_DEBUG
EOF

kubectl logs cm-valuefrom
# 输出:ENV=production DEBUG=false

# === 方式 3:Volume 挂载(文件方式) ===
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: cm-volume
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "cat /etc/config/nginx.conf; sleep 3600"]
volumeMounts:
- name: config
mountPath: /etc/config
volumes:
- name: config
configMap:
name: nginx-conf
EOF

kubectl logs cm-volume
# 输出 nginx.conf 内容

# 查看挂载的文件
kubectl exec cm-volume -- ls -la /etc/config/
kubectl exec cm-volume -- cat /etc/config/nginx.conf

# 清理
kubectl delete pod cm-envfrom cm-valuefrom cm-volume

练习 12.3:ConfigMap 热更新验证

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
# 1. 创建 ConfigMap 并以 Volume 方式挂载
kubectl create configmap dynamic-config --from-literal=message="version-1"

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: hot-reload
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do cat /etc/config/message; sleep 2; done"]
volumeMounts:
- name: cfg
mountPath: /etc/config
volumes:
- name: cfg
configMap:
name: dynamic-config
EOF

# 2. 观察当前值
kubectl logs hot-reload --tail=5
# 输出:version-1

# 3. 更新 ConfigMap
kubectl create configmap dynamic-config --from-literal=message="version-2" \
--dry-run=client -o yaml | kubectl apply -f -

# 4. 等待约 60-90 秒后查看(kubelet 同步周期)
sleep 60
kubectl logs hot-reload --tail=5
# 输出变为:version-2

# ⚠️ 注意:
# - Volume 挂载方式:热更新生效(有延迟,约 60-90 秒)
# - envFrom/valueFrom 方式:不会热更新!需要重建 Pod

# 5. 清理
kubectl delete pod hot-reload
kubectl delete cm dynamic-config

练习 12.4:Secret 实战

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
# 1. 创建 Opaque Secret
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='P@ssw0rd!2024'

kubectl get secret db-credentials
kubectl describe secret db-credentials
# 不显示值,只显示 key 名

# 查看解码内容
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
echo

# 2. 在 Pod 中使用
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: secret-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo USER=\$DB_USER PASS=\$DB_PASS; sleep 3600"]
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: password
EOF

kubectl logs secret-demo
# 输出:USER=admin PASS=P@ssw0rd!2024

# 3. 创建 TLS Secret
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /tmp/tls.key -out /tmp/tls.crt \
-subj "/CN=myapp.example.com"

kubectl create secret tls my-tls \
--key=/tmp/tls.key --cert=/tmp/tls.crt

kubectl describe secret my-tls
# Type: kubernetes.io/tls

# 4. 创建镜像拉取 Secret
kubectl create secret docker-registry my-registry \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=my@example.com

kubectl describe secret my-registry
# Type: kubernetes.io/dockerconfigjson

# 5. 清理
kubectl delete pod secret-demo
kubectl delete secret db-credentials my-tls my-registry
rm -f /tmp/tls.key /tmp/tls.crt

练习 12.5:Immutable ConfigMap/Secret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 不可变(immutable)的 ConfigMap 不能修改,只能删除重建
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: immutable-cm
immutable: true
data:
version: "1.0"
build-time: "$(date)"
EOF

# 尝试修改会报错
kubectl patch cm immutable-cm -p '{"data":{"version":"2.0"}}'
# Error: ConfigMap is immutable

# 清理
kubectl delete cm immutable-cm

🐛 排错练习(30 分钟)

场景 1:ConfigMap 未更新

1
2
3
4
5
6
7
8
9
10
11
12
13
# 问题:修改了 ConfigMap,但应用没有变化

# 排查:
# 1. 确认是哪种消费方式
kubectl get pod <pod> -o yaml | grep -A5 envFrom
# envFrom → 需要重建 Pod
# volume → 等待 kubelet 同步(~90s)

# 2. 如果是 envFrom,强制重建
kubectl rollout restart deploy/<name>

# 3. 如果是 volume,检查挂载点内容
kubectl exec <pod> -- cat <mount-path>/<key>

场景 2:Secret 未正确解码

1
2
3
4
5
6
# 问题:Pod 中的环境变量显示乱码或空值

# 排查:
kubectl get secret <name> -o jsonpath='{.data.<key>}' | base64 -d
# 如果解码后为空:创建 Secret 时输入有误
# 如果解码失败:不是合法的 base64(--from-literal 会自动编码)

🏆 赛题模拟(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
34
【操作要求】

1. 创建 ConfigMap app-settings:
- APP_ENV=staging
- APP_LOG_LEVEL=debug
- APP_MAX_CONNECTIONS=100

2. 创建 Secret app-secrets:
- DB_PASSWORD=secret123
- API_KEY=sk-abcdef123456
- REDIS_PASSWORD=redispass

3. 创建 Deployment config-app(2 副本,nginx:alpine):
- 用 envFrom 注入 app-settings 的所有键
- 用 valueFrom 注入 DB_PASSWORD(命名为 DATABASE_PASSWORD)
- 将 app-settings 以 Volume 方式挂载到 /etc/app/config

4. 创建 TLS Secret app-tls(自签名证书,CN=secure.app.com)

5. 更新 app-settings 中的 APP_LOG_LEVEL=info,
验证 Volume 挂载的配置文件是否更新

6. 验证:
- kubectl exec 进入 Pod 查看环境变量
- 确认 /etc/app/config 下有配置文件的符号链接
- 确认 Secret 数据可正常读取

【评分标准】
- ConfigMap 创建正确(15 分)
- Secret 创建正确(15 分)
- Deployment 配置注入正确(35 分)
- TLS Secret 正确(10 分)
- 热更新验证(15 分)
- Immutable 属性(10 分)

📋 命令速查

命令 功能 注解
kubectl get cm 列出 ConfigMap short name: cm
kubectl get cm <name> -o yaml 查看 ConfigMap 完整内容 检查所有 key-value 数据
kubectl create cm <name> --from-file=<file> 从文件创建 ConfigMap key 为文件名,value 为文件内容
kubectl create cm <name> --from-file=key=<file> 从文件创建并指定 key key 可以不同于文件名
kubectl create cm <name> --from-literal=key=value 从字面值创建 ConfigMap 快速创建简单配置项,多个 --from-literal 可合并
kubectl create cm <name> --from-env-file=<file> 从 .env 文件创建 ConfigMap 每行 KEY=VALUE 格式
kubectl get secret 列出 Secret 默认只显示名称和类型(不显示值)
kubectl get secret <name> -o yaml 查看 Secret 完整 YAML data 字段为 base64 编码
kubectl get secret <name> -o jsonpath='{.data.<key>}' | base64 -d 解码 Secret 某个 key jsonpath 提取 + base64 解码,常用组合
kubectl get secret <name> -o jsonpath='{.data}' | jq 'map_values(@base64d)' 解码所有 Secret 字段 jq 一次性解码所有 base64 值
kubectl create secret generic <name> --from-literal=key=value 创建 Opaque Secret 值自动 base64 编码存储(注意:仅仅是编码非加密)
kubectl create secret generic <name> --from-file=<file> 从文件创建 Secret 文件内容自动 base64
kubectl create secret docker-registry <name> --docker-server=<url> --docker-username=<user> --docker-password=<pass> 创建镜像拉取密钥 私有镜像仓库认证
kubectl create secret tls <name> --cert=cert.pem --key=key.pem 创建 TLS Secret Ingress HTTPS 专用
kubectl describe cm <name> ConfigMap 概要 显示 key 列表但不显示 value
kubectl edit cm <name> 在线编辑 ConfigMap 立即生效,挂载到 Pod 中的文件会延迟更新(取决于 kubelet 同步周期)
kubectl set env deploy/<name> --from=cm/<cm-name> 将 ConfigMap 注入为环境变量 更新 Deployment 环境变量,触发滚动更新
echo -n "secret-value" | base64 base64 编码 手动编码 Secret 值,填入 YAML 中的 data 字段
kubectl exec <pod> -- cat /etc/config/<key> 验证 ConfigMap 挂载内容 确认文件内容和路径是否正确

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:ConfigMap https://kubernetes.io/docs/concepts/configuration/configmap/
Kubernetes 官方:Secret https://kubernetes.io/docs/concepts/configuration/secret/
Kubernetes 官方:配置 Pod 使用 ConfigMap https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/
Kubernetes 官方:Secret 管理最佳实践 https://kubernetes.io/docs/concepts/configuration/secret/#best-practices
Kubernetes 官方:不可变 ConfigMap 与 Secret https://kubernetes.io/docs/concepts/configuration/configmap/#configmap-immutable

📘 Day 13:Volume 与 PV/PVC

🎯 今日目标

  • 会用 emptyDir 和 hostPath Volume
  • 理解 PV/PVC 的绑定流程
  • 能创建静态 PV 并供 Pod 使用
  • 掌握 AccessMode(RWO/ROX/RWX)
  • 掌握 ReclaimPolicy(Retain/Recycle/Delete)

🧠 理论精讲(30 分钟)

存储层次

1
2
3
Pod ──→ PVC(声明:我要 5Gi RWO 存储)──→ PV(物理存储:这里是 5Gi NFS)

└──→ 实际存储后端(NFS/本地盘/云盘)
概念 说明 类比
Volume Pod 级别存储定义 直接在 Pod 里写磁盘配置
PV 集群级别存储资源 管理员准备的存储池
PVC 用户存储请求 申请单

PV 关键属性

属性 可选值 说明
accessModes RWO / ROX / RWX 读写模式
ReclaimPolicy Retain / Recycle / Delete PV 释放后行为
volumeMode Filesystem / Block 文件系统还是块设备

AccessMode 速查

缩写 全称 含义
RWO ReadWriteOnce 单节点读写
ROX ReadOnlyMany 多节点只读
RWX ReadWriteMany 多节点读写

PV 生命周期

1
2
Provisioning(创建)→ Available(可用)→ Bound(已绑定)→ Released(释放)
→ Retained(保留)

🔧 动手实操(120 分钟)

练习 13.1:emptyDir Volume

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
# emptyDir:Pod 生命周期内存在,Pod 删除即销毁
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: emptydir-demo
spec:
containers:
- name: writer
image: busybox:1.36
command:
- sh
- -c
- |
i=0
while true; do
echo "Data-$i" >> /data/shared.log
i=$((i+1))
sleep 2
done
volumeMounts:
- name: shared-data
mountPath: /data

- name: reader
image: busybox:1.36
command:
- sh
- -c
- tail -f /data/shared.log
volumeMounts:
- name: shared-data
mountPath: /data

volumes:
- name: shared-data
emptyDir: {}
EOF

# 验证容器间共享
kubectl logs emptydir-demo -c reader --tail=10
# 看到 writer 写入的内容

# 清理
kubectl delete pod emptydir-demo

练习 13.2:hostPath Volume

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
# hostPath:挂载节点上的目录
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: hostpath-demo
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- |
echo "Written at \$(date)" >> /host-data/hostpath-test.log
cat /host-data/hostpath-test.log
sleep 3600
volumeMounts:
- name: host-vol
mountPath: /host-data
volumes:
- name: host-vol
hostPath:
path: /tmp/k8s-hostpath
type: DirectoryOrCreate
EOF

# 获取 Pod 所在节点
NODE=$(kubectl get pod hostpath-demo -o jsonpath='{.spec.nodeName}')
echo "Pod is on: $NODE"

# SSH 到该节点验证文件
# ssh $NODE cat /tmp/k8s-hostpath/hostpath-test.log

# 删除 Pod 后,节点上的文件仍在
kubectl delete pod hostpath-demo
# ssh $NODE cat /tmp/k8s-hostpath/hostpath-test.log # 文件还在

练习 13.3:静态 PV 创建与绑定

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
# 1. 先在节点上创建实际目录
# 在所有 worker 节点上执行:
# sudo mkdir -p /data/pv-volumes/pv01

# 2. 创建 PersistentVolume
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-manual-01
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/pv-volumes/pv01
EOF

# 3. 查看 PV 状态
kubectl get pv pv-manual-01
# NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM
# pv-manual-01 1Gi RWO Retain Available

# 4. 创建 PersistentVolumeClaim
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-manual-01
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
EOF

# 5. 观察绑定
kubectl get pvc pvc-manual-01
# STATUS: Bound

kubectl get pv pv-manual-01
# STATUS: Bound, CLAIM: default/pvc-manual-01

# 6. 在 Pod 中使用 PVC
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: pv-pod
spec:
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- |
echo "Persistent data!" >> /data/persistent.txt
cat /data/persistent.txt
sleep 3600
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: pvc-manual-01
EOF

# 7. 验证持久性
kubectl exec pv-pod -- cat /data/persistent.txt
# 输出:Persistent data!

# 8. 删除 Pod 和 PVC
kubectl delete pod pv-pod
kubectl delete pvc pvc-manual-01

# 9. 查看 PV 状态
kubectl get pv pv-manual-01
# STATUS: Released(因为 ReclaimPolicy=Retain,不会自动删除)

# 10. 清理 PV
kubectl delete pv pv-manual-01

练习 13.4:多 PV 自动匹配

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
# 创建 3 个不同大小的 PV
for i in 1 2 3; do
mkdir -p /data/pv-volumes/pv0${i}
done

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-small
spec:
capacity:
storage: 100Mi
accessModes: ["ReadWriteOnce"]
hostPath:
path: /data/pv-volumes/pv01
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-medium
spec:
capacity:
storage: 500Mi
accessModes: ["ReadWriteOnce"]
hostPath:
path: /data/pv-volumes/pv02
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-large
spec:
capacity:
storage: 2Gi
accessModes: ["ReadWriteOnce"]
hostPath:
path: /data/pv-volumes/pv03
EOF

# 创建 PVC 请求 300Mi
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-auto
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 300Mi
EOF

# 观察匹配结果(应绑定到 pv-medium,500Mi)
kubectl get pvc pvc-auto
kubectl get pv | grep pvc-auto
# 应绑定到 pv-medium(最小能匹配的 PV)

# 清理
kubectl delete pvc pvc-auto
kubectl delete pv pv-small pv-medium pv-large

🐛 排错练习(30 分钟)

场景:PVC 一直 Pending

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 排查清单
# 1. 有没有 PV?
kubectl get pv
# 如果没有 Available PV → 需要创建 PV 或配置动态供给

# 2. PV 的 accessModes 是否匹配 PVC?
kubectl get pv <pv-name> -o yaml | grep -A3 accessModes
kubectl get pvc <pvc-name> -o yaml | grep -A3 accessModes

# 3. PV 的容量是否 >= PVC?
kubectl get pv <pv-name> -o jsonpath='{.spec.capacity.storage}'
kubectl get pvc <pvc-name> -o jsonpath='{.spec.resources.requests.storage}'

# 4. PVC 的 storageClassName 是否为空字符串?
kubectl get pvc <pvc-name> -o yaml | grep storageClassName
# 如果 PVC 指定了 storageClassName,只能匹配同名的 PV

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 35 分钟

题目:静态 PV 存储管理

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
【操作要求】

1. 创建 3 个 hostPath 类型的 PV:
- pv-1g:容量 1Gi,RWO,路径 /data/pv/vol1
- pv-5g:容量 5Gi,RWO,路径 /data/pv/vol2
- pv-10g:容量 10Gi,RWO,路径 /data/pv/vol3

2. 创建 2 个 PVC:
- pvc-db:请求 4Gi,RWO
- pvc-logs:请求 8Gi,RWO

3. 验证 pvc-db 绑定到 pv-5g,pvc-logs 绑定到 pv-10g

4. 创建一个 Pod 同时使用两个 PVC:
- db 容器挂载 pvc-db 到 /var/lib/db
- app 容器挂载 pvc-logs 到 /var/log/app
- 写入测试数据到各自目录

5. 删除 Pod 和 PVC,检查 PV 状态变化

6. 手动清理 PV,释放存储

【评分标准】
- PV 创建正确(20 分)
- PVC 绑定结果正确(25 分)
- Pod 双 Volume 挂载正确(25 分)
- 数据持久性验证(15 分)
- PV 生命周期理解(15 分)

📋 命令速查

命令 功能 注解
kubectl get pv 列出 PersistentVolume STATUS: Available (空闲)/Bound (已绑定)/Released (PVC 已删但未回收)/Failed
kubectl get pv -o wide PV + 容量/访问模式/回收策略/状态/声明 快速审计存储资源
kubectl describe pv <name> PV 详细信息 查看 Reclaim Policy、AccessModes、底层存储类型(hostPath/NFS/CSI 等)
kubectl get pvc 列出 PersistentVolumeClaim STATUS: Pending (无匹配 PV)/Bound (已绑定)
kubectl get pvc -A 所有命名空间的 PVC 跨 NS 视角排查存储资源使用
kubectl describe pvc <name> PVC 详细信息 Events 段显示绑定过程;Pending 时查看失败原因
kubectl get pv,pvc 同时查看 PV/PVC 逗号分隔查看绑定关系
kubectl patch pv <name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' 修改 PV 回收策略 防止 PVC 删除时 PV 被自动回收(Retain/Recycle/Delete)
kubectl delete pvc <name> 删除 PVC PV 的 Reclaim Policy 决定 PV 后续状态
kubectl get pods -o wide | grep <pod> 查看 Pod 所在节点 PV hostPath 必须确认 Pod 调度到了正确节点
kubectl exec <pod> -- df -h 查看 Pod 内挂载存储空间 验证 PV 是否挂载成功及容量是否正确
kubectl exec <pod> -- ls -la /mnt/data 查看挂载目录内容 验证文件是否存在和权限
kubectl exec <pod> -- touch /mnt/data/test && echo "ok" > /mnt/data/test 验证存储读写 确认 PV 不是只读挂载
kubectl get sc 列出 StorageClass 动态供给与 PV/PVC 配合使用

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:PersistentVolume https://kubernetes.io/docs/concepts/storage/persistent-volumes/
Kubernetes 官方:卷 https://kubernetes.io/docs/concepts/storage/volumes/
Kubernetes 官方:配置 Pod 使用 PV https://kubernetes.io/docs/concepts/storage/persistent-volumes/
Kubernetes 官方:访问模式 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
Kubernetes 官方:回收策略 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reclaim-policy

📘 Day 10:网络策略与 CNI 原理

🎯 今日目标

  • 理解 Calico CNI 的基本原理
  • 能创建 NetworkPolicy 实现 Pod 间访问控制
  • 掌握 namespaceSelector 和 podSelector
  • 能配置 ingress 和 egress 规则
  • 能通过 NetworkPolicy 实现零信任网络

🧠 理论精讲(30 分钟)

CNI 工作流程

1
2
3
4
5
1. kubelet 创建 Pod → 
2. 调用 CNI 插件 →
3. CNI 分配 IP、创建 veth pair →
4. 配置路由规则 →
5. Pod 获得网络

Calico 数据路径

1
Pod-A (veth) ←→ Host-A (cali-xxx) ←→ BGP/IPIP ←→ Host-B (cali-yyy) ←→ Pod-B (veth)

NetworkPolicy 核心概念

1
2
3
4
5
6
7
8
9
10
# 默认规则:所有流量允许(无 NetworkPolicy 时)
# 一旦创建 NetworkPolicy,默认拒绝所有未明确允许的流量

spec:
podSelector: {} # 选择受影响的 Pod
policyTypes: # 规则方向
- Ingress
- Egress
ingress: [...] # 入站规则
egress: [...] # 出站规则

选择器组合

选择器 作用域
podSelector 选 Pod
namespaceSelector 选命名空间
ipBlock 选 IP CIDR

🔧 动手实操(120 分钟)

练习 10.1:Deny-All 策略

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. 创建测试命名空间和 Pod
kubectl create ns netpol-test

kubectl run web --image=nginx:alpine -n netpol-test --port=80
kubectl expose pod web -n netpol-test --port=80

kubectl run client --image=busybox:1.36 -n netpol-test -- sleep 3600

# 2. 验证默认情况下可访问
kubectl exec client -n netpol-test -- wget -q -O- http://web.netpol-test
# 正常返回

# 3. 创建 Deny-All 策略
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: netpol-test
spec:
podSelector: {} # 选择所有 Pod
policyTypes:
- Ingress
- Egress
# 空的 ingress/egress = 不允许任何流量
EOF

# 4. 再次尝试访问
kubectl exec client -n netpol-test -- wget -q -O- http://web.netpol-test --timeout=5
# 超时!被 NetworkPolicy 阻止

# 5. 清理
kubectl delete networkpolicy deny-all -n netpol-test

练习 10.2:精确允许 Ingress

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
# 1. 只允许特定标签的 Pod 访问 web
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-access
namespace: netpol-test
spec:
podSelector:
matchLabels:
run: web # 应用到 web Pod
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend # 只允许有 role=frontend 的 Pod
ports:
- protocol: TCP
port: 80
EOF

# 2. client 没有 role=frontend 标签,应被拒绝
kubectl exec client -n netpol-test -- wget -q -O- http://web.netpol-test --timeout=5
# 超时

# 3. 给 client 打上标签
kubectl label pod client -n netpol-test role=frontend

# 4. 再次访问,应成功
kubectl exec client -n netpol-test -- wget -q -O- http://web.netpol-test
# 正常返回

# 5. 清理
kubectl delete networkpolicy allow-web-access -n netpol-test
kubectl label pod client -n netpol-test role-

练习 10.3:namespaceSelector 跨命名空间控制

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
# 1. 创建第二个命名空间
kubectl create ns netpol-external

# 2. 在 external 命名空间创建 Pod
kubectl run ext-client --image=busybox:1.36 -n netpol-external -- sleep 3600
kubectl label ns netpol-external env=trusted

# 3. 创建允许来自 trusted 命名空间的策略
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-trusted-ns
namespace: netpol-test
spec:
podSelector:
matchLabels:
run: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
env: trusted # 允许 env=trusted 的命名空间
ports:
- protocol: TCP
port: 80
EOF

# 4. 测试从不同命名空间访问
kubectl exec ext-client -n netpol-external -- \
wget -q -O- http://web.netpol-test.netpol-test.svc.cluster.local --timeout=5
# 正常返回

kubectl exec client -n netpol-test -- \
wget -q -O- http://web --timeout=5
# 超时(test 命名空间内默认 Pod 被拒绝)

# 5. 清理
kubectl delete networkpolicy allow-trusted-ns -n netpol-test
kubectl label ns netpol-external env-

练习 10.4:Egress 出站控制

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
# 1. 限制 Pod 只能访问特定外部地址
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: netpol-test
spec:
podSelector:
matchLabels:
run: client
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
run: web # 允许访问 web
ports:
- protocol: TCP
port: 80
- to: # 还允许 DNS
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
EOF

# 2. 测试访问 web(应允许)
kubectl exec client -n netpol-test -- wget -q -O- http://web --timeout=5
# 正常返回

# 3. 测试访问外部(应被拒绝)
kubectl exec client -n netpol-test -- wget -q -O- http://www.baidu.com --timeout=5
# 超时

# 4. 清理
kubectl delete networkpolicy restrict-egress -n netpol-test

练习 10.5:ipBlock 白名单

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
# 允许来自特定 IP 段的访问
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ipblock
namespace: netpol-test
spec:
podSelector:
matchLabels:
run: web
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 10.244.0.0/16 # Pod 网络
- ipBlock:
cidr: 10.0.0.0/8 # 节点网络
ports:
- protocol: TCP
port: 80
EOF

kubectl get networkpolicy allow-ipblock -n netpol-test -o yaml

🐛 排错练习(30 分钟)

场景:NetworkPolicy 导致服务不可用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 问题:创建 NetworkPolicy 后某些服务突然不可用

# 排查步骤:
# 1. 列出所有 NetworkPolicy
kubectl get networkpolicy --all-namespaces

# 2. 查看具体策略规则
kubectl describe networkpolicy <name> -n <ns>

# 3. 检查是否缺少必需的规则
# - CoreDNS 访问(UDP 53)
# - api-server 访问
# - 健康检查端点

# 4. 临时删除策略验证
kubectl delete networkpolicy <name> -n <ns>

# 5. 恢复后确认问题根源,补充遗漏规则

🏆 赛题模拟(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
【初始环境】
- 命名空间 secure-app 中存在以下 Pod:
- db(标签 app=db,端口 5432)
- api(标签 app=api,端口 8080)
- web(标签 app=web,端口 80)

【操作要求】

1. 创建 Deny-All 默认策略(拒绝所有入站和出站流量)

2. 放行规则:
a. web → api:8080(前端调用 API)
b. api → db:5432(API 调用数据库)
c. 所有 Pod → CoreDNS(UDP 53,kube-system 命名空间)
d. web 允许来自 Ingress Controller 命名空间的入站(标签 app=ingress-nginx)

3. 验证:
- web 可以访问 api:8080 ✅
- api 可以访问 db:5432 ✅
- web 无法直接访问 db:5432 ❌(应被拒绝)
- api 无法访问外部网络 ❌(应被拒绝)

【评分标准】
- Deny-All 策略正确(15 分)
- web→api 规则正确(20 分)
- api→db 规则正确(20 分)
- DNS 放行规则正确(20 分)
- Ingress Controller 入站规则正确(15 分)
- 隔离验证通过(10 分)

🧹 环境清理

1
kubectl delete ns netpol-test netpol-external

📋 命令速查

命令 功能 注解
kubectl get networkpolicy 列出所有 NetworkPolicy short name: netpol
kubectl get networkpolicy -o yaml 查看 NetworkPolicy 详细规则 查看 podSelector、ingress/egress 规则、policyTypes
kubectl describe networkpolicy <name> NetworkPolicy 详情 包含规则匹配的 Pod 和流向
kubectl get pods -n kube-system -l k8s-app=calico-node 查看 Calico node Pod Calico 在每个节点运行一个 agent
kubectl -n kube-system logs -l k8s-app=calico-node --tail=50 查看 Calico 日志 NetworkPolicy 不生效时首要排查
calicoctl get ippool -o wide 查看 IP 地址池 确认 Pod CIDR 与 kubeadm init 一致
calicoctl get felixconfiguration 查看 Calico Felix 配置 Felix 是每个节点的策略执行引擎
calicoctl get networkpolicy 查看 Calico 层面的网络策略 比 kubectl get netpol 更底层
kubectl run test --image=busybox --rm -it --labels="app=test" -- wget -O- --timeout=3 http://<svc> 携带标签测试连通性 NetworkPolicy 基于标签匹配,运行测试 Pod 时需带正确的 labels
kubectl exec <pod> -- nc -zv <target-ip> <port> 测试 TCP 端口通断 比 curl/wget 更底层的连通性测试
kubectl get nodes -o wide | awk '{print $6}' 提取所有节点 CIDR Calico IPAM 基于节点 CIDR 分配 Pod IP
kubectl exec <pod> -- ip route 查看 Pod 内路由表 理解 Pod 出站流量路径

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:NetworkPolicy https://kubernetes.io/docs/concepts/services-networking/network-policies/
Kubernetes 官方:声明 Network Policy https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/
Calico 官方文档 https://docs.tigera.io/calico/latest/about/
Calico 网络策略指南 https://docs.tigera.io/calico/latest/network-policy/
CNI 规范 https://github.com/containernetworking/cni/blob/master/SPEC.md
Calico IPAM 原理 https://docs.tigera.io/calico/latest/networking/ipam/

📘 Day 08:Service 与集群内服务发现

🎯 今日目标

  • 理解 kube-proxy 如何实现 Service 流量转发
  • 创建四种类型 Service 并验证
  • 理解 Endpoint 与 Service 的关系
  • 掌握 CoreDNS 域名格式和 DNS 策略
  • 能通过 Pod DNS 名称实现服务间通信

🧠 理论精讲(30 分钟)

Service 解决了什么问题

Pod 是短暂的(IP 会变),Service 提供稳定的访问入口

1
2
3
Pod-A (IP: 10.244.1.5) ─┐
Pod-B (IP: 10.244.2.3) ─┤──→ Service (ClusterIP: 10.96.0.1) ──→ 客户端
Pod-C (IP: 10.244.1.9) ─┘

无论后端 Pod 如何变化,Service 的 ClusterIP 不变。

kube-proxy 工作模式

模式 原理 性能
iptables 通过 iptables 规则随机转发 规则数多时性能下降
IPVS 内核级负载均衡 高性能,支持多种调度算法

四种 Service 类型

类型 访问范围 典型场景
ClusterIP 集群内部 内部服务间通信
NodePort 节点 IP + 端口 开发调试、简单外部访问
LoadBalancer 外部 LB 分发 生产环境外部入口
ExternalName DNS CNAME 外部服务映射

CoreDNS 域名格式

1
<service-name>.<namespace>.svc.cluster.local
1
2
3
# 示例
api-server.default.svc.cluster.local
redis-cache.production.svc.cluster.local

🔧 动手实操(120 分钟)

练习 8.1:ClusterIP 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
# 1. 创建后端 Deployment
kubectl create deploy backend --image=nginx:alpine --replicas=3

# 2. 暴露为 ClusterIP Service
kubectl expose deploy backend --port=80 --target-port=80

# 3. 查看 Service
kubectl get svc backend
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# backend ClusterIP 10.96.xxx.xxx <none> 80/TCP 10s

# 4. 查看 Endpoint
kubectl get endpoints backend
# NAME ENDPOINTS AGE
# backend 10.244.1.2:80,10.244.2.3:80,10.244.1.4:80 10s

# 5. 用临时 Pod 测试访问
kubectl run test-client --image=busybox:1.36 --rm -it --restart=Never -- \
wget -q -O- http://backend
# 输出:nginx 默认页面

# 6. 测试完整的 FQDN
kubectl run test-dns --image=busybox:1.36 --rm -it --restart=Never -- \
wget -q -O- http://backend.default.svc.cluster.local

练习 8.2:NodePort Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 创建 NodePort Service
kubectl expose deploy backend --type=NodePort --port=80 --name=backend-np

# 2. 查看分配的端口
kubectl get svc backend-np
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# backend-np NodePort 10.96.xxx.xxx <none> 80:30080/TCP 5s

# NodePort 范围:30000-32767

# 3. 通过节点 IP 访问
# 获取任一节点 IP
kubectl get nodes -o wide
# 在集群外浏览器或 curl 访问:
curl http://<任意节点IP>:30080

# 4. 验证 NodePort 也在 ClusterIP 上可用
kubectl run test-np --image=busybox:1.36 --rm -it --restart=Never -- \
wget -q -O- http://backend-np

# 5. 清理
kubectl delete svc backend-np

练习 8.3:ExternalName Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 将外部域名映射为集群内部 Service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: database.example.com
EOF

# 验证
kubectl get svc external-db
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# external-db ExternalName <none> database.example.com <none> 5s

# 集群内 Pod 访问 external-db 会解析到 database.example.com
kubectl run dns-check --image=busybox:1.36 --rm -it --restart=Never -- \
nslookup external-db

# 清理
kubectl delete svc external-db

练习 8.4:Headless Service + 直接 Pod DNS

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
# 1. 创建 Headless Service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None
selector:
app: backend
ports:
- port: 80
EOF

# 2. 验证 ClusterIP 为空
kubectl get svc headless-svc
# CLUSTER-IP: None

# 3. DNS 查询 Headless Service
kubectl run dns-hl --image=busybox:1.36 --rm -it --restart=Never -- \
nslookup headless-svc
# 返回所有后端 Pod 的 IP(而不是一个 ClusterIP)

# 4. 清理
kubectl delete svc headless-svc

练习 8.5: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
36
37
38
39
40
41
42
# 1. 创建一个能区分 Pod 的后端
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: lb-test
spec:
replicas: 3
selector:
matchLabels:
app: lb-test
template:
metadata:
labels:
app: lb-test
spec:
containers:
- name: http
image: busybox:1.36
command:
- sh
- -c
- |
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello from $(hostname)" > /tmp/response
while true; do nc -l -p 8080 < /tmp/response; done
ports:
- containerPort: 8080
EOF

kubectl expose deploy lb-test --port=8080 --target-port=8080

# 2. 多次请求验证负载均衡效果
kubectl run lb-client --image=busybox:1.36 --rm -it --restart=Never -- \
sh -c 'for i in $(seq 1 10); do wget -q -O- http://lb-test:8080; echo; done'
# 输出应分布在不同 Pod

# 3. 检查 kube-proxy 模式
kubectl logs -n kube-system -l k8s-app=kube-proxy | grep "Using"

# 4. 清理
kubectl delete deploy backend lb-test
kubectl delete svc backend lb-test

🐛 排错练习(30 分钟)

场景 1:Service 无法访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 排查清单
# 1. Service 的 selector 是否匹配 Pod 的 labels?
kubectl get svc <svc-name> -o jsonpath='{.spec.selector}'
kubectl get pod -l <key>=<value>

# 2. Endpoint 是否为空?
kubectl get endpoints <svc-name>
# 如果 ENDPOINTS 为空,说明 selector 不匹配

# 3. targetPort 是否正确?
kubectl get svc <svc-name> -o jsonpath='{.spec.ports[*].targetPort}'
kubectl get pod -l app=<name> -o jsonpath='{.spec.containers[*].ports}'

# 4. 网络策略是否阻止?
kubectl get networkpolicies

场景 2:CoreDNS 解析失败

1
2
3
4
5
6
7
8
9
10
11
# 测试 DNS 解析
kubectl run dns-debug --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default

# 检查 CoreDNS Pod
kubectl get pods -n kube-system -l k8s-app=kube-dns

# 检查 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20

# 重启 CoreDNS
kubectl rollout restart deploy/coredns -n kube-system

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 35 分钟

题目:多层服务暴露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
【操作要求】

1. 创建 Deployment app-v1(3 副本,nginx:alpine),暴露为 ClusterIP Service app-svc
2. 创建 Deployment app-v2(2 副本,httpd:alpine),暴露为 ClusterIP Service app-v2-svc
3. 在 app-v1 的 nginx 中配置反向代理到 app-v2-svc(通过 Service 名称)
4. 创建 NodePort Service app-gateway,暴露 app-svc 到节点 31080 端口
5. 验证:
- 集群内 curl http://app-svc → 返回 nginx 页面
- 集群内 curl http://app-v2-svc → 返回 httpd 页面
- 节点 IP:31080 → 返回 nginx 页面
- CoreDNS 解析 app-svc 和 app-v2-svc 均正常
6. 输出所有 Service 的 Endpoint 信息

【评分标准】
- Service 创建正确(30 分)
- 集群内通信正常(25 分)
- NodePort 访问正常(20 分)
- DNS 解析正常(15 分)
- Endpoint 正确(10 分)

📋 命令速查

命令 功能 注解
kubectl get svc 列出所有 Service TYPE 列显示 ClusterIP/NodePort/LoadBalancer/ExternalName
kubectl get svc -o wide Service + 选择器/端口 确认 Selector 和暴露的端口
kubectl get endpoints 查看 Service 后端端点 为空说明 Selector 没匹配到 Running Pod
kubectl describe svc <name> Service 详细信息 查看 SessionAffinity、Endpoints、Events
kubectl expose deploy <name> --port=80 --target-port=8080 快速创建 Service 暴露 Deployment 默认创建 ClusterIP 类型
kubectl expose deploy <name> --type=NodePort --port=80 --target-port=8080 创建 NodePort Service 节点 IP:30000-32767 可外部访问
kubectl expose deploy <name> --type=LoadBalancer --port=80 创建 LoadBalancer Service 云厂商分配外部 LB IP(学习环境会一直 Pending)
kubectl run test --image=busybox --rm -it -- wget -O- http://<svc-name> 临时 Pod 测试 Service 可达性 --rm 退出即删除,网络调试首选
kubectl exec <pod> -- nslookup <svc-name> 验证 CoreDNS 解析 解析失败说明 CoreDNS 故障或 SVC 不存在
kubectl exec <pod> -- nslookup <svc-name>.<ns>.svc.cluster.local 验证 FQDN 解析 全限定域名格式:<svc>.<namespace>.svc.cluster.local
kubectl exec <pod> -- curl -s <svc-name>.<ns>.svc.cluster.local:<port> 通过 FQDN 访问服务 跨命名空间通信必须用 FQDN
kubectl get pods -l app=<label> 按标签查 Pod 验证 Service Selector 是否匹配到正确的 Pod
kubectl patch svc <name> -p '{"spec":{"sessionAffinity":"ClientIP"}}' 修改会话亲和性 同一客户端 IP 始终路由到同一 Pod
kubectl create svc clusterip <name> --tcp=80:8080 --dry-run=client -o yaml 生成 ClusterIP Service YAML --tcp 指定 <port>:<targetPort>
kubectl create svc nodeport <name> --tcp=80:8080 --node-port=30080 --dry-run=client -o yaml 生成 NodePort Service YAML 指定固定 NodePort 而非随机端口
kubectl -n kube-system logs -l k8s-app=kube-dns 查看 CoreDNS 日志 DNS 解析故障时首要排查

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:Service https://kubernetes.io/docs/concepts/services-networking/service/
Kubernetes 官方:DNS 与服务发现 https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
Kubernetes 官方:Service 调试 https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/
Kubernetes 官方:EndpointSlice https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/
CoreDNS 官方文档 https://coredns.io/manual/toc/

📘 Day 09:Ingress 与外部流量接入

🎯 今日目标

  • 理解 Ingress 不是 Service 的替代,而是 L7 路由层
  • 安装并配置 NGINX Ingress Controller
  • 创建基于路径的路由规则
  • 创建基于域名的路由规则
  • 配置 TLS 证书实现 HTTPS

🧠 理论精讲(30 分钟)

Ingress 是什么

1
2
3
4
5
6
7
8
9
                ┌──────────────┐
│ Ingress │ ← L7 路由规则
│ Controller │
└──────┬───────┘

┌─────────────────┼─────────────────┐
│ │ │
/api ──→ api-svc /web ──→ web-svc / ──→ frontend-svc
(ClusterIP) (ClusterIP) (ClusterIP)

Ingress 提供 HTTP/HTTPS 路由SSL 终止基于名称的虚拟托管

Ingress vs Service

特性 Service (NodePort/LB) Ingress
工作层级 L4 (TCP/UDP) L7 (HTTP/HTTPS)
路由能力 端口映射 路径/域名路由
SSL 需手动配置 内置 TLS 终止
外部入口 每 Service 一个 统一入口

Ingress Controller 选择

Controller 特点
NGINX Ingress 最常用,功能全面
Traefik 自动发现,适合微服务
Istio Gateway 服务网格场景
Contour/Envoy 高性能

🔧 动手实操(120 分钟)

练习 9.1:安装 NGINX Ingress Controller

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
# 方式 1:使用 Helm(推荐)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.service.type=NodePort \
--set controller.service.nodePorts.http=30080 \
--set controller.service.nodePorts.https=30443

# 方式 2:使用 kubectl apply(如果没装 Helm)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.0/deploy/static/provider/cloud/deploy.yaml

# 验证安装
kubectl get pods -n ingress-nginx
# ingress-nginx-controller-xxx Running

kubectl get svc -n ingress-nginx
# ingress-nginx-controller NodePort/LoadBalancer

# 验证 Controller 可访问
# 获取 NodePort 端口
kubectl get svc -n ingress-nginx ingress-nginx-controller

# 测试 404 页面(还没有 Ingress 规则)
curl http://<节点IP>:30080
# 返回 404(正常,还没有配置后端)

练习 9.2:基于路径的路由

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
# 1. 创建两个后端应用
kubectl create deploy app-v1 --image=nginx:alpine
kubectl expose deploy app-v1 --port=80

kubectl create deploy app-v2 --image=httpd:alpine
kubectl expose deploy app-v2 --port=80

# 2. 创建 Ingress 规则
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 80
- path: /v2
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80
EOF

# 3. 查看 Ingress
kubectl get ingress path-ingress
# NAME CLASS HOSTS ADDRESS PORTS AGE
# path-ingress nginx * <IP> 80 10s

kubectl describe ingress path-ingress

# 4. 测试路由
curl http://<节点IP>:30080/v1
# 返回 nginx 默认页面

curl http://<节点IP>:30080/v2
# 返回 httpd 的 "It works!" 页面

# 5. 清理
kubectl delete ingress path-ingress
kubectl delete deploy app-v1 app-v2
kubectl delete svc app-v1 app-v2

练习 9.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 1. 创建两个后端服务
kubectl create deploy web-a --image=nginx:alpine
kubectl expose deploy web-a --port=80
kubectl create deploy web-b --image=httpd:alpine
kubectl expose deploy web-b --port=80

# 2. 创建域名 Ingress
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: host-ingress
spec:
ingressClassName: nginx
rules:
- host: app-a.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-a
port:
number: 80
- host: app-b.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-b
port:
number: 80
EOF

# 3. 测试域名路由(通过 Host 头)
curl -H "Host: app-a.example.com" http://<节点IP>:30080/
# 返回 nginx 页面

curl -H "Host: app-b.example.com" http://<节点IP>:30080/
# 返回 httpd 页面

# 4. 清理
kubectl delete ingress host-ingress
kubectl delete deploy web-a web-b
kubectl delete svc web-a web-b

练习 9.4:Ingress TLS 配置

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
# 1. 生成自签名证书
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=tls-demo.example.com/O=tls-demo"

# 2. 创建 TLS Secret
kubectl create secret tls demo-tls --key=tls.key --cert=tls.crt

# 3. 创建后端服务
kubectl create deploy secure-app --image=nginx:alpine
kubectl expose deploy secure-app --port=80

# 4. 创建 TLS Ingress
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: nginx
tls:
- hosts:
- tls-demo.example.com
secretName: demo-tls
rules:
- host: tls-demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-app
port:
number: 80
EOF

# 5. 测试 HTTPS 访问(-k 跳过证书验证)
# 先获取 HTTPS NodePort
kubectl get svc -n ingress-nginx ingress-nginx-controller

curl -k -H "Host: tls-demo.example.com" https://<节点IP>:30443/
# 返回 nginx 页面

# 6. 查看证书信息
echo | openssl s_client -connect <节点IP>:30443 -servername tls-demo.example.com 2>/dev/null | openssl x509 -noout -subject -dates

# 7. 清理
rm -f tls.key tls.crt
kubectl delete ingress tls-ingress
kubectl delete secret demo-tls
kubectl delete deploy secure-app
kubectl delete svc secure-app

🐛 排错练习(30 分钟)

场景 1:Ingress 返回 404

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 排查步骤
# 1. 确认 Ingress Controller 是否运行
kubectl get pods -n ingress-nginx

# 2. 确认 Ingress 资源创建成功
kubectl get ingress

# 3. 检查 Ingress 详情
kubectl describe ingress <name>
# 看 Rules 和 Backend 是否正确

# 4. 确认后端 Service 存在且有 Endpoint
kubectl get svc <svc-name>
kubectl get endpoints <svc-name>

# 5. 检查 ingressClassName
kubectl get ingress <name> -o yaml | grep ingressClassName
# 应为 nginx(或与 Controller 类型一致)

# 6. 查看 Ingress Controller 日志
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=50

场景 2:TLS 证书不生效

1
2
3
4
5
6
7
8
9
10
11
# 排查步骤
# 1. 检查 Secret 是否存在且类型正确
kubectl get secret <secret-name>
kubectl describe secret <secret-name>
# 应有 tls.crt 和 tls.key

# 2. 检查证书内容
kubectl get secret <secret-name> -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout | head -20

# 3. 检查 Ingress tls 段
kubectl get ingress <name> -o yaml | grep -A5 tls

🏆 赛题模拟(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
【环境要求】
- NGINX Ingress Controller 已安装(NodePort 30080/30443)

【操作要求】

1. 创建 3 个后端应用:
- api(nginx:alpine,2 副本,ClusterIP Service api-svc)
- web(httpd:alpine,2 副本,ClusterIP Service web-svc)
- admin(nginx:alpine,1 副本,ClusterIP Service admin-svc)

2. 创建 Ingress 规则:
a. 路径路由:
- /api → api-svc
- / → web-svc
b. 域名路由(新增第二个 Ingress 或合并):
- admin.example.com → admin-svc

3. 配置注解:
- nginx.ingress.kubernetes.io/rewrite-target: /
- 为 api 路径配置 CORS 允许所有来源

4. 为 admin.example.com 配置自签名 TLS

5. 验证:
- curl <NodeIP>:30080/api → 返回 nginx
- curl <NodeIP>:30080/ → 返回 httpd
- curl -H "Host: admin.example.com" <NodeIP>:30080/ → 返回 nginx
- curl -k -H "Host: admin.example.com" https://<NodeIP>:30443/ → 返回 nginx

【评分标准】
- 后端服务创建(20 分)
- 路径路由正确(25 分)
- 域名路由正确(20 分)
- TLS 配置正确(25 分)
- 验证全部通过(10 分)

📋 命令速查

命令 功能 注解
kubectl get ingress 列出 Ingress ADDRESS 为空说明 Ingress Controller 未就绪
kubectl get ingress -o wide Ingress + 主机/地址 查看 Host 规则和分配的 ADDRESS
kubectl describe ingress <name> Ingress 详细规则 查看每条 Path 规则和后端 Service
kubectl get ingressclass 列出 IngressClass 多个 Ingress Controller 时用于区分
kubectl -n ingress-nginx get pods 查看 Ingress Controller Pod nginx-ingress 默认部署在 ingress-nginx 命名空间
kubectl -n ingress-nginx logs -l app.kubernetes.io/name=ingress-nginx 查看 Ingress Controller 日志 Ingress 路由异常时的首要排查
kubectl -n ingress-nginx get svc 查看 Ingress Controller Service 确认是否分配了 EXTERNAL-IP
curl -H "Host: <hostname>" http://<node-ip>:<nodeport> 测试 Ingress 路由 绕过 DNS 直接用 Header 指定 Host
curl -k https://<hostname> 测试 HTTPS Ingress -k 跳过自签名证书验证
kubectl create ingress <name> --rule="host/path=svc:port" --dry-run=client -o yaml 生成 Ingress YAML 快速生成基本的 Ingress 规则模板
kubectl get svc -A | grep LoadBalancer 查找所有 LB 类型 Service 云环境中 LB 会分配公网 IP
kubectl patch ingress <name> -p '{"metadata":{"annotations":{"nginx.ingress.kubernetes.io/rewrite-target":"/"}}}}' 添加 Ingress 注解 nginx-ingress rewrite 注解实现 URL 重写
kubectl create secret tls <name> --cert=cert.pem --key=key.pem 创建 TLS Secret Ingress HTTPS 必须用此类型 Secret

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:Ingress https://kubernetes.io/docs/concepts/services-networking/ingress/
Kubernetes 官方:Ingress Controller https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/
NGINX Ingress Controller 文档 https://kubernetes.github.io/ingress-nginx/
NGINX Ingress 注解参考 https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
Kubernetes 官方:TLS/SSL https://kubernetes.io/docs/concepts/services-networking/ingress/#tls

📘 Day 05:Deployment 与 ReplicaSet

🎯 今日目标

  • 理解 Deployment → ReplicaSet → Pod 的层级关系
  • 能执行滚动更新并观察 Pod 替换过程
  • 能用 rollout undo 回滚到任意版本
  • 能用 rollout pause/resume 实现金丝雀发布
  • 能排查滚动更新卡住的常见原因

🧠 理论精讲(30 分钟)

三层关系

1
2
3
4
5
Deployment  ─── 管理版本、更新策略

└──→ ReplicaSet ─── 确保 Pod 副本数达标

└──→ Pod ─── 运行容器

每次 Deployment 更新会创建新 ReplicaSet,旧的 ReplicaSet 保留(用于回滚)。

滚动更新策略

参数 含义 默认值
maxSurge 更新期间最多超出多少个 Pod 25%
maxUnavailable 更新期间最多不可用多少个 Pod 25%
1
2
3
4
5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 一次最多多创建 1 个
maxUnavailable: 0 # 任何时刻必须有全部 Pod 可用

关键命令速查

命令 用途
kubectl rollout status deploy/<name> 查看更新进度
kubectl rollout history deploy/<name> 查看版本历史
kubectl rollout undo deploy/<name> 回滚到上一版本
kubectl rollout pause deploy/<name> 暂停更新
kubectl rollout resume deploy/<name> 恢复更新

🔧 动手实操(120 分钟)

练习 5.1:创建 Deployment + 观察 ReplicaSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 创建 Deployment(3 副本)
kubectl create deploy nginx --image=nginx:1.21 --replicas=3

# 2. 查看 Deployment
kubectl get deploy nginx
# NAME READY UP-TO-DATE AVAILABLE AGE
# nginx 3/3 3 3 30s

# 3. 查看 ReplicaSet(自动创建)
kubectl get rs -l app=nginx
# NAME DESIRED CURRENT READY AGE
# nginx-<hash> 3 3 3 30s

# 4. 查看 RS 管理的 Pod
kubectl get pod -l app=nginx -o wide
# 3 个 Pod 分布在不同节点

# 5. 验证 RS 的自我修复:删除一个 Pod
kubectl delete pod <任意一个 nginx pod>
kubectl get pod -l app=nginx -w
# RS 立即创建新 Pod 补足 3 副本

练习 5.2:滚动更新全流程

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
# 打开 3 个终端窗口:

# 终端1:观察 Deployment 状态
kubectl get deploy nginx -w

# 终端2:观察 Pod 变化
kubectl get pod -l app=nginx -w

# 终端3:观察 ReplicaSet 变化
kubectl get rs -l app=nginx -w

# === 触发滚动更新 ===

# 终端4:更新镜像
kubectl set image deploy/nginx nginx=nginx:1.22

# 在终端1 观察:
# UP-TO-DATE 从 0 → 1 → 2 → 3
# 在终端2 观察:
# 新 Pod 创建,旧 Pod 逐步 Terminating
# 在终端3 观察:
# 新 RS 的 DESIRED 递增,旧 RS 的 DECREASED 递减

# 查看更新状态
kubectl rollout status deploy/nginx

# 查看更新历史
kubectl rollout history deploy/nginx
# REVISION CHANGE-CAUSE
# 1 <none>
# 2 <none>

练习 5.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
# 1. 制造一个失败的更新
kubectl set image deploy/nginx nginx=nginx:not-exists

# 2. 观察卡住
kubectl rollout status deploy/nginx
# 看到 "Waiting for deployment rollout to finish..."

# 3. 查看 Pod 状态
kubectl get pod -l app=nginx
# 新 Pod ImagePullBackOff 或 ErrImagePull

# 4. 立即回滚!
kubectl rollout undo deploy/nginx

# 5. 查看回滚后的 Pod 状态
kubectl get pod -l app=nginx
# 恢复为旧版本

# 6. 查看历史记录
kubectl rollout history deploy/nginx
# REVISION CHANGE-CAUSE
# 1 <none>
# 3 <none> # REVISION 2 被跳过了

# 7. 回滚到指定 REVISION
kubectl rollout undo deploy/nginx --to-revision=1

# 8. 为更新添加记录标记
kubectl set image deploy/nginx nginx=nginx:1.23 --record=false
# 注意:--record 在新版中已弃用,使用 annotation 替代
kubectl annotate deploy/nginx kubernetes.io/change-cause="update to 1.23"
kubectl rollout history deploy/nginx

练习 5.4:扩缩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 快速扩容到 6 个副本
kubectl scale deploy/nginx --replicas=6

# 2. 观察 Pod 增加
kubectl get pod -l app=nginx -w

# 3. 缩容到 2 个
kubectl scale deploy/nginx --replicas=2

# 4. 观察 Pod 被终止
kubectl get pod -l app=nginx -w

# 5. 查看 Deployment 的当前期望和实际状态
kubectl get deploy nginx -o jsonpath='
期望副本:{.spec.replicas}
就绪副本:{.status.readyReplicas}
可用副本:{.status.availableReplicas}
'

练习 5.5:金丝雀发布基础(pause/resume)

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
# 原理:暂停滚动更新 → 验证小部分新 Pod → 恢复全量更新

# 1. 恢复到稳定状态
kubectl scale deploy/nginx --replicas=10

# 2. 暂停 Deployment 的滚动更新
kubectl rollout pause deploy/nginx

# 3. 触发镜像更新(因为有 pause,只会创建新 RS 但不创建新 Pod)
kubectl set image deploy/nginx nginx=nginx:1.24
kubectl annotate deploy/nginx kubernetes.io/change-cause="canary 1.24"

# 4. 查看当前的 ReplicaSet
kubectl get rs -l app=nginx
# 看到新 RS 的 DESIRED=0(因为暂停了)

# 5. "放行" 1 个新版本 Pod 做金丝雀验证
# 方法:手动改新 RS 的 replicas(不太好)
# 更好方法:直接 resume 但限制 maxSurge
kubectl rollout resume deploy/nginx

# 6. 实时观察
kubectl rollout status deploy/nginx

# 7. 清理
kubectl delete deploy nginx

🐛 排错练习(30 分钟)

场景 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
# 1. 创建 Deployment
kubectl create deploy test --image=nginx:1.21 --replicas=3

# 2. 更新到不存在镜像
kubectl set image deploy/test nginx=nginx:99.99

# 3. 观察状态
kubectl get deploy test
# 看到 READY 3/3、UP-TO-DATE 1、AVAILABLE 3

kubectl get rs -l app=test
# 新 RS READY=0,旧 RS READY=3

kubectl get pod -l app=test
# 新 Pod ImagePullBackOff

# 4. 查看详细原因
kubectl describe pod <新 pod> | grep -A5 Events

# 5. 排查命令总结
kubectl rollout status deploy/test
kubectl describe deploy test
kubectl logs <pod-name> # 如果是应用启动失败

# 6. 回滚
kubectl rollout undo deploy/test

# 7. 清理
kubectl delete deploy test

场景 2:回滚失败(无历史记录)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建新 Deployment
kubectl create deploy test2 --image=nginx:1.21 --replicas=2

# 查看 revisionHistoryLimit(默认 10)
kubectl get deploy test2 -o jsonpath='{.spec.revisionHistoryLimit}'

# 模拟没有历史
kubectl rollout undo deploy/test2
# 可能报错:no rollout history found(首次创建没有历史)

# 解决方案:至少要有一次成功的更新才能回滚
kubectl set image deploy/test2 nginx=nginx:1.22
kubectl rollout status deploy/test2
kubectl rollout history deploy/test2
# 现在有了两个 revision

kubectl delete deploy test2

🏆 赛题模拟(40 分钟)

⚠️ 严格限时 40 分钟

题目:Deployment 滚动发布综合

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
【操作要求】

1. 用 --dry-run=client 生成 Deployment YAML:
- 名称:web-app
- 镜像:nginx:1.21-alpine
- 副本数:5
- 资源限制:cpu 100m, memory 128Mi
- 滚动更新策略:maxSurge=1, maxUnavailable=0

2. 应用该 YAML,等待所有 Pod Ready

3. 执行以下版本变更序列:
a. 更新到 nginx:1.22-alpine,记录 change-cause
b. 等待完成
c. 更新到 nginx:1.23-alpine,记录 change-cause
d. 等待完成

4. 假设 1.23 版本有 bug,请:
a. 先回滚到上一个版本
b. 再回滚到最初版本(1.21-alpine)
c. 确认最终所有 Pod 使用 nginx:1.21-alpine

5. 将副本数缩放到 2,再扩容到 5

6. 输出 rollout history 完整记录

【验证命令】
kubectl rollout history deploy/web-app
kubectl get deploy web-app -o jsonpath='{.spec.template.spec.containers[0].image}'
kubectl get pod -l app=web-app -o jsonpath='{range .items[*]}{.metadata.name}{": "}{.spec.containers[0].image}{"\n"}{end}'

【评分标准】
- YAML 正确(20 分)
- 滚动更新步骤正确(30 分)
- 回滚到指定版本正确(25 分)
- 扩缩容正确(15 分)
- history 记录完整(10 分)

📋 命令速查

命令 功能 注解
kubectl create deploy nginx --image=nginx:alpine 创建 Deployment 最快捷的部署方式
kubectl get deploy 列出 Deployment DESIRED/CURRENT/READY/UP-TO-DATE/AVAILABLE 五个状态字段
kubectl get deploy -o wide Deployment + 镜像/标签/选择器 确认当前使用的镜像版本
kubectl get rs 列出 ReplicaSet Deployment 每次更新生成新 RS
kubectl get deploy,rs,pod 同时查看 Deploy→RS→Pod 层级 逗号分隔多资源类型
kubectl describe deploy <name> Deployment 详细信息 包含滚动更新策略、事件历史
kubectl scale deploy <name> --replicas=5 扩缩副本 等价于修改 spec.replicas
kubectl set image deploy/<name> <container>=<new-image>:<tag> 更新容器镜像 触发滚动更新,比 edit 效率高
kubectl set resources deploy/<name> -c=<container> --limits=cpu=200m,memory=256Mi --requests=cpu=100m,memory=128Mi 设置资源限制 更新 Pod 模板中的 resources 字段
kubectl rollout status deploy/<name> 查看滚动更新进度 输出 “successfully rolled out” 即完成
kubectl rollout history deploy/<name> 查看部署历史 每次更新生成一个 revision 号
kubectl rollout history deploy/<name> --revision=3 查看第 3 个版本的详情 对比不同版本的镜像和配置
kubectl rollout undo deploy/<name> 回滚上一版本 创建新 RS 并回退 Pod 模板
kubectl rollout undo deploy/<name> --to-revision=2 回滚到指定版本 revision 必须存在于历史中(默认保留 10 个)
kubectl rollout pause deploy/<name> 暂停滚动更新 金丝雀发布时暂停以验证新版
kubectl rollout resume deploy/<name> 恢复滚动更新 暂停后恢复继续滚动
kubectl rollout restart deploy/<name> 重启所有 Pod(滚动重建) 1.15+ 支持,配置变更后快速生效
kubectl patch deploy <name> -p '{"spec":{"minReadySeconds":10}}' JSON Patch 修改字段 比 edit 安全,适合 CI/CD
kubectl delete deploy <name> 删除 Deployment 级联删除 RS 和 Pod(默认)
kubectl delete deploy <name> --cascade=orphan 仅删 Deploy 保留 Pod Pod 变为孤儿,由新控制器接管
kubectl get pod --show-labels Pod + 标签 验证标签选择器是否匹配

📚 参考来源

来源 链接 / 说明
Kubernetes 官方:Deployment https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
Kubernetes 官方:ReplicaSet https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
Kubernetes 官方:滚动更新 https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/
Kubernetes 官方:回滚 Deployment https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#rolling-back-a-deployment
Kubernetes 官方:kubectl rollout https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#rollout
0%