外观
第三节 kind 集群验证与节点管理
约 3324 字大约 11 分钟
KuberneteskindCNICoreDNS
2026-07-28
项目目标
将创建好的 yunfan 集群从“命令返回成功”进一步验证到“API 端点、节点 Conditions、CNI 网络、CoreDNS 与 Pod 调度能力持续就绪”;在工作区根目录编写探针工作负载 smoke-test.yaml,体验拓扑打散调度;完成工作节点的 cordon(封锁)、drain(驱逐)与 uncordon(解封)维护闭环,深入理解 Eviction API 机制。
3.1 验收标准:集群创建成功不等于服务可用
在上一课中,kind 成功创建了包含 1 个控制平面节点与 2 个工作节点的集群。kind 命令退出码为 0,仅能代表 Docker Engine 完成了节点容器的拉起和基础初始化,不能单独证明集群底座的健康度。
必须通过严谨的逐层验证链,确保每个核心组件均达到可承载业务的状态:
| 验证维度 | 核心观察对象 | 仅凭 kind create 无法排查的隐藏风险 |
|---|---|---|
| API 连通性 | kubeconfig、context、/readyz?verbose 端点 | kubectl 连错集群,或 API Server 鉴权模块异常 |
| 节点健康 | Node Conditions (Ready, MemoryPressure 等) | kubelet 汇报中断或磁盘空间写满导致节点卡在 NotReady |
| Pod 网络 | kindnet CNI DaemonSet | CNI 插件拉取失败导致 Pod 无法获取 IP 或跨节点通信掉包 |
| 集群 DNS | CoreDNS Deployment 与 Service | CoreDNS 副本没有就绪,导致集群内部服务域名解析失败 |
| 工作负载调度 | Pod 的实际节点调度分布 | 调度器无法按预期将副本均匀打散到不同工作节点 |
| 节点运维可控 | cordon / drain / uncordon | 运维封锁或驱逐节点时,忽略了 DaemonSet 或造成无法恢复的故障 |
下面是标准的集群底座能力验收顺序:
3.2 机制解析:kind 自动化背后的 Kubernetes 初始化流程
3.2.1 kind 自动完成了哪些 kubeadm 步骤
在真实的生产服务器环境中,部署 Kubernetes 集群需要使用 kubeadm init 初始化控制平面,再使用 kubeadm join 逐个加入工作节点,并手动配置 CNI 网络插件与 kubeconfig。kind 将这些繁琐的操作打包封装到了节点容器内。
| kind 的执行阶段 | 对应的原生 K8s / kubeadm 动作 | 底层产生的结果与对象 |
|---|---|---|
| 启动 control-plane 容器 | kubeadm init 自动化初始化 | 启动 etcd、API Server、Scheduler 和 Controller Manager |
| 部署集群网络 CNI | 自动 apply kindnet YAML | 为每个 Pod 分配独立 IP,配置节点间的虚拟 Bridge 路由 |
| 启动 worker 容器 | kubeadm join 节点加入 | 节点上的 kubelet 携带 Token 向控制平面建立双向 TLS 认证 |
| 导出 kubeconfig | 提取控制平面 CA 证书与 Context | 将 API 访问凭据写入宿主机 ~/.kube/config |
| 就绪等待轮询 | 访问集群 /readyz 接口 | 确认所有系统 Pod 达到 Running 状态后返回控制权 |
观察下面的交互面板,理解 kind 命令与 Kubernetes 内部引导机制的映射关系:
kind 自动化与 Kubernetes 内部初始化映射演示
观察 kind 命令背后的底层步骤:从容器启动、kubeadm 初始化、CNI 安装到凭据导出。
1 / 6
kind 视角动作
节点内部 kubeadm / K8s 动作
节点可观察结果
1. 解析配置文件
资源校验与预检查生成多节点节点容器创建方案
2. 启动控制平面容器
kubeadm init 初始化启动 etcd, API Server, Scheduler
3. 部署集群网络 CNI
安装 kindnet DaemonSet给各节点分配 Pod CIDR 子网
4. 启动工作节点容器
kubeadm join 注册节点worker 节点加入集群
5. 导出 kubeconfig
生成 client 访问凭据kubectl 可以访问 API Server
6. 探针轮询与健康就绪
查询 /readyz 与 Node Status集群节点全部变为 Ready 状态
3.2.2 深入 API Server 的 /readyz?verbose 健康检查端点
API Server 提供了 /readyz 端点用于精细诊断控制平面的各项内部子组件是否达到准备状态。
在终端中执行以下命令查看 API 健康大管家:
kubectl get --raw='/readyz?verbose'命令解析与输出解读:
kubectl get --raw: 绕过 kubectl 的普通资源格式化输出,直接向 API Server 发起原生 HTTP GET 请求。?verbose: 展开逐项细节。输出中每个以[+]开头的子检查项代表一个关键模块:[+]ping: 验证 API Server HTTP 基础服务响应状态。[+]log: 验证日志子系统挂载点。[+]etcd: 验证 API Server 与后端存储数据库 etcd 的读写连通性。[+]poststarthook/...: 验证 API 启动后的 hook 钩子任务(如 CRD 注册、ServiceAccount Token 生成器等)。
- 结尾显示
readyz check passed表示集群控制平面完全健康。
3.3 基线检查:节点 Conditions 与系统组件分析
3.3.1 Ready 状态与节点 Conditions 的深层含义
运行以下命令,查看节点摘要与详细状态:
kubectl get nodes -o wide
kubectl describe node yunfan-worker在 kubectl describe node 的输出中,重点关注 Conditions(节点状态列表):
| 状态类型 (Condition) | 健康期望值 | 异常影响与底层排查思路 |
|---|---|---|
Ready | True | 若为 False 或 Unknown,说明 kubelet 掉线或节点 CNI/网络故障。 |
MemoryPressure | False | 若为 True,说明节点内存使用率触发临界阈值,kubelet 开始驱逐低优先级 Pod。 |
DiskPressure | False | 若为 True,说明节点容器运行时磁盘空间受限,无法继续拉取新镜像或创建临时文件。 |
PIDPressure | False | 若为 True,说明节点内部进程数达到上限,阻止创建新进程。 |
3.3.2 区分 kube-system 中的三大核心组件
在 kube-system 命名空间中,有三个非常容易混淆的核心组件:
| 组件名称 | 部署类型 | 核心职责 |
|---|---|---|
| kindnet | DaemonSet | CNI 网络插件:负责为节点分配 Pod CIDR,实现集群内 Pod 之间的网络平铺连通。 |
| kube-proxy | DaemonSet | 网络代理:在每个节点维护 iptables/IPVS 规则,将 Service 的 ClusterIP 访问请求负载均衡到后端 Pod。 |
| CoreDNS | Deployment | 域名解析:在集群内部提供 DNS 服务的 Pod,将 Service 名称(如 web-smoke)解析为具体的虚拟 IP。 |
执行以下命令,验证系统级 Pod 的运行状况:
kubectl get pods -n kube-system -o wide预期输出: coredns 部署的副本数应达到 Ready;kindnet 与 kube-proxy 必须在 3 个节点上均有一个 Running 状态的实例。
3.4 实训:用探针工作负载验证调度、网络与 DNS
仅靠检查节点和系统 Pod 仍然不够,我们需要在项目根目录下创建一个包含 Deployment 和 Service 的探针配置文件 smoke-test.yaml,从真实工作负载的角度验证 Pod 调度分布 和 CoreDNS 名称解析。
3.4.1 配置文件完整代码:smoke-test.yaml
在项目根目录下直接创建 smoke-test.yaml 文件:
smoke-test.yaml
apiVersion: v1
kind: Namespace
metadata:
name: yunfan-smoke
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-smoke
namespace: yunfan-smoke
spec:
replicas: 2
selector:
matchLabels:
app: web-smoke
template:
metadata:
labels:
app: web-smoke
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web-smoke
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-smoke
namespace: yunfan-smoke
spec:
selector:
app: web-smoke
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dnsutils
namespace: yunfan-smoke
spec:
replicas: 1
selector:
matchLabels:
app: dnsutils
template:
metadata:
labels:
app: dnsutils
spec:
containers:
- name: dnsutils
image: registry.k8s.io/e2e-test-images/agnhost:2.39
imagePullPolicy: IfNotPresent3.4.2 关键 YAML 语法与调度限制解析
topologySpreadConstraints(拓扑分布约束):maxSkew: 1: 允许不同主机间 Pod 数量的最大偏差值为 1,强制调度器尽量将 2 个web-smoke副本分布在不同的工作节点上(yunfan-worker与yunfan-worker2)。topologyKey: kubernetes.io/hostname: 依据节点的拓扑标签kubernetes.io/hostname进行域名打散。whenUnsatisfiable: ScheduleAnyway: 软约束策略。当可用工作节点不足时,依然允许调度,避免阻塞。
dnsutils探针容器:镜像内置了nslookup工具,专门用于发起 DNS 验证查询。
3.4.3 部署探针与网络/DNS 连通性测试
在终端中依次执行以下命令:
1. 部署探针资源并等待就绪:
kubectl apply -f smoke-test.yaml
kubectl wait -n yunfan-smoke --for=condition=Available deployment/web-smoke --timeout=120s
kubectl wait -n yunfan-smoke --for=condition=Available deployment/dnsutils --timeout=120s2. 检查 Pod 调度分布:
kubectl get pods -n yunfan-smoke -o wide预期输出: 你应该能看到 2 个 web-smoke 副本分别被调度到了 yunfan-worker 和 yunfan-worker2 节点上,验证了跨节点调度正常。
3. 测试 CoreDNS 域名解析:
kubectl exec -n yunfan-smoke deploy/dnsutils -- nslookup web-smoke.yunfan-smoke.svc.cluster.local命令解析与结果: 在 dnsutils Pod 内向集群 DNS 节点查询 web-smoke 服务的全限定域名(FQDN)。正确返回了 web-smoke Service 的 ClusterIP,证明 CoreDNS 服务完美工作。
4. 测试 Service 连通性:
kubectl exec -n yunfan-smoke deploy/web-smoke -- wget -qO- http://web-smoke该命令测试了 Pod 到 Service 再转发至另一个 Pod 的完整 HTTP 连通链路。
3.5 运维演练:完成 cordon、drain 与 uncordon 节点维护闭环
当某个工作节点需要升级 Docker、维护硬件或重启时,不能直接强制关闭节点容器。必须使用标准的节点运维三部曲:cordon -> drain -> uncordon。
3.5.1 三大运维动作的本质区别与 Eviction API 机制
| 命令动作 | 节点调度状态 (Scheduling) | 节点现有 Pod 的变化 | 底层执行机制与原则 |
|---|---|---|---|
kubectl cordon | 变为 SchedulingDisabled | 无变化。现有的 Pod 继续正常运行。 | 阻止新 Pod 调度到该节点,但不主动打扰已有工作负载。 |
kubectl drain | 保持 SchedulingDisabled | 驱逐业务 Pod,并在其他节点重建副本。 | 向 API 发送 Eviction API 请求优雅删除 Pod。必须配合 --ignore-daemonsets 忽略 CNI 等节点守护进程。 |
kubectl uncordon | 恢复为 Ready (可调度) | 无变化。已迁走的 Pod 不会自动搬回该节点。 | 解除封锁。K8s 维持调度稳定性,不会强制把 Pod 迁回原节点。 |
观察下面的节点维护与 Pod 动态迁移过程,体会 Eviction API 驱动下副本平移的效果:
工作节点 Cordon / Drain / Uncordon 维护流程演示
观察节点封锁、Eviction 驱逐时 Pod 的平移复原逻辑,以及 DaemonSet 的特殊留存机制。
1 / 4
yunfan-workerReady (可调度)
🚀 web-smoke (Pod)
🛡️ kindnet (DaemonSet)
yunfan-worker2Ready (可调度)
🛡️ kindnet (DaemonSet)
3.5.2 执行节点维护全流程演练
选择工作节点 yunfan-worker 进行维护演练。在终端中依次执行以下命令:
1. 步骤一:封锁节点 (Cordon)
# 将 yunfan-worker 标记为不可调度
kubectl cordon yunfan-worker
kubectl get nodes预期观察:yunfan-worker 的 STATUS 列变为 Ready,SchedulingDisabled。
2. 步骤二:驱逐节点工作负载 (Drain)
# 驱逐 yunfan-worker 节点上的 Pod,忽略 DaemonSet
kubectl drain yunfan-worker --ignore-daemonsets --timeout=120s
kubectl get pods -n yunfan-smoke -o wide参数解析与预期观察:
--ignore-daemonsets: 必需参数。DaemonSet(如 kindnet、kube-proxy)必须在每个节点运行,无法被驱逐到其他节点。此参数指示 drain 忽略它们。- 此时运行在
yunfan-worker上的web-smokePod 被优雅终止,Deployment 在yunfan-worker2节点上自动拉起了替代 Pod!
3. 步骤三:恢复节点调度 (Uncordon)
# 恢复 yunfan-worker 的可调度状态
kubectl uncordon yunfan-worker
kubectl get nodes预期观察:节点状态恢复为 Ready。yunfan-worker2 上运行的 Pod 继续运行,不会自动搬回 yunfan-worker。
3.6 实验交付与资源清理
3.6.1 本课提交成果清单
完成本课实训后,提交以下三项成果即可:
- 配置文件:项目根目录下的
smoke-test.yaml。 - 第一张截图
03-health-baseline.png:同一终端画面中包含kubectl get nodes、kubectl get pods -n kube-system和kubectl get pods -n yunfan-smoke -o wide的验证结果。 - 第二张截图
03-drain-uncordon.png:包含kubectl cordon、kubectl drain和kubectl uncordon完整执行与节点状态变化的画面。
3.6.2 常见错误与排错矩阵
| 故障现象 | 可能原因 | 修复排错路径 |
|---|---|---|
/readyz 返回 500 或 unready | API Server 或 etcd 未完全启动 | 稍等 30 秒;若仍报错,使用 docker logs yunfan-control-plane 查看控制平面容器日志 |
nslookup 解析超时或报错 NXDOMAIN | CoreDNS 副本异常或处于 Pending 状态 | 运行 kubectl get pods -n kube-system -l k8s-app=kube-dns,检查 CoreDNS 日志 |
kubectl drain 报错 cannot delete DaemonSet-managed Pods | 缺少必要的跳过参数 | 必须显式加上 --ignore-daemonsets 参数 |
uncordon 后 Pod 没有回到节点 1 | 属于 Kubernetes 正常调度行为 | 不需要修复。K8s 保证的是期望副本数(Replicas),不会随意移动稳定运行的 Pod |
挑战任务:探索控制平面的静态 Pod 清单
运行 docker exec -it yunfan-control-plane bash 进入控制平面节点容器,查看 /etc/kubernetes/manifests/ 目录下的配置文件。这里的 YAML 文件是节点 kubelet 用来自动加载并启动 kube-apiserver、etcd 等核心组件的静态 Pod 清单(Static Pod)。你可以尝试打开其中一个文件,观察 Kubernetes 最核心的控制平面组件是如何通过声明式配置定义的。
3.6.3 清理探针资源
实训验证完成后,删除探针命名空间,保留 yunfan 集群供下一课使用:
kubectl delete namespace yunfan-smoke本章小测
本章小结
完成本课后,你已经掌握了:
- 集群底座验证能力:理解了不能仅凭创建命令返回 0 断定集群可用,学会了通过 context、API
/readyz?verbose和 Node Conditions 建立健康基线。 - Kubernetes 核心系统组件架构:理清了 kindnet (CNI)、kube-proxy 与 CoreDNS 的分工与关系。
- 声明式探针与调度约束:独立编写了
smoke-test.yaml,并掌握了topologySpreadConstraints拓扑分布限制属性的写法。 - 节点运维三部曲:掌握了
cordon、drain和uncordon的完整运维闭环,理解了 Eviction API 驱逐机制与--ignore-daemonsets的底层逻辑。
下一课中,我们将使用 Label、Annotation、API 发现和 Namespace 进一步为业务建立规范的资源治理规则!
