Skip to content

附录:动手实验(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 章的面试题自测,全部能流畅答出即为"精通"毕业!