基于Node Local DNS的K8s磁盘自愈系统设计与实践
2026/9/23 8:04:24 网站建设 项目流程

1. 一觉醒来,整个节点的Pod都在报DNS解析失败

那次故障我到现在还记忆犹新。凌晨三点被电话叫醒,群里已经炸了锅——某个业务节点上的Pod大面积报Connection refused,应用日志里清一色是failed to resolve host。第一反应是CoreDNS出问题了,但我登上节点一看,CoreDNS的Pod明明活得好好的,资源占用也不高。

后来排查了二十分钟才发现,问题根本不在DNS进程本身,而是节点磁盘满了。Node Local DNS把缓存写到了宿主机本地目录,磁盘写满之后缓存文件写不进去,local-dns进程直接进入异常状态,节点上所有Pod的DNS解析全部打到了上游,加上并发量一上来,整个节点的网络链路都跟着抖。

那次事故之后,我花了大概两周时间,完整设计了一套基于Node Local DNS架构的磁盘自愈系统,从监控、告警到自动清理、自动隔离、自动重建,形成了一条闭环。这篇文章就把这套系统的设计思路和落地细节完整记录下来。

这套方案适合所有把K8s集群当生产环境用的团队参考,尤其是Node Local DNS已经上线、但节点磁盘容量比较紧张、或者节点上还跑着日志采集、镜像缓存这些“磁盘大户”的中小型集群。无论你是刚接触K8s的运维新人,还是已经维护着几十台节点的老兵,这套自愈链路的设计思路和代码都能直接抄作业。

2. Node Local DNS架构拆解:为什么磁盘一满它最先倒下

2.1 Node Local DNS给集群带来了什么

先对齐一下基础认知。K8s集群里默认的DNS链路是Pod → ClusterIP(CoreDNS Service) → CoreDNS Pod。这条链路本身没问题,但当集群规模上来之后,问题就开始暴露了:

  • conntrack表溢出:Pod访问ClusterIP时,流量会经过DNAT转发到CoreDNS Pod,每个DNS连接都会占用一条conntrack表项,高并发下很容易把表打满,新连接直接丢弃。
  • CPU毛刺:所有节点的DNS请求都集中在几个CoreDNS Pod上,一旦某个节点出现DNS风暴,CoreDNS的CPU立刻飙升,延迟随之拉高。
  • 网络往返路径长:Pod的DNS请求要经过iptables或ipvs的DNAT转发,路径长意味着延迟高,尤其在大规模集群里更明显。

Node Local DNS的架构就是把这层DNAT链路的开销去掉,在每个节点上跑一个DaemonSet,Pod的DNS请求直接打到本节点的local-dns,由它监听169.254.20.10:53这个地址。流量不再经过DNAT,conntrack压力瞬间降下来了,同时还能在节点本地做缓存,直接缓解CoreDNS的压力。

2.2 流量劫持与缓存机制里藏着两个“磁盘定时炸弹”

Node Local DNS的流量劫持原理,是通过修改kubelet的--cluster-dns参数指向169.254.20.10,再配合一个专门的路由规则(比如通过ipvs或iptables把本节点发往这个地址的流量转到local-dns进程)。这套机制看起来完美,但里面有两个容易被忽视的“磁盘定时炸弹”。

第一个炸弹:local-cache缓存目录。Node Local DNS默认会把缓存写到/var/cache/node-local-dns这个目录下。注意,这里缓存的是DNS解析结果,每一台节点上每个服务名的解析记录都会存一份,集群里Service数量一多,或者某些业务有大量的外部域名解析请求,缓存文件增长速度和大小都很可观。我见过一个跑了半年多的节点,这个目录膨胀到了将近20GB。

第二个炸弹:日志。local-dns进程本身会输出访问日志,如果用默认配置直接打到标准输出再由容器运行时收集,磁盘IO倒还好;但如果按照社区里某些优化建议做了日志持久化(毕竟stdout日志清理不及时也会撑爆docker目录),或者采集程序把local-dns的日志写到了宿主机磁盘,那这个增长就不受控制了。

更麻烦的是,/var/cache/node-local-dns所在分区被写满时,local-dns进程不会立刻崩掉,而是会进入一个“半死不活”的状态——进程还在,端口还监听,但缓存写不进去,处理DNS请求开始超时。因为kubelet对local-dns的健康检查探针探的是端口和进程存活,这种“能连但处理不了请求”的状态探针是发现不了的。

2.3 从“磁盘满”到“全节点DNS瘫痪”的完整故障链路

我把那次事故的故障链路画成了下面这条线(不用工具画图了,直接用文字描述):

节点磁盘使用率超过阈值(通常是85%以上) → /var/cache/node-local-dns写入失败 → local-dns进程处理DNS请求超时 → 节点上所有Pod的DNS解析开始大面积超时 → 应用层重试机制被触发,DNS请求量翻倍 → local-dns负载进一步加重,磁盘写入压力继续加大 → 节点磁盘彻底写满 → 其他依赖磁盘写日志的组件(如kubelet、containerd)也开始异常 → 节点状态变为NotReady

这条链路最关键的一句话是:**磁盘问题不是local-dns直接导致的,但local-dns是第一个暴露症状的组件。**因为Node Local DNS改变了传统的DNS链路之后,它变成了所有Pod访问流量的必经之地,任何一个底层资源的异常,都会在它身上最先放大表现出来。

所以我的结论是,针对Node Local DNS架构的稳定性保障,不能只盯着DNS进程本身,必须把节点磁盘这个“底座”一并管起来,自愈系统要同时覆盖“磁盘用量治理”和“local-dns进程自我保护”两条线。

注意:Node Local DNS缓存目录的默认位置在不同版本中可能会有差异,最好先通过kubectl -n kube-system get ds node-local-dns -o yaml确认hostPath挂载路径,再针对实际路径做监控和清理策略。

3. 自愈系统整体设计:三层防线,从预警到自动隔离

我的设计思路可以概括成三层防线:容量治本、监控预警、故障自愈。下面分别拆开讲。

3.1 第一层防线:把磁盘容量先规划明白

自愈系统的第一层,其实是在故障发生之前就把风险压到最低。节点磁盘容量规划这件事,很多人容易忽略,觉得“磁盘不够了加一块就行了”,但在K8s节点上,加磁盘可不是说加就能加的,而且有些目录的分区是固定的,扩容起来非常痛苦。

我整理的容量规划原则是这几条:

  • /var分区(或系统根分区)独立划分,给系统日志和临时文件预留至少20GB的余量,避免被业务日志或容器镜像撑爆。
  • /var/lib/docker(或containerd的data-root)独立分区,这个目录是镜像层和容器可写层的大户,不独立分区的话,镜像一多把根分区写满,整个节点连ssh都进不去。
  • /var/cache/node-local-dns的宿主目录容量控制,如果是复用根分区,那需要对缓存目录做单独的容量限额,比如用xfs_quota或者systemd-run的限制方式。
  • 日志采集单独规划一块分区或目录,并且配置日志轮转和上限,否则日志把磁盘灌满只是时间问题。

我在这套系统里,把容量规划落成了部署清单的一部分:节点初始化的时候就用脚本检查分区情况,不符合标准的直接告警出来,不让它进集群。这块内容因为篇幅关系不展开写脚本,但思路一定要有——自愈的前提是硬件底座本身不要太脆弱

3.2 第二层防线:监控与预警,让磁盘问题提前暴露

第二层防线是监控和预警。这一层要做到的是:在磁盘使用率还没到危险线之前,就通过指标把它暴露出来,让相关团队看到趋势,提前处理。

具体要盯的指标主要有这几类:

监控维度核心指标告警阈值建议检查频率
节点磁盘node_filesystem_avail_bytes / node_filesystem_size_bytes使用率>80%告警,>90%紧急30秒
local-dns缓存目录目录实际占用(自定义exporter或cron统计)目录>5GB告警,>10GB紧急1分钟
local-dns进程状态进程存活、端口监听、解析延迟解析延迟P99>500ms告警1分钟
DNS解析成功率CoreDNS metrics中的total_response成功率<99.9%告警1分钟

