轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • 前端框架
  • 前端工程化
  • 浏览器与Web API
  • 架构设计与模式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • 前端框架
  • 前端工程化
  • 浏览器与Web API
  • 架构设计与模式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 架构设计与模式

    • 高可用-分布式基础之CAP理论
    • 高可用-服务容错与降级策略
    • 高可用-故障检测与自动恢复
    • 高可用-混沌工程实践
    • 高可用-分布式事务实战
    • 高可用-多活与容灾架构设计
    • 高性能-缓存架构设计
    • 高性能-性能优化方法论
    • 高性能-异步处理与消息队列
    • 高性能-数据库性能优化
    • 高性能-负载均衡策略详解
    • 高性能-CDN与静态资源优化
    • 高扩展-水平扩展vs垂直扩展
    • 高扩展-无状态服务设计
    • 高扩展-微服务架构演进
    • 高扩展-弹性伸缩设计
      • 一、弹性伸缩的价值
        • 1、业务流量的波动性
        • 2、传统固定容量的问题
        • 3、弹性伸缩的优势
      • 二、弹性伸缩的核心概念
        • 1、伸缩维度
        • 2、伸缩指标
        • 3、伸缩策略
        • 4、伸缩边界
      • 三、Kubernetes 弹性伸缩
        • 1、HPA (Horizontal Pod Autoscaler)
        • 2、VPA (Vertical Pod Autoscaler)
        • 3、CA (Cluster Autoscaler)
        • 4、KEDA (Kubernetes Event-driven Autoscaling)
      • 四、应用层弹性伸缩
        • 1、Spring Boot 应用优化
        • 2、连接池动态调整
        • 3、预热机制
      • 五、云平台弹性伸缩
        • 1、AWS Auto Scaling
        • 2、阿里云弹性伸缩
        • 3、腾讯云弹性伸缩
      • 六、伸缩策略最佳实践
        • 1、指标选择
        • 2、伸缩参数调优
        • 3、多指标组合
        • 4、防抖动设计
      • 七、成本优化
        • 1、竞价实例
        • 2、按优先级伸缩
        • 3、多层级伸缩
      • 八、监控与调优
        • 1、关键监控指标
        • 2、Grafana 仪表盘
        • 3、告警规则
        • 4、调优建议
      • 九、总结
        • 弹性伸缩的核心价值
        • 实施路径
        • 关键最佳实践
        • 常见误区
  • 代码质量管理

  • 基础

  • 操作系统

  • 计算机网络

  • AI

  • 编程范式

  • 安全

  • 中间件

  • 心得

  • 架构
  • 架构设计与模式
轩辕李
2025-06-05
目录

高扩展-弹性伸缩设计

在云原生时代,弹性伸缩(Elastic Scaling)是应对流量波动、提升资源利用率的核心能力。通过自动增减服务实例,系统可以在流量高峰时快速扩容,在低谷时缩容节省成本,真正实现按需使用资源。

据统计,合理的弹性伸缩策略可以将资源利用率从 30% 提升到 70%,成本降低 40% 以上。本文将深入探讨弹性伸缩的设计原理、实现方式和最佳实践。

# 一、弹性伸缩的价值

# 1、业务流量的波动性

典型流量波动场景:

电商促销:

平时流量:     1000 QPS
双11零点:    50000 QPS (50倍)
促销结束后:   2000 QPS

新闻资讯:

平时流量:     5000 QPS
突发热点:    100000 QPS (20倍)
热点消退:     6000 QPS

在线教育:

工作日白天:   2000 QPS
晚上8-10点:  20000 QPS (10倍)
凌晨时段:     200 QPS

# 2、传统固定容量的问题

按峰值规划容量:

┌────────────────────────────────────┐
│ 固定容量: 100台服务器                │
│                                    │
│ ▲ 流量                             │
│ │     峰值                          │
│ │      ▲                           │
│ │     ╱ ╲                          │
│ │    ╱   ╲                         │
│ │───╱─────╲─── 容量线 (浪费)        │
│ │  ╱       ╲                       │
│ │_╱_________╲________________► 时间│
└────────────────────────────────────┘

问题:
- 平时资源利用率低(30%)
- 成本浪费严重
- 固定成本无法优化

按平均容量规划:

┌────────────────────────────────────┐
│ 固定容量: 30台服务器                 │
│                                    │
│ ▲ 流量                             │
│ │      峰值(超载)                   │
│ │       ▲                          │
│ │      ╱ ╲                         │
│ │─────╱───╲───── 容量线            │
│ │    ╱     ╲                       │
│ │___╱_______╲_______________► 时间 │
└────────────────────────────────────┘

问题:
- 峰值时系统过载
- 用户体验差
- 可能导致服务崩溃

# 3、弹性伸缩的优势

按需伸缩:

┌────────────────────────────────────┐
│ 弹性容量: 10-100台                  │
│                                    │
│ ▲ 流量与容量                        │
│ │       ▲                          │
│ │      ╱█╲  ← 容量跟随流量          │
│ │     ╱███╲                        │
│ │    ╱█████╲                       │
│ │___╱███████╲_______________► 时间 │
│   流量曲线                          │
└────────────────────────────────────┘

优势:
✅ 资源利用率高(70%+)
✅ 成本优化(降低40%)
✅ 性能稳定
✅ 自动化运维

成本对比:

策略 服务器数 月成本 利用率 峰值性能
按峰值固定 100台 ¥50万 30% 优秀
按平均固定 30台 ¥15万 80% 差(过载)
弹性伸缩 平均50台 ¥25万 70% 优秀

弹性伸缩在保证性能的同时,成本降低 50%。

# 二、弹性伸缩的核心概念

# 1、伸缩维度

水平伸缩(Horizontal Scaling):

  • 增加或减少实例数量
  • 适用于无状态服务
  • 是弹性伸缩的主要方式
扩容: [实例1] [实例2] → [实例1] [实例2] [实例3] [实例4]
缩容: [实例1] [实例2] [实例3] [实例4] → [实例1] [实例2]

垂直伸缩(Vertical Scaling):

  • 调整单个实例的资源配置
  • 需要重启实例
  • 较少用于自动伸缩
扩容: 2核4G → 4核8G
缩容: 4核8G → 2核4G

# 2、伸缩指标

资源指标:

  • CPU 使用率: 最常用的指标
  • 内存使用率: 内存密集型应用
  • 网络带宽: 带宽敏感型应用
  • 磁盘 I/O: I/O 密集型应用

业务指标:

  • QPS(每秒请求数): 流量型应用
  • 并发连接数: WebSocket 等长连接
  • 消息队列长度: 异步处理场景
  • 响应时间: 性能敏感型应用

自定义指标:

  • 订单数量: 电商场景
  • 在线用户数: 社交应用
  • 转码任务数: 视频处理

# 3、伸缩策略

目标追踪(Target Tracking):

  • 维持指标在目标值附近
  • 最常用的策略
目标: CPU 使用率 70%
当前: CPU 使用率 85% → 扩容
当前: CPU 使用率 40% → 缩容

步进伸缩(Step Scaling):

  • 根据指标区间执行不同动作
  • 更精细的控制
CPU < 30%:  缩容 2 个实例
30% ≤ CPU < 50%: 缩容 1 个实例
50% ≤ CPU < 70%: 不变
70% ≤ CPU < 85%: 扩容 1 个实例
CPU ≥ 85%:  扩容 3 个实例

定时伸缩(Scheduled Scaling):

  • 按时间计划伸缩
  • 适合可预测的流量模式
周一至周五:
  08:00 扩容到 50 个实例
  20:00 缩容到 20 个实例

周末:
  全天保持 10 个实例

预测性伸缩(Predictive Scaling):

  • 基于历史数据预测未来流量
  • 提前扩容,避免流量突增
分析过去 7 天的流量模式:
→ 预测明天 20:00 会有流量高峰
→ 提前 10 分钟扩容

# 4、伸缩边界

最小实例数(Min Instances):

  • 保证基本可用性
  • 避免缩容到零导致冷启动
minReplicas: 2  # 至少保持 2 个实例

最大实例数(Max Instances):

  • 控制成本上限
  • 避免无限扩容
maxReplicas: 100  # 最多 100 个实例

缩容冷却时间(Scale Down Cooldown):

  • 缩容后等待一段时间再次评估
  • 避免频繁缩容
scaleDownStabilizationWindowSeconds: 300  # 5分钟

扩容冷却时间(Scale Up Cooldown):

  • 扩容后等待新实例就绪
  • 避免频繁扩容
scaleUpStabilizationWindowSeconds: 60  # 1分钟

# 三、Kubernetes 弹性伸缩

# 1、HPA (Horizontal Pod Autoscaler)

基于 CPU 的 HPA:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # 目标 CPU 70%
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # 缩容冷却 5 分钟
      policies:
      - type: Percent
        value: 50  # 每次最多缩容 50%
        periodSeconds: 60
      - type: Pods
        value: 2   # 每次最多缩容 2 个 Pod
        periodSeconds: 60
      selectPolicy: Min  # 选择最保守的策略
    scaleUp:
      stabilizationWindowSeconds: 60  # 扩容冷却 1 分钟
      policies:
      - type: Percent
        value: 100  # 每次最多扩容 100%
        periodSeconds: 30
      - type: Pods
        value: 5    # 每次最多扩容 5 个 Pod
        periodSeconds: 30
      selectPolicy: Max  # 选择最激进的策略

基于多指标的 HPA:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3
  maxReplicas: 50
  metrics:
  # CPU 指标
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  
  # 内存指标
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  
  # QPS 指标(自定义指标)
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "1000"  # 每个 Pod 平均 1000 QPS
  
  # 响应时间指标
  - type: Pods
    pods:
      metric:
        name: http_request_duration_p95
      target:
        type: AverageValue
        averageValue: "200m"  # P95 响应时间 200ms

计算公式:

期望副本数 = ceil[当前副本数 × (当前指标值 / 目标指标值)]

示例:
当前副本数: 10
当前 CPU: 140%
目标 CPU: 70%

期望副本数 = ceil[10 × (140 / 70)] = ceil[20] = 20

需要扩容到 20 个副本

# 2、VPA (Vertical Pod Autoscaler)

VPA 配置:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-app-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  updatePolicy:
    updateMode: "Auto"  # Auto: 自动更新, Recreate: 重建, Initial: 仅初始化, Off: 仅推荐
  resourcePolicy:
    containerPolicies:
    - containerName: web-app
      minAllowed:
        cpu: 100m
        memory: 128Mi
      maxAllowed:
        cpu: 2000m
        memory: 2Gi
      controlledResources: ["cpu", "memory"]

VPA 推荐结果:

# VPA 分析后的推荐配置
status:
  recommendation:
    containerRecommendations:
    - containerName: web-app
      lowerBound:    # 最低建议
        cpu: 250m
        memory: 256Mi
      target:        # 推荐值
        cpu: 500m
        memory: 512Mi
      uncappedTarget: # 无上限推荐
        cpu: 800m
        memory: 1Gi
      upperBound:    # 最高建议
        cpu: 1000m
        memory: 1.5Gi

# 3、CA (Cluster Autoscaler)

集群级别自动扩缩容:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cluster-autoscaler
  namespace: kube-system
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: cluster-autoscaler
        image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.27.0
        command:
        - ./cluster-autoscaler
        - --v=4
        - --stderrthreshold=info
        - --cloud-provider=aws
        - --skip-nodes-with-local-storage=false
        - --expander=least-waste
        - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/my-cluster
        - --balance-similar-node-groups
        - --skip-nodes-with-system-pods=false
        - --scale-down-enabled=true
        - --scale-down-delay-after-add=10m
        - --scale-down-unneeded-time=10m
        - --max-node-provision-time=15m

工作原理:

Pod 调度失败(资源不足)
       ↓
Cluster Autoscaler 检测到
       ↓
向云平台申请新节点
       ↓
新节点加入集群
       ↓
Pod 调度到新节点

节点利用率低
       ↓
Cluster Autoscaler 检测到
       ↓
驱逐 Pod 到其他节点
       ↓
删除空闲节点
       ↓
释放云资源

# 4、KEDA (Kubernetes Event-driven Autoscaling)

基于消息队列的伸缩:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-processor-scaler
spec:
  scaleTargetRef:
    name: order-processor
  minReplicaCount: 1
  maxReplicaCount: 50
  pollingInterval: 30  # 检查间隔 30 秒
  cooldownPeriod: 300  # 冷却时间 5 分钟
  triggers:
  # RabbitMQ 触发器
  - type: rabbitmq
    metadata:
      queueName: orders
      queueLength: "10"  # 每个 Pod 处理 10 条消息
      host: amqp://guest:guest@rabbitmq:5672/
  
  # Kafka 触发器
  - type: kafka
    metadata:
      bootstrapServers: kafka:9092
      consumerGroup: order-processor
      topic: orders
      lagThreshold: "100"  # lag 超过 100 则扩容

基于 HTTP 请求的伸缩:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: api-scaler
spec:
  scaleTargetRef:
    name: api-server
  minReplicaCount: 2
  maxReplicaCount: 100
  triggers:
  # Prometheus 查询
  - type: prometheus
    metadata:
      serverAddress: http://prometheus:9090
      metricName: http_requests_per_second
      query: sum(rate(http_requests_total[1m]))
      threshold: "1000"  # 总 QPS 超过 1000 则扩容

基于 Cron 的定时伸缩:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: scheduled-scaler
spec:
  scaleTargetRef:
    name: web-app
  minReplicaCount: 2
  maxReplicaCount: 100
  triggers:
  # 工作日高峰期
  - type: cron
    metadata:
      timezone: Asia/Shanghai
      start: 0 9 * * 1-5   # 周一到周五 9:00
      end: 0 22 * * 1-5    # 周一到周五 22:00
      desiredReplicas: "50"
  
  # 周末
  - type: cron
    metadata:
      timezone: Asia/Shanghai
      start: 0 0 * * 0,6   # 周末全天
      end: 0 0 * * 0,6
      desiredReplicas: "10"

# 四、应用层弹性伸缩

# 1、Spring Boot 应用优化

快速启动配置:

# application.yml
spring:
  main:
    lazy-initialization: true  # 懒加载,加快启动
  
  jpa:
    hibernate:
      ddl-auto: none  # 生产环境不自动建表
    open-in-view: false  # 关闭 OSIV
  
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      initialization-fail-timeout: -1  # 启动时数据库不可用不报错

# JVM 参数优化
JAVA_OPTS: >
  -Xms512m -Xmx1024m
  -XX:+UseG1GC
  -XX:MaxGCPauseMillis=200
  -XX:+ParallelRefProcEnabled
  -XX:InitiatingHeapOccupancyPercent=70
  -Djava.security.egd=file:/dev/./urandom

健康检查接口:

/**
 * 健康检查配置
 */
@Configuration
public class HealthCheckConfig {
    
    @Bean
    public HealthIndicator customHealthIndicator() {
        return new AbstractHealthIndicator() {
            @Override
            protected void doHealthCheck(Health.Builder builder) {
                // 快速健康检查,避免阻塞
                builder.up()
                       .withDetail("status", "UP")
                       .withDetail("timestamp", System.currentTimeMillis());
            }
        };
    }
}
# Kubernetes Probe 配置
livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 20
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 3

优雅启动与关闭:

/**
 * 优雅关闭配置
 */
@Configuration
public class GracefulShutdownConfig {
    
    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown();
    }
    
    @Bean
    public ServletWebServerFactory servletContainer(GracefulShutdown gracefulShutdown) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers(gracefulShutdown);
        return factory;
    }
    
    private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
        
        private volatile Connector connector;
        
        @Override
        public void customize(Connector connector) {
            this.connector = connector;
        }
        
        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            if (this.connector == null) {
                return;
            }
            
            // 停止接收新请求
            this.connector.pause();
            
            Executor executor = this.connector.getProtocolHandler().getExecutor();
            if (executor instanceof ThreadPoolExecutor) {
                try {
                    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                    threadPoolExecutor.shutdown();
                    
                    // 等待现有请求处理完成(最多 30 秒)
                    if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
                        log.warn("Tomcat 线程池未在 30 秒内完成关闭");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
}
# application.yml
server:
  shutdown: graceful  # Spring Boot 2.3+ 支持优雅关闭

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 关闭超时时间

# 2、连接池动态调整

数据库连接池:

/**
 * 动态数据库连接池
 */
@Configuration
public class DynamicDataSourceConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public HikariConfig hikariConfig() {
        HikariConfig config = new HikariConfig();
        
        // 基于副本数动态调整连接池大小
        int replicas = getReplicaCount();
        int maxPoolSize = Math.max(10, 200 / replicas);  // 总共 200 个连接
        
        config.setMaximumPoolSize(maxPoolSize);
        config.setMinimumIdle(maxPoolSize / 4);
        
        return config;
    }
    
    private int getReplicaCount() {
        // 从环境变量或配置中心获取当前副本数
        String replicas = System.getenv("REPLICA_COUNT");
        return replicas != null ? Integer.parseInt(replicas) : 10;
    }
}

Redis 连接池:

/**
 * 动态 Redis 连接池
 */
@Configuration
public class RedisConfig {
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        int replicas = getReplicaCount();
        int maxActive = Math.max(20, 500 / replicas);
        
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(maxActive);
        poolConfig.setMaxIdle(maxActive / 2);
        poolConfig.setMinIdle(maxActive / 4);
        poolConfig.setMaxWaitMillis(3000);
        
        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .poolConfig(poolConfig)
                .build();
        
        RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration();
        serverConfig.setHostName("redis-server");
        serverConfig.setPort(6379);
        
        return new LettuceConnectionFactory(serverConfig, clientConfig);
    }
}

# 3、预热机制

应用预热:

/**
 * 应用启动预热
 */
@Component
@Slf4j
public class ApplicationWarmup implements ApplicationRunner {
    
    @Autowired
    private CacheManager cacheManager;
    
    @Autowired
    private UserService userService;
    
    @Override
    public void run(ApplicationArguments args) {
        log.info("开始应用预热...");
        
        // 1. 预加载热点数据到缓存
        warmupCache();
        
        // 2. 预编译正则表达式
        warmupRegex();
        
        // 3. 初始化连接池
        warmupConnectionPools();
        
        log.info("应用预热完成");
    }
    
    private void warmupCache() {
        // 加载热门商品到缓存
        List<Long> hotProductIds = Arrays.asList(1L, 2L, 3L, 100L, 200L);
        for (Long productId : hotProductIds) {
            try {
                productService.getById(productId);
            } catch (Exception e) {
                log.warn("预加载商品失败: {}", productId, e);
            }
        }
    }
    
    private void warmupRegex() {
        // 预编译常用正则表达式
        Pattern.compile("^[a-zA-Z0-9]+$");
        Pattern.compile("^\\d{11}$");
        Pattern.compile("^[\\u4e00-\\u9fa5]+$");
    }
    
    private void warmupConnectionPools() {
        // 提前建立数据库连接
        try {
            jdbcTemplate.queryForObject("SELECT 1", Integer.class);
        } catch (Exception e) {
            log.warn("预热数据库连接失败", e);
        }
        
        // 提前建立 Redis 连接
        try {
            redisTemplate.opsForValue().get("warmup");
        } catch (Exception e) {
            log.warn("预热 Redis 连接失败", e);
        }
    }
}

Kubernetes PreStop Hook:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  template:
    spec:
      containers:
      - name: web-app
        image: myapp:v1.0
        lifecycle:
          # 启动后预热
          postStart:
            exec:
              command:
              - /bin/sh
              - -c
              - |
                # 等待应用启动
                sleep 10
                # 调用预热接口
                curl -X POST http://localhost:8080/actuator/warmup
          
          # 关闭前通知
          preStop:
            exec:
              command:
              - /bin/sh
              - -c
              - |
                # 等待 30 秒,让负载均衡器摘除此实例
                sleep 30

# 五、云平台弹性伸缩

# 1、AWS Auto Scaling

EC2 Auto Scaling Group:

{
  "AutoScalingGroupName": "web-app-asg",
  "MinSize": 3,
  "MaxSize": 50,
  "DesiredCapacity": 10,
  "DefaultCooldown": 300,
  "HealthCheckType": "ELB",
  "HealthCheckGracePeriod": 300,
  "LaunchTemplate": {
    "LaunchTemplateId": "lt-1234567890abcdef0",
    "Version": "$Latest"
  },
  "TargetGroupARNs": [
    "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/web-app-tg/50dc6c495c0c9188"
  ],
  "VPCZoneIdentifier": "subnet-12345678,subnet-87654321"
}

Target Tracking Scaling Policy:

{
  "PolicyName": "target-tracking-cpu",
  "PolicyType": "TargetTrackingScaling",
  "TargetTrackingConfiguration": {
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "ASGAverageCPUUtilization"
    },
    "TargetValue": 70.0
  }
}

Step Scaling Policy:

{
  "PolicyName": "step-scaling-cpu",
  "PolicyType": "StepScaling",
  "AdjustmentType": "PercentChangeInCapacity",
  "MetricAggregationType": "Average",
  "StepAdjustments": [
    {
      "MetricIntervalLowerBound": 0.0,
      "MetricIntervalUpperBound": 10.0,
      "ScalingAdjustment": 10
    },
    {
      "MetricIntervalLowerBound": 10.0,
      "MetricIntervalUpperBound": 20.0,
      "ScalingAdjustment": 20
    },
    {
      "MetricIntervalLowerBound": 20.0,
      "ScalingAdjustment": 30
    }
  ]
}

Scheduled Scaling:

{
  "ScheduledActionName": "scale-up-morning",
  "Schedule": "0 8 * * 1-5",
  "MinSize": 20,
  "MaxSize": 50,
  "DesiredCapacity": 30
}

# 2、阿里云弹性伸缩

伸缩组配置:

# 伸缩组
ScalingGroupId: asg-bp1igpak5ft1flyp****
ScalingGroupName: web-app-scaling-group
MinSize: 3
MaxSize: 50
DefaultCooldown: 300
RemovalPolicies:
  - OldestInstance
  - OldestScalingConfiguration
HealthCheckType: ECS
LoadBalancerIds:
  - lb-bp1hoxb3i7i5jk6mk****
VServerGroups:
  - LoadBalancerId: lb-bp1hoxb3i7i5jk6mk****
    VServerGroupAttributes:
      - VServerGroupId: rsp-bp1jp1rge7f71o0j****
        Port: 80

目标追踪规则:

ScalingRuleName: target-tracking-cpu
ScalingRuleType: TargetTrackingScalingRule
MetricName: CpuUtilization
TargetValue: 70
EstimatedInstanceWarmup: 300

简单规则:

ScalingRuleName: add-3-instances
ScalingRuleType: SimpleScalingRule
AdjustmentType: QuantityChangeInCapacity
AdjustmentValue: 3
Cooldown: 180

定时任务:

ScheduledTaskName: scale-up-morning
ScheduledAction: arn:acs:ess:cn-hangzhou:123456789:scalingrule/asr-bp1****
RecurrenceType: Cron
RecurrenceValue: 0 8 * * 1-5
LaunchTime: 2024-01-01T08:00:00Z
LaunchExpirationTime: 600
MinValue: 30
MaxValue: 50
DesiredCapacity: 40

# 3、腾讯云弹性伸缩

伸缩组:

{
  "AutoScalingGroupName": "web-app-asg",
  "LaunchConfigurationId": "asc-abc123",
  "MaxSize": 50,
  "MinSize": 3,
  "DesiredCapacity": 10,
  "DefaultCooldown": 300,
  "LoadBalancerIds": ["lb-abc123"],
  "VpcId": "vpc-abc123",
  "SubnetIds": ["subnet-abc123", "subnet-def456"],
  "TerminationPolicies": ["OLDEST_INSTANCE"]
}

# 六、伸缩策略最佳实践

# 1、指标选择

不同场景的指标选择:

场景 主要指标 辅助指标 目标值
Web 应用 CPU 使用率 QPS、响应时间 CPU 70%
API 服务 QPS CPU、并发连接数 1000 QPS/Pod
消息处理 队列长度 CPU、内存 10 msg/Pod
视频转码 CPU 使用率 任务队列长度 CPU 80%
数据库 连接数 CPU、IOPS 100 conn/实例
缓存服务 内存使用率 QPS、命中率 Memory 70%

# 2、伸缩参数调优

扩容策略:

# 激进扩容(适合流量突增场景)
scaleUp:
  stabilizationWindowSeconds: 30   # 短冷却时间
  policies:
  - type: Percent
    value: 100                     # 每次翻倍
    periodSeconds: 30
  selectPolicy: Max                # 选择最激进策略
# 保守扩容(适合稳定场景)
scaleUp:
  stabilizationWindowSeconds: 120  # 较长冷却时间
  policies:
  - type: Percent
    value: 25                      # 每次增加 25%
    periodSeconds: 60
  selectPolicy: Min                # 选择最保守策略

缩容策略:

# 保守缩容(推荐,避免频繁缩容)
scaleDown:
  stabilizationWindowSeconds: 300  # 5 分钟冷却
  policies:
  - type: Percent
    value: 10                      # 每次最多缩 10%
    periodSeconds: 120
  - type: Pods
    value: 1                       # 每次最多缩 1 个
    periodSeconds: 120
  selectPolicy: Min                # 选择最保守策略

# 3、多指标组合

组合策略示例:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 5
  maxReplicas: 100
  metrics:
  # 指标 1: CPU(权重最高)
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  
  # 指标 2: 内存(防止 OOM)
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  
  # 指标 3: QPS(业务指标)
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "800"
  
  # 指标 4: 响应时间(用户体验)
  - type: Pods
    pods:
      metric:
        name: http_request_duration_p95
      target:
        type: AverageValue
        averageValue: "200m"

# HPA 会选择所有指标计算的副本数中的最大值

计算逻辑:

当前状态:
- 10 个副本
- CPU: 140% (期望 70%) → 需要 20 个副本
- Memory: 60% (期望 80%) → 需要 8 个副本
- QPS: 10000 (期望 800/Pod) → 需要 13 个副本
- P95: 300ms (期望 200ms) → 需要 15 个副本

最终选择: max(20, 8, 13, 15) = 20 个副本

# 4、防抖动设计

问题: 指标在阈值附近波动,导致频繁扩缩容。

CPU 使用率:
71% → 扩容到 12 个副本
69% → 缩容到 10 个副本
71% → 扩容到 12 个副本
69% → 缩容到 10 个副本
...

解决方案 1: 设置缓冲区:

metrics:
- type: Resource
  resource:
    name: cpu
    target:
      type: Utilization
      averageUtilization: 65  # 目标值设置为 65%,而非 70%

# 这样:
# CPU > 70% 才扩容
# CPU < 60% 才缩容
# 60%-70% 之间不动作

解决方案 2: 延长稳定窗口:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 600  # 10 分钟内多次计算,取最大值

解决方案 3: 限制缩容速度:

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 1              # 每次只缩 1 个
      periodSeconds: 300    # 5 分钟缩一次

# 七、成本优化

# 1、竞价实例

AWS Spot Instances:

apiVersion: v1
kind: Node
metadata:
  labels:
    node.kubernetes.io/instance-type: spot
spec:
  taints:
  - key: spot
    value: "true"
    effect: NoSchedule
# Pod 配置容忍 Spot 实例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: batch-processor
spec:
  template:
    spec:
      tolerations:
      - key: spot
        operator: Equal
        value: "true"
        effect: NoSchedule
      
      # Spot 实例可能被回收,设置优雅终止
      terminationGracePeriodSeconds: 120
      
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            preference:
              matchExpressions:
              - key: node.kubernetes.io/instance-type
                operator: In
                values:
                - spot

混合使用按需和竞价实例:

# 核心服务使用按需实例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 10
  template:
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node.kubernetes.io/instance-type
                operator: NotIn
                values:
                - spot

---
# 批处理任务使用竞价实例
apiVersion: batch/v1
kind: Job
metadata:
  name: video-transcode
spec:
  template:
    spec:
      tolerations:
      - key: spot
        operator: Equal
        value: "true"
        effect: NoSchedule

# 2、按优先级伸缩

定义 PriorityClass:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000
globalDefault: false
description: "高优先级服务(核心业务)"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: medium-priority
value: 500
globalDefault: false
description: "中优先级服务(重要业务)"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority
value: 100
globalDefault: true
description: "低优先级服务(非核心业务)"

应用优先级:

# 核心 API 服务 - 高优先级
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 20
  template:
    spec:
      priorityClassName: high-priority
      containers:
      - name: api
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 1000m
            memory: 1Gi

---
# 数据分析服务 - 低优先级
apiVersion: apps/v1
kind: Deployment
metadata:
  name: analytics
spec:
  replicas: 5
  template:
    spec:
      priorityClassName: low-priority
      containers:
      - name: analytics
        resources:
          requests:
            cpu: 200m
            memory: 256Mi

资源不足时的驱逐顺序:

资源不足 → 优先驱逐低优先级 Pod → 释放资源给高优先级 Pod

# 3、多层级伸缩

三层伸缩架构:

# L1: 应用层 HPA(最快,秒级响应)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  minReplicas: 10
  maxReplicas: 100  # 受限于节点容量
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 70

---
# L2: 节点池 CA(中等速度,分钟级响应)
# 当 Pod pending 时,Cluster Autoscaler 自动扩容节点

---
# L3: 定时伸缩(预测性,提前准备)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: scheduled-scaler
spec:
  minReplicas: 50  # 提前扩容
  maxReplicas: 200
  # 通过外部控制器在流量高峰前调整 minReplicas

成本对比:

策略 月成本 资源利用率 响应速度
固定容量(按峰值) ¥100万 30% 无需响应
单层 HPA ¥60万 60% 秒级
HPA + CA ¥50万 70% 分钟级
HPA + CA + 定时 + Spot ¥35万 75% 混合

最优策略节省成本 65%。

# 八、监控与调优

# 1、关键监控指标

伸缩事件监控:

# HPA 伸缩事件
kube_hpa_status_current_replicas{hpa="web-app-hpa"}  # 当前副本数
kube_hpa_status_desired_replicas{hpa="web-app-hpa"} # 期望副本数
kube_hpa_spec_min_replicas{hpa="web-app-hpa"}       # 最小副本数
kube_hpa_spec_max_replicas{hpa="web-app-hpa"}       # 最大副本数

# 伸缩频率
rate(kube_hpa_status_desired_replicas[5m])

# Pod pending 时间
kube_pod_status_phase{phase="Pending"}

性能指标:

# CPU 使用率
100 * (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])))

# 内存使用率
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)

# Pod 启动时间
histogram_quantile(0.95, rate(kube_pod_start_duration_seconds_bucket[5m]))

成本指标:

# 实例数量
count(kube_node_info)

# 按实例类型统计成本
sum by (instance_type) (
  kube_node_labels * on(node) group_left(instance_type) 
  node_instance_cost
)

# 2、Grafana 仪表盘

HPA 监控面板:

{
  "dashboard": {
    "title": "HPA Monitoring",
    "panels": [
      {
        "title": "副本数变化",
        "targets": [
          {
            "expr": "kube_hpa_status_current_replicas{hpa='web-app-hpa'}",
            "legendFormat": "当前副本数"
          },
          {
            "expr": "kube_hpa_status_desired_replicas{hpa='web-app-hpa'}",
            "legendFormat": "期望副本数"
          }
        ]
      },
      {
        "title": "CPU 使用率",
        "targets": [
          {
            "expr": "100 * avg(rate(container_cpu_usage_seconds_total{pod=~'web-app-.*'}[5m]))",
            "legendFormat": "平均 CPU 使用率"
          }
        ]
      },
      {
        "title": "伸缩事件",
        "targets": [
          {
            "expr": "changes(kube_hpa_status_desired_replicas{hpa='web-app-hpa'}[1h])",
            "legendFormat": "伸缩次数"
          }
        ]
      }
    ]
  }
}

# 3、告警规则

Prometheus 告警:

groups:
- name: autoscaling-alerts
  rules:
  # HPA 达到上限
  - alert: HPAMaxedOut
    expr: |
      kube_hpa_status_current_replicas / kube_hpa_spec_max_replicas > 0.9
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "HPA 接近上限"
      description: "{{ $labels.hpa }} 当前副本数已达到最大值的 90%"
  
  # HPA 达到下限
  - alert: HPAMinedOut
    expr: |
      kube_hpa_status_current_replicas == kube_hpa_spec_min_replicas
    for: 30m
    labels:
      severity: info
    annotations:
      summary: "HPA 处于下限"
      description: "{{ $labels.hpa }} 长时间处于最小副本数,可能可以进一步缩容"
  
  # 频繁伸缩
  - alert: FrequentScaling
    expr: |
      changes(kube_hpa_status_desired_replicas[30m]) > 10
    labels:
      severity: warning
    annotations:
      summary: "HPA 频繁伸缩"
      description: "{{ $labels.hpa }} 30分钟内伸缩超过10次,可能需要调整参数"
  
  # Pod 启动慢
  - alert: SlowPodStartup
    expr: |
      histogram_quantile(0.95, rate(kube_pod_start_duration_seconds_bucket[10m])) > 120
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "Pod 启动时间过长"
      description: "P95 Pod 启动时间超过 2 分钟"

# 4、调优建议

场景 1: 频繁扩缩容

原因分析:

  • 指标在阈值附近波动
  • 冷却时间过短
  • 伸缩步长过大

解决方案:

# 调整前
metrics:
- type: Resource
  resource:
    name: cpu
    target:
      averageUtilization: 70
behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

# 调整后
metrics:
- type: Resource
  resource:
    name: cpu
    target:
      averageUtilization: 65  # 降低目标值,增加缓冲
behavior:
  scaleDown:
    stabilizationWindowSeconds: 300  # 延长冷却时间
    policies:
    - type: Pods
      value: 1
      periodSeconds: 120  # 减缓缩容速度

场景 2: 扩容不及时

原因分析:

  • 扩容策略过于保守
  • Pod 启动时间长
  • 节点资源不足

解决方案:

# 1. 调整扩容策略
behavior:
  scaleUp:
    stabilizationWindowSeconds: 30  # 缩短冷却时间
    policies:
    - type: Percent
      value: 100  # 激进扩容,每次翻倍
      periodSeconds: 30

# 2. 优化 Pod 启动
spec:
  containers:
  - name: app
    startupProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 5
      failureThreshold: 30

# 3. 启用 Cluster Autoscaler
# 确保有足够的节点资源

场景 3: 成本过高

原因分析:

  • 最小副本数设置过高
  • 长期处于高负载
  • 未使用竞价实例

解决方案:

# 1. 降低最小副本数
spec:
  minReplicas: 3  # 从 10 降到 3

# 2. 使用定时伸缩
# 低峰期进一步降低副本数

# 3. 使用竞价实例
# 非核心服务使用 Spot 实例

# 4. 启用 VPA 优化资源配置
# 避免资源浪费

# 九、总结

# 弹性伸缩的核心价值

  1. 成本优化: 按需使用资源,降低 40%-60% 成本
  2. 性能保障: 自动应对流量波动,保证用户体验
  3. 运维自动化: 减少人工干预,提升效率
  4. 资源利用率: 从 30% 提升到 70%+

# 实施路径

阶段 内容 难度
入门 基于 CPU 的 HPA 低
进阶 多指标 HPA + CA 中
高级 KEDA + 定时伸缩 + Spot 实例 高
专家 预测性伸缩 + 多层伸缩 很高

# 关键最佳实践

  1. ✅ 合理设置边界: minReplicas 保证可用性,maxReplicas 控制成本
  2. ✅ 延长缩容冷却: 避免频繁缩容,推荐 5-10 分钟
  3. ✅ 多指标组合: CPU + 内存 + 业务指标
  4. ✅ 优化启动速度: 减少冷启动时间,提升扩容响应速度
  5. ✅ 监控先行: 建立完善的监控和告警体系
  6. ✅ 渐进式调优: 从保守策略开始,逐步优化
  7. ✅ 成本优化: 定时伸缩 + 竞价实例 + 优先级调度

# 常见误区

  • ❌ 过度激进的扩容策略
  • ❌ 过度保守的缩容策略
  • ❌ 忽视 Pod 启动时间
  • ❌ 单一指标决策
  • ❌ 未设置合理的边界
  • ❌ 缺乏监控和告警

弹性伸缩是云原生架构的核心能力,掌握弹性伸缩的原理和实践,是构建高可用、高性价比系统的关键。

祝你变得更强!

编辑 (opens new window)
#弹性伸缩#自动扩缩容#云原生#Kubernetes
上次更新: 2025/12/18
高扩展-微服务架构演进
代码质量管理之规范篇

← 高扩展-微服务架构演进 代码质量管理之规范篇→

最近更新
01
AI编程时代的一些心得
09-11
02
Claude Code 最佳实践(个人版)
08-01
03
高扩展-微服务架构演进
05-29
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式