第八章:应用的扩缩容与更新策略

在云原生时代,应用的生命周期远不止于“部署”这一步。一个成功的应用需要能够优雅地应对流量的波峰波谷,并能以最小的风险、最快的速度迭代更新。Kubernetes为此提供了一整套强大且自动化的工具,让应用的**弹性(Elasticity)敏捷性(Agility)**不再是难题。

本章将深入探讨两大核心主题:

  1. 应用的扩缩容:如何根据实际需求,动态地增加或减少应用的Pod副本数量,以达到资源利用和性能表现的最佳平衡。
  2. 应用的更新策略:如何将应用从一个版本升级到另一个版本,同时确保服务的连续性和稳定性,实现真正的零停机发布。

8.1 手动扩缩容: kubectl scale命令的使用

在深入了解自动化机制之前,我们先从最直接、最简单的手动扩缩容开始。Kubernetes提供了kubectl scale命令,允许你快速地修改Deployment、ReplicaSet、StatefulSet等可伸缩资源的副本数量。

这是一种命令式的操作,当你明确知道需要多少个副本时,它非常有用。

使用场景:

  • 计划内活动:在进行市场推广活动或预期有流量高峰之前,手动扩容。
  • 紧急应对:发现服务负载过高,作为应急措施快速增加副本。
  • 资源节省:在业务低谷期(如深夜),手动缩容以节省成本。
  • 测试与调试:在测试环境中快速调整副本数以模拟不同场景。

命令语法:

kubectl scale [--replicas=COUNT] <resource_type>/<resource_name>

实践示例:

假设我们有一个在前面章节创建的名为nginx-deployment的Deployment,初始replicas为3。

  1. 扩容(Scale Out):将Nginx应用的副本数从3个增加到5个。

    kubectl scale --replicas=5 deployment/nginx-deployment
    

    执行命令后,你会看到输出 deployment.apps/nginx-deployment scaled。此时,Deployment控制器会立即开始工作,创建2个新的Pod,直到总数达到5个。你可以通过 kubectl get pods 来观察这个过程。

  2. 缩容(Scale In):将Nginx应用的副本数从5个减少到2个。

    kubectl scale --replicas=2 deployment/nginx-deployment
    

    同样,Deployment控制器会优雅地终止3个Pod,直到总数剩下2个。

背后原理:
kubectl scale命令本质上只是一个快捷方式,它所做的就是修改Deployment资源对象在etcd中存储的YAML定义,将其spec.replicas字段的值更新为你指定的数字。Deployment控制器监测到这个“期望状态”的变化后,便会采取行动(增加或减少Pod),使“实际状态”与“期望状态”保持一致。

局限性:
手动扩缩容虽然简单,但其局限性也非常明显:它依赖于人的判断和干预,无法实时响应不可预测的负载变化。要实现真正的弹性,我们必须依赖自动化机制。

8.2 水平Pod自动扩缩容(HPA)

Horizontal Pod Autoscaler (HPA)是Kubernetes中实现应用自动化弹性的核心组件。它能够自动地根据观察到的CPU利用率、内存使用量或其他自定义指标,来调整Pod的副本数量。

“水平”扩缩容指的是通过增减Pod的数量来调整应用的整体处理能力,与之对应的是“垂直”扩缩容(Vertical Pod Autoscaling, VPA),即调整单个Pod的CPU或内存大小。HPA是无状态应用扩缩容最常用的方式。

HPA工作的前提条件

为了让HPA能够正常工作,必须满足两个关键前提:

  1. Metrics Server的部署: HPA需要一个数据来源来获取Pod的资源使用指标。Metrics Server是Kubernetes集群中的一个核心插件,它从每个节点的kubelet收集资源使用数据(CPU、内存),并通过Kubernetes API聚合起来,以metrics.k8s.io API的形式暴露给其他组件使用。没有Metrics Server,HPA就如同一个没有眼睛的司机,无法做出任何决策。

    • 在大多数托管的K8s服务(如GKE, EKS, AKS)和一些本地工具(如Docker Desktop)中,Metrics Server是默认安装的。你可以通过kubectl top podskubectl top nodes命令来检查它是否正常工作。
  2. 为Pod设置资源请求(Requests): 这是最容易被忽略但至关重要的前提。HPA在计算CPU利用率时,使用的公式是:
    当前CPU利用率 = Pod当前实际CPU使用量 / Pod的CPU请求值(Requests)
    如果你没有在Pod的容器定义中设置resources.requests.cpu,那么HPA就无法计算利用率百分比,也就无法工作。这再次强调了第七章中为Pod设置requests的重要性。