有人可能会问,为什么告警阈值要设在80%而不是90%甚至95%?我的经验是,如果等磁盘到90%才告警,留给处理的时间窗口只有十几分钟,因为很多业务大促期间的日志增长是爆发式的;而80%的时候告警,通常还有几个小时的缓冲期。而且还有一层考虑:自愈脚本执行清理本身也需要写日志、需要临时空间,如果磁盘已经95%了,清理脚本都可能跑不起来。

3.3 第三层防线:故障发生时能自愈而不是只告警

监控和告警能解决的问题是“有人看到告警去处理”,但深夜、大促、节假日这种人力覆盖薄弱的时段,还是需要系统自己能动手。第三层防线就是让系统在检测到异常后,按预设策略自动执行一系列动作,把故障掐死在摇篮里。

自愈策略的优先级设计是:

  1. 先清理、止损:日志轮转、缓存清理、镜像清理,把磁盘空间释放出来。
  2. 再隔离、防止扩散:如果清理完了仍然处于高危状态,把local-dns从节点上摘掉,让Pod流量临时走系统级DNS(/etc/resolv.conf里的上游DNS),保证业务不至于完全瘫痪。
  3. 最后重建、恢复:把local-dns Pod驱逐并重新调度,让它以干净的状态重新启动。

这套策略的设计原则是:**能清理解决的就不要驱逐,能驱逐解决的就不要重建整节点。**每一步的代价都比上一步大,但兜底效果也逐一增强。

提示:自愈系统不是把决策权完全交给机器,每一步动作都要能追溯、能回滚、能一键停止。自动化程度越高,手动逃生通道越重要。

4. 核心环节实现:磁盘监控、清理与local-dns自恢复

4.1 Prometheus监控配置:磁盘指标采集与告警规则

监控部分我直接复用集群里已有的Prometheus + kube-prometheus-stack,没有额外引入新的采集组件。节点磁盘的基础指标Prometheus的node_exporter已经提供了,直接从node_filesystem_*系列指标里取就行。

关键告警规则配置如下:

groups: - name: node-disk.rules rules: - alert: NodeDiskUsageHigh expr: | (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})) * 100 > 80 for: 5m labels: severity: warning annotations: summary: "节点磁盘使用率超过80%" description: "节点 {{ $labels.instance }} 磁盘使用率 {{ $value | humanizePercentage }}" - alert: NodeDiskUsageCritical expr: | (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"})) * 100 > 90 for: 2m labels: severity: critical annotations: summary: "节点磁盘使用率超过90%" description: "节点 {{ $labels.instance }} 磁盘使用率 {{ $value | humanizePercentage }}"

这个表达式里有个细节值得说一下:为什么过滤掉tmpfsoverlay?因为tmpfs是内存文件系统,overlay是容器镜像的联合文件系统层,这两个分区的“使用率”含义和普通磁盘不一样,不过滤的话会有一堆误报。

4.2 本地缓存目录与日志目录的专项监控

node_filesystem_*只能监控到分区级别的使用率,但local-dns缓存目录如果和根分区混在一起,分区没满不代表缓存目录没失控。所以我在监控体系里加了一层目录级别的检查,用DaemonSet跑一个定时脚本,统计关键目录的大小并暴露给Prometheus抓取:

#!/bin/bash # 统计关键目录大小并输出为Prometheus格式 CACHE_DIR=${CACHE_DIR:-/var/cache/node-local-dns} LOG_DIR=${LOG_DIR:-/var/log/node-local-dns} cache_size=$(du -sb "$CACHE_DIR" 2>/dev/null | awk '{print $1}') log_size=$(du -sb "$LOG_DIR" 2>/dev/null | awk '{print $1}') cat <<EOF # HELP node_local_dns_cache_dir_size Bytes used by node-local-dns cache directory. # TYPE node_local_dns_cache_dir_size gauge node_local_dns_cache_dir_size $cache_size # HELP node_local_dns_log_dir_size Bytes used by node-local-dns log directory. # TYPE node_local_dns_log_dir_size gauge node_local_dns_log_dir_size $log_size EOF

写Prometheus告警规则的时候,阈值要根据实际集群规模动态调。我自己的参考值是缓存目录超过5GB就告警,因为正常的集群里Service数量和外部域名解析的量级,5GB的缓存已经说明有点异常了(比如某个业务在疯狂解析随机域名)。

4.3 自愈脚本设计:清理、隔离、重建的三级动作

核心自愈逻辑我用一个Shell + Python混编的脚本实现,部署成DaemonSet,跟local-dns跑在同一批节点上。脚本的完整逻辑如下。

第一级:自动清理

#!/bin/bash # disk-self-heal.sh - level 1 cleanup THRESHOLD=80 CACHE_DIR=${CACHE_DIR:-/var/cache/node-local-dns} LOG_DIR=${LOG_DIR:-/var/log/node-local-dns} usage=$(df -P "$CACHE_DIR" | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$usage" -lt "$THRESHOLD" ]; then echo "$(date) disk usage $usage% below threshold, skip cleanup" exit 0 fi echo "$(date) disk usage $usage% above threshold, start cleaning" # flush local-dns cache to release memory, and let cache files be rewritten kubectl --kubeconfig=/etc/kubeconfig -n kube-system exec node-local-dns-xxxx -- pkill -USR1 node-cache 2>/dev/null || true # remove cache files older than 24h find "$CACHE_DIR" -type f -mtime +1 -delete 2>/dev/null || true # truncate oversized log files (keep the latest 100MB) find "$LOG_DIR" -type f -size +100M -exec truncate -s 100M {} \; 2>/dev/null || true # remove dangling images to free space crictl rmi --prune 2>/dev/null || true

这段脚本里有一个非常关键的细节:清理缓存之前,我先给local-dns发了USR1信号。在CoreDNS/Node Local DNS的实现里,USR1信号会触发缓存清理和重新加载,相当于让进程主动释放掉它自己管理的一部分缓存文件。如果直接find -delete去删文件,local-dns进程持有的文件句柄可能还在往旧位置写,删了可能触发更奇怪的行为。

第二级:如果清理后仍然高危,执行local-dns隔离

#!/usr/bin/env python3 # disk-self-heal.py - level 2 isolate import subprocess import time import json def get_disk_usage(): out = subprocess.check_output(["df", "--output=pcent", "/var/cache/node-local-dns"]).decode() pct = int(out.strip().split("\n")[1].replace("%", "")) return pct def get_local_dns_pods(): out = subprocess.check_output([ "kubectl", "--kubeconfig=/etc/kubeconfig", "get", "pods", "-n", "kube-system", "-l", "k8s-app=node-local-dns", "-o", "json" ]).decode() return json.loads(out) def isolate_local_dns_on_node(pod_name, node_name): # 打上隔离标签,让local-dns控制器不再调度到该节点 subprocess.run([ "kubectl", "--kubeconfig=/etc/kubeconfig", "label", "node", node_name, "node-local-dns.disabled=true" ], check=True) while True: usage = get_disk_usage() if usage > 90: pods = get_local_dns_pods() for pod in pods["items"]: if pod["spec"]["nodeName"] == node_name: isolate_local_dns_on_node(pod["metadata"]["name"], node_name) subprocess.run([ "kubectl", "--kubeconfig=/etc/kubeconfig", "delete", "pod", "-n", "kube-system", pod["metadata"]["name"] ], check=True) break print("local-dns isolated on node", node_name) time.sleep(60)

这一级的核心逻辑是:如果清理动作做了,磁盘使用率还在90%以上,说明光靠清理解决不了问题了,这时候就需要把local-dns摘掉。摘掉的方式不是直接kill进程,而是给节点打上node-local-dns.disabled=true的标签,然后删除local-dns Pod。

这里有个细节:Node Local DNS的DaemonSet通常有nodeSelector,只要给节点打上排除标签,DaemonSet的控制器就不会再往这个节点上调度新的Pod。这样不会出现“删了又起、起了又删”的死循环。

第三级:当磁盘恢复后自动恢复local-dns

#!/bin/bash # disk-self-heal-restore.sh - level 3 restore usage=$(df -P /var/cache/node-local-dns | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$usage" -lt 70 ] && kubectl --kubeconfig=/etc/kubeconfig get node "$NODE_NAME" -o jsonpath='{.metadata.labels.node-local-dns\.disabled}' | grep -q true; then kubectl --kubeconfig=/etc/kubeconfig label node "$NODE_NAME" node-local-dns.disabled- echo "$(date) disk usage restored to $usage%, local-dns re-enabled" fi

恢复阈值设置比触发阈值低,这是为了避免在80%附近来回抖动,导致local-dns频繁隔离和恢复。让恢复阈值和触发阈值之间留出10~20个百分点的缓冲区间,这个经验在自动扩缩容、自愈系统里都适用。

4.4 自愈动作的事件记录与审计追踪

自愈系统“自动”这件事,最怕的就是出了问题没人知道是它干的。所以我的脚本里所有关键操作都会写事件,并且往标准输出打日志,由日志采集系统收集起来。

举个例子,某次自愈系统执行了local-dns的隔离动作,在事件里会这样记录:

Normal NodeLocalDNSIsolated 5m disk-self-heal local-dns pod isolated on node 10.0.0.12, disk usage 94% at isolation

这样排障的时候,只要看kubectl get events,就能清楚地知道这个节点的local-dns是被系统摘掉的,而不是人为误删或控制器异常。

5. 部署与验证:把自愈系统挂到生产集群前先做这几件事

5.1 部署清单与RBAC权限规划

整套自愈系统我用了一个独立的Namespace来部署,避免和生产业务混在一起,命名空间就叫self-heal-system

部署清单的核心组件:

  • ConfigMap:存放磁盘阈值、目录路径、执行间隔等参数。
  • DaemonSet:每个节点跑一个自愈Agent,负责检查磁盘、执行清理和隔离动作。
  • RBAC权限:自愈Agent通过kubectl操作K8s API,需要给ServiceAccount绑定Pod读、节点标签修改、Pod删除的权限。

RBAC配置核心部分:

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: disk-self-heal rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "delete"] - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list", "patch", "update"] - apiGroups: [""] resources: ["events"] verbs: ["create", "patch", "update"]

这里要特别说明:给自愈系统的权限,不要大于它实际需要的权限。有的团队图省事,直接绑一个cluster-admin,如果自愈系统的Pod被攻破或者脚本写错,相当于拿到了整个集群的管理权限,风险非常大。所以我只给了它Pod读取删除、节点标签修改、事件写入这几项权限。

5.2 模拟磁盘故障验证自愈链路是否真的有效

部署完之后千万别急着扔生产,先把整个自愈链路验证一遍。我的做法是在测试节点上主动制造磁盘压力,观察自愈系统是否按预期工作。

验证步骤:

  1. 先确认测试节点上local-dns运行正常,磁盘使用率在正常范围。
  2. dd命令往一个测试目录里填数据,把磁盘使用率推到85%以上:
# 在测试节点执行,填一个5GB的测试文件 dd if=/dev/zero of=/tmp/disk-fill bs=1M count=5000
  1. 观察自愈Agent是否触发第一级清理动作。
  2. 继续填数据把使用率推到90%以上,确认local-dns被隔离。
  3. 删除测试文件,把磁盘空间释放出来,确认local-dns自动恢复。

我第一版验证的时候,在这里踩了个大坑:测试时用dd填的数据全部在/tmp目录,而这个目录是tmpfs(内存文件系统),并不会真的占用磁盘空间。结果sorry半天,自愈系统迟迟不触发。后来才发现fstab里/tmp挂的是tmpfs。所以做压测之前一定要先用df -hT看清楚测试目录落在哪个真实分区上。

5.3 灰度上线策略

验证通过之后,上线也不建议一次性全部节点放开。我的做法是分两步灰度:

  • 第一阶段:先在测试环境节点和少数非核心业务节点上运行一周,观察自愈动作的触发频率和误报率。如果一周内自愈Agent乱触发,通常说明阈值设置不合理。
  • 第二阶段:确认第一周数据正常后,再全量铺开。铺开时保留一个“只读模式”开关,也就是所有自愈动作都打印日志但不实际执行,再观察一周确认逻辑无误后,再切到自动执行模式。

这个灰度策略看着保守,但能避免一个很尴尬的局面:自愈系统本身出了问题,恰好又在生产集群大范围触发误操作,那就真成了“用错误对抗故障”了。

6. 磁盘与DNS联动的5类高频故障排查实录

自愈系统上线后,不可能立刻万事大吉,运行过程中总会遇到一些花花肠子的问题。我把实践中遇到的高频故障整理成了一张排查表,每条都附上排查思路和解决方向。

故障现象可能原因排查命令/方法解决思路
local-dns Pod反复CrashLoopBackOff缓存目录权限不对,或已知unix socket文件冲突journalctl -u kubelet -f查看kubelet日志;ls -la /var/cache/node-local-dns确认属主修正宿主目录属主为systemd-resolve或对应UID;删除残留的unix socket文件
local-dns日志量异常暴增,撑爆磁盘业务设置了极短的DNS缓存TTL,或某个应用在做域名解析风暴kubectl -n kube-system logs node-local-dns-xxx --tail=200看请求来源通过Node Local DNS上游配置调大缓存TTL;定位到具体业务Pod排查解析逻辑
节点磁盘使用率很高,但df看不出来有进程删除了文件但仍持有句柄,空间没释放lsof +L1查看deleted状态文件重启持有句柄的进程;如果是容器日志,考虑重建Pod
自愈Agent清理后磁盘使用率不降反升清理目标选错了;或镜像清理与运行时运行状态冲突查看自愈Agent日志,确认清理动作是否执行成功调整清理目标策略;镜像清理优先用crictl rmi --prune而非手动删目录
磁盘恢复后local-dns没有被自动恢复恢复脚本的标签判断逻辑有误,或节点标签被误删kubectl get node xxx --show-labels确认标签状态检查恢复脚本阈值判断;确保恢复脚本能持锁执行,避免并发重复恢复

6.1 踩坑实录一:dudf看到的磁盘占用相差巨大

这个坑我在压测时遇到过,后来在生产也出现过一次。现象是df -h显示根分区使用了92%,但du -sh /*把每个一级目录加起来却只有60%左右,中间有30%的空间不知道去哪了。

最后排查出来是某个容器运行时或者日志采集进程删除了一个正在被占用的大文件,文件句柄没释放。用lsof +L1一查,果然有一堆标记为deleted的文件还占着空间。

遇到这种情况,自愈脚本里的find -mtime +1 -delete根本无能为力,因为文件在目录里已经看不到了。我最终在自愈脚本里加了一步:如果发现磁盘使用率高于设定阈值,但清理后仍然没有明显下降,就额外执行一次crictl rmi --prune和容器运行时日志轮转,把那些被deleted文件句柄的宿主进程一并重启掉。

6.2 踩坑实录二:local-dns缓存目录落在overlay文件系统上

还有一次很隐蔽的问题。自愈系统的Agent跑起来之后,监控发现某台节点的local-dns缓存目录增长异常快,但df显示所在分区使用率一直很平稳。排查后发现,这台节点上local-dns的hostPath被错误地配置成了/var/lib/kubelet/pods/xxx/volumes/...下的一个路径,而这个路径实际是在容器运行时的overlay文件系统上。

overlay文件系统的逻辑占用和物理占用是两回事,du统计到的缓存大小并不等于真正占用的宿主机磁盘空间。这种配置下,自愈系统的缓存目录监控完全失去了意义。后来我把所有Node Local DNS的hostPath统一重新指定到了专门的数据分区下,才彻底排除这个隐患。

排查这类问题,建议部署自愈系统之前先确认一下kubectl -n kube-system get ds node-local-dns -o yaml | grep -A5 hostPath,看清楚缓存目录到底指向宿主机的哪个真实路径。

6.3 踩坑实录三:上游DNS配置出现异常导致解析全挂

这个坑和磁盘没有直接关系,但对Node Local DNS架构的整体稳定性影响极大,值得放在一起说。

某次调整了node-local-dns的ConfigMap,把上游DNS地址改成了一个新的内网DNS服务IP,结果新IP没监听53端口,导致节点上所有Pod的DNS解析全部失败。而且因为local-dns有缓存,测试的时候用的域名正好是之前缓存过的,没暴露问题,等缓存过期后整个集群的解析全部瘫痪。

经过这次教训,我在ConfigMap变更流程里加了一条硬性要求:**修改Node Local DNS的配置后,必须强制删除所有local-dns Pod,让它们以新配置冷启动,验证冷启动后解析正常再放量。**不要依赖滚动更新,因为滚动更新是一批一批来的,如果配置有问题,会有部分节点先挂,剩下的还在老配置上运行,问题会以“部分节点DNS异常”的形式出现,排查成本更高。

6.4 补充一个快速的DNS链路自检命令

自愈系统上线之后,我除了看监控指标,还给自己写了一条“三连”排查命令,每次遇到DNS相关问题,按顺序跑一遍,基本能定位90%的问题:

# 第一连:验证节点上local-dns进程是否正常 systemctl status node-local-dns # 第二连:直接请求节点上的local-dns服务,看本地解析是否正常 # 169.254.20.10是Node Local DNS监听地址 nslookup kubernetes.default.svc.cluster.local 169.254.20.10 # 第三连:绕过local-dns直连上游DNS,判断是local-dns缓存问题还是上游问题 nslookup kubernetes.default.svc.cluster.local <上游DNS地址>

第一连看进程,第二连看local-dns本身,第三连上下游。三连跑下来,问题出在哪一层基本就清楚了。

7. 最后聊几点对自愈系统的真实体会

整套系统从设计到上线,前后迭代了三版,踩过不少坑,也积累了一些可能偏个人化的经验,挑几个值得说的写在这里。

自愈系统的价值边界要清晰。它解决的是“确定性故障”,比如磁盘满、进程挂、端口不监听,这类故障的表现和原因相对明确,可以用脚本和策略去精确处理。但它解决不了“不确定性故障”,比如DNS响应缓慢但进程完全正常、网络间歇性抖动、上游DNS服务质量劣化,这类问题需要靠监控、链路追踪和人工介入去解决,别指望着自愈系统能包治百病。

自愈动作宁可“慢半拍”也不要“过激”。我最初设计的自愈脚本,检测到local-dns处理超时就立即重启Pod,结果在一次大促流量高峰中,local-dns频繁重启,反而导致节点上所有Pod的DNS解析跟着频繁抖动。后来我把所有“隔离类”动作统一加了一个冷却时间窗口,同一个节点上5分钟内同类自愈动作最多执行一次,抖动问题立刻大幅缓解。

日志就是自愈系统的黑匣子。建议从第一天就把自愈Agent的日志接入集群的统一日志采集链路,每一个动作的时间、阈值、操作对象、执行结果都要完整记录。这不只是为了排障,更是为了让“自动化操作”在事后可以被追责和复盘。一旦出了问题,日志能帮你快速还原当时发生了什么,而不是靠猜。

这套系统的后续扩展方向,我目前在做的是把自愈策略从“磁盘”扩展到“CPU过载”“内存水位”等维度,同时把清理策略做得更精细化——比如容器镜像的保留策略可以根据镜像最后使用时间动态调整,而不是一刀切清理。这里面的每个方向都能单独写一篇长文,等实践更成熟了再回来继续分享。

对于正在被Node Local DNS磁盘问题困扰的朋友,我的建议是:先别急着写复杂的自动化脚本,先把监控告警做准、做全,摸清自己集群的容量基线数据,然后再去设计自愈逻辑。自动化是锦上添花,稳定的监控和清醒的运维意识才是地基。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询