You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Kubernetes Services是否有故障转移功能?主备数据库流量切换方案咨询

1. Kubernetes Services是否具备故障转移功能?

当然有,但得看具体的Service类型和配置细节。

Kubernetes的Service默认就自带基础的故障转移能力:当后端的Pod出现故障(比如Pod崩溃、就绪探针检测失败)时,kube-proxy会自动把这个Pod从Service的端点列表中移除,后续流量就不会再转发到这个故障Pod上了。

不过要注意几个关键点:

  • 这个故障转移是基于Pod的就绪探针状态的——只有当Pod的就绪探针返回失败时,Service才会将其剔除出流量池。如果你的应用内部出了问题但没被就绪探针检测到,Service还是会继续把流量发过去。
  • 普通的ClusterIP/NodePort Service默认是轮询负载均衡,故障转移只是把故障Pod从负载池里拿掉,剩下的正常Pod承接流量。但它不会做「优先主节点、主节点挂了才切从节点」这种带优先级的切换——这种场景需要额外的配置或者工具。
2. 基于StatefulSet + Headless Service的主从数据库流量自动切换方案

你的场景很典型:用StatefulSet部署主从数据库,Headless Service用来做Pod的DNS解析,现在要实现访问database-service.default.svc.cluster.local时优先走主库D-1,D-1挂了自动切到D-2,而且不能在客户端加逻辑,得在K8s层面搞定。

首先得明确:Headless Service本身不会做负载均衡或者流量切换,它只是返回所有后端Pod的DNS记录。所以要实现你要的优先级切换,核心思路是让Service只在主库正常时指向主库,主库故障时自动指向从库,具体可以通过「动态就绪探针+普通ClusterIP Service」的组合来实现,步骤如下:

第一步:给StatefulSet的Pod配置动态就绪探针

我们需要让主库D-1的就绪探针始终返回成功,而从库D-2的就绪探针只有在主库不可用时才返回成功。这样当主库正常时,只有D-1会被加入到Service的端点列表;主库挂了,D-2的就绪探针变成成功,就会被Service纳入流量池,实现自动切换。

以MySQL为例,你可以给每个DB Pod写一个就绪探针脚本,逻辑如下:

  1. 先判断当前Pod是不是主库(比如执行SELECT @@read_only,主库返回0,从库返回1);
  2. 如果是主库,直接返回成功;
  3. 如果是从库,检查主库(通过Headless Service的DNS:D-1.database-service-headless.default.svc.cluster.local)是否可达、是否还是主库;如果主库不可用,从库返回成功,否则返回失败。

示例的就绪探针配置(放到StatefulSet的Pod模板里):

readinessProbe:
  exec:
    command:
    - sh
    - -c
    - |
      # 用环境变量传递数据库密码,避免硬编码
      IS_MASTER=$(mysql -u root -p"${MYSQL_ROOT_PASSWORD}" -e "SELECT @@read_only" -sN)
      # 当前是主库,直接就绪
      if [ "$IS_MASTER" = "0" ]; then
        exit 0
      fi
      # 检查主库是否可达(用数据库连接测试更准确)
      mysql -u root -p"${MYSQL_ROOT_PASSWORD}" -h D-1.database-service-headless.default.svc.cluster.local -e "SELECT 1" > /dev/null 2>&1
      if [ $? -ne 0 ]; then
        # 主库连不上,从库就绪
        exit 0
      else
        # 主库正常,从库不就绪
        exit 1
      fi
  initialDelaySeconds: 30
  periodSeconds: 5
  failureThreshold: 2

这里的database-service-headless是你原来的Headless Service名称,用来直接解析到D-1 Pod的IP。

第二步:创建普通ClusterIP Service作为流量入口

不要直接用Headless Service做外部访问入口,而是创建一个普通的ClusterIP Service,标签选择器和你的StatefulSet的Pod标签一致:

apiVersion: v1
kind: Service
metadata:
  name: database-service
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: my-db # 替换成你的StatefulSet的Pod标签
  ports:
  - port: 3306
    targetPort: 3306

这样,当D-1正常时,它的就绪探针成功,会被加入这个Service的端点列表,所有访问database-service.default.svc.cluster.local的流量都会打到D-1;当D-1故障(Pod崩溃或者就绪探针失败),kube-proxy会把它从端点列表移除,此时D-2的就绪探针检测到主库不可用,返回成功,就会被加入端点列表,流量自动切换到D-2。

可选优化:用Operator实现更可靠的切换

如果是生产环境,我更推荐用数据库Operator(比如MySQL Operator、PostgreSQL Operator)来管理主从集群。这些Operator会自动处理主从切换、更新Service端点,不需要你自己写就绪探针脚本,逻辑更健壮,还能处理比如主库恢复后的回切、从库晋升等复杂场景。

内容的提问来源于stack exchange,提问作者akuscu

火山引擎 最新活动