HPA的工作原理:一个持续的控制循环

HPA由Master节点上的Controller Manager中的一个专用控制器(HPA Controller)实现。其工作流程是一个经典的控制循环:

  1. 用户定义HPA: 用户创建一个HPA资源对象,指定要监控的目标(如一个Deployment)、扩缩容的副本数范围(minReplicasmaxReplicas),以及触发扩缩容的指标阈值(例如,CPU平均利用率达到80%)。
  2. 周期性检查: HPA控制器以一个可配置的周期(默认为15秒)运行。
  3. 获取指标: 在每个周期,HPA控制器通过metrics.k8s.io API从Metrics Server查询目标(如Deployment)下所有Pod的当前指标值(例如,每个Pod的CPU使用量)。
  4. 计算期望副本数: 控制器将所有Pod的指标值进行平均,得到一个当前的平均指标值。然后,它使用一个核心算法来计算期望的副本数:
    期望副本数 = ceil [ 当前副本数 * ( 当前平均指标值 / 期望指标值 ) ]
    • ceil是向上取整函数。
    • 例如:当前有3个副本,期望CPU利用率为50%,当前实际平均利用率为75%。那么期望副本数 = ceil [ 3 * ( 75% / 50% ) ] = ceil [ 3 * 1.5 ] = ceil [ 4.5 ] = 5个。
  5. 执行扩缩容: 如果计算出的期望副本数与当前副本数不同,HPA控制器就会更新目标Deployment的spec.replicas字段。
  6. Deployment响应: Deployment控制器接管后续工作,根据新的replicas值来创建或删除Pod。
  7. 冷却时间: 为了防止因指标值的瞬时抖动而导致频繁的扩缩容(称为“抖动”或“颠簸”),HPA在执行一次扩容后,会进入一个冷却期(默认为3分钟),在此期间不会再次执行扩容。缩容的冷却期则更长(默认为5分钟)。
实践示例:为Deployment配置HPA

让我们为一个PHP-Apache应用创建一个Deployment,并为其配置HPA。

步骤1:创建Deployment和Service
这个Deployment运行一个会消耗CPU的PHP页面。

# php-apache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  replicas: 1
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: k8s.gcr.io/hpa-example
        ports:
        - containerPort: 80
        # !!!必须设置requests!!!
        resources:
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
spec:
  selector:
    run: php-apache
  ports:
  - port: 80
