--- tags: [arch/cicd, kubernetes, rolling-update, canary-deployment, hpa, vpa, prometheus, grafana] create time: 2026-08-08 18:00 update time: 2026-08-08 18:00 --- # K8s 发布与可观测性体系 ## 概述 Kubernetes 是现代 CI/CD 体系的编排核心,提供从滚动发布、灰度策略到自动扩缩容的完整能力链。配合 Prometheus + Grafana + 日志采集组件,构成生产级别的可观测性基础设施。本文系统梳理这些组件的设计原理与协作方式。 ## 核心原理 ### 整体架构概览 ```mermaid 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): ```mermaid 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): ```yaml 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 副本数: ```go // 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 指标采集链路 ```mermaid flowchart LR CLIENT_LIB["Client Library
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 的指标导出 | 综合容器监控面板 | ### 日志收集方案 ```mermaid 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 暴露自定义指标: ```go 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 会误判当前副本数从而做出错误的扩缩容决策。 ## 关联笔记 - [[负载均衡算法]] - [[限流熔断降级]]