Posted in

【Go云原生部署军规】:K8s readiness/liveness探针配置不当导致滚动更新失败的8类高频配置错误

第一章:Go云原生部署军规总览与探针机制本质

云原生环境对Go服务的可靠性提出刚性约束,部署不再仅关乎“能跑”,而需满足可观测性、弹性伸缩与故障自愈三位一体的军规级要求。这些军规并非抽象原则,而是由Kubernetes调度器、Service Mesh控制面及运维平台共同执行的硬性契约。

探针不是健康检查的别名

Liveness、Readiness与Startup探针在语义与生命周期上存在本质差异:

  • Liveness 触发容器重启,适用于不可恢复的死锁或goroutine泄漏;
  • Readiness 控制流量接入,适用于依赖未就绪(如DB连接池未填充、配置中心未同步);
  • Startup 专为启动耗时长的Go应用设计(如预热缓存、加载大模型),避免过早被kill。

Go原生探针实现范式

标准库net/http配合http.ServeMux可构建零依赖探针端点,但须规避常见陷阱:

func main() {
    mux := http.NewServeMux()

    // ✅ 正确:Readiness检查应验证所有关键依赖
    mux.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) {
        if !db.PingContext(r.Context()) || !redis.Ping(r.Context()).Err() != nil {
            http.Error(w, "dependencies unavailable", http.StatusServiceUnavailable)
            return
        }
        w.WriteHeader(http.StatusOK) // 显式设置状态码
        w.Write([]byte("ok"))
    })

    // ❌ 错误:Liveness不应检查外部依赖(否则可能引发级联重启)
    // mux.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) {
    //     // 这里只应检查进程自身状态:goroutine数突增、内存泄漏标记等
    // })

    http.ListenAndServe(":8080", mux)
}

关键军规对照表

军规项 Go实践要点 Kubernetes配置建议
启动超时 StartupProbe failureThreshold × periodSeconds ≥ 应用冷启动最大耗时 periodSeconds: 10, failureThreshold: 30
资源隔离 使用runtime.GOMAXPROCS()适配容器CPU limit requests/limits中CPU单位严格一致
优雅终止 捕获os.Interrupt + http.Server.Shutdown() terminationGracePeriodSeconds ≥ 30

所有探针端点必须独立于主业务路由,禁止复用/health等模糊路径;HTTP响应体应保持空或极简字符串,避免序列化开销干扰探测时效性。

第二章:readiness探针配置的五大致命误区

2.1 HTTP探针路径未隔离健康端点与业务路由导致误判

/health/api/users 共享同一中间件链(如认证、限流、DB连接池校验),健康检查将触发完整业务逻辑。

常见错误配置示例

# ❌ 错误:所有路径统一处理
routes:
  - path: /.*
    handler: businessMiddlewareChain  # 含 JWT 验证 + DB ping

逻辑分析:Kubernetes 的 livenessProbe 请求 /health 时,被强制执行 JWT 解析与数据库连通性检测;若 DB 短暂抖动,Pod 被反复重启,掩盖真实故障边界。

正确隔离方案对比

维度 混合路由 隔离路由
中间件栈 认证/限流/DB ping 仅基础日志+状态码返回
探针响应耗时 320ms(含DB超时) 8ms(内存态计算)
故障传播面 全量实例滚动重启 仅真实异常实例下线

探针路径分流流程

graph TD
    A[HTTP Request] --> B{Path == /health?}
    B -->|Yes| C[SkipAuth → Return 200]
    B -->|No| D[ApplyFullMiddlewareChain]

2.2 TCP探针忽略服务监听延迟引发过早就绪判定

Kubernetes 的 tcpSocket 探针在容器启动后立即尝试建立 TCP 连接,但未等待应用完成 socket bind + listen + accept 队列初始化。

典型误判场景

  • 应用启动耗时 2–5 秒(如 Spring Boot 内嵌 Tomcat 加载上下文)
  • 探针在 livenessProbe.initialDelaySeconds=0 时第 1 秒即发起连接
  • 此时端口已 bind,但 listen() 尚未调用或 backlog 队列未就绪 → 连接被内核 RST 拒绝 → 探针误判为“服务不可达”

