一、先搞懂:啥是EDRS策略下的频繁迁移?为啥会搞出问题?

很多做线上服务的朋友应该都碰到过这种糟心事儿:本来好好跑着的业务,突然就卡了、慢了,甚至还报错,查日志才发现是服务实例被挪来挪去(专业点叫“迁移”),而且这种迁移还不是偶尔一次,是隔三差五就来一次。这背后大概率就跟EDRS策略脱不了关系。

先给大家把EDRS用大白话讲明白,别被名字唬住。EDRS的核心就是“弹性动态伸缩”——简单说就是你的服务集群里,服务器(或者说跑服务的容器、实例)不能闲得慌,也不能累趴下。具体规则一般是两条:第一,要是某台服务器上跑的服务太多、资源占满了(比如CPU飙到90%以上),就得把上面的一部分服务挪到资源空的服务器上;第二,要是某台服务器空了大半天(比如连续几小时CPU都不到10%),就得把上面的服务全挪走,把这台服务器关掉省成本。

那为啥会频繁迁移?举个最常见的例子:你做的是电商大促的实时推荐服务,平时流量少的时候,集群里只开2台服务器,每台跑5个推荐实例,CPU都在20%左右;大促前1小时,流量突然涨了,每台服务器的CPU直接冲到95%,EDRS一看“这台要累垮了”,就触发迁移,把每台的2个实例挪到新开的2台服务器上;结果大促开场才20分钟,因为用户没抢到心仪商品退单,流量又骤降,新的2台服务器CPU直接掉到5%,EDRS又触发迁移,把这2台的实例挪回原来的2台,然后把新服务器关掉。结果这大促前前后后1小时里,推荐服务就被挪了4次,用户刷商品页的时候要么加载半天,要么直接报错——这就是“频繁迁移”搞出来的“业务抖动”和“性能衰减”。

说白了,频繁迁移的本质就是EDRS的规则太“敏感”,稍微有点资源波动就触发动作,结果反而折腾了业务。

二、核心解决思路:给EDRS加“刹车”,别让它瞎动

要解决频繁迁移的问题,核心不是把EDRS关掉(毕竟EDRS是为了省成本、保服务),而是给它加几个“刹车机制”,让它别一有点风吹草动就乱动。具体可以从三个层面入手:给迁移加“冷静期”、给资源阈值加“缓冲带”、给迁移加“白名单”。

2.1 给迁移加“冷静期”:别刚挪完又挪

啥是“冷静期”?就是EDRS触发一次迁移之后,必须等一段时间才能触发下一次,这段时间里不管资源怎么波动,都不能再动。比如我们给EDRS设个30分钟的冷静期,刚才大促的例子里,第一次迁移是大促前1小时,冷静期到30分钟后才结束,那大促开场20分钟的流量骤降,EDRS因为还在冷静期里,就不会触发第二次迁移,直到冷静期结束才会判断要不要动,这样就避免了1小时内挪4次的情况。

冷静期的设置不是随便定的,得根据你的业务特点来:比如实时推荐服务的业务逻辑比较轻,实例启动只要1分钟,那冷静期可以设成10分钟;要是你做的是大数据离线计算服务,实例启动要20分钟,那冷静期就得设成1小时甚至更久。

2.2 给资源阈值加“缓冲带”:别一沾线就触发

EDRS原来的触发规则是“硬阈值”,比如CPU超过90%就触发迁移,低于10%就触发缩容(也就是迁移所有实例后关服务器)。但这种硬阈值太敏感,稍微波动就触发。“缓冲带”就是把硬阈值改成“区间阈值”,比如把CPU的触发规则改成:只有当CPU连续5分钟超过95%,才触发迁移;只有当CPU连续10分钟低于5%,才触发缩容。

为啥要加“连续时间”?因为很多资源波动是临时的,比如某台服务器突然有个用户集中刷商品,CPU瞬间冲到92%,但1分钟后就降下来了,要是没有连续时间的要求,EDRS就会误判,触发没必要的迁移。

2.3 给迁移加“白名单”:重要业务别乱挪

不是所有服务都适合被随便迁移的,比如电商的支付服务、用户登录服务,这些服务一旦迁移,哪怕只卡1秒,都可能导致用户付款失败、登录超时,影响特别大。所以我们可以给EDRS加个“白名单”机制:白名单里的服务,EDRS绝对不能触发迁移,哪怕这台服务器资源满了,也得先挪其他非白名单的服务,实在不够了再扩容新服务器,绝对不能动白名单服务。

白名单的设置也得谨慎,不能啥服务都加,不然EDRS的弹性伸缩作用就没了。一般来说,只有核心交易类、强实时类的服务才加白名单。

三、完整实战示例:用K8s的HPA和VPA结合EDRS规则优化(技术栈:Kubernetes)

光说思路不够,我们来搞个完整的实战示例,用现在最常用的容器编排工具K8s来实现刚才的三个解决思路。先明确技术栈:Kubernetes(简称K8s),我们会用到K8s的两个核心组件:HPA(水平Pod自动伸缩)和VPA(垂直Pod自动伸缩),然后给EDRS加自定义规则。

3.1 先搞懂K8s里的EDRS逻辑

在K8s里,EDRS的逻辑是这样的:HPA负责水平伸缩(也就是加Pod或者减Pod),VPA负责垂直伸缩(也就是给Pod加资源或者减资源),然后K8s的调度器负责把Pod调度到合适的节点(服务器)上,要是节点资源满了,调度器就会触发Pod迁移,把Pod挪到其他节点上。

原来的默认规则是没有冷静期、没有缓冲带、没有白名单的,所以很容易出现频繁迁移的问题。

3.2 给HPA加冷静期和缓冲带

首先我们给HPA加冷静期和缓冲带,HPA的配置里有个参数叫stabilizationWindowSeconds,这个参数就是冷静期的核心,意思是“只有当连续这么多秒内,伸缩需求没有变化,才会触发伸缩”。还有个参数叫behavior,可以设置缓冲带的阈值。

我们来写个HPA的配置文件,针对电商的实时推荐服务:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: recommend-service-hpa
  namespace: online-service
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: recommend-service
  minReplicas: 2 # 最小Pod数
  maxReplicas: 10 # 最大Pod数
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 80 # 目标CPU利用率80%
  behavior:
    scaleUp: # 扩容行为
      stabilizationWindowSeconds: 300 # 冷静期:连续5分钟(300秒)内需要扩容,才触发
      policies:
      - type: Percent
        value: 50 # 每次最多扩容50%的Pod
        periodSeconds: 60
    scaleDown: # 缩容行为
      stabilizationWindowSeconds: 600 # 冷静期:连续10分钟(600秒)内需要缩容,才触发
      policies:
      - type: Percent
        value: 20 # 每次最多缩容20%的Pod
        periodSeconds: 60

这个配置的意思是:实时推荐服务的Pod,CPU利用率超过80%时,必须连续5分钟都需要扩容,才会触发扩容;CPU利用率低于80%时,必须连续10分钟都需要缩容,才会触发缩容,而且每次扩容缩容的幅度都控制在合理范围,不会一下子加很多或者减很多。

3.3 给VPA加缓冲带

VPA负责给Pod调整资源配额,原来的VPA可能会频繁调整资源,导致Pod被重新调度(也就是迁移)。我们给VPA加缓冲带,设置只有当资源需求连续一段时间变化,才会调整。

VPA的配置文件:

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: recommend-service-vpa
  namespace: online-service
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: recommend-service
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: recommend-service-container
      minAllowed:
        cpu: "100m" # 最小CPU配额100毫核
        memory: "128Mi" # 最小内存128兆
      maxAllowed:
        cpu: "500m" # 最大CPU配额500毫核
        memory: "512Mi" # 最大内存512兆
      controlledResources: ["cpu", "memory"]
    # 加缓冲带:只有当资源需求变化超过20%,才会调整
      scaleDownStabilizationWindowSeconds: 600
      scaleUpStabilizationWindowSeconds: 300

这个配置的意思是:实时推荐服务的Pod,只有当CPU或者内存的需求连续10分钟变化超过20%,才会调整资源配额,避免频繁调整导致迁移。

3.4 给K8s调度器加白名单

最后我们给K8s调度器加白名单,让核心服务不会被随便迁移。K8s里可以用nodeSelectoraffinity来实现,我们给核心服务的Deployment加个标签,然后让调度器优先把核心服务调度到专门的节点上,不会因为资源满了就把核心服务挪走。

比如电商的支付服务的Deployment配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  namespace: online-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
        core-service: "true" # 核心服务标签
    spec:
      containers:
      - name: payment-service-container
        image: payment-service:v1.0
        resources:
          requests:
            cpu: "200m"
            memory: "256Mi"
          limits:
            cpu: "400m"
            memory: "512Mi"
      # 节点亲和性:优先调度到打了core-node标签的节点上
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: core-node
                operator: In
                values:
                - "true"

然后我们给专门的核心节点打标签:

# 给节点node-01打core-node=true的标签
kubectl label nodes node-01 core-node=true

这样配置之后,支付服务的Pod只会被调度到打了core-node标签的节点上,这些节点的资源配额会提前预留足够的,不会因为其他服务的资源需求就把支付服务挪走,实现了白名单的效果。

四、应用场景、优缺点、注意事项

4.1 应用场景

这套优化方案几乎适用于所有用EDRS做弹性伸缩的服务,尤其是以下几个场景:

  1. 电商、直播等流量波动大的行业:大促、开播前流量骤增,开播后流量骤降,很容易触发频繁迁移,优化后可以避免业务抖动;
  2. 核心交易类服务:支付、登录、订单等服务,一旦迁移出现问题,影响极大,优化后可以保证核心服务的稳定性;
  3. 长连接服务:比如即时通讯、游戏服务,迁移会导致长连接断开,优化后可以减少迁移次数,提升用户体验;
  4. 大数据服务:比如离线计算、实时数仓,实例启动时间长,频繁迁移会导致计算任务中断,优化后可以保证任务的连续性。

4.2 技术优缺点

优点:

  1. 成本可控:没有关掉EDRS,还是可以根据流量变化调整资源,不会浪费成本;
  2. 稳定性提升:通过冷静期、缓冲带、白名单三个机制,大大减少了频繁迁移的次数,避免了业务抖动和性能衰减;
  3. 灵活适配:可以根据不同业务的特点调整参数,比如实时服务的冷静期短,大数据服务的冷静期长;
  4. 可扩展性强:这套方案可以和其他监控、告警系统结合,比如当资源波动超过阈值时,先告警,再判断要不要触发迁移,进一步优化。

缺点:

  1. 参数调整复杂:冷静期、缓冲带的参数需要根据业务的实际情况不断测试调整,一开始可能会走弯路;
  2. 核心节点资源浪费:白名单的核心节点需要预留足够的资源,平时可能会有一定的资源闲置;
  3. 可能错过最佳伸缩时机:比如冷静期内流量突然暴涨,EDRS不会触发扩容,可能会导致服务性能下降,需要结合告警系统及时调整。

4.3 注意事项

  1. 参数测试要充分:上线前一定要在测试环境模拟各种流量场景,测试冷静期、缓冲带的参数是否合理,比如模拟大促的流量骤增骤降,看会不会出现频繁迁移;
  2. 白名单不能滥用:只有真正的核心服务才加白名单,不然EDRS的弹性伸缩作用就会大打折扣,导致成本上升;
  3. 结合监控系统:一定要配套监控系统,实时监控资源利用率、迁移次数、业务性能指标,一旦出现异常及时调整参数;
  4. 定期复盘优化:每隔一段时间复盘一下EDRS的运行情况,比如看最近一个月有多少次迁移是不必要的,然后调整参数,让方案更贴合业务;
  5. 留好应急预案:比如冷静期内流量暴涨导致服务性能下降,要有应急预案,比如手动扩容,或者临时调整冷静期参数。

五、文章总结

EDRS策略下的频繁迁移,本质上是弹性伸缩规则太敏感,没有考虑业务的实际情况,导致为了调整资源反而折腾了业务。解决这个问题的核心思路是给EDRS加“刹车”,通过冷静期、缓冲带、白名单三个机制,让EDRS的动作更合理,既保证资源的弹性,又避免业务出现抖动和性能衰减。

从实战示例可以看到,这套方案不需要改动EDRS的核心逻辑,只需要在原来的规则基础上增加一些限制条件,就可以大大提升服务的稳定性。而且这套方案的灵活性很强,可以根据不同业务的特点调整参数,适配各种场景。

最后要强调的是,这套方案不是一成不变的,需要结合业务的实际情况不断测试、调整、优化,才能达到最佳的效果。毕竟没有最好的规则,只有最适合自己业务的规则。