Files
autumn-recruitment/05.架构/ci-cd/K8s 发布与可观测性体系.md
T

11 KiB
Raw Blame History

tags, create time, update time
tags create time update time
arch/cicd
kubernetes
rolling-update
canary-deployment
hpa
vpa
prometheus
grafana
2026-08-08 18:00 2026-08-08 18:00

K8s 发布与可观测性体系

概述

Kubernetes 是现代 CI/CD 体系的编排核心,提供从滚动发布、灰度策略到自动扩缩容的完整能力链。配合 Prometheus + Grafana + 日志采集组件,构成生产级别的可观测性基础设施。本文系统梳理这些组件的设计原理与协作方式。

核心原理

整体架构概览

graph TD
    subgraph UserTraffic["用户流量"]
        U[用户请求] --> INGRESS[Nginx Ingress Controller]
    end

    subgraph K8sCluster["K8s 集群"]
        SUBNAME["Service Name: user-service"] --> EP[Endpoint: pod IPs]
        EP --> P1[Pod A: v2.3.1]
        EP --> P2[Pod B: v2.3.1]
        EP --> P3[Pod C: v2.4.0-new]

        DEPLOY[Deployment] -->|管理| P1
        DEPLOY -->|管理| P2
        DEPLOY -->|管理| P3

        HPA[Horizontal Pod Autoscaler] -->|扩缩容指令| DEPLOY

        sub kubeSystem["kube-system 组件"]
            APISERVER[API Server]
            ETCD[(etcd)]
            CONTROLLER[M-controller Manager]
            SCHEDULER[Scheduler]
        end

        P1 -->|心跳 / metrics| PROMETHEUS[Prometheus TSDB]
        P2 -->|心跳 / metrics| PROMETHEUS
        P3 -->|心跳 / metrics| PROMETHEUS
        P1 -.->Fluentd --> ES[ES / Loki]
        P2 -.->Fluentd --> ES
        P3 -.->Fluentd --> ES
    end

    sub GrafanaUI["可视化层"]
        GRAFANA[Grafana Dashboard] -->|查询| PROMETHEUS
        GRAFANA -->|告警规则| ALERTMANAGER[Alertmanager]
    end

    INGRESS --> SUBNAME

RollingUpdate 策略详解

K8s Deployment 默认使用 RollingUpdate 策略,通过两个参数控制滚动速度:

参数 默认值 含义 影响
maxSurge 25% 更新过程中允许超出目标副本数的最大数量 控制新 Pod 先启动的数量
maxUnavailable 25% 更新过程中允许不可用的最大副本数 控制旧 Pod 被终止的速度

两种典型配置对比:

配置 行为 适用场景
maxSurge=1, maxUnavailable=0 逐台升级,零停机 对可用性要求极高的生产环境
maxSurge=25%, maxUnavailable=25% 批量快速滚动 测试环境或无需保持全量的场景

以 3 副本升级为 4 副本为例(maxSurge=1, maxUnavailable=0):

gantt
    title RollingUpdate 过程 (3 -> 4 pods, surge=1)
    dateFormat YYYY-MM-DD
    axisFormat %H:%M

    section Pod Group
    old-1              :done,    1,  2026-08-08T00:00, 2026-08-08T00:05
    old-2              :done,    1,  2026-08-08T00:00, 2026-08-08T00:05
    old-3              :done,    1,  2026-08-08T00:00, 2026-08-08T00:05
    new-1 (surge +1)   :active,  2026-08-08T00:01, 2026-08-08T00:05
    old-3 terminate    :crit,    2026-08-08T00:06, 2026-08-08T00:07
    new-2 (surge +1)   :active,  2026-08-08T00:07, 2026-08-08T00:11
    old-2 terminate    :crit,    2026-08-08T00:12, 2026-08-08T00:13
    new-3 (surge +1)   :active,  2026-08-08T00:13, 2026-08-08T00:17
    old-1 terminate    :crit,    2026-08-08T00:18, 2026-08-08T00:19
    new-4              :active,  2026-08-08T00:19, 2026-08-08T00:23

关键约束:就绪探针(Readiness Probe)

  • 新 Pod 必须通过 Readiness Probe 才会被加入 Service 的 Endpoint 列表。
  • 如果 Readiness 失败,即使新 Pod 已运行也不会接收流量。
  • livenessProbe 用于检测进程是否存活并可能触发重启;readinessProbe 用于决定"这个 Pod 是否可以接受流量"。

蓝绿部署 vs Canary 发布

维度 RollingUpdate 蓝绿部署 Canary 发布
资源消耗 低(增量替换) 高(需要双倍实例) 中(少量 Canary 实例)
回滚速度 慢(需要滚动回去) 秒级(切换流量即可) 快(减少 Canary 流量比例)
风险 低(渐进式) 中高(全部切到新版本后才发现问题) 最低(小部分用户先体验)
复杂度 低(K8s 原生支持) 中(需要外部 LB 管理) 高(需要 Istio 或 Nginx 权重配置)
适用团队 所有规模 大团队,有独立预发环境 有成熟 DevOps 能力的团队

Canary 发布的流量权重示例(基于 Istio VirtualService):

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: user-service-canary
spec:
  hosts: ["user-service.default.svc.cluster.local"]
  http:
  - route:
    - destination:
        host: user-service
        subset: v1
      weight: 90     # 90% 流量走稳定版
    - destination:
        host: user-service
        subset: v2
      weight: 10     # 10% 流量进 Canary

HPA — 水平自动扩缩容

HPA 基于指标监控动态调整 Pod 副本数:

// HPA 核心指标类型
type MetricSourceType string

const (
    // CPU 利用率(容器内统计)
    ContainerCPU MetricSourceType = "ContainerResource"
    // 内存利用率
    ContainerMemory MetricSourceType = "ContainerResource"
    // Prometheus 自定义指标(外部指标)
    External MetricSourceType = "External"
    // Pods 级别指标(如并发连接数)
    Pods MetricSourceType = "Pods"
)

HPA 的计算公式:targetReplicas = ceil(currentReplicas * (currentMetricValue / desiredMetricValue))

例如 CPU 目标 70%,当前平均 CPU 使用率 91%,当前 5 个副本: ceil(5 * 91 / 70) = ceil(6.5) = 7 → 扩容到 7 个副本。

Warning

HPA 不是实时的——它依赖 metrics-server 每 15~60 秒的采集间隔,扩容动作有分钟级延迟。对于需要即时响应的秒杀场景,应该提前设置 minReplicas >= 预期峰值的 80%。

VPA — 垂直自动扩缩容

VPA 自动调整单个 Pod 的 CPU/Memory requests 和 limits:

模式 行为 说明
Offline 只推荐不应用 人工审核后手动调整
Initial 在 Pod 创建时设置建议值 对新 Pod 生效
Auto 直接在运行时修改 Pod Spec 会导致 Pod 被杀掉重建
Recreate 类似 Auto 但先删除旧的再创建 保证平滑过渡

VPA 的典型场景:微服务刚上线阶段,运维难以精确预估资源需求,可以让 VPA 学习实际使用模式后给出最优建议。

Prometheus 指标采集链路

flowchart LR
    CLIENT_LIB["Client Library<br/>Go SDK / Java SDK / Python SDK"] -->|暴露 /metrics HTTP endpoint| APP_POD["App Pod"]
    APP_POD -->|HTTP GET /metrics| SCRAPE["Prometheus Scrape"]
    SCRAPE -->|写入| TSDB["TSDB: Thanos / VictoriaMetrics"]
    TSDB --> QUERY["PromQL Query Engine"]
    QUERY --> ALERT["Alert Rule Evaluation"]
    ALERT -->|告警触发| AM["Alertmanager"]
    AM -->|Webhook / 邮件 / 钉钉| NOTIFICATION["通知通道"]

核心概念:

  • Counter:单调递增计数器(如总请求数),适合画 Rate 曲线。
  • Gauge:可上可下的仪表值(如活跃连接数)。
  • Histogram:分桶统计(如请求延迟分布),自动生成 count + sum + bucket。
  • Summary:客户端计算分位数(P50/P99),服务器端不做计算但有额外开销。

Tip

面试常考点:为什么 Prometheus 用 Pull 而不是 Push?因为 Prometheus 作为中心化的采集器天然知道所有 target 的地址,Pull 模式下失联的 service 会自动被忽略(不再收到数据即标记为 down)。如果是 Push 模式(如 StatsD),挂了的服务会停止发送数据但采集器不知道它们已经不存在了。

Grafana Dashboard 模板来源

Grafana 本身不提供指标,它只是可视化层。主要数据来源:

数据源 采集内容 典型用途
cAdvisor 容器级别的 CPU/内存/网络/Disk I/O 容器资源监控
kube-state-metrics Pod/Node/Deployment/RS 的状态元数据 K8s 资源健康度
node-exporter 宿主机硬件指标(CPU、内存、磁盘、网络) 节点层面性能分析
cadvisor+prometheus 组合 cAdvisor 和 Prometheus 的指标导出 综合容器监控面板

日志收集方案

graph LR
    PODS["所有 Pod 的 stdout/stderr"] --> FLUENTD["Fluentd / Filebeat DaemonSet"]
    FLUENTD -->|"Filesystem tail"| LOG_FILES["文件日志"]
    FLUENTD --> ES_ELB["Elasticsearch Cluster"]
    FLUENTD --> LOKI["Loki"]
    ES_ELB --> KIBANA["Kibana"]
    LOKI --> GRAFANA["Grafana Log Panel"]
  • ELK 栈(Elasticsearch + Logstash/Filebeat + Kibana):功能最强大,支持全文搜索、聚合分析,但存储成本高(JSON 结构化索引非常占空间)。
  • Loki(Grafana 生态):不建立全文索引,只索引 Labels。查询时用 label 过滤后再检索日志内容,存储成本极低。适合与 Grafana 深度集成的场景。

Note

最佳实践:日志采集应该在 Pod 层面做 sidecar 注入(而非直接写文件),这样即使 Pod 崩溃也能收集到最后一段输出。

代码示例

Go 中使用 Prometheus Client Library 暴露自定义指标:

import "github.com/prometheus/client_golang/prometheus"

var requestDuration = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "HTTP request duration in seconds",
        Buckets: prometheus.DefBuckets,
    },
    []string{"method", "path"},
)

func init() {
    prometheus.MustRegister(requestDuration)
}

实践场景

秋招高频问题:

  • "HPA 能基于自定义业务指标扩缩容吗?" — 可以。通过 Custom Metrics API + adapter(如 prometheus-adapter),HPA 可以从 Prometheus 读取任意业务指标。例如根据 QPS、错误率、或者 Kafka 消费 lag 来触发扩缩容。
  • "为什么蓝绿部署要双倍资源?" — 蓝绿的本质是同时保留新旧两套完整环境,用负载均衡器切换流量。这保证了随时可以秒级回滚,代价就是付费双倍的生产实例。
  • "Prometheus 在什么场景下不适合?" — 海量短生命周期任务(如 Serverless 函数,每分钟创销毁成千上万 Pod)。Prometheus 的拉取模型无法跟得上如此动态的变化。此时更适合用 Pushgateway 或改为 push-based 的方案。

Warning

K8s 的 Ready/NotReady 状态切换不会自动通知 HPA——只有当 Pod 处于 Running + Ready 时才计入副本基数。所以在 deployment.yaml 里一定要配好 readiness probe,否则 HPA 会误判当前副本数从而做出错误的扩缩容决策。

关联笔记