探针配置与风险对照表

配置项 风险等级 原因
initialDelaySeconds ⚠️高 容器进程可能尚未调用 listen()
periodSeconds 1 ⚠️中 高频探测加剧 RST 冲击
timeoutSeconds 1 ⚠️低 实际影响有限,但掩盖根本问题
# 错误示例:无延迟容忍
livenessProbe:
  tcpSocket:
    port: 8080
  initialDelaySeconds: 0  # ❌ 忽略监听延迟
  periodSeconds: 1

逻辑分析tcpSocket 探针仅检测端口是否可 connect(),不校验 SO_ACCEPTCONN 套接字选项或 ss -ltn 中的 LISTEN 状态。当内核返回 ECONNREFUSED(非 ENOTCONN),探针即判定失败——而此时应用可能正执行 bind() 后、listen() 前的初始化逻辑。

正确应对路径

  • 强制设置 initialDelaySeconds ≥ 应用冷启最大耗时
  • 或改用 exec 探针调用 ss -tln | grep ':8080' | grep 'LISTEN'
graph TD
    A[容器启动] --> B[应用进程 fork]
    B --> C[bind port 8080]
    C --> D[加载配置/初始化 Bean]
    D --> E[调用 listen()]
    E --> F[accept queue ready]
    G[TCP探针触发] -->|早于E| H[收到RST → 误判]
    G -->|晚于F| I[成功握手 → 准确就绪]

2.3 Exec探针未校验进程状态码及超时语义引发假成功

Kubernetes 的 exec 探针仅检查命令是否“执行完成”,却忽略其退出码与实际超时行为。

典型误配置示例

livenessProbe:
  exec:
    command: ["sh", "-c", "curl -s http://localhost:8080/health || true"]
  initialDelaySeconds: 5
  timeoutSeconds: 1  # 实际被忽略!

timeoutSecondsexec 探针无约束力:容器运行时(如 containerd)不强制中断子进程,curl 即使卡死 30 秒也视为“成功执行”,返回 (因 || true 强制覆盖退出码)。

根本问题归因

  • ✅ 探针仅捕获 waitpid() 返回的 status,但未解析 WEXITSTATUS(status)
  • timeoutSecondsexec 场景下仅作用于 kubelet 等待 fork 完成,不控制子进程生命周期
  • ⚠️ 进程挂起、信号丢失、僵尸子进程均导致“假健康”

正确实践对照表

检查项 错误写法 推荐写法
退出码校验 command: ["sh", "-c", "cmd || true"] command: ["sh", "-c", "cmd && exit 0 || exit 1"]
超时控制 依赖 timeoutSeconds 使用 timeout 3s cmd 显式封装
graph TD
  A[Exec Probe触发] --> B{fork/exec子进程}
  B --> C[子进程运行]
  C --> D[等待子进程exit]
  D --> E[仅检查waitpid返回值]
  E --> F[忽略WEXITSTATUS/WTERMSIG]
  F --> G[返回“成功”]

2.4 探针初始延迟(initialDelaySeconds)未适配Go应用冷启动特性

Go 应用在容器化部署中常因模块初始化、依赖注入或 TLS 握手等操作导致首请求耗时显著(典型冷启动 3–8s)。若 livenessProbereadinessProbeinitialDelaySeconds 固定设为 5,可能在 Go 应用尚未完成 HTTP server 启动时即触发探针失败,引发反复重启。

常见错误配置示例

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 5  # ❌ 低于实际冷启动时间
  periodSeconds: 10

逻辑分析:initialDelaySeconds: 5 表示容器启动后 5 秒即开始探测,但 Go 应用 main() 中若含 http.ListenAndServe() 前的 database.Open()redis.Dial()logrus.SetFormatter() 等同步初始化逻辑,实际监听端口可能延迟至第 6–7 秒,此时探针已失败 1–2 次。

推荐适配策略

  • 对新上线 Go 服务,基于压测日志统计 startup_duration_ms 的 P95 值;
  • initialDelaySeconds 设为 ceil(P95 / 1000) + 2(预留缓冲);
环境 平均冷启动 P95 冷启动 推荐 initialDelaySeconds
Dev (local) 1.2s 1.8s 4
Prod (ARM64) 4.3s 6.7s 9

