1、简介
本文介绍了基于k8s watch informer机制的PostgreSQL数据库的读写分离方案
2、方案介绍
- k8s的informer机制
- k8s的informer机制工作流程如上图所示,包括informer和controller两部分;
- informer 核心组件为Reflector,首先于k8s的apiserver建立连接,ListAndWatch会通过apiservice列举集群内某个资源的所有的实例;
- watch方法会监听对应类型资源的变化,如果某个资源发生了变化(例如某类资源发生了创建、删除和更新操作),Reflector会接收到对应的信号通知,并将接收到的资源的信息放入deltafifo中,通过Controller做对应的资源的处理;
- postgreSQL资源部署:
- 两个service,分别为pg-service-primary和pg-service-replica,pg-service-primary作为对外提供写的服务,pg-service-replica作为对外提供读的服务;
主从service的部署文件如下: # 指向主pod的service的yaml文件 apiVersion: v1 kind: Service metadata: name: demo pg-service-primary namespace: pg # 将所有的资源都部署在名称为pg的namespace下,资源隔离 labels: - name: pg-service-primary spec: type: ClusterIP ports: - port: 6432 # 对外提供服务的端口为6432 targetPort: 5432 # 指向pod对外暴露的端口5432 protocol: TCP # 端口协议,支持TCP或UDP,默认TCP name: pg_port # 端口名称 selector: # label选择器,该service始终指向label为primary的pod role: primary deploy_name: pg-ha # 指向从pod的service的yaml文件 apiVersion: v1 kind: Service metadata: name: pg-service-replica namespace: pg # 将所有的资源都部署在名称为pg的namespace下,资源隔离 labels: - name: pg-service-replica spec: type: ClusterIP ports: - port: 6432 # 对外提供服务的端口为6432 targetPort: 5432 # 指向pod对外暴露的端口5432 protocol: TCP # 端口协议,支持TCP或UDP,默认TCP name: pg_port # 端口名称 selector: # label选择器,该service始终指向label为replica的pod role: replica deploy_name: pg-ha
- 两个pod,采用两个deployment进行部署,其中运行postgresql进程;
-
# 定义主deployment的yaml,细节省略,保留pod与service互联部分的label apiVersion: apps/v1 kind: Deployment metadata: name: pg-node1 namespace: pg # 与service部署在相同的namespace下; labels: deployment_name: pg_ha spec: replicas: 1 revisionHistoryLimit: 3 selector: matchLabels: deployment_name: pg_ha template: labels: # 此处定义的是pod运行阶段的labels,此处的label与service的labelSelector保持一致 deployment_name: pg_ha role: primary spec: ....... # 定义从deployment的yaml,细节省略,保留pod与service互联部分的label apiVersion: apps/v1 kind: Deployment metadata: name: pg-node1 namespace: pg # 与service部署在相同的namespace下; labels: deployment_name: pg_ha spec: replicas: 1 revisionHistoryLimit: 3 selector: matchLabels: deployment_name: pg_ha template: labels: # 此处定义的是pod运行阶段的labels,此处的label与service的labelSelector保持一致 deployment_name: pg_ha role: replica spec: .......
- 两个service,分别为pg-service-primary和pg-service-replica,pg-service-primary作为对外提供写的服务,pg-service-replica作为对外提供读的服务;
- 自动切主流程
- 首先使用go语言编写operator,我们以crunchydata的开源的operator的代码为参考,在onAdd,onUpdate中对label的切换进行处理:
// Controller holds the connections for the controller type Controller struct { Client *kubeapi.Client Informer coreinformers.PodInformer } // onAdd is called when a pod is added func (c *Controller) onAdd(obj interface{}) { newPod := obj.(*apiv1.Pod) newPodLabels := newPod.GetObjectMeta().GetLabels() // TODO:新建pod的处理,查询当前的主从节点,对pod打label } // onUpdate is called when a pod is updated func (c *Controller) onUpdate(oldObj, newObj interface{}) { ctx := context.TODO() oldPod := oldObj.(*apiv1.Pod) newPod := newObj.(*apiv1.Pod) newPodLabels := newPod.GetObjectMeta().GetLabels() ... // TODO: update场景,查询当前的主从节点,对pod打label }
- 对于新建pod,判断是否是postgresql数据库所在的pod,如果是调用SQL指令SELECT pg_is_in_recovery()查看当前的主节点,对主pod打上role=primary的label,对当前的备pod打上role=replica的label
- 对外提供pg_service_primary的域名作为写数据库的域名,对外提供pg_service_replica的域名作为读数据库的域名,pg_service_primary流量始终指向role=primary的pod,pg_service_replica的请求始终转发给role=replica的pod,当集群中有pod的启停,导致数据库的主从在两个pod之间切换时,用户不感知;
- 首先使用go语言编写operator,我们以crunchydata的开源的operator的代码为参考,在onAdd,onUpdate中对label的切换进行处理: