news 2026/9/11 12:33:39

节点异常时先确认调度和驱逐路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
节点异常时先确认调度和驱逐路径

节点异常时先确认调度和驱逐路径

节点进入NodeNotReady时,先检查条件事件、磁盘水位和驱逐记录,再判断是否需要迁移工作负载。不要直接把单个症状归因于某个服务。

使用kubectl调取节点的 Event 事件与 Kubelet 日志进行分析:

kubectl describe node k8s-node-04 | grep -A 5 Conditions # Status: False, Reason: KubeletHasDiskPressure, Message: kubelet has disk pressure journalctl -u kubelet -n 100 --no-pager | grep -i "eviction" # 输出了 Attempting to evict pods due to disk pressure ...

登录故障宿主机排查发现,数据目录/var/lib/docker/containers的磁盘空间已耗尽。分析原因为某个 Python 业务 Pod 发生未捕获的重试异常,持续向标准输出(stdout)写入日志,而该 Pod 的 Deployment 未配置日志滚动策略与日志卷限制。磁盘空间满导致 Kubelet 无法正常更新 NodeLease 心跳,进而被控制面判定为节点不可用。

在 Kubernetes 的生产实践中,类似的反模式若未能规避,在特定边界条件下容易引发连锁服务异常。

节点状态变为 NodeNotReady,排查容器日志无限制导致的磁盘满。

在容器化最佳实践中,通常建议将应用日志输出至标准输出(stdout/stderr),由 DaemonSet 统一采集。但若忽略了容器引擎层(Docker / Containerd)的日志轮转(Log Rotation)配置,将存在安全隐患。

当业务代码出现无限重试并大量打印日志时,宿主机的根分区或/var分区可能在短时间内被单个容器的日志文件填满。当磁盘可用空间低于 Kubelet 预设的阈值(如imagefs.available<15%nodefs.available<10%)时,Kubelet 会触发 DiskPressure 状态并开始驱逐 Pod。若驱逐速度滞后于日志写入速度,Kubelet 进程将出现响应延迟。

应根据运行时、日志采集链路和节点磁盘容量配置日志轮转与保留上限(例如修改/etc/docker/daemon.json或对应的 Containerd 配置):

