261 lines
11 KiB
Markdown
261 lines
11 KiB
Markdown
---
|
||
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 集群"]
|
||
SVC["user-service"] --> EP[Endpoint<br/>pod IPs]
|
||
EP --> P1[Pod A<br/>v2.3.1]
|
||
EP --> P2[Pod B<br/>v2.3.1]
|
||
EP --> P3[Pod C<br/>v2.4.0-new]
|
||
|
||
DEPLOY[Deployment] -->|管理| P1
|
||
DEPLOY -->|管理| P2
|
||
DEPLOY -->|管理| P3
|
||
|
||
HPA[Horizontal Pod Autoscaler] -->|扩缩容指令| DEPLOY
|
||
|
||
subgraph kubeSystem["kube-system 组件"]
|
||
comp1[API Server]
|
||
comp2[(etcd)]
|
||
comp3[Controller Manager]
|
||
comp4[Scheduler]
|
||
end
|
||
|
||
P1 -->|心跳 / metrics| PROMETHEUS[Prometheus TSDB]
|
||
P2 -->|心跳 / metrics| PROMETHEUS
|
||
P3 -->|心跳 / metrics| PROMETHEUS
|
||
|
||
P1 -.-> FLUENTD[Fluentd]
|
||
P2 -.-> FLUENTD
|
||
P3 -.-> FLUENTD
|
||
FLUENTD --> ES_LOG[ES / Loki]
|
||
end
|
||
|
||
subgraph GrafanaUI["可视化层"]
|
||
GRAFANA[Grafana Dashboard] -->|查询| PROMETHEUS
|
||
GRAFANA -->|告警规则| ALERTMANAGER[Alertmanager]
|
||
end
|
||
|
||
INGRESS --> SVC
|
||
```
|
||
|
||
### 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<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 的指标导出 | 综合容器监控面板 |
|
||
|
||
### 日志收集方案
|
||
|
||
```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 会误判当前副本数从而做出错误的扩缩容决策。
|
||
|
||
## 关联笔记
|
||
|
||
- [[负载均衡算法]]
|
||
- [[限流熔断降级]]
|