文档首页/ 云容器引擎 CCE/ 最佳实践/ 容灾/ 在CCE中实现应用高可用部署
更新时间:2026-05-21 GMT+08:00
分享

在CCE中实现应用高可用部署

在云原生环境中,容器化应用虽然具备弹性与敏捷性,但其运行环境本身存在诸多不确定性风险。例如某个单一节点的失效、一次错误的滚动更新,都可能导致服务中断,引发核心业务受损。

为了解决上述问题,应用的高可用部署不能仅依赖于创建多个副本,而需要在Pod拓扑分布、Pod中断预算、Pod健康状态管理、优雅生命周期等多个维度进行考虑:
  • Pod拓扑分布:为了确保高可用性和容错性,建议工作负载跨可用区、跨节点均匀分布,避免单点故障。
  • Pod中断预算:为了保护关键工作负载,建议使用Pod中断预算(PodDisruptionBudget, PDB)来维护应用的稳定性,限制自愿中断时最多有多少Pod不可用。
  • Pod健康状态管理:为了确保Pod正常运行和服务流量,建议配置就绪、存活、启动探针,让Kubernetes能自主管理Pod健康状态。
  • 优雅生命周期:通过preStop钩子和优雅终止窗口,确保Pod退出时业务不中断。

本文通过构建一个高可用Nginx示例,为您介绍部署方案。

构建高可用应用示例

Pod拓扑分布

假设集群有4个节点,分布在3个可用区。

  • 方案一:使用拓扑分布约束(推荐)

    使用拓扑分布约束(topologySpreadConstraints)能够精确控制Pod在不同拓扑域(可用区、节点)之间的分布偏差,支持同时设置多个拓扑维度,且不会因其他Pod的干扰导致分布不均。

    配置示例如下:

    当副本数为4且有三个可用区时,调度器会尝试按2,1,1形式尽可能均匀分布(如果某个可用区无节点可用也可能出现2,2,0的形式),优先避免所有Pod挤在同一可用区。

    ...
    spec:
      replicas: 4
      template:
        spec:
          topologySpreadConstraints:
          # 约束1:在可用区之间均匀分布
          - maxSkew: 1                     # 允许的最大Pod数量差,表示任意两个可用区的Pod数差 ≤ 1
            topologyKey: topology.kubernetes.io/zone
            whenUnsatisfiable: DoNotSchedule  # 硬约束,无法满足时禁止调度(也可用ScheduleAnyway)
            labelSelector:
              matchLabels:
                app: nginx-ha
          # 约束2:在节点之间均匀分布(可选,进一步打散)
          - maxSkew: 1
            topologyKey: kubernetes.io/hostname
            whenUnsatisfiable: DoNotSchedule
            labelSelector:
              matchLabels:
                app: nginx-ha
    ...

    参数说明:

    • maxSkew: 1:任意两个拓扑域之间的Pod数量差不超过1,实现最均匀的分布。
    • whenUnsatisfiable: DoNotSchedule:硬约束,若找不到满足条件的节点则Pod保持Pending。
    • whenUnsatisfiable: ScheduleAnyway:软约束,调度器会优先尝试均匀分布,实在不行也会调度。
  • 方案二:使用Pod反亲和性

    使用Pod反亲和性更灵活,适合复杂逻辑,通过preferredDuringScheduling规则将Pod优先分散到不同可用区的节点上,需要时也可以结合requiredDuringScheduling规则实现硬约束。

    配置示例如下:

    ...
    affinity:
      podAntiAffinity:
        preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 80                      # 高权重:优先跨可用区
          podAffinityTerm:
            labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx-ha
            topologyKey: topology.kubernetes.io/zone
        - weight: 20                      # 低权重:其次跨节点
          podAffinityTerm:
            labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx-ha
            topologyKey: kubernetes.io/hostname
    ...

Pod中断预算

Pod中断预算(PodDisruptionBudget, PDB)是Kubernetes中用于限制自愿性中断的资源对象。其中自愿性中断是指由系统或管理员主动触发的Pod删除操作,例如Deployment的滚动更新、手动删除Pod等。

PDB通过设置 minAvailable(最小可用Pod数)或 maxUnavailable(最大不可用Pod数),确保在执行这些操作时,不会让服务容量低于安全线。

配置示例如下:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: nginx-ha-pdb
spec:
  minAvailable: 2        # 至少保持2个Pod可用
  selector:
    matchLabels:
      app: nginx-ha
  • minAvailable:表示在任何时候,至少要有多少个Pod保持可用(可以是绝对数字,如 2;也可以是百分比,如 50%)。
  • maxUnavailable:表示最多允许多少个Pod同时不可用(可以是绝对数字或百分比)。
  • selector:通过标签选择器匹配需要受此PDB约束的Pod集合。

minAvailable和maxUnavailable是互斥的,只能设置其中一个。

Pod健康状态管理

探针是Kubelet定期在容器上执行的诊断检查,根据结果判断Pod的健康状态并采取相应动作。Kubernetes提供三种探针:

  • 就绪探针(readinessProbe):判断Pod是否准备好接收流量。失败时,Pod会被从Service的Endpoints中移除,但不会重启容器。常用于滚动更新时控制新版本的上线节奏。
  • 存活探针(livenessProbe):判断Pod是否处于健康状态。失败时,Kubelet会重启容器。用于修复卡死、死锁等应用级故障。
  • 启动探针(startupProbe):判断容器内的应用是否已经启动完成。在启动探针成功之前,存活和就绪探针会被禁用。适用于启动时间较长的应用(如Java Spring Boot),防止它们在启动期间被存活探针误杀。

配置示例如下:

...
containers:
- name: nginx
  image: nginx:alpine
  ports:
  - containerPort: 80
  # 就绪探针:准备就绪才接收流量
  readinessProbe:
    httpGet:
      path: /
      port: 80
    initialDelaySeconds: 5 # 延迟多少秒才开始探测
    periodSeconds: 10 # 每隔多少秒执行一次探测
    failureThreshold: 3 # 连续失败多少次才判定为失败
  # 存活探针:异常则重启容器
  livenessProbe:
    httpGet:
      path: /
      port: 80
    initialDelaySeconds: 15
    periodSeconds: 20
    failureThreshold: 3  # 可适当放宽(如3-5次),避免因短暂波动(如高负载、GC停顿)导致容器频繁重启
  # 启动探针:保护启动慢的应用(如Java)
  startupProbe: 
    httpGet:
      path: /
      port: 80
    failureThreshold: 30
    periodSeconds: 10
...

优雅生命周期

当Pod被删除(如滚动更新、节点排空、缩容)时,Kubernetes会向Pod中的容器发送SIGTERM信号。如果容器立即退出,正在处理中的请求可能会中断,导致客户端收到错误响应(如Connection Refused)。配置preStop与优雅终止,可以等Pod先摘除流量,等容器完成所有清理工作并退出。

配置示例如下:

...
containers:
- name: nginx
  lifecycle:
    preStop:
      exec:
        command: ["/bin/sh", "-c", "sleep 30"] # 等待30秒,给ELB摘除后端的时间
terminationGracePeriodSeconds: 45   # 总宽限期45秒,必须大于 preStop sleep 时间
...

完整YAML示例

此示例副本数为4,整合了拓扑分布约束(推荐方案)、PDB、探针和优雅终止,可应对单节点故障和单可用区中断。

# 1. PodDisruptionBudget: 保证自愿中断时至少有2个Pod运行
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: nginx-ha-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: nginx-ha
---
# 2. Deployment: 整合了拓扑分布约束、探针、优雅终止
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ha
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx-ha
  template:
    metadata:
      labels:
        app: nginx-ha
    spec:
      terminationGracePeriodSeconds: 45
      # 使用拓扑分布约束,均匀分布到可用区和节点
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: nginx-ha
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: nginx-ha
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
        # 优雅生命周期
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 30"]
        # 探针配置
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 15
          periodSeconds: 20
        resources:
          requests:
            cpu: 250m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi
      imagePullSecrets:
      - name: default-secret

验证与测试

  1. 验证Pod分布:
    kubectl get pod -owide -l app=nginx-ha

    观察输出中的NODE字段,应分布在多个节点和可用区。

  2. 验证PDB:
    kubectl get pdb nginx-ha-pdb

    输出示例:

    STATUS: Healthy, Allowed disruptions: 2
  3. 模拟节点故障:排空一个节点,观察Pod是否漂移,且总不可用Pod数不超过PDB限制。
  4. 模拟滚动更新:触发滚动更新,观察旧Pod终止前是否等待了preStop定义的时间。
  5. 模拟应用故障:手动关闭应用内部端口,观察就绪探针是否将Pod摘除,存活探针是否重启容器。

常见问题

PDB与滚动更新参数maxSurge和maxUnavailable冲突吗?

不冲突。Deployment的maxSurge和maxUnavailable与PDB共同作用,Kubernetes会取更严格的限制。

preStop应该设置多久?

取决于业务处理完现有请求的平均+最大时间。通常15-30秒,再配合terminationGracePeriodSeconds多留出5-10秒余量。

反亲和性使用required还是preferred?

若集群中可供调度的节点数充足,且副本数 ≤ 节点/可用区数,可以使用required强制打散。否则建议用preferred,避免Pod因无法满足硬约束而Pending。

拓扑分布约束中maxSkew应设为多少?

maxSkew设置为1可实现最严格的均匀分布。如果节点数不是副本数的整数倍,设为1仍能保证最大差为1。

相关文档