构建媒体节点弹性伸缩体系的K8s HPA自定义指标扩容技巧
在流媒体、直播转码、实时通信等媒体处理场景中,业务流量呈现显著的潮汐特征:晚高峰并发激增、突发热点事件引发流量洪峰、闲时资源大量闲置。传统基于CPU/内存的Horizontal Pod Autoscaler(HPA)难以精准感知媒体业务的真实负载压力,往往导致扩容滞后或过度扩容。本文结合生产环境实践,系统梳理基于自定义指标构建媒体节点弹性伸缩体系的关键技术要点。
一、 媒体业务负载特征与扩容痛点分析
媒体处理节点的资源消耗模式与通用Web服务存在本质差异:
| 维度 | 通用Web服务 | 媒体处理节点 |
|---|---|---|
| 核心瓶颈 | CPU计算、内存吞吐 | 编解码器占用、GPU显存、网络带宽、连接数 |
| 负载指标 | QPS、RT、CPU利用率 | 并发流数、转码任务队列长度、GPU利用率、带宽使用率 |
| 扩容敏感度 | 秒级响应可接受 | 毫秒级调度延迟直接影响首屏秒开率、卡顿率 |
典型痛点:
- 指标失真:CPU利用率低时GPU显存已满,HPA不扩容导致新任务调度失败
- 抖动频发:基于瞬时流量波动触发频繁扩缩容,引发Pod频繁重建,加重控制面压力
- 冷启动长:媒体节点镜像体积大、依赖动态库加载、需建立编解码管线,冷启动耗时常达30-60秒,扩容滞后窗口大
二、 自定义指标体系设计原则
构建面向媒体节点的HPA指标体系,建议遵循“业务语义化、采集标准化、聚合多维度”三大原则。
2.1 核心指标分层模型
┌─────────────────────────────────────────────────────┐
│ 业务决策层 (HPA 直接消费) │
│ • media_active_streams (当前活跃流总数) │
│ • media_pending_tasks (待处理转码任务队列长度) │
│ • media_gpu_utilization (GPU 算力综合利用率) │
│ • media_bandwidth_usage_pct (出口带宽使用率) │
├─────────────────────────────────────────────────────┤
│ 指标聚合层 (Prometheus Rules) │
│ • 多副本求和/求平均 • 分位数平滑 • 趋势预测 │
├─────────────────────────────────────────────────────┤
│ 采集暴露层 (Exporter / Pushgateway) │
│ • Node Exporter + GPU Exporter • 业务侧埋点上报 │
└─────────────────────────────────────────────────────┘
2.2 关键指标定义与采集建议
| 指标名称 | 类型 | 采集来源 | 业务含义 | 推荐采集频率 |
|---|---|---|---|---|
media_node_active_streams |
Gauge | 业务进程 /metrics | 单节点当前承载的有效推/拉流数 | 10s |
media_node_pending_transcode_tasks |
Gauge | 任务队列中间件 | 积压待调度的转码/截图/水印任务数 | 15s |
media_node_gpu_memory_used_bytes |
Gauge | DCGM Exporter | 单卡显存占用字节数 | 10s |
media_node_nic_tx_bytes_per_sec |
Counter | Node Exporter | 网卡发送字节率,换算带宽利用率 | 10s |
media_node_session_accept_rate |
Gauge | 业务进程 | 单位时间内成功建立会话占比 | 30s |
合规提示:指标命名建议遵循Prometheus Best Practices,使用
media_node_前缀避免命名冲突;采集组件部署需满足等保三级审计要求,日志脱敏处理。
三、 Prometheus Adapter 对接 HPA 实战配置
3.1 部署架构选型
推荐采用 Prometheus + Prometheus Adapter + Custom Metrics API 架构,而非外部指标,原因如下:
- 原生支持
custom.metrics.k8s.ioAPI,HPA 无需额外配置external类型 - 复用现有监控栈,减少运维组件
- 支持
metricSelector标签过滤,实现多租户/多集群隔离
3.2 Adapter 规则配置示例
# prometheus-adapter configmap 关键片段
rules:
custom:
- seriesQuery: 'media_node_active_streams{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)$"
as: "active_streams"
metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
- seriesQuery: 'media_node_pending_transcode_tasks{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)$"
as: "pending_tasks"
metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
- seriesQuery: 'media_node_gpu_memory_used_bytes{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)$"
as: "gpu_mem_usage_pct"
metricsQuery: |
(sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)
/
sum(nvidia_gpu_memory_total_bytes{<<.LabelMatchers>>}) by (<<.GroupBy>>)
) * 100
技巧:
metricsQuery中使用录制规则预聚合可降低 Adapter 查询延迟,建议为高频指标创建media_node:active_streams:sum_1m等预聚合规则。
3.3 HPA 资源清单示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: media-transcoder-hpa
namespace: media-prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: media-transcoder
minReplicas: 3
maxReplicas: 200
behavior:
scaleDown:
stabilizationWindowSeconds: 600 # 缩容冷却 10 分钟,防抖
policies:
- type: Percent
value: 10
periodSeconds: 120
scaleUp:
stabilizationWindowSeconds: 30 # 扩容快速响应
policies:
- type: Percent
value: 50
periodSeconds: 30
- type: Pods
value: 10
periodSeconds: 30
selectPolicy: Max
metrics:
# 核心指标:活跃流数,单 Pod 目标 50 路
- type: Pods
pods:
metric:
name: active_streams
target:
type: AverageValue
averageValue: "50"
# 兜底指标:任务队列积压,单 Pod 目标 < 20
- type: Pods
pods:
metric:
name: pending_tasks
target:
type: AverageValue
averageValue: "20"
# 硬性约束:GPU 显存不超过 85%
- type: Pods
pods:
metric:
name: gpu_mem_usage_pct
target:
type: AverageValue
averageValue: "85"
关键参数解读:
behavior.scaleDown.stabilizationWindowSeconds: 600:媒体节点缩容需极度谨慎,防止正在转码的任务被驱逐导致用户投诉metrics多指标 AND 关系:任一指标触发阈值即扩容,所有指标均低于阈值才缩容,实现“激进扩容、保守缩容”averageValue使用字符串形式,避免科学计数法解析异常
四、 扩容延迟优化:从分钟级压缩至秒级
4.1 镜像与启动链路优化
| 优化手段 | 典型收益 | 实施要点 |
|---|---|---|
| 基础镜像精简 | -40% 镜像体积 | 使用 distroless 或 alpine 基础镜像,多阶段构建剔除编译工具链 |
| 依赖预装与层缓存 | -30% 拉取耗时 | 将 FFmpeg、CUDA 库、字体文件等固化至基础镜像层 |
| 预热容器 | 冷启动 -50% | initContainer 预加载模型、建立编解码管线、预热 JIT 编译 |
| 镜像预拉取 | 节点就绪 -20s | DaemonSet 运行 crictl pull 或节点池 imagePullSecrets 预热 |
4.2 调度与就绪探针协同
# Deployment 片段
spec:
template:
spec:
# 1. 调度侧:优先调度到有 GPU 资源、低负载节点
schedulerName: default-scheduler
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: nvidia.com/gpu.present
operator: Exists
- weight: 20
preference:
matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values: ["gpu.v100", "gpu.t4"]
# 2. 启动侧:宽容启动期资源抖动
containers:
- name: transcoder
startupProbe:
httpGet:
path: /health/startup
port: 8080
failureThreshold: 30 # 允许 5 分钟启动窗口
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
# 3. 资源声明:显式声明 GPU,防止调度到无 GPU 节点
resources:
limits:
nvidia.com/gpu: "1"
memory: "8Gi"
cpu: "4"
requests:
nvidia.com/gpu: "1"
memory: "6Gi"
cpu: "2"
避坑指南:
startupProbe.failureThreshold * periodSeconds必须覆盖最长冷启动时间,否则 Pod 会被误判为启动失败而被杀掉,触发无限重启风暴。
五、 缩容保护与业务平滑迁移
媒体节点缩容的核心矛盾:Kubernetes 默认随机删除 Pod vs 业务要求连接不中断、任务不丢失。
5.1 生命周期钩子实现优雅下线
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
# 1. 标记节点进入“下线中”状态,停止接收新流
curl -X POST localhost:8080/admin/drain
# 2. 等待现有流自然结束或强制迁移(设定超时 300s)
for i in {1..60}; do
active=$(curl -s localhost:8080/metrics | grep media_node_active_streams | awk '{print $2}')
[ "$active" -eq 0 ] && break
sleep 5
done
# 3. 兜底:强制切断残留连接,上报任务重调度
curl -X POST localhost:8080/admin/force_shutdown
5.2 PodDisruptionBudget 兜底
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: media-transcoder-pdb
namespace: media-prod
spec:
minAvailable: 80% # 任意时刻至少保留 80% 副本可用
selector:
matchLabels:
app: media-transcoder
配合集群自动缩容器:设置 --scale-down-unneeded-time=10m、--scale-down-utilization-threshold=0.5,避免节点层面过早缩容导致 Pod 驱逐。
六、 观测与运维闭环建设
6.1 关键仪表盘指标
建议在 Grafana 构建 “媒体节点弹性伸缩全景仪表盘”,包含:
- 扩缩容事件时间轴:叠加业务流量曲线,直观评估响应及时性
- 指标触发分布:统计各指标触发扩容的占比,识别失效指标
- 冷启动耗时分位数:P50/P90/P99 启动延迟,指导镜像优化
- 缩容成功率:优雅下线完成率、强制杀掉占比
6.2 告警规则示例
groups:
- name: media-hpa-alerts
rules:
- alert: MediaHPAScaleUpStuck
expr: |
(kube_hpa_status_desired_replicas - kube_hpa_status_current_replicas) > 0
and
(time() - kube_pod_status_ready_time{condition="true"}) > 300
for: 5m
labels:
severity: critical
annotations:
summary: "HPA 期望扩容但 Pod 长时间未 Ready"
description: "Deployment {{ $labels.deployment }} 已连续 5 分钟未达到期望副本数,疑似镜像拉取失败或调度受阻"
- alert: MediaHPAMetricStale
expr: |
time() - media_node_active_streams{job="media-transcoder"} > 60
for: 2m
labels:
severity: warning
annotations:
summary: "媒体节点自定义指标采集中断"
description: "Pod {{ $labels.pod }} 指标超过 60 秒未更新,HPA 可能基于陈旧数据决策"
七、 常见误区与规避清单
| 误区 | 后果 | 规避方案 |
|---|---|---|
直接用 external 指标对接业务 HTTP 接口 |
单点故障、延迟高、无认证鉴权 | 统一接入 Prometheus,走 Custom Metrics API |
| 仅监控 GPU 显存,忽略编码器实例数 | 显存充足但编码器槽位耗尽,新任务报错 | 引入 media_node_encoder_slots_used 指标 |
| HPA 目标值设为固定常数 | 无法适应不同时段、不同码率的负载差异 | 结合 VerticalPodAutoscaler 推荐值,或引入 KEDA ScaledObject 动态阈值 |
| 缩容不设 PDB,依赖滚动更新 | 运维变更、节点故障导致大面积掉流 | 必须配置 PDB,配合 preStop 钩子 |
八、 总结与演进建议
构建媒体节点弹性伸缩体系,核心在于“指标贴近业务语义、扩容快于业务增长、缩容慢于业务消退”。建议按三阶段演进:
- 基础期(1-2 周):接入 GPU/带宽/队列指标,上线 HPA 多指标 AND 逻辑,建立基础仪表盘与告警
- 稳定期(1 个月):引入启动预热、优雅下线、PDB 保护,将扩容延迟压缩至 60 秒内,缩容零投诉
- 智能期(持续):接入 KEDA 或自研预测器,基于历史流量曲线、CDN 日志预测峰值,实现预测性扩容;探索 Knative Serving 或 Karmada 实现跨集群、跨云弹性
通过上述体系化建设,可在保障媒体业务极致体验的前提下,将节点资源利用率从 30%-40% 提升至 65%-75%,显著降低算力成本。
免责声明:本文所述技术方案基于通用 Kubernetes 生态与典型媒体业务场景整理,具体落地需结合企业基础设施现状、合规要求及业务 SLA 进行调整验证。文中配置示例仅供参考,不构成任何明示或暗示的性能承诺。
� 媒体节点K8s弹性伸缩体系进阶:立体弹性、异构调度与成本治理实战
接上文基础体系构建,本文进一步探讨立体弹性联动、异构算力精细调度、Spot实例混部降本、流量治理协同四大进阶课题,助力媒体平台从“能扩容”向“扩得准、缩得稳、成本优”跨越。
一、 立体弹性:HPA/VPA/CA 三维联动机制
单一 HPA 仅解决“副本数”水平扩展,媒体节点在单 Pod 资源规格不匹配、集群节点资源不足时仍会卡死。需构建 HPA(水平)+ VPA(垂直)+ CA(集群) 立体弹性闭环。
1.1 三维弹性协同架构
graph TD
A[业务流量洪峰] --> B{HPA 判断}
B -- 副本数达上限/资源请求不足 --> C[VPA Recommender 分析历史用量]
C -- 建议调整 Request/Limit --> D[VPA Updater 驱动滚动更新]
B -- Pending Pods 触发 --> E[Cluster Autoscaler 扩容节点池]
E -- 新节点 Ready --> F[HPA 调度新副本]
G[业务低谷] --> H[HPA 缩容副本]
H -- 节点利用率低 --> I[CA 缩容节点]
I -- 驱逐 Pod --> J[PDB + preStop 优雅下线]
1.2 关键冲突与规避策略
| 冲突场景 | 根因 | 解决方案 |
|---|---|---|
| HPA 与 VPA 争抢 CPU/内存指标 | 双向调节震荡 | VPA 仅推荐 Request/Limit(updateMode: Off 或 Initial),HPA 独占自定义指标(流数/队列)做副本伸缩 |
| CA 扩容节点慢于 HPA 创建 Pod | Pending Pod 积压触发 scaleUp 延迟 |
配置 expander: least-waste + 节点池预留 overprovisioning Pause Pod |
| VPA 重建 Pod 触发 HPA 误判 | 滚动更新期间副本数波动 | HPA behavior.scaleDown.stabilizationWindowSeconds: 600 覆盖 VMA 更新周期 |
1.3 生产级 VPA 配置模板
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: media-transcoder-vpa
namespace: media-prod
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: media-transcoder
updatePolicy:
updateMode: "Auto" # 生产建议 "Initial" 或 "Off" + 手动发布
containerPolicies:
- containerName: transcoder
controlledResources: ["cpu", "memory"] # 不控制 nvidia.com/gpu
minAllowed:
cpu: "500m"
memory: "2Gi"
maxAllowed:
cpu: "8"
memory: "16Gi"
resourcePolicy:
containerPolicies:
- containerName: transcoder
# GPU 资源由设备插件独占,禁止 VPA 触碰
controlledResources: ["cpu", "memory"]
核心原则:媒体节点 GPU 显存/编码器槽位为硬性不可压缩资源,VPA 仅托管 CPU/内存弹性;HPA 托管副本数弹性;CA 托管节点供给弹性。三者分工互不干涉核心指标。
二、 异构算力精细调度:从“卡级”进化到“实例级”隔离
媒体转码集群常混跑 V100/T4/A10/A100 等多代 GPU,且单卡需承载多路转码任务。默认 nvidia.com/gpu: 1 独占模式导致资源碎片率超 40%。
2.1 GPU 共享与隔离方案选型
| 方案 | 适用场景 | 隔离强度 | 运维复杂度 | 推荐指数 |
|---|---|---|---|---|
| NVIDIA Time-Slicing | 低码率转码、推理服务 | 弱(显存/算力软隔离) | 低(原生支持) | ⭐⭐⭐ |
| MIG (Multi-Instance GPU) | A100/H100 多租户、强隔离 | 硬(硬件级显存/计算单元切分) | 中(需驱动/固件支持) | ⭐⭐⭐⭐⭐ |
| vGPU (vCS/vWS) | 桌面云、持久化桌面 | 硬(Hypervisor 级) | 高(License 成本) | ⭐⭐ |
| HAMi (原 k8s-vGPU-scheduler) | 混部、显存/核心细粒度配额 | 中强(内核模块拦截) | 中(需安装 Agent) | ⭐⭐⭐⭐ |
媒体转码推荐组合:
- A100/A800 节点:启用 MIG 1g.5gb/2g.10gb Profile,将 1 张物理卡切分为 7/3 个隔离实例,Pod 申请
nvidia.com/mig-1g.5gb: 1 - T4/V100 节点:部署 HAMi,Pod 指定
nvidia.com/gpu: 1+nvidia.com/gpumem: 3000+nvidia.com/gpucores: 30实现显存/算力双维配额
2.2 调度器扩展:感知拓扑与亲和性
# Pod Spec 片段:HAMi 细粒度申请 + 拓扑感知
spec:
schedulerName: hami-scheduler # 或 default-scheduler + 插件
containers:
- name: transcoder
resources:
limits:
nvidia.com/gpu: "1"
nvidia.com/gpumem: "4096" # 显存 4GiB
nvidia.com/gpucores: "50" # 50% 算力核心
requests:
nvidia.com/gpu: "1"
nvidia.com/gpumem: "4096"
nvidia.com/gpucores: "50"
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values: ["Tesla-T4", "A100-SXM4-40GB"] # 指定 GPU 型号
# 反亲和:避免同一物理机单点故障
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: [media-transcoder]
topologyKey: kubernetes.io/hostname
2.3 扩容时的设备插件健康检查
痛点:CA 新增 GPU 节点后,nvidia-device-plugin 启动注册延迟导致 Pod 长期 Pending。
对策:
- 节点初始化脚本 内嵌
nvidia-ctk配置、驱动加载、设备插件静态 Pod 预拉取 - CA
--skip-nodes-with-local-storage=false确保 GPU 节点纳入扩容范围 - 编写 MutatingWebhook:Pod 调度前校验目标节点
nvidia.com/gpu.allocatable > 0,否则重调度
三、 Spot/Preemptible 实例混部:极致成本优化与存活博弈
媒体转码任务具备可中断、可重试、无状态特征,极适合 Spot 实例(抢占式实例),成本可降低 70%-90%。
3.1 混部架构设计
┌────────────────────────────────────────────────────────────┐
│ 节点池分层策略 │
├──────────────────┬──────────────────────┬──────────────────┤
│ 基础池 │ 弹性池 │ 超售池 │
│ (On-Demand) │ (Spot/Preemptible) │ (Spot + 过载保护)│
├──────────────────┼──────────────────────┼──────────────────┤
│ 比例: 30% │ 比例: 60% │ 比例: 10% │
│ 角色: 兜底、长任务│ 角色: 主力转码、短任务 │ 角色: 削峰填谷 │
│ Taint: 无 │ Taint: spot=true │ Taint: spot=true │
│ │ Toleration: 仅弹性负载│ Toleration: 仅 │
│ │ │ 可降级任务 │
└──────────────────┴──────────────────────┴──────────────────┘
3.2 抢占感知与优雅驱逐体系
云厂商抢占通知通常提前 2-5 分钟,必须在 Pod 被强制杀掉前完成任务迁移。
方案 A:云厂商元数据服务 + Node Problem Detector (NPD)
# DaemonSet 运行在每个 Spot 节点
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: spot-termination-handler
spec:
template:
spec:
containers:
- name: handler
image: myregistry/spot-handler:v1
env:
- name: PROVIDER
value: "aliyun" # 或 aws, tencent, huawei
securityContext:
privileged: true # 需访问宿主机元数据
volumeMounts:
- name: host-root
mountPath: /host
volumes:
- name: host-root
hostPath:
path: /
Handler 逻辑:
- 轮询元数据接口(如
http://100.100.100.200/latest/meta-data/instance/spot/termination-time) - 检测到终止时间 → 给 Node 打 Taint
node.kubernetes.io/spot-terminating:NoExecute+ Labelspot-terminating="true" - 触发 Pod 驱逐 → Pod
preStop钩子执行任务检查点保存/流迁移 - 等待宽限期结束 → 节点自动关机
方案 B:KubeSpotter / AWS Node Termination Handler (开源组件)
直接部署社区成熟组件,支持钩子脚本自定义:
# AWS NTH 配置示例
--enable-spot-interruption-draining=true
--pod-termination-grace-period=120
--pre-drain-script=/scripts/save_checkpoint.sh
3.3 任务级检查点与断点续传设计
应用层必须配合:
- 转码任务:FFmpeg
-segment_time切片 + 进度上报 Redis,重调度时从最后完成切片继续 - 直播转码:无状态,直接杀掉重拉流,依赖 HPA 秒级补齐副本
- 文件分发:对象存储分片上传,支持断点续传
成本收益测算:某头部短视频平台引入 Spot 混部后,转码单位成本从 ¥0.12/分钟 降至 ¥0.035/分钟,年节省算力成本超 800 万元,抢占导致的任务重试率 < 0.5%。
四、 流量治理与扩容协同:从“被动响应”到“主动感知”
传统 HPA 基于节点侧指标反向推演,存在固有滞后。引入 Ingress/Service Mesh 层面的流量信号,实现“流量预判 -> 提前扩容”。
4.1 入口网关指标直连 HPA
利用 NGINX Ingress / APISIX / Envoy 暴露的实时流量指标,绕过业务进程采集延迟。
# PrometheusRule: 从 Ingress 聚合流量指标
groups:
- name: ingress-media-metrics
rules:
- expr: |
sum(rate(nginx_ingress_controller_requests{service=~"media-.*"}[30s])) by (service, namespace)
record: media:ingress:qps_per_service
- expr: |
histogram_quantile(0.99, sum(rate(nginx_ingress_controller_request_duration_seconds_bucket{service=~"media-.*"}[1m])) by (le, service))
record: media:ingress:p99_latency
HPA 直接消费 media:ingress:qps_per_service:
- 优势:流量进来第一时间感知,比业务进程上报快 10-30 秒
- 适用:拉流分发、API 网关、信令服务等入口型媒体节点
4.2 影子流量验证扩容有效性
新版本/新节点池上线前,用 Mirror 流量 验证扩容性能,避免扩容后引入新故障。
# APISIX / NGINX 配置片段:镜像 10% 流量到 Canary 节点池
plugin_config:
traffic-split:
rules:
- match:
- vars: [["arg_canary", "==", "true"]]
weighted_upstreams:
- upstream_id: media-transcoder-stable
weight: 90
- upstream_id: media-transcoder-canary
weight: 10
mirror:
upstream_id: media-transcoder-canary
percentage: 10 # 复制 10% 请求到新节点池,不阻塞主流程
验证指标:Canary 池 active_streams、gpu_mem_usage_pct、error_rate 是否在阈值内。通过则全量切流,失败则回滚并触发告警。
4.3 熔断降级与扩容联动
当下游存储/CDN/数据库成为瓶颈时,盲目扩容转码节点只会加剧雪崩。
# HPA 扩容前置条件:依赖服务健康度检查
# 通过自定义 Metrics Server 扩展或 KEDA ScaledObject 实现
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: media-transcoder-keda
spec:
scaleTargetRef:
name: media-transcoder
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: media_active_streams
query: |
sum(media_node_active_streams) by (namespace)
/
sum(media_node_active_streams{job="media-transcoder"}) by (namespace)
threshold: "50"
authModes: "basic"
# 熔断门控:仅当下游存储 P99 延迟 < 500ms 时允许扩容
- type: external
metadata:
targetValue: "1"
scalerAddress: http://dependency-health-checker:8080/check/storage
依赖健康检查器逻辑:
- 定期探测 MinIO/Ceph/CDN 写入延迟、错误率
- 返回
1=健康允许扩容,0=不健康冻结扩容(保持当前副本) - 配合
behavior.scaleUp.selectPolicy: Min实现“依赖不健康则不扩容”
五、 多集群/多地域弹性:联邦调度与数据亲和
当单集群 GPU 资源上限、或需就近接入(降低首屏延迟)时,需跨集群调度。
5.1 Karmada / Cluster Federation 架构
用户请求 -> 全局负载均衡 (GTM/GSLB) -> 就近地域集群
|
┌─────────────────┼─────────────────┐
▼ ▼ ▼
集群 A (华东) 集群 B (华南) 集群 C (海外)
HPA 本地扩容 HPA 本地扩容 HPA 本地扩容
│ │ │
└─────────────────┼─────────────────┘
▼
Karmada 控制面 (资源聚合视图)
- 跨集群 Pod 调度策略
- 统一 HPA 联邦资源视图
5.2 跨集群扩容策略:PropagationPolicy + OverridePolicy
# Karmada: 定义媒体转码工作负载跨集群分发策略
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
name: media-transcoder-pp
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: media-transcoder
placement:
clusterAffinity:
clusterNames: ["cluster-east", "cluster-south", "cluster-oversea"]
replicaScheduling: Weighted
replicaSchedulingType: Divided
weightPreference:
staticWeightList:
- targetCluster:
clusterNames: ["cluster-east"]
weight: 50
- targetCluster:
clusterNames: ["cluster-south"]
weight: 30
- targetCluster:
clusterNames: ["cluster-oversea"]
weight: 20
---
# OverridePolicy: 不同地域集群差异化配置 (如 GPU 型号、镜像仓库)
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
name: media-transcoder-overrides
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: media-transcoder
overriders:
plaintext:
- clusterNames: ["cluster-oversea"]
patcher: json
patch: |
[
{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "registry.intl.example.com/media-transcoder:v1.2.0"},
{"op": "replace", "path": "/spec/template/spec/nodeSelector/nvidia.com/gpu.product", "value": "A10G"}
]
5.3 数据亲和性约束
媒体任务依赖源视频文件(对象存储/NAS),跨地域拉取带宽成本高、延迟大。
调度约束:
- 输入数据就近原则:任务调度到源桶同地域集群(通过
nodeAffinity匹配topology.kubernetes.io/region) - 输出数据预热:转码完成后,异步推流至 CDN 边缘或跨地域复制,不阻塞转码流程
- 状态存储分离:Redis/Etcd 仅存任务元数据(KB 级),不存媒体流数据,规避跨集群状态同步复杂度
六、 混沌工程验证:在生产环境“演练”弹性韧性
体系建设完成后,必须通过自动化混沌演练持续验证,而非等待真实故障暴露问题。
6.1 核心演练场景矩阵
| 场景编号 | 故障注入点 | 预期系统行为 | 验证指标 | 频次 |
|---|---|---|---|---|
| CHAOS-HPA-01 | 模拟流量突增 300% (流量回放/压测) | HPA 60s 内扩容至目标副本,新 Pod 30s 内 Ready 承载流量 | 扩容延迟 P99 < 90s、零丢流、零报错 | 每周 |
| CHAOS-HPA-02 | Prometheus Adapter 故障/指标采集中断 | HPA 维持原副本数不缩容,告警触发,人工介入 | 无误缩容、告警触达 < 1min | 每月 |
| CHAOS-SPOT-01 | 模拟 Spot 实例收到抢占通知 (Mock 元数据) | Node 打污点 -> Pod 优雅下线 -> 任务检查点保存 -> HPA 在其他池补齐副本 | 任务重试率 < 1%、无数据丢失 | 每周 |
| CHAOS-GPU-01 | 物理 GPU 硬件故障 / Xid Error 注入 | Node Problem Detector 上报 -> Node NotReady -> Pod 驱逐 -> CA 扩容新节点 | 故障发现 < 30s、服务恢复 < 3min | 每季度 |
| CHAOS-DEP-01 | 下游存储/数据库延迟注入 2s | 熔断生效 -> HPA 冻结扩容 -> 现有 Pod 触发降级逻辑 (如仅转码不入库) | 无级联故障、核心链路可用 | 每月 |
6.2 演练自动化流水线
# .gitlab-ci.yml / GitHub Actions 片段
stages:
- validate
- chaos
- report
chaos_weekly:
stage: chaos
image: chaosiq/chaostoolkit:latest
script:
- chaosiq login $CHAOSIQ_TOKEN
- chaos run experiments/hpa_scale_up.yaml --settings=env=prod
- chaos run experiments/spot_termination.yaml --settings=env=prod
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" # 仅定时触发
artifacts:
reports:
junit: chaos-report.xml
演练红线:任何演练导致核心业务 SLA 下跌(如卡顿率 > 1%、转码失败率 > 0.1%)立即自动熔停并回滚,生成复盘报告。
七、 合规与审计:扩容过程的安全合规闭环
媒体业务常涉及版权内容、用户隐私,扩容过程必须满足等保三级、GDPR、内容安全合规要求。
7.1 镜像供应链安全
- 镜像签名验证:部署
cosign+sigstore+Kyverno/Gatekeeper准入策略,仅允许通过 CI/CD 签名的镜像运行 - SBOM 生成:构建流水线强制生成
SPDX/CycloneDX格式 SBOM,入库备查 - 漏洞扫描阻断:
Trivy/Grype扫描 High/Critical 漏洞阻断发布,媒体库(FFmpeg/x264)需重点关注 CVE
# Kyverno ClusterPolicy: 验证镜像签名
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-cosign-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- image: "registry.example.com/media/*"
key: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
7.2 扩容审计日志留存
所有扩缩容事件、调度决策、镜像拉取、特权操作需全链路审计:
# 审计策略示例 (audit-policy.yaml)
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: autoscaling
resources: [horizontalpodautoscalers]
- group: ""
resources: [pods, nodes, events]
- group: apps
resources: [deployments, replicasets]
- level: RequestResponse
users: ["system:serviceaccount:kube-system:cluster-autoscaler"]
resources:
- group: ""
resources: [nodes, pods/eviction]
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
resources:
- group: ""
resources: [secrets, configmaps] # 敏感配置变更
日志归档:审计日志实时流式写入 不可篡改存储(WORM 对象存储/合规日志服务),保留 ≥ 6 年,支持合规溯源。
八、 总结:构建可进化的媒体弹性基因
| 演进阶段 | 核心能力 | 关键技术标志 | 业务价值 |
|---|---|---|---|
| L1 基础可用 | 多指标 HPA + 基础监控 | Custom Metrics API、PDB、启动探针 | 解决“会扩容”,告别人工扩缩容 |
| L2 稳定高效 | 立体弹性 + 优雅下线 + Spot 混部 | HPA/VPA/CA 联动、HAMi/MIG、Spot 钩子 | 资源利用率 ↑ 2 倍,成本 ↓ 60%+ |
| L3 智能预测 | 流量感知 + 预测性扩容 + 熔断联动 | Ingress 指标直连、KEDA、依赖健康门控 | 扩容提前 2-5 分钟,零雪崩 |
| L4 多活联邦 | 跨集群调度 + 数据亲和 + 全球就近 | Karmada、GTM、跨地域存储分层 | 单集群上限突破、灾备 RPO=0 |
| L5 自进化 | 混沌验证常态化 + 合规内生 + 自愈 | Chaos Mesh、Kyverno、GitOps 闭环 | 系统抗脆弱,合规零违规 |
给架构师的三条建议:
- 指标先行,业务定义:不要为监控而监控,每个自定义指标必须对应明确的扩容触发阈值与业务 SLA 映射关系。
- 缩容即风控:扩容是成本,缩容是风险。投入 80% 精力打磨“优雅下线、任务迁移、PDB 兜底、Spot 驱逐”,而非单纯追求扩容速度。
- 体系化建设,拒绝点状工具:HPA 只是执行器,指标体系、调度拓扑、镜像管线、流量治理、混沌验证、合规审计才是弹性体系的骨架与血肉。
通过将上述进阶能力沉淀为平台化能力(Internal Developer Platform),让业务研发通过简单的 MediaAutoscaler CRD 即可声明式享受企业级弹性红利,才是媒体基础设施团队的终极交付物。
版权声明:本文为技术经验分享,所述方案需结合具体云厂商特性(如 AWS EFA、阿里云 ACS、腾讯云 TKE Serverless)、硬件驱动版本、业务代码架构进行落地验证。文中提及的开源组件版本、参数配置随社区演进可能变更,生产使用前请以官方文档最新版为准。