自适应探测流程

graph TD
  A[容器启动] --> B{Go main() 执行}
  B --> C[执行 init()/DB/Redis 初始化]
  C --> D[调用 http.ListenAndServe]
  D --> E[端口真正可 bind & accept]
  E --> F[首次 readinessProbe 成功]
  F --> G[流量导入]

2.5 readiness失败后未阻断流量却持续接收请求的并发陷阱

当 Kubernetes 的 readinessProbe 失败时,Pod 会从 Service 的 EndpointSlice 中被移除——但移除过程非原子且存在延迟

流量残留的根源

Kube-proxy 更新 iptables/IPVS 规则需时间(通常 1–3 秒),而客户端连接池(如 HTTP Keep-Alive)仍复用旧连接,导致请求持续抵达已失活 Pod。

典型竞态流程

graph TD
    A[readinessProbe 返回 503] --> B[API Server 更新 EndpointSlice]
    B --> C[Kube-proxy 检测变更]
    C --> D[重写内核转发规则]
    D --> E[旧连接仍活跃]
    E --> F[新请求被路由至已不就绪 Pod]

关键防护代码示例

// 应用层优雅退出:在 probe 失败时主动关闭监听
func startHTTPServer() {
    srv := &http.Server{Addr: ":8080", Handler: mux}
    go func() {
        if err := srv.ListenAndServe(); err != http.ErrServerClosed {
            log.Fatal(err) // 非预期错误
        }
    }()

    // 接收 SIGTERM 后先停 probe,再关 server
    sig := make(chan os.Signal, 1)
    signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
    <-sig

    // 1. 立即让 readiness 返回 false(如通过 /healthz 状态机)
    health.SetReady(false) // 控制 probe 响应

    // 2. 延迟 2s 确保 endpoint 更新完成,再关闭服务
    time.Sleep(2 * time.Second)
    srv.Shutdown(context.Background()) // 主动断连存量请求
}

逻辑分析:health.SetReady(false) 立即影响下一次 probe;srv.Shutdown() 不接受新连接、并等待活跃请求完成(默认无超时,建议配合 context.WithTimeout);time.Sleep(2s) 是经验性缓冲,避免 endpoint 未同步前就终止监听。

最佳实践清单

  • ✅ 在 readiness 探针逻辑中引入应用内部状态快照(非仅依赖 DB 连接)
  • ✅ 客户端启用短连接或配置合理的 connection idle timeout(如 http.Transport.IdleConnTimeout = 30s
  • ❌ 禁用 livenessProbereadinessProbe 共享同一端点(易引发误杀)
风险环节 默认延迟 可控手段
Endpoint 更新 1–3s 调整 kube-controller-manager --node-monitor-grace-period
Kube-proxy 同步 ~1s 启用 --proxy-mode=iptables + --iptables-sync-period=1s
客户端连接复用 无限制 强制 Connection: close 或设置 Keep-Alive: timeout=5

第三章:liveness探针设计的三大反模式

3.1 将业务逻辑健康检查误作存活判定引发非必要重启

当把数据库连接、下游API调用或缓存同步等业务级依赖嵌入 /healthz 存活探针时,Kubernetes 会因短暂网络抖动或依赖服务降级而反复终止 Pod。

数据同步机制

# ❌ 错误示例:存活探针耦合业务逻辑
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  # 若 /healthz 内部执行 Redis 同步校验,延迟 >3s 即触发重启

该配置使探针实际承担了“业务就绪性”职责,违背 liveness 仅反映进程是否存活的语义。

探针职责分离建议

探针类型 检查目标 建议路径 超时阈值
liveness 进程是否僵死 /healthz ≤1s
readiness 业务是否可服务 /readyz ≤3s
graph TD
  A[Pod启动] --> B{Liveness Probe}
  B -->|/healthz 返回5xx| C[强制Kill容器]
  B -->|仅检查HTTP服务端口| D[避免误杀]
  D --> E[业务逻辑由readiness独立保障]

3.2 探针超时(timeoutSeconds)短于Go HTTP Server Graceful Shutdown周期

当 Kubernetes livenessProbereadinessProbetimeoutSeconds 设置过短(如 1),而 Go 应用启用优雅关闭(srv.Shutdown(ctx) 默认需数秒完成连接 draining),将导致探针频繁失败,触发不必要的重启。

典型错误配置示例

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  timeoutSeconds: 1  # ⚠️ 小于 graceful shutdown 实际耗时
  periodSeconds: 5

timeoutSeconds: 1 表示探针等待 HTTP 响应不得超过 1 秒;但若此时服务器正执行 Shutdown(),已停止接受新连接、正在等待活跃请求完成(如长轮询、慢查询),健康端点可能超时返回 503 或无响应。

Go 服务优雅关闭关键参数对照

参数 默认值 影响
http.Server.ReadTimeout 0(禁用) 控制请求头读取时限
http.Server.IdleTimeout 0(禁用) 空闲连接最大存活时间
Shutdown() 超时上下文 由调用方传入(常设 10s) 决定 graceful shutdown 总窗口

探针与 Shutdown 协同逻辑

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
    log.Fatal(err) // 若 10s 内未完成,强制终止
}

