ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Node Local DNS的K8s磁盘自愈系统设计与实践

基于Node Local DNS的K8s磁盘自愈系统设计与实践 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链路的开销去掉在每个节点上跑一个DaemonSetPod的DNS请求直接打到本节点的local-dns由它监听169.254.20.10:53这个地址。流量不再经过DNATconntrack压力瞬间降下来了同时还能在节点本地做缓存直接缓解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进程状态进程存活、端口监听、解析延迟解析延迟P99500ms告警1分钟DNS解析成功率CoreDNS metrics中的total_response成功率99.9%告警1分钟有人可能会问为什么告警阈值要设在80%而不是90%甚至95%我的经验是如果等磁盘到90%才告警留给处理的时间窗口只有十几分钟因为很多业务大促期间的日志增长是爆发式的而80%的时候告警通常还有几个小时的缓冲期。而且还有一层考虑自愈脚本执行清理本身也需要写日志、需要临时空间如果磁盘已经95%了清理脚本都可能跑不起来。3.3 第三层防线故障发生时能自愈而不是只告警监控和告警能解决的问题是“有人看到告警去处理”但深夜、大促、节假日这种人力覆盖薄弱的时段还是需要系统自己能动手。第三层防线就是让系统在检测到异常后按预设策略自动执行一系列动作把故障掐死在摇篮里。自愈策略的优先级设计是先清理、止损日志轮转、缓存清理、镜像清理把磁盘空间释放出来。再隔离、防止扩散如果清理完了仍然处于高危状态把local-dns从节点上摘掉让Pod流量临时走系统级DNS/etc/resolv.conf里的上游DNS保证业务不至于完全瘫痪。最后重建、恢复把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 }}这个表达式里有个细节值得说一下为什么过滤掉tmpfs和overlay因为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 THRESHOLD80 CACHE_DIR${CACHE_DIR:-/var/cache/node-local-dns} LOG_DIR${LOG_DIR:-/var/log/node-local-dns} usage$(df -P $CACHE_DIR | awk NR2 {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, --outputpcent, /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-appnode-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.disabledtrue ], checkTrue) 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] ], checkTrue) break print(local-dns isolated on node, node_name) time.sleep(60)这一级的核心逻辑是如果清理动作做了磁盘使用率还在90%以上说明光靠清理解决不了问题了这时候就需要把local-dns摘掉。摘掉的方式不是直接kill进程而是给节点打上node-local-dns.disabledtrue的标签然后删除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 NR2 {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 模拟磁盘故障验证自愈链路是否真的有效部署完之后千万别急着扔生产先把整个自愈链路验证一遍。我的做法是在测试节点上主动制造磁盘压力观察自愈系统是否按预期工作。验证步骤先确认测试节点上local-dns运行正常磁盘使用率在正常范围。用dd命令往一个测试目录里填数据把磁盘使用率推到85%以上# 在测试节点执行填一个5GB的测试文件 dd if/dev/zero of/tmp/disk-fill bs1M count5000观察自愈Agent是否触发第一级清理动作。继续填数据把使用率推到90%以上确认local-dns被隔离。删除测试文件把磁盘空间释放出来确认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 --tail200看请求来源通过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 踩坑实录一du与df看到的磁盘占用相差巨大这个坑我在压测时遇到过后来在生产也出现过一次。现象是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磁盘问题困扰的朋友我的建议是先别急着写复杂的自动化脚本先把监控告警做准、做全摸清自己集群的容量基线数据然后再去设计自愈逻辑。自动化是锦上添花稳定的监控和清醒的运维意识才是地基。
返回列表