主题
附录:动手实验(kind 多节点集群)
5 个循序渐进的实验,覆盖本教材全部核心知识点。全程约 60-90 分钟。
0. 环境准备
安装 kind(如已安装可跳过)
bash
# Linux
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
# 确认 kubectl 可用
kubectl version --client创建 3 节点集群(1 控制面 + 2 工作节点)
bash
cat <<EOF | kind create cluster --name taint-lab --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
kubectl get nodes
# NAME STATUS ROLES AGE
# taint-lab-control-plane Ready control-plane ...
# taint-lab-worker Ready <none> ...
# taint-lab-worker2 Ready <none> ...实验结束后清理:
kind delete cluster --name taint-lab
实验 1:观察默认污点与容忍(10 分钟)
目标:认识 control-plane 污点和自动注入的默认容忍。
bash
# 1. 查看控制面节点的污点
kubectl describe node taint-lab-control-plane | grep -A5 Taints
# 预期:Taints: node-role.kubernetes.io/control-plane:NoSchedule
# 2. 部署一个普通 Deployment,观察它去了哪些节点
kubectl create deployment web --image=nginx --replicas=4
kubectl get pods -o wide
# 预期:所有 Pod 都在两个 worker 上,没有一个在 control-plane(被污点挡住)
# 3. 查看任意 Pod 自动注入的默认容忍
kubectl get pod -l app=web -o jsonpath='{.items[0].spec.tolerations}' | jq .
# 预期:not-ready 和 unreachable 的 NoExecute 容忍,tolerationSeconds=300思考:为什么没给 web 配任何 toleration,它却能正常调度?(提示:它去的节点上没有污点)
实验 2:NoSchedule 硬隔离(15 分钟)
目标:验证污点阻挡 + 容忍放行。
bash
# 1. 给 worker2 打污点
kubectl taint nodes taint-lab-worker2 dedicated=special:NoSchedule
# 2. 扩容 web 到 6 副本,观察分布
kubectl scale deployment web --replicas=6
kubectl get pods -o wide
# 预期:新 Pod 全部堆在 worker 上,没有一个去 worker2
# 3. 创建带容忍的 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: tolerated-pod
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "special"
effect: "NoSchedule"
containers:
- name: nginx
image: nginx
EOF
kubectl get pod tolerated-pod -o wide
# 观察:它可能调度到 worker2,也可能到 worker(容忍≠定向!)
# 4. 验证"万能容忍"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: tolerate-all
spec:
tolerations:
- operator: "Exists"
containers:
- name: nginx
image: nginx
EOF
kubectl get pod tolerate-all -o wide
# 观察:它甚至可以被调度到 control-plane!(这就是生产禁用万能容忍的原因)实验 3:定向调度组合(15 分钟)
目标:nodeAffinity + toleration 四件套,保证 Pod 必须且只能去 worker2。
bash
# 1. 给 worker2 打标签
kubectl label nodes taint-lab-worker2 team=special
# 2. 部署专用服务
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: special-app
spec:
replicas: 3
selector:
matchLabels:
app: special-app
template:
metadata:
labels:
app: special-app
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: team
operator: In
values: ["special"]
tolerations:
- key: "dedicated"
operator: "Equal"
value: "special"
effect: "NoSchedule"
containers:
- name: nginx
image: nginx
EOF
kubectl get pods -l app=special-app -o wide
# 预期:3 个 Pod 全部在 worker2
# 3. 反向验证:删掉 toleration 会发生什么?
kubectl patch deployment special-app --type=json \
-p='[{"op":"remove","path":"/spec/template/spec/tolerations"}]'
kubectl get pods -l app=special-app -o wide
# 预期:新 Pod 全部 Pending
kubectl describe pod -l app=special-app | grep FailedScheduling | tail -1
# 预期报错:node(s) had untolerated taint {dedicated: special}
# 恢复容忍
kubectl patch deployment special-app --type=json -p='[
{"op":"add","path":"/spec/template/spec/tolerations","value":
[{"key":"dedicated","operator":"Equal","value":"special","effect":"NoSchedule"}]}]'结论:affinity 管"必须去",toleration 管"允许进",缺一不可。
实验 4:NoExecute 驱逐与 tolerationSeconds(20 分钟)
目标:观察三种 Pod 在 NoExecute 污点的不同命运。
bash
# 1. 准备三个 Pod(先确保它们都调度到 worker,即无污点的节点)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: no-toleration
spec:
containers: [{name: nginx, image: nginx}]
---
apiVersion: v1
kind: Pod
metadata:
name: toleration-30s
spec:
tolerations:
- key: "evict-test"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 30
containers: [{name: nginx, image: nginx}]
---
apiVersion: v1
kind: Pod
metadata:
name: toleration-forever
spec:
tolerations:
- key: "evict-test"
operator: "Exists"
effect: "NoExecute"
containers: [{name: nginx, image: nginx}]
EOF
# 2. 确认三个 Pod 都在同一个节点(如不在,删除重建或在 spec 加 nodeName)
kubectl get pods no-toleration toleration-30s toleration-forever -o wide
NODE=$(kubectl get pod no-toleration -o jsonpath='{.spec.nodeName}')
echo "目标节点: $NODE"
# 3. 打开另一个终端观察:kubectl get pods -w
# 然后给该节点打 NoExecute 污点
kubectl taint nodes $NODE evict-test=true:NoExecute
# 预期结果:
# - no-toleration → 立即被驱逐(Pod 消失,无控制器不会重建)
# - toleration-30s → 约 30 秒后被驱逐
# - toleration-forever→ 存活,直到污点被删除
# 4. 清理污点,观察 toleration-forever 是否还在运行
kubectl taint nodes $NODE evict-test=true:NoExecute-
kubectl get pods结论:tolerationSeconds 是"缓刑时长",不设置 = 永久容忍,0 = 立即驱逐。
实验 5:故障转移时间调优(15 分钟)
目标:验证显式 tolerationSeconds 覆盖默认 300 秒。
bash
# 1. 部署一个 30 秒故障转移的 Deployment
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: fast-failover
spec:
replicas: 1
selector:
matchLabels:
app: fast-failover
template:
metadata:
labels:
app: fast-failover
spec:
tolerations:
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 30
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 30
containers:
- name: nginx
image: nginx
EOF
# 2. 查看它调度到了哪个节点
kubectl get pod -l app=fast-failover -o wide
# 3. 模拟节点宕机:停掉该节点容器内的 kubelet(kind 玩法)
NODE=$(kubectl get pod -l app=fast-failover -o jsonpath='{.items[0].spec.nodeName}')
docker exec $NODE systemctl stop kubelet
# 4. 观察时间线(另一个终端 watch)
kubectl get pods -l app=fast-failover -w
# 预期:节点 Ready 变 Unknown → 被打 unreachable 污点
# → 约 30 秒后 Pod 被驱逐并在健康节点重建(而非默认的 300 秒)
# 5. 恢复节点
docker exec $NODE systemctl start kubelet
kubectl get nodes -w # 等节点恢复 Ready说明:kind 节点是容器,可直接用 docker 操作其 kubelet,非常适合做故障演练。
实验总结自查表
| 知识点 | 实验 | 我掌握了 |
|---|---|---|
| control-plane 默认污点 | 实验 1 | ☐ |
| 默认注入的 not-ready/unreachable 容忍 | 实验 1 | ☐ |
| NoSchedule 阻挡新 Pod、不影响存量 | 实验 2 | ☐ |
| Equal / Exists / 万能容忍 | 实验 2 | ☐ |
| 容忍≠定向,四件套组合 | 实验 3 | ☐ |
| NoExecute 驱逐 + tolerationSeconds | 实验 4 | ☐ |
| 覆盖默认 300 秒故障转移时间 | 实验 5 | ☐ |
清理
bash
kind delete cluster --name taint-lab🎉 完成全部实验后,回到第 8 章的面试题自测,全部能流畅答出即为"精通"毕业!