如何让Deployment仅向单个Pod导流并实现故障自动切换?
实现单主Pod+备用Pod的流量切换方案
要实现3个Pod中仅一个接收流量、故障后自动切换的需求,有几种实用的落地方案,以下是最易实现的两种:
方案一:基于标签选择器的动态主Pod切换
核心思路是让ClusterIP Service仅指向带role: primary标签的Pod,通过监控脚本自动检测主Pod状态,故障时将备用Pod(带role: standby标签)升级为主Pod。
1. 部署基础资源
创建Deployment(初始3个备用Pod)
apiVersion: apps/v1 kind: Deployment metadata: name: app-deployment spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app role: standby spec: containers: - name: my-app-container image: your-app-image:latest ports: - containerPort: 8080
创建ClusterIP Service(仅路由主Pod)
apiVersion: v1 kind: Service metadata: name: app-service spec: type: ClusterIP selector: app: my-app role: primary ports: - port: 80 targetPort: 8080
2. 初始化主Pod
Deployment创建完成后,手动将第一个就绪的备用Pod标记为主Pod:
kubectl label pod $(kubectl get pods -l app=my-app,role=standby -o jsonpath='{.items[?(@.status.ready==true)][0].metadata.name}') role=primary --overwrite
3. 部署主Pod监控脚本
用CronJob定期检查主Pod状态,故障时自动切换:
先创建RBAC权限(让脚本能修改Pod标签)
apiVersion: v1 kind: ServiceAccount metadata: name: pod-label-manager --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: pod-label-manager rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: pod-label-manager-binding subjects: - kind: ServiceAccount name: pod-label-manager namespace: default roleRef: kind: ClusterRole name: pod-label-manager apiGroup: rbac.authorization.k8s.io
创建CronJob执行监控逻辑
apiVersion: batch/v1 kind: CronJob metadata: name: primary-pod-monitor spec: schedule: "*/1 * * * *" # 每分钟执行一次 jobTemplate: spec: template: spec: serviceAccountName: pod-label-manager containers: - name: monitor image: bitnami/kubectl:latest command: ["bash", "-c"] args: - | # 获取当前就绪的主Pod PRIMARY_POD=$(kubectl get pods -l app=my-app,role=primary -o jsonpath='{.items[?(@.status.ready==true)][0].metadata.name}') if [ -z "$PRIMARY_POD" ]; then # 无可用主Pod,选第一个就绪的备用Pod升级 STANDBY_POD=$(kubectl get pods -l app=my-app,role=standby -o jsonpath='{.items[?(@.status.ready==true)][0].metadata.name}') if [ ! -z "$STANDBY_POD" ]; then kubectl label pod $STANDBY_POD role=primary --overwrite echo "Promoted $STANDBY_POD to primary" fi else # 检查主Pod是否仍处于运行状态 POD_PHASE=$(kubectl get pod $PRIMARY_POD -o jsonpath='{.status.phase}') if [ "$POD_PHASE" != "Running" ]; then # 主Pod故障,降级为备用并升级新主Pod kubectl label pod $PRIMARY_POD role=standby --overwrite STANDBY_POD=$(kubectl get pods -l app=my-app,role=standby -o jsonpath='{.items[?(@.status.ready==true)][0].metadata.name}') if [ ! -z "$STANDBY_POD" ]; then kubectl label pod $STANDBY_POD role=primary --overwrite echo "Promoted $STANDBY_POD to primary, demoted $PRIMARY_POD to standby" fi fi fi restartPolicy: OnFailure
方案二:基于StatefulSet的固定标识切换(适合有状态应用)
如果你的应用是有状态的(需要稳定存储/网络标识),可以用StatefulSet实现:
1. 创建StatefulSet
apiVersion: apps/v1 kind: StatefulSet metadata: name: app-statefulset spec: serviceName: app-headless replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app-container image: your-app-image:latest ports: - containerPort: 8080 # 如需持久化存储,添加volumeClaimTemplates字段
2. 创建ClusterIP Service(初始指向第一个Pod)
apiVersion: v1 kind: Service metadata: name: app-service spec: type: ClusterIP selector: app: my-app statefulset.kubernetes.io/pod-name: app-statefulset-0 ports: - port: 80 targetPort: 8080
3. 监控切换脚本
逻辑和方案一类似,检测app-statefulset-0的状态,故障时修改Service的选择器为app-statefulset-1或app-statefulset-2,恢复后可按需切回。
注意事项
- 脚本优先选择就绪状态的备用Pod,避免路由到未就绪的实例
- 若需更高可靠性,可给监控脚本添加分布式锁(比如用ConfigMap实现),防止多实例同时切换导致冲突
- 有状态应用需提前做好数据同步,确保备用Pod能无缝接管主Pod的业务
内容的提问来源于stack exchange,提问作者Raz Buchnik
相关产品推荐
相关产品推荐

