searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

基于k8s informer机制的PostgreSQL读写分离方案

2023-05-19 08:09:16
75
0
 

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: 	
             .......
      
  • 自动切主流程
    • 首先使用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之间切换时,用户不感知;
0条评论
0 / 1000
l****n
3文章数
0粉丝数
l****n
3 文章 | 0 粉丝
原创

基于k8s informer机制的PostgreSQL读写分离方案

2023-05-19 08:09:16
75
0
 

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: 	
             .......
      
  • 自动切主流程
    • 首先使用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之间切换时,用户不感知;
文章来自个人专栏
存储-异步远程复制
3 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0