````kubectl apply -f php-apache.yaml`

**步骤2:创建HPA资源**

*   **使用`kubectl autoscale`命令(快捷方式)**:
    ```bash
    kubectl autoscale deployment php-apache \
      --cpu-percent=50 \
      --min=1 \
      --max=10
    ```
    这条命令会创建一个HPA,目标是`php-apache`这个Deployment,当其Pod的平均CPU利用率超过50%时触发扩容,副本数范围在1到10之间。

*   **使用YAML文件(声明式)**:
    ```yaml
    # hpa.yaml
    apiVersion: autoscaling/v2beta2 # 使用v2beta2可以支持更多类型的指标
    kind: HorizontalPodAutoscaler
    metadata:
      name: php-apache-hpa
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: php-apache  # 指向要扩缩容的目标
      minReplicas: 1
      maxReplicas: 10
      metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: Utilization
            averageUtilization: 50 # 目标CPU利用率
    ```
    `kubectl apply -f hpa.yaml`

**步骤3:施加负载并观察HPA工作**

1.  **监控HPA状态**: 打开一个新的终端,持续观察HPA的状态。
    ```bash
    kubectl get hpa -w
    ```
    初始状态下,`TARGETS`应该显示一个较低的百分比,例如 `<unknown>/50%` 或 `2%/50%`。

2.  **施加负载**: 打开另一个终端,创建一个临时的Pod来向`php-apache`服务发送大量请求。
    ```bash
    kubectl run -it --rm load-generator --image=busybox -- /bin/sh
    # 在load-generator的shell中,执行无限循环的wget
    # while true; do wget -q -O- http://php-apache; done
    ```

3.  **观察扩容**: 回到第一个终端,你会看到HPA的状态开始变化:
    *   `TARGETS`列的CPU利用率会迅速飙升,远超50%。
    *   `REPLICAS`列的数字会从1开始,逐渐增加到2, 3, 4... 直到CPU利用率稳定在50%左右。
    *   你可以通过`kubectl get pods`看到新的Pod被创建出来。

4.  **撤销负载并观察缩容**:
    *   在`load-generator`的终端中,按`Ctrl+C`停止循环,然后`exit`退出Pod。
    *   再次观察HPA的状态。`TARGETS`列的CPU利用率会下降到接近0%。
    *   等待缩容的冷却时间(默认为5分钟)过后,你会看到`REPLICAS`的数量会逐渐减少,最终回到`minReplicas`所设定的1个。

这个实验完整地展示了HPA如何自动化地维护应用的性能和资源使用。

### 8.3 Deployment的更新策略

在敏捷开发和DevOps实践中,频繁地发布新版本的应用是常态。Deployment资源通过其`spec.strategy`字段,提供了两种内置的更新策略来管理这个过程。

#### 1. Recreate(重新创建)

*   **机制**: 这是一种简单粗暴的“先删后建”策略。当触发更新时,Deployment控制器会**首先终止所有**正在运行的旧版本Pod。当所有旧Pod都被完全删除后,它才会开始创建新版本的Pod。
*   **优点**: 策略简单,逻辑清晰。可以确保在任何时候,集群中都不会同时存在新旧两个版本的应用实例,这对于某些无法兼容新旧版本共存的应用(例如,数据库schema发生重大不兼容变更)是必要的。
*   **缺点**: **会导致服务中断**。在旧Pod被删除和新Pod启动并准备好接收流量之间,会有一段明显的服务不可用时间窗口。
*   **适用场景**: 极少用于生产环境的在线服务。适用于开发环境、一次性任务,或可以接受停机窗口的维护场景。

```yaml
# deployment-recreate.yaml
spec:
  replicas: 3
  strategy:
    type: Recreate # 明确指定策略类型
2. RollingUpdate(滚动更新) - 默认策略
  • 机制: 这是一种平滑、优雅的“渐进式替换”策略。当触发更新时,Deployment控制器会逐个地用新版本的Pod替换旧版本的Pod,同时确保在整个更新过程中,始终有一定数量的Pod在对外提供服务。
  • 优点: 实现零停机发布。用户的流量会被平滑地从旧版本迁移到新版本,对用户完全无感。
  • 缺点: 更新过程比Recreate慢。在更新期间,集群中会同时存在新旧两个版本的Pod,需要应用自身能够处理这种情况(向后兼容)。
  • 适用场景: 绝大多数生产环境应用更新的标准选择

滚动更新的行为可以通过两个非常重要的参数进行精细化控制:maxSurgemaxUnavailable

【重难点】 理解HPA的工作原理和指标选择,掌握滚动更新中的maxSurge和maxUnavailable参数以实现精细化发布控制

HPA的工作原理与指标选择深度解析
  • HPA的核心是期望状态与实际状态的比较:再次强调,HPA是Kubernetes声明式API哲学的完美体现。它不断地将用户定义的“期望指标”(如CPU 50%)与从Metrics Server获取的“实际指标”进行比较,并计算出一个“期望副本数”,然后通过修改Deployment的replicas字段来驱动整个系统趋向于这个新的期望状态。

  • 指标选择的艺术

    • CPU利用率: 最常用、最直观的指标,适用于CPU密集型应用(如视频转码、复杂计算)。它的优点是响应灵敏。
    • 内存使用量: 这是一个非常棘手的指标,通常不建议作为HPA的主要触发条件。原因是,很多应用(尤其是Java等带垃圾回收机制的语言)在内存使用量上升后,并不会轻易地将其归还给操作系统,即使负载下降。这会导致Pod数量只增不减,无法有效缩容。内存指标更适合用于垂直Pod自动扩缩容(VPA)
    • 自定义指标(Custom Metrics): 对于业务应用来说,CPU利用率往往不能直接反映业务负载。例如,一个I/O密集型的Web应用,其瓶颈可能在于并发连接数或请求处理队列。通过适配器(如Prometheus Adapter),HPA可以基于从Prometheus等监控系统采集的任何自定义指标进行扩缩容,例如:
      • requests_per_second (每秒请求数)
      • queue_length (消息队列长度)
      • active_sessions (活跃用户会话数)
        使用自定义指标通常能更准确、更及时地反映真实业务压力,是构建高级弹性系统的关键。
精细化发布控制:maxSurgemaxUnavailable的策略艺术

这两个参数定义了滚动更新过程中的“步调”和“边界”,共同决定了更新的速度、风险和资源消耗。它们可以被设置为一个绝对数字(如1)或一个百分比(如25%)。

假设我们有一个replicas: 10的Deployment。

  • maxUnavailable (最大不可用):

    • 含义: 在滚动更新期间,最多允许有多少个Pod处于不可用状态。不可用的Pod包括正在被删除的旧Pod和尚未完全就绪的新Pod。
    • 作用: 控制了服务的最低可用容量
    • 示例: maxUnavailable: 20% (即2个Pod)。这意味着在任何时刻,Kubernetes都必须保证至少有 10 - 2 = 8 个Pod是健康且可用的。它会先删除2个旧Pod,然后创建2个新Pod,等待新Pod就绪后,再进行下一轮替换。
  • maxSurge (最大峰值):

    • 含义: 在滚动更新期间,最多允许比期望的replicas数量多出多少个Pod。
    • 作用: 控制了更新的速度资源消耗
    • 示例: maxSurge: 25% (即2.5,向上取整为3个Pod)。这意味着在任何时刻,Pod的总数最多不能超过 10 + 3 = 13 个。Kubernetes可以先创建3个新Pod,然后再删除3个旧Pod,从而加快更新速度。

这两个参数是协同工作的,Kubernetes会在不违反任何一个约束的前提下执行更新。

不同策略配置下的行为分析:

策略组合 (replicas: 10) 行为 优点 缺点 适用场景
默认值
maxSurge: 25%
maxUnavailable: 25%
一边创建2-3个新Pod,一边删除2-3个旧Pod。总副本数在8到13之间波动。 均衡了速度、资源和可用性。 无明显缺点,是通用选择。 大多数常规应用发布。
高可用优先 (金丝雀/蓝绿)
maxSurge: 100%
maxUnavailable: 0
先创建10个新Pod。当所有新Pod都Ready后,再瞬间将流量切换过去,并删除所有10个旧Pod。 绝对的零服务容量损失。更新期间总容量会翻倍,切换速度快。 需要双倍的节点资源来容纳新旧两套Pod。 对可用性要求极高的核心服务,发布窗口期的资源充足。
资源受限环境
maxSurge: 0
maxUnavailable: 1 (或 10%)
必须先删除1个旧Pod,释放出资源后,才能创建1个新Pod。总副本数不会超过10。 不占用任何额外资源 更新速度最慢,并且服务容量会暂时下降。 节点资源极其紧张,可以接受较慢更新和轻微容量波动的环境。
快速发布
maxSurge: 50%
maxUnavailable: 50%
同时创建5个新Pod,并删除5个旧Pod,以非常快的速度进行替换。 发布速度极快 风险较高,如果新版本有问题,会快速影响到一半的服务实例。 测试环境或对发布速度要求极高、且回滚机制非常完善的场景。

YAML示例:配置一个高可用优先的更新策略

apiVersion: apps/v1
kind: Deployment
metadata:
  name: critical-service
spec:
  replicas: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0      # 保证服务容量不低于10
      maxSurge: "100%"       # 允许额外创建10个Pod

通过灵活地组合maxSurgemaxUnavailable,你可以根据应用的特性、资源状况和风险偏好,设计出最适合你的发布策略,从而在敏捷迭代和系统稳定性之间找到完美的平衡点。这正是Kubernetes声明式配置强大能力的体现。

K8s 详细学习笔记 目录

以下是整个系列的10章目录,点击章节标题即可跳转阅读:

更多推荐