Files
cs-note/hhs/MS/05-部署运维/02-Kubernetes/04-扩缩容与有状态应用.md
T
2026-05-24 11:42:38 +08:00

218 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [kubernetes, hpa, autoscaling, statefulset, horizontal-pod-autoscaler]
create time: 2026-05-18 00:30
---
# K8s 扩缩容与有状态应用 — HPA & StatefulSet
## 概述
K8s 提供多种扩缩容机制。**HPA** 基于 CPU/Memory 或其他自定义指标实现 Pod 级别的弹性伸缩,适用于无状态服务。**StatefulSet** 则是数据库、消息队列等有状态组件的理想选择。两者各有适用场景。
## HPA 弹性伸缩
### 基础示例
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU > 70% 时扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # 扩容稳定期
policies:
- type: Pods
value: 2
periodSeconds: 60 # 每分钟最多扩 2 个
scaleDown:
stabilizationWindowSeconds: 300 # 缩容稳定期 5min(防抖动)
```
### 扩展指标:基于自定义 Metric 扩缩容
```yaml
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100" # 每个 Pod 平均 100 QPS 时扩容
- type: Object
object:
metric:
name: active_users
describedObject:
apiVersion: apps/v1
kind: Deployment
name: web-app
target:
type: Value
value: "500" # 每 500 活跃用户触发一次扩容
```
> [!info] HPA 的工作机制
>
> HPA 控制器每 15 秒查询 Metrics Server(CPU/Memory)或自定义 Metrics Adapter(QPS 等),计算所需 Replica 数。公式:`ceil(当前指标值 / 目标指标值 × 当前副本数)`。为了防止抖动,scaleDown 的稳定窗口通常比 scaleUp 长很多。
### HPA 调优最佳实践
| 参数 | 推荐值 | 说明 |
|------|--------|------|
| `averageUtilization` (CPU) | 60~70% | 低于 60% 浪费资源,高于 80% 延迟风险大 |
| `scaleUp.stabilizationWindow` | 0~60s | 突发流量快速响应 |
| `scaleDown.stabilizationWindow` | 300~600s | 防止抖动导致的频繁扩缩 |
| `pods[].periodSeconds` | 60s | 限制扩容频率,避免雪崩 |
## StatefulSet — 有状态应用
Deployment 适合无状态服务,而有状态服务(数据库、分布式协调器等)需要 **稳定的网络标识 + 有序的启停**。StatefulSet 就是为此而生的控制器。
### 完整示例
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-cluster
spec:
serviceName: mysql # 绑定 Headless Service
replicas: 3
podManagementPolicy: Parallel # 并行启动(生产建议 OrderedReady 逐步启动)
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0 # 从最后一个 Pod 开始逐步滚动
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
```
### StatefulSet 的核心特性
| 特性 | 说明 |
|------|------|
| **稳定名称** | Pod 名为 `{statefulset-name}-{ordinal}`,如 `mysql-cluster-0` |
| **有序部署** | 默认依次启动:0 → 1 → 2(设置 `Parallel` 可并行) |
| **有序删除** | 反向删除:2 → 1 → 0(保证主从切换安全) |
| **持久化存储** | `volumeClaimTemplates` 为每个 Pod 独立创建 PVC,Pod 重建后数据不丢失 |
| **Headless Service** | 需搭配 `ClusterIP: None` 的 Service,每个 Pod 有独立的 DNS 记录 |
### StatefulSet vs Deployment 对比
| 维度 | Deployment | StatefulSet |
|------|-----------|-------------|
| **Pod 命名** | 随机后缀 | `{name}-{0,1,2,...}` |
| **存储** | 共享 PVC 或不持久 | 每个 Pod 独立 PVC |
| **启动顺序** | 并发 | 串行(默认) |
| **删除顺序** | 随机 | 逆序 |
| **DNS 稳定性** | 每次重建 IP 变 | 域名不变 |
| **适用场景** | Web/API 无状态服务 | DB、ZooKeeper、Kafka |
> [!note] 为什么有状态应用不适合 Deployment?
>
> Deployment 会随机销毁和重建 Pod——这意味着 PVC 会被重新绑定到不同的 Pod,存储卷中的数据和节点 ID 完全错位。StatefulSet 保证同一个 Pod 始终使用同一片存储,这对数据库、Kafka、ZooKeeper 等组件至关重要。
### Headless Service — StatefulSet 的灵魂搭档
```yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None # 关键:不分配 ClusterIP
selector:
app: mysql
ports:
- port: 3306
```
通过 DNS 可以直接访问单个 Pod:
- `mysql-cluster-0.mysql-headless.production.svc.cluster.local:3306`
- `mysql-cluster-1.mysql-headless.production.svc.cluster.local:3306`
## 补充:Vertical Pod Autoscaler (VPA)
HPA 只管 Pod 数量,不管单 Pod 的资源配置。如果你经常遇到 OOMKill 或 CPU Throttling,可以尝试 VPA:
```yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: order-service-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
updatePolicy:
updateMode: "Auto" # Auto / Initial / Off
resourcePolicy:
containerPolicy:
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: "2"
memory: 4Gi
```
> [!warning] VPA 的限制
>
> VPA 在调整资源时会**驱逐并重建 Pod**。与 HPA 不同,VPA 不能无缝运行——建议在开发/测试环境中先用 `Initial` 模式观察推荐值,确定合理后再切到 `Auto`。生产环境通常结合 HPA(管数量)+ 手动调优 Resources(管质量)。
## 关联笔记
- [[../01-核心概念与Deployment]] — Deployment 基础,HPA 的目标控制器
- [[../02-配置管理]] — StatefulSet 的数据库凭据通过 Secret 注入
- [[../03-网络与服务发现]] — Headless Service 为 StatefulSet 提供 DNS 解析
- [[../hhs/MS/05-部署运维/04-SRE实践]] — SLO/Error Budget 决定扩容阈值