You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

K8S中如何确保单体应用仅运行一个Pod?避免多副本同时启动

在Kubernetes中部署单实例独占式单体应用的解决方案(不修改代码)

针对你提到的必须严格单实例运行、旧Pod完全停止后才能启动新Pod、故障自动重启的需求,以下是无需修改应用代码的可行方案:

基础核心配置:Deployment + Recreate 更新策略

这是K8S原生就能实现的基础方案,能覆盖大部分常规场景:

  • 把Deployment的更新策略设为Recreate,同时固定副本数为1:
    spec:
      replicas: 1
      strategy:
        type: Recreate
    
    这个策略的逻辑是先彻底删掉所有旧Pod,再创建新Pod,从根源上避免更新过程中出现多实例。
  • 配置健康检查实现故障自动重启:给应用加上livenessProbe,一旦健康检查失败,K8S会自动重启Pod,示例配置:
    spec:
      containers:
      - name: your-app
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
    

解决节点故障/网络分区的多实例风险

上面的方案在正常更新、自愿节点维护时没问题,但如果遇到节点网络分区(旧Pod实际还在运行,但K8S判定它已死亡),可能会启动新Pod导致多实例。可以用以下两种增强方案:

方案1:PVC独占挂载锁

利用K8S PVC的ReadWriteOnce访问模式特性——同一个PVC只能被一个Pod挂载,以此实现实例互斥:

  1. 创建一个小容量的PVC(哪怕应用不需要持久化数据,也可以用它当锁),访问模式设为ReadWriteOnce:
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: app-singleton-lock
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
    
  2. 在Pod模板里挂载这个PVC:
    spec:
      containers:
      - name: your-app
        volumeMounts:
        - name: lock-volume
          mountPath: /tmp/app-lock
      volumes:
      - name: lock-volume
        persistentVolumeClaim:
          claimName: app-singleton-lock
    
    如果旧Pod因为节点故障没正常释放PVC,新Pod会卡在Pending状态,直到K8S清理掉旧Pod的挂载(一般节点失联5分钟后会强制解除),从物理层面避免多实例同时运行。

方案2:Sidecar分布式租约锁

如果对多实例风险要求极高,可以加个Sidecar容器,用K8S的Lease API实现分布式锁,只有拿到锁的Pod才启动主应用:

  1. 找个现成的租约锁工具或者写个简单的Sidecar(基于K8S client-go实现),它的逻辑是向K8S API申请独占租约,拿到锁后在共享目录里生成一个标记文件。
  2. 配置主应用的启动命令,等待标记文件出现后再启动:
    spec:
      containers:
      - name: singleton-lock-sidecar
        image: your-lock-tool-image
        args: ["--lock-name", "app-singleton-lock", "--lease-duration", "30"]
        volumeMounts:
        - name: shared-signal
          mountPath: /shared
      - name: your-app
        command: ["sh", "-c", "until [ -f /shared/lock-acquired ]; do sleep 1; done; /path/to/your/app"]
        volumeMounts:
        - name: shared-signal
          mountPath: /shared
      volumes:
      - name: shared-signal
        emptyDir: {}
    
    租约会定期续约,一旦旧Pod失联超过租期,租约自动释放,新Pod就能拿到锁启动,彻底避免网络分区导致的多实例问题。

补充:限制自愿驱逐的行为

配合PodDisruptionBudget,确保节点维护等自愿驱逐操作也符合单实例要求:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-singleton-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: your-app

这个配置允许在自愿驱逐时删除旧Pod,但不会同时启动多个新Pod,和Recreate策略配合就能保证驱逐时严格先停旧、再启新。

内容的提问来源于stack exchange,提问作者F. Alessandro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 17:46:02