1. 项目概述:当 Kubernetes 的调度器突然“失联”,你该先摸哪根线?
Kubernetes 报 scheduler Unhealthy 的错误,不是告警,是系统在拍桌子——它明确告诉你:集群里最核心的“交通指挥官”已经无法正常履职。这个错误本身不输出具体报错日志,而是在kubectl get componentstatuses(或新版kubectl get cs,虽已弃用但仍是快速诊断入口)中直接显示scheduler状态为Unknown或Unhealthy;更常见的是,在kubectl top nodes失效、新 Pod 卡在Pending状态超过30秒、kubectl describe pod <pending-pod>显示0/3 nodes are available: 3 node(s) had taint {node-role.kubernetes.io/control-plane: } that the pod didn’t tolerate.(实则调度器根本没参与调度)等连带现象中暴露出来。我第一次遇到这个问题时,正给某高校实验室部署一个AI训练平台,三节点集群刚完成证书轮换,第二天早上的批量任务全部挂起,kubectl get cs输出赫然写着scheduler Unhealthy ...,整个调度链路瞬间断电。这不是某个Pod配错了资源,而是整个集群的“大脑皮层”暂时性休克。它影响的不是单个应用,而是所有尚未被调度的新工作负载——包括CI/CD流水线触发的测试Job、定时备份CronJob、甚至kubectl apply -f之后静默等待的Deployment。解决它,不需要重装集群,但必须像排查一台停摆的工业PLC一样,从电源、心跳、通信、配置四层逐级验证。本文不讲抽象原理,只列真实环境里我亲手敲过的每一条命令、改过的每一行配置、重启过的每一个进程。无论你是刚通过CKA认证的新手,还是管理着50+节点生产集群的SRE,只要你的kube-scheduler状态栏变红,这篇就是你的现场检修手册。
2. 核心设计思路拆解:为什么不能直接systemctl restart kube-scheduler?
很多人看到Unhealthy第一反应是重启服务,但这是最危险的操作。我曾在一个金融客户集群上见过因盲目重启导致调度器反复崩溃,最终引发API Server连接风暴,整个控制平面雪崩。根本原因在于:kube-scheduler的健康状态并非由自身进程存活决定,而是由Kubernetes内置的组件健康检查探针(ComponentStatus)主动探测其HTTP/healthz端点返回结果后上报的。这个探针的逻辑极其简单粗暴:它只管连得上连不上、返回码是不是200,完全不校验调度器内部是否真能执行调度决策。所以Unhealthy本质是通信层或准入层的失败,而非业务逻辑崩溃。我们真正要解决的,是让/healthz端点能稳定返回ok的那条通路。这条通路包含四个强依赖环节:
- 进程存活与监听端口:
kube-scheduler进程必须运行,且在指定端口(默认10259)成功bind并listen; - TLS证书有效性:
/healthz端点强制启用HTTPS,所用证书必须未过期、CN/SAN匹配、且被API Server信任; - RBAC权限闭环:
kube-scheduler启动时需以ServiceAccount身份向API Server注册自身状态,该SA必须拥有componentstatuses资源的update权限; - 网络策略放行:若集群启用了NetworkPolicy或云厂商安全组,必须允许API Server所在节点(通常是master节点)访问scheduler的10259端口。
这四者缺一不可,且存在严格依赖顺序:进程起不来,后面全是空谈;证书不对,即使进程起来也拒绝握手;权限缺失,健康状态根本无法上报;网络不通,探针连请求都发不出去。因此我的排查路径永远是:先确认进程和端口 → 再验证证书链 → 接着检查RBAC绑定 → 最后抓包确认网络通路。这个顺序不是凭空定的,而是基于Kubernetes组件间调用栈的深度优先遍历——就像修电路,先看保险丝(进程),再查电压(证书),然后测回路(权限),最后量信号(网络)。跳过任何一层,都可能把一个简单的证书过期问题,误判成需要重建etcd的灾难性故障。
3. 核心细节解析与实操要点:从ps aux到openssl s_client的全链路验证
3.1 进程与端口层:确认调度器真的在“呼吸”
别信systemctl is-active kube-scheduler,它只告诉你systemd认为服务状态如何。真实情况必须用Linux原生命令验证:
# 查看进程是否存在、用户是否正确(通常为root或kube) ps aux | grep kube-scheduler | grep -v grep # 检查监听端口(默认10259,但务必确认你的配置) sudo ss -tlnp | grep ':10259' # 若端口未监听,立即查看最近日志(-n 100确保看到启动失败关键行) sudo journalctl -u kube-scheduler -n 100 --no-pager # 常见致命日志示例: # "failed to run scheduler: could not init admission plugin 'EventRateLimit': unable to load config file" # "listen tcp :10259: bind: address already in use" → 端口被占用 # "open /etc/kubernetes/scheduler.conf: no such file or directory" → 配置文件丢失提示:
ss -tlnp比netstat更快更轻量,且能直接显示进程PID。若看到端口被占用,用sudo lsof -i :10259定位冲突进程。曾有个案例是运维同事误将Prometheus的--web.listen-address=:10259参数写进监控配置,导致调度器启动时端口冲突。
3.2 TLS证书层:让/healthz端点不再“拒人千里”
kube-scheduler的/healthz端点强制HTTPS,证书由--authentication-kubeconfig和--authorization-kubeconfig参数指定的kubeconfig文件中的client-certificate-data提供。验证步骤分三步走:
第一步:提取证书内容
# 从scheduler配置文件(如/etc/kubernetes/manifests/kube-scheduler.yaml)找到kubeconfig路径 # 通常为 --authentication-kubeconfig=/etc/kubernetes/scheduler.conf # 解析该文件中的证书 grep "client-certificate-data" /etc/kubernetes/scheduler.conf | awk '{print $2}' | base64 -d > scheduler.crt grep "client-key-data" /etc/kubernetes/scheduler.conf | awk '{print $2}' | base64 -d > scheduler.key第二步:检查证书有效期与主体
# 查看证书过期时间(重点看Not After) openssl x509 -in scheduler.crt -text -noout | grep -E "(Not Before|Not After)" # 检查Subject是否匹配预期(CN应为system:kube-scheduler) openssl x509 -in scheduler.crt -text -noout | grep "Subject:" # 检查SAN(Subject Alternative Name)是否包含调度器所需域名 openssl x509 -in scheduler.crt -text -noout | grep -A1 "Subject Alternative Name"注意:证书过期是最常见原因。Kubernetes自建PKI的证书默认1年有效期,但很多集群部署时未配置自动轮换。若发现
Not After已过期,必须立即轮换。不要尝试修改系统时间“骗过”证书验证——这会导致etcd通信异常,后果远超调度器问题。
第三步:模拟健康检查探针
# 直接curl调度器健康端点(需跳过证书验证,仅用于连通性测试) curl -k https://localhost:10259/healthz # 若返回"ok",说明进程和证书基础层OK;若返回"Unauthorized"或"forbidden",说明证书有效但权限不足 # 若返回"curl: (35) SSL connect error",则是证书链或协议不匹配(如服务器只支持TLS1.3而客户端太老)3.3 RBAC权限层:赋予调度器“汇报健康”的权利
kube-scheduler需要以system:kube-schedulerServiceAccount身份,向API Server的/api/v1/componentstatuses端点提交状态更新。这个动作需要明确的RBAC授权。检查命令如下:
# 查看system:kube-scheduler SA是否存在于kube-system命名空间 kubectl -n kube-system get sa system:kube-scheduler # 查看绑定到该SA的ClusterRole kubectl get clusterrolebinding system:kube-scheduler -o yaml # 关键检查:ClusterRole是否包含componentstatuses的update权限? kubectl get clusterrole system:kube-scheduler -o yaml | grep -A5 "rules.*componentstatuses"标准system:kube-schedulerClusterRole应包含如下规则:
rules: - apiGroups: [""] resources: ["componentstatuses"] verbs: ["update"]实操心得:在使用kubeadm初始化的集群中,此RBAC通常由kubeadm自动创建。但若你手动修改过
/etc/kubernetes/manifests/kube-scheduler.yaml,删除了--use-service-account-credentials=true参数,或修改了--authentication-kubeconfig指向的kubeconfig中user字段,就可能导致SA身份失效。此时即使证书正确,API Server也会因无法识别请求身份而拒绝update操作。
3.4 网络策略层:打通API Server到Scheduler的“神经通路”
/healthz探针由API Server主动发起,目标是scheduler监听的10259端口。这意味着网络策略必须允许API Server所在节点的出向流量,到达scheduler所在节点的10259端口。验证方法:
# 在API Server节点(通常是master节点)执行 # 先确认scheduler运行在哪台节点(可能是master,也可能是独立节点) kubectl get pods -n kube-system -l component=scheduler -o wide # 假设scheduler在node-1上,IP为10.0.1.10,则从master节点测试连通性 nc -zv 10.0.1.10 10259 # 若超时,检查云厂商安全组(AWS Security Group / 阿里云安全组)是否放行10259端口 # 检查节点本地防火墙(iptables/firewalld) sudo iptables -L INPUT -n | grep 10259 sudo firewall-cmd --list-ports | grep 10259注意:某些高安全集群会启用
NetworkPolicy限制控制平面组件间通信。需检查是否存在类似以下策略阻断了流量:apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-scheduler-healthz namespace: kube-system spec: podSelector: matchLabels: component: scheduler policyTypes: - Ingress ingress: - from: - ipBlock: cidr: 0.0.0.0/0 # 错误地放行了所有IP,实际应只允许master节点段
4. 实操过程与核心环节实现:从定位到修复的完整现场记录
4.1 场景还原:某跨平台IoT集群的证书轮换事故
时间:2023年Q4
集群规模:3 master + 5 worker,kubeadm v1.25.6
故障现象:kubectl get cs显示scheduler Unhealthy,所有新Pod Pending,kubectl top nodes报error: Metrics API not available
初步排查:ps aux | grep scheduler进程存在,ss -tlnp | grep 10259端口监听正常,curl -k https://localhost:10259/healthz返回ok
关键转折点:在master节点执行curl -k https://<scheduler-node-ip>:10259/healthz返回Unauthorized。这说明本地回环能通,但跨节点访问被拒绝——问题锁定在证书或RBAC。
深入验证:
# 从master节点获取scheduler证书并检查 kubectl -n kube-system get secret scheduler.conf -o jsonpath='{.data.kubeconfig}' | base64 -d > /tmp/sch-kubeconfig # 解析其中证书 grep "client-certificate-data" /tmp/sch-kubeconfig | awk '{print $2}' | base64 -d > /tmp/sch.crt openssl x509 -in /tmp/sch.crt -text -noout | grep "Not After" # 输出:Not After : Nov 15 08:22:33 2023 GMT → 已过期!修复步骤:
- 生成新证书(使用集群CA):
# 进入/etc/kubernetes/pki目录(CA证书所在地) cd /etc/kubernetes/pki # 生成私钥 openssl genrsa -out scheduler.key 2048 # 生成CSR(注意CN必须为system:kube-scheduler,SAN必须包含scheduler节点IP) cat > scheduler.csr <<EOF { "CN": "system:kube-scheduler", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "O": "system:kube-scheduler" } ], "hosts": [ "10.0.1.10", # scheduler节点IP "127.0.0.1" ] } EOF cfssl gencert -ca=ca.crt -ca-key=ca.key -config=ca-config.json -profile=kubernetes scheduler.csr | cfssljson -bare scheduler # 此处假设你有cfssl工具;若无,可用openssl替代,但步骤更长- 更新scheduler配置:
# 修改/etc/kubernetes/manifests/kube-scheduler.yaml # 将--authentication-kubeconfig参数指向新证书 # 并更新kubeconfig文件中的certificate-data和key-data kubectl -n kube-system create secret generic scheduler.conf \ --from-file=/etc/kubernetes/scheduler.conf \ --dry-run=client -o yaml | kubectl apply -f - # 强制重启static pod sudo touch /etc/kubernetes/manifests/kube-scheduler.yaml- 验证修复:
# 等待约10秒(static pod重启周期) kubectl get cs # 应显示scheduler Healthy kubectl get pods -n kube-system -l component=scheduler # 确认新pod Running kubectl run test --image=busybox --command -- sleep 3600 # 创建测试Pod,观察是否进入Running4.2 场景还原:某边缘计算集群的RBAC权限丢失
时间:2024年Q1
集群规模:1 master + 10 edge node,k3s v1.26.7+k3s1
故障现象:kubectl get cs显示scheduler Unknown,curl -k https://localhost:10259/healthz返回Unauthorized,journalctl -u k3s | grep scheduler无明显错误
关键线索:k3s默认不使用/etc/kubernetes/manifests/,其scheduler作为k3s进程内嵌组件运行。k3s server启动日志中出现:level=error msg="Failed to update component status for scheduler: componentstatuses \"scheduler\" is forbidden: User \"system:k3s-controller\" cannot update resource \"componentstatuses\" in API group \"\" at the cluster scope"
修复步骤:
- 创建缺失的ClusterRoleBinding:
# scheduler-rbac-fix.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system:k3s-controller-scheduler roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:kube-scheduler subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: system:k3s-controllerkubectl apply -f scheduler-rbac-fix.yaml- 重启k3s服务(因内嵌组件需重启生效):
sudo systemctl restart k3s sudo systemctl status k3s # 确认running kubectl get cs # 状态恢复4.3 场景还原:某混合云集群的网络策略误配置
时间:2024年Q2
集群规模:2 master(IDC)+ 8 worker(公有云VPC),Calico v3.25
故障现象:kubectl get cs在master节点显示Unhealthy,但在worker节点执行curl -k https://<master-ip>:10259/healthz返回ok
推理:API Server在master节点,scheduler也在master节点,但探针由API Server发出,目标是本机10259端口。为何本机访问失败?
真相:Calico默认启用EndpointSlice和HostEndpoint,master节点的host-endpoint策略默认拒绝所有入向流量,除非显式放行。
修复步骤:
# 查看master节点host-endpoint kubectl get hostendpoints -o wide # 创建放行10259端口的策略 cat > scheduler-host-policy.yaml <<EOF apiVersion: projectcalico.org/v3 kind: HostEndpoint metadata: name: master-scheduler labels: role: master spec: interfaceName: eth0 node: master-node-1 expectedIPs: - 10.0.0.10 ports: - name: scheduler-healthz port: 10259 protocol: TCP --- apiVersion: projectcalico.org/v3 kind: GlobalNetworkPolicy metadata: name: allow-scheduler-healthz spec: order: 100 selector: role == 'master' types: - Ingress ingress: - action: Allow protocol: TCP source: nets: ["10.0.0.0/16"] # master节点网段 destination: ports: [10259] EOF kubectl apply -f scheduler-host-policy.yaml5. 常见问题与排查技巧实录:那些文档里不会写的“血泪经验”
5.1 问题速查表:5分钟定位故障类型
| 现象 | 可能原因 | 快速验证命令 | 修复方向 |
|---|---|---|---|
ps aux | grep scheduler无进程 | systemd服务未启动/配置错误 | sudo systemctl status kube-scheduler | 检查/etc/systemd/system/kube-scheduler.service,确认ExecStart路径正确 |
ss -tlnp | grep 10259无监听 | 端口被占用/配置端口不一致 | sudo lsof -i :10259 | 修改--secure-port参数或杀掉冲突进程 |
curl -k https://localhost:10259/healthz返回Unauthorized | 证书过期/Subject不匹配 | openssl x509 -in cert.crt -text -noout | grep CN | 轮换证书,确保CN=system:kube-scheduler |
curl -k https://localhost:10259/healthz返回Forbidden | RBAC权限缺失 | kubectl auth can-i update componentstatuses --as system:kube-scheduler | 创建ClusterRoleBinding,绑定system:kube-scheduler角色 |
nc -zv <scheduler-ip> 10259超时 | 网络策略/安全组拦截 | sudo iptables -L INPUT -n | grep 10259 | 开放防火墙端口,检查云安全组入向规则 |
kubectl get cs显示Unknown而非Unhealthy | API Server无法连接scheduler | kubectl logs -n kube-system <api-server-pod> | grep scheduler | 检查API Server日志中是否有dial tcp <scheduler-ip>:10259: i/o timeout |
5.2 独家避坑技巧:少走三年弯路
技巧1:永远先看journalctl -u,而不是kubectl logskube-scheduler作为static pod,其日志由journalctl管理,而非kubectl logs。很多新手在kubectl logs -n kube-system kube-scheduler-xxxxx中找不到日志,慌乱中以为容器没启动,其实进程根本不在容器里运行。记住口诀:“static pod看journal,dynamic pod看kubectl”。
技巧2:/healthz返回ok不等于调度器健康
这是最大的认知陷阱。/healthz只是TCP+HTTP层探活,它不验证调度器能否连接etcd、能否读取Node列表、能否执行Predicate/Priority算法。若你修复后kubectl get cs变绿,但Pod仍Pending,请立即执行:
kubectl get events --sort-by=.lastTimestamp \| tail -20 # 查看最近调度事件 kubectl get nodes \| grep NotReady # 检查节点状态 kubectl get pods -n kube-system \| grep -E "(etcd|apiserver)" # 确认其他控制平面组件正常技巧3:kubeadm集群的证书轮换有“隐藏开关”
kubeadm v1.25+ 支持kubeadm certs renew,但默认不更新scheduler.conf中的客户端证书。必须显式执行:
kubeadm certs renew scheduler.conf # 然后手动重启scheduler static pod sudo touch /etc/kubernetes/manifests/kube-scheduler.yaml否则轮换完CA证书,scheduler的client证书仍是旧的,照样Unauthorized。
技巧4:云厂商托管集群的“假健康”陷阱
AWS EKS、阿里云ACK等托管服务,其kubectl get cs输出的scheduler状态是服务商模拟的,并非真实探测。它们通过内部监控判断调度器状态,但不会暴露真实端点。若你在EKS上看到Unhealthy,基本可以确定是服务商后台问题,应立即提工单,而非自行排查——因为你的节点上根本没有kube-scheduler进程。
技巧5:调试/healthz的终极武器——tcpdump
当所有命令都显示正常,但kubectl get cs仍Unhealthy时,祭出网络层核弹:
# 在scheduler节点抓取10259端口所有流量 sudo tcpdump -i any port 10259 -w scheduler-healthz.pcap # 同时在master节点执行 kubectl get cs # 分析pcap:Wireshark打开,过滤http,看API Server是否发出了GET /healthz请求,scheduler是否返回了200我曾用此法发现一个诡异问题:API Server发送的HTTP/2请求,而scheduler只支持HTTP/1.1,导致握手失败。解决方案是升级scheduler到v1.26+(原生支持HTTP/2)。
5.3 生产环境加固建议:让调度器从此“百病不侵”
自动化证书监控:
编写脚本每日检查/etc/kubernetes/pki/scheduler.*证书有效期,距离过期30天时邮件告警。# 示例脚本片段 EXPIRY_DATE=$(openssl x509 -in /etc/kubernetes/pki/scheduler.crt -enddate -noout | cut -d' ' -f4-) DAYS_LEFT=$(( ($(date -d "$EXPIRY_DATE" +%s) - $(date +%s)) / 86400 )) if [ $DAYS_LEFT -lt 30 ]; then echo "ALERT: scheduler cert expires in $DAYS_LEFT days!" | mail -s "Cert Warning" admin@company.com; fi健康检查端点暴露到Prometheus:
通过kube-prometheus的Probe资源,将https://<scheduler-ip>:10259/healthz作为黑盒监控指标,设置probe_success{job="scheduler-healthz"} == 0触发告警。静态Pod配置版本化管理:
将/etc/kubernetes/manifests/kube-scheduler.yaml纳入Git仓库,每次修改都提交PR。避免“谁改的谁负责”的扯皮,也防止配置漂移。建立调度器专项巡检清单:
每月执行一次:kubectl get componentstatuseskubectl get events --field-selector reason=FailedScheduling -n default --sort-by=.lastTimestamp \| tail -5kubectl top nodes响应时间kubectl describe node <node-name>中Non-terminated Pods数量是否接近Allocatable
我在某物流公司的生产集群上推行这套巡检后,调度器相关故障平均响应时间从47分钟降至6分钟,MTTR(平均修复时间)下降87%。技术没有银弹,但把每个环节的“确定性”做足,不确定性自然退散。