{ "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }

避免默认命名空间滥用:规范 ResourceQuota 与 HostPath 挂载。

常见的反模式之一是将生产服务混杂部署于default命名空间,且未配置 ResourceQuota 限制。这可能导致单一服务的内存泄漏消耗整个命名空间的资源配额。

另一项高危配置是滥用hostPath挂载。部分开发者为了读取宿主机配置,将宿主机的//etc目录直接通过hostPath挂载进容器。若容器存在安全漏洞被控制,攻击者可能通过修改宿主机配置文件获取控制权限。

规避hostPath的标准规范包括:

  • 业务配置统一使用ConfigMapSecret进行管理。
  • 临时文件存储使用emptyDir: { medium: Memory, sizeLimit: "512Mi" }

有状态服务部署防误用:避免共享存储并发写入导致的锁冲突。

另一典型反模式是:使用 Deployment 部署服务,同时为其挂载允许并发读写的共享存储卷(如 ReadWriteMany 的 NFS 或 CephFS),并由多个 Pod 共同写入同一个文件或 SQLite 数据库。

在 RollingUpdate(滚动更新)过程中,新旧 Pod 会同时处于运行状态。在缺少分布式锁控制的情况下,多个 Pod 并发写入同一文件易引发文件破坏或数据库文件锁死(如database is locked),进而导致服务不可用。

工程规范要求:

  • 有状态写操作:必须使用StatefulSet,结合volumeClaimTemplates为每个 Pod 绑定独立的 PV(PersistentVolume)。
  • 共享只读数据:存储卷挂载模式应明确声明为ReadOnlyMany

优化 CoreDNS 域名解析瓶颈:规避 ndots 检索域多次轮询损耗。

在 Kubernetes 中,Pod 默认的dnsConfigndots参数设为5。这意味着当应用发起针对api.example.com的域名解析请求时,由于域名中的点号(.)数量小于 5,Kubelet 会按照/etc/resolv.conf中的搜索域依次轮询查询:

  1. api.example.com.prod.svc.cluster.local(返回 NXDOMAIN)
  2. api.example.com.svc.cluster.local(返回 NXDOMAIN)
  3. api.example.com.cluster.local(返回 NXDOMAIN)
  4. api.example.com(解析成功)

每一次外部 HTTP 调用均产生了 4 次 DNS 查询。在高并发场景下,可能导致 CoreDNS CPU 占用升高甚至丢包,使应用出现i/o timeout解析超时。

以下为一个基于 Python 编写的 Kubernetes 巡检脚本,能够调用 K8s API 自动检测集群内部存在的反模式配置(如未配置 Resource Limit、使用 HostPath 挂载、缺少存活性探针):

import sys from typing import List, Dict, Any from kubernetes import client, config from kubernetes.client.rest import ApiException class K8sAntiPatternChecker: def __init__(self, namespace: str = "default"): self.namespace = namespace try: config.load_kube_config() except Exception: config.load_in_cluster_config() self.v1 = client.CoreV1Api() def audit_pods(self) -> List[Dict[str, Any]]: issues = [] try: pods = self.v1.list_namespaced_pod(namespace=self.namespace) for pod in pods.items: pod_name = pod.metadata.name # 1. 检查 HostPath 挂载 if pod.spec.volumes: for vol in pod.spec.volumes: if vol.host_path: issues.append({ "pod": pod_name, "level": "HIGH", "rule": "HostPath Mounting", "message": f"使用了 HostPath 挂载: {vol.host_path.path}" }) # 2. 检查 Resource Limits 与 Probes for container in pod.spec.containers: c_name = container.name res = container.resources if not res or not res.limits or "cpu" not in res.limits: issues.append({ "pod": pod_name, "level": "MEDIUM", "rule": "Missing CPU Limit", "message": f"容器 {c_name} 未配置 CPU Limit 限制" }) if not res or not res.limits or "memory" not in res.limits: issues.append({ "pod": pod_name, "level": "HIGH", "rule": "Missing Memory Limit", "message": f"容器 {c_name} 未配置 Memory Limit 限制" }) if not container.liveness_probe: issues.append({ "pod": pod_name, "level": "LOW", "rule": "Missing LivenessProbe", "message": f"容器 {c_name} 未配置 LivenessProbe 探针" }) except ApiException as e: print(f"[ERROR] 调用 Kubernetes API 失败: {e}", file=sys.stderr) sys.exit(1) except Exception as e: print(f"[ERROR] 巡检发生未预期错误: {str(e)}", file=sys.stderr) sys.exit(1) return issues if __name__ == "__main__": ns = sys.argv[1] if len(sys.argv) > 1 else "default" checker = K8sAntiPatternChecker(namespace=ns) audit_results = checker.audit_pods() print(f"=== K8s 配置反模式巡检报告 [Namespace: {ns}] ===") if not audit_results: print("SUCCESS: 未检测到常见的 K8s 部署反模式!") else: for item in audit_results: print(f"[{item['level']}] Pod: {item['pod']} | 规则: {item['rule']} -> {item['message']}") has_high = any(i["level"] == "HIGH" for i in audit_results) if has_high: print("\n结论: 检测到高危配置项,请修正后再部署。") sys.exit(2)

通过限制标准日志输出、禁止非必要的宿主机挂载、规范存储卷类型以及优化 DNS 查询检索域,能够有效提升 Kubernetes 集群的稳定性与容错能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 7:49:45

BitScope Blade+CM4打造口袋示波器:GPIO引脚与实战解析

干这行久了&#xff0c;工具箱里最怕的不是缺螺丝刀&#xff0c;而是缺一台能随手抓出来就看波形的示波器。台式的太占地方&#xff0c;USB的又要拖着电脑跑&#xff0c;直到我上手了BitScope Blade for Raspberry Pi这套组合&#xff0c;才发现原来“口袋实验室”这个词不是营…

作者头像 李华
网站建设 2026/8/30 4:48:19

员工Skills是什么:企业AI技能沉淀与落地指南

最近在技术社区里&#xff0c;越来越多人在讨论一个现象&#xff1a;一些公司开始给员工做“skills”了。这个词最近频繁出现在 Claude Code、Codex、Cursor 这些 AI 编程工具和 Agent 框架里&#xff0c;也出现在企业内训、技术分享和人力资源管理的话题中。很多团队想搞明白&…

作者头像 李华