此处 10s 是应用层可控的 graceful 窗口;若探针 timeoutSeconds < 10,则在 shutdown 中期探针即失败,K8s 误判为“进程僵死”,违背优雅退出本意。

graph TD A[Probe 发起 HTTP 请求] –> B{Server 是否仍在响应?} B –>|是| C[返回 200] B –>|否:Shutdown 中| D[连接被拒绝/超时] D –> E[K8s 触发重启] E –> F[中断未完成的 graceful shutdown]

3.3 忽略Goroutine泄漏与内存压力场景下的虚假存活信号

在高负载下,runtime.ReadMemStats 可能返回滞后的堆指标,导致 pprof 或自定义健康检查误判 Goroutine 仍“活跃”。

虚假存活的典型诱因

  • GC 暂停期间的统计延迟
  • GOMAXPROCS 突增引发的调度抖动
  • 频繁 sync.Pool Get/Put 引起的临时对象驻留

诊断代码示例

func isLikelyFalsePositive() bool {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    // 注意:Sys > 512MB 且 NumGC 停滞 ≥3s 是内存压力强信号
    return m.Sys > 512*1024*1024 && time.Since(lastGC).Seconds() >= 3
}

该函数规避了单纯依赖 NumGoroutine() 的陷阱——它用 Sys(系统分配总内存)与 GC 时间戳交叉验证,避免将 GC 暂停期的 Goroutine 队列误认为泄漏。

指标 安全阈值 风险含义
NumGoroutine() 单纯数量无意义
MemStats.Sys > 512MB 内存压力主导虚假信号
NumGC 停滞时长 ≥ 3s GC 受阻,统计失真
graph TD
    A[触发健康检查] --> B{MemStats.Sys > 512MB?}
    B -->|否| C[按常规 Goroutine 数判断]
    B -->|是| D[查 lastGC 时间戳]
    D --> E[停滞 ≥3s?]
    E -->|是| F[标记为可疑虚假信号]
    E -->|否| C

第四章:滚动更新协同失效的四类配置耦合缺陷

4.1 minReadySeconds与探针探测间隔(periodSeconds)未形成安全时间窗

minReadySeconds 设置为 10,而 livenessProbe.periodSeconds 为 5 时,容器可能在就绪但尚未完成内部状态同步时被误杀。

探针竞争时序风险

# deployment.yaml 片段
spec:
  minReadySeconds: 10
  strategy:
    rollingUpdate:
      maxUnavailable: 0
  template:
    spec:
      containers:
      - livenessProbe:
          httpGet: { path: /health }
          periodSeconds: 5  # 每5秒探测一次

逻辑分析:minReadySeconds=10 要求 Pod 运行满10秒才参与负载分发;但 periodSeconds=5 导致第5、10、15秒连续探测——第10秒探测恰在就绪临界点触发,若应用此时正写入缓存,健康检查可能因短暂阻塞返回失败,触发重启。

安全时间窗建议配置

参数 推荐值 说明
minReadySeconds 15 留出初始化+缓冲余量
periodSeconds 10 minReadySeconds × 1.5 倍,避免临界重叠
graph TD
  A[Pod启动] --> B[第0秒:容器进程启动]
  B --> C[第8秒:服务监听建立]
  C --> D[第12秒:minReadySeconds达标]
  D --> E[第15秒:首次livenessProbe]
  E --> F[安全窗口:3秒缓冲]

4.2 deployment maxSurge/maxUnavailable策略与探针收敛节奏不匹配

maxSurge=1maxUnavailable=0 时,Deployment 采用“滚动替换”模式,但若就绪探针(readinessProbe)初始延迟(initialDelaySeconds: 30)远超 Pod 启动耗时,新 Pod 可能长期处于 Initializing 状态,而旧 Pod 已被强制终止。

探针与扩缩容节奏冲突示例

# deployment.yaml 片段
strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0
readinessProbe:
  httpGet:
    path: /health
  initialDelaySeconds: 30  # ⚠️ 此值大于实际服务就绪时间(约8s)
  periodSeconds: 5

逻辑分析:Kubernetes 在 initialDelaySeconds 到期前不执行任何探针检查,即使容器进程已就绪;此时 Deployment 等待就绪信号超时(默认 timeoutSeconds=1),可能触发回滚或卡在 Progressing 状态。

关键参数对照表

参数 推荐值 风险说明
initialDelaySeconds ≤ 服务真实就绪时间 + 2s 过大会阻塞滚动更新流程
periodSeconds 3–5 过长导致探针响应滞后
maxSurge + maxUnavailable 需 ≥ 探针收敛窗口 否则更新流中断

更新流程状态依赖

graph TD
  A[新Pod创建] --> B{initialDelaySeconds到期?}
  B -- 否 --> C[持续Pending/ContainerCreating]
  B -- 是 --> D[首次探针调用]
  D --> E{HTTP 200?}
  E -- 否 --> F[标记未就绪,旧Pod保持运行]
  E -- 是 --> G[标记就绪,旧Pod开始终止]

4.3 PodDisruptionBudget未对齐探针失败窗口引发强制驱逐中断

PodDisruptionBudget(PDB)的 minAvailable 策略与 Liveness/Readiness 探针的 failureThreshold × periodSeconds 时间窗不匹配时,节点维护期间可能触发非预期驱逐。

探针与PDB时间窗冲突示例

# pod.yaml 片段
livenessProbe:
  httpGet: { path: /health }
  initialDelaySeconds: 10
  periodSeconds: 5      # 每5秒探测一次
  failureThreshold: 3   # 连续3次失败即重启 → 实际容忍失败窗口 = 15s

逻辑分析:该探针在15秒内连续失败即触发容器重启;若PDB设置 minAvailable: 1 但集群执行 kubectl drain 时未预留足够缓冲,Kubelet 可能在探针尚未恢复前判定 Pod “不可用”,绕过PDB强制驱逐。

关键参数对齐建议

参数项 推荐值 说明
PDB.maxUnavailable failureThreshold × periodSeconds / 60 确保驱逐节奏慢于探针失败收敛周期
readinessProbe.periodSeconds PDB.minAvailable 计算窗口的1/3 避免就绪状态滞后导致误判

驱逐决策流程

graph TD
  A[drain发起] --> B{Pod是否Ready?}
  B -- 否 --> C[检查PDB约束]
  B -- 是 --> D[等待优雅终止]
  C --> E{当前可用Pod数 ≥ minAvailable?}
  E -- 否 --> F[阻塞驱逐]
  E -- 是 --> G[执行驱逐]

4.4 HorizontalPodAutoscaler指标采集延迟与探针状态更新不同步

HorizontalPodAutoscaler(HPA)依赖 Metrics Server 拉取 CPU/内存指标,而就绪探针(Readiness Probe)由 kubelet 独立执行并上报至 API Server——二者无协调机制。

数据同步机制

HPA 默认每 --horizontal-pod-autoscaler-sync-period=15s 同步一次指标;而探针状态更新间隔由 periodSeconds(默认10s)和 failureThreshold 共同决定,存在天然时间窗错位。

关键参数对比

组件 默认周期 更新源 是否影响扩缩容决策
HPA指标采集 15s Metrics Server ✅ 直接触发
Readiness Probe 10s(可配) kubelet → API Server ❌ 仅影响Service流量路由
# 示例:HPA配置中无法绑定探针状态
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60

此配置仅消费资源指标,对容器就绪状态无感知。当探针短暂失败但指标仍健康时,HPA可能继续扩容,导致流量被转发至未就绪实例。

graph TD
  A[Metrics Server] -->|每15s推送| B(HPA Controller)
  C[kubelet] -->|每10s执行探针| D[Pod Status]
  D -->|异步更新| E[API Server]
  B -->|仅读取/metrics| F[不感知D状态]

第五章:从K8s调度器到Go运行时的全链路诊断方法论

在生产环境排查一个持续30秒的Pod启动延迟问题时,我们发现调度器日志显示SchedulingCycle耗时仅12ms,但kubelet上报ContainerCreating状态却滞后了28秒。这种跨组件的时间差无法通过单一组件日志定位,必须构建端到端可观测链路。

构建统一时间基准

所有组件(kube-scheduler、kubelet、containerd、runtime)均启用NTP同步,并在关键路径注入trace_id与纳秒级start_time。例如,在调度器ScheduleAlgorithm.Schedule()入口处记录:

startTime := time.Now().UnixNano()
span := tracer.StartSpan("k8s.scheduler.schedule", 
    opentracing.Tag{Key: "pod.name", Value: pod.Name},
    opentracing.Tag{Key: "start_ns", Value: startTime})

关键路径埋点覆盖表

组件 埋点位置 采集字段 采样率
kube-scheduler Scheduler.Schedule() 返回前 pod_uid, node_name, schedule_duration_ms 100%
kubelet syncPod()RunContainer调用前/后 container_id, image_pull_duration_ms, cgroup_setup_ns 5%(带error自动升100%)
runc libcontainer.(*LinuxFactory).Start() cgroup_v2_path, clone_syscall_ns, seccomp_load_ns 1%(perf_event触发)

Go运行时深度探针

当容器进程启动后出现goroutine阻塞,启用runtime/trace + pprof组合分析:

  • 启动时添加GODEBUG=gctrace=1,httpdebug=1
  • main.init()中启动net/http/pprof并暴露/debug/pprof/trace?seconds=30
  • 使用go tool trace解析生成的二进制trace文件,定位STW期间的GC pausenetwork poller block

跨组件时序对齐流程图

flowchart LR
    A[kube-scheduler: ScheduleCycle] -->|trace_id=abc123| B[kubelet: syncPod]
    B --> C[containerd: CreateTask]
    C --> D[runc: Start]
    D --> E[Go runtime: main.main]
    E --> F[Go runtime: net/http server start]
    style A fill:#4CAF50,stroke:#388E3C
    style F fill:#2196F3,stroke:#0D47A1

真实案例:etcd TLS握手超时传导

某集群中Pod就绪延迟突增,通过链路追踪发现:

  • kube-scheduler耗时正常(
  • kubelet中PullImage耗时达22s → 追踪到containerd日志failed to fetch image: context deadline exceeded
  • 抓包发现containerd向registry发起TLS握手时,ClientHello发出后18s才收到ServerHello
  • 进一步检查Go运行时crypto/tls源码,在handshakeMessage.WriteTo调用处插入runtime/debug.ReadGCStats,确认无GC干扰
  • 最终定位为宿主机/dev/random熵池枯竭,导致crypto/rand.Read阻塞,影响所有Go进程TLS初始化

动态注入诊断代码

使用eBPF工具bpftrace实时捕获Go运行时事件:

bpftrace -e '
  uprobe:/usr/local/bin/kube-scheduler:runtime.mstart {
    printf("mstart on CPU %d at %s\n", cpu, strftime("%H:%M:%S", nsecs));
  }
  uretprobe:/usr/local/bin/kube-scheduler:runtime.findrunnable /pid == 1234/ {
    @runtime[comm] = hist(arg0);
  }
'

该方法论已在金融核心交易系统K8s集群落地,将平均故障定位时间从47分钟压缩至6分12秒。

记录分布式系统搭建过程,从零到一,步步为营。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注