postgresql 高可用_Postgresql高可用实现方案

本文主要讲解一种Postgresql高可用实现方案。由于项目需要,我们需要在短时间实现底层Postgresql DB的高可用,而网络上大多数的解决方案比较复杂,需要深入了解Postgresql。

背景

我们原先只是使用Postgresql DB来存放一些kong的配置信息,作为单实例以k8s pod的形式进行部署使用。这样,在项目中postgresql DB就存在单点故障的隐患。为了解决这个问题,我们需要实现高可用的Postgresql。本文中,利用PostDock来实现master-slave架构的Postgresql高可用集群。

下面是实现后的架构图。

bac1a1a9939cef201462652a6628ea97.png

PostDock

PostDock项目的代码参见GitHub项目PostDock,该项目在云和docker环境中提供高可用和自愈作用的Postgresql集群。

项目中使用PostDock提供的k8s中的样例example2-single-statefulset并对其进行了简化。简化内容如下:

  • 使用hostPath来替换样例中的PVC;
  • 去除样例中的configMap和secret,将其直接配置在SatatefulSet和Deployment中;
  • 将所有k8s资源创建在default namespace,不单独创建其他namespace;

简化后的PostDock样例包含2个service,一个具有3个Pod的Postgresql StatefulSet和一个具有2个Pod的Pgpool Deployment。postgresql service为headless service,对应一个postgresql statefuleset,其中包含3个Postgresql Pod,1个master DB,2个slave DB; pgpool service对应一个pgpool deploy,其中包含2个pgpool pod。

-Postgresql Service

apiVersion

-Postgresql SatetfulSet

apiVersion

-pgpool service

apiVersion

-pgpool Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: postgresql-database-pgpool
  labels:
    name: database-balancer
    node: pgpool
    system: postgresql
    app: postgresql
spec:
  replicas: 2
  revisionHistoryLimit: 5
  selector:
    matchLabels:
      name: database-balancer
      node: pgpool
      system: postgresql
      app: postgresql
  template:
    metadata:
      name: database-pgpool
      labels:
        name: database-balancer
        node: pgpool
        system: postgresql
        app: postgresql
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                name: database-balancer
      		node: pgpool
      		system: postgresql
      		app: postgresql
            namespaces:
            - default
            topologyKey: kubernetes.io/hostname
      containers:
      - name: database-pgpool
        image: postdock/pgpool:latest-pgpool37-postgres11
        livenessProbe:
          exec:
            command: ['bash', '-c', '/usr/local/bin/pgpool/has_write_node.sh && /usr/local/bin/pgpool/has_enough_backends.sh']
          initialDelaySeconds: 600
          timeoutSeconds: 10
          periodSeconds: 30
          successThreshold: 1
          failureThreshold: 3
        imagePullPolicy: Always
        resources:
          requests:
            memory: "100Mi"
            cpu: "100m"

        ports:
          - containerPort: 5432
        env:
          - name: "CONFIGS"
            value: "num_init_children:60,max_pool:4,client_idle_limit:900,connection_life_time:300"
          - name: "PCP_USER"
            value: "pcp_user"
          - name: "PCP_PASSWORD"
            value: "pcp_pass"

          - name: "CHECK_USER"
            value: "rain"
          - name: "CHECK_PASSWORD"
            value: "passwd"
          - name: "DB_USERS"
            value: "wide:pass"
          - name: "BACKENDS"
            value: "0:postgresql-db-node-0.postgresql-db-service:5432:1:/var/lib/postgresql/data:ALLOW_TO_FAILOVER,1:postgresql-db-node-1.postgresql-db-service:5432:1:/var/lib/postgresql/data:ALLOW_TO_FAILOVER,2:postgresql-db-node-2.postgresql-db-service:5432:1:/var/lib/postgresql/data:ALLOW_TO_FAILOVER"

将上面的yaml文件使用kubectl命令执行后生成像一个的k8s resource。

kubectl apply -f *.yaml

连接Postgresql高可用集群

创建高可用集群后,我们需要验证高可用集群的可用性,即在master, slave DB挂掉后,Postgresql集群是否能正确竞选出新的master DB并继续提供DB服务。

-安装Postgresql client

为了验证Postgresql高可用集群,我们需要赞centos7上安装postgresql client来连接DB。

centos7上安装postgresql: 详情参见安装Postgresql。注意:需要安装相同版本的Postgresql Client才能访问DB。

yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm -y
yum install postgresql11-server -y

注意:若安装过程中出现证书问题,请检查机器的系统时间date,因为证书有时效性,系统时间不对可能导致证书验证不通过。关于如何修改和同步集群上的时间,请参考之前的文章服务集群时间同步 - Chrony。

卸载Postgresql DB,使用下面的命令。

yum remove postgresql*

-获取DB及Service信息

为了访问创建的Postgresql集群,需要获得我们之前创建的k8s资源信息。访问Postgresql集群,可以通过Service postgresql-pgpool-service来访问,也可以直接访问postgresql-db-node pod来访问,只是直接访问pod会因为访问的不是master DB时而不能执行写操作。

| grep postgresql
postgresql-db-service-sophondeps2             ClusterIP   None           <none>        5432/TCP                                                      20h
postgresql-pgpool-service-sophondeps2         ClusterIP   10.10.10.129   <none>        5432/TCP                                                      20h

$ kubectl get po -o wide 

通过上面信息,我们可以通过下面命令来连接Postgresql集群。在输入密码passwd后以user rain的身份连接到DB rain。当然,也可以通过pod的IP作为这里的host来分别访问集群下的指定DB,master DB和slave DB,只是访问slave DB时不能执行写操作。

$ psql -h 10.10.10.129 -p 5432 -U rain rain
用户 sophon 的口令:passwd

注意:Postgresql DB通常都会创建user postgres及其相应DB,有时需要使用下面命令切换到该用户来连接DB。

sudo -i -u postgres

创建Postgresql cluster后,我们需要连接DB后执行下面的命令来获得高可用cluster的架构信息。由下面的信息可知,3个Postgresql Pod正在执行,其中postgresql-db-node-sophondeps2-0 为master DB,其他2个为slave DB。

sophon

-使用DB

在master-slave架构中,master负责读写,而slave只负责读取。下面时操作Postgresql DB的一些常规操作,SQL跟其他DB一样。

查看所有DB:
list

查看当前连接的DB信息:
conninfo

切换当前DB到DBNAME:
c DBNAME

查看DB中table:
d

查看表结构,相当于desc tblname,show columns from tbname
d tblname
di 查看索引 

退出 q

验证Postgresql Cluster高可用

高可用验证当master DB或slave DB挂掉时,cluster仍然能够继续使用。

-slave DB挂掉

我们使用下面命令来停止slave DB. 我们发现slave DB所在Pod挂掉后,马上会被重新创建(k8s中statefulset中的pod会被重建),新创建的Pod具有相同的名称,并被作为slave DB重新添加进cluster。而且,在slave DB挂掉重启的整个过程,Postgresql cluster仍然能够正常访问。

kubectl delete po postgresql-db-node-sophondeps2-2

-master DB挂掉

我们使用下面命令来停止master DB.

kubectl delete po postgresql-db-node-sophondeps2-0

master DB挂掉后,Postgresql Cluster会在剩下的2个slave DB中选取master DB, 新master DB来负责读写,而且pgpool service会自动使用新master。旧master DB会因为重启失败进入Error状态,不会重新回到Postgresql Cluster。

此时,Postgresql cluster的架构如下。发现postgresql-db-node-0为down状态,cluster只有2个DB,其中postgresql-db-node-1为master DB,而postgresql-db-node-2为slave DB.

sophon=# select * from repmgr.nodes;
 node_id | upstream_node_id | active |      node_name       |  type   | location | priority |                                                       conninfo                                                
        | repluser |    slot_name     |   config_file    
---------+------------------+--------+----------------------+---------+----------+----------+---------------------------------------------------------------------------------------------------------------
--------+----------+------------------+------------------
    1001 |                  | t      | postgresql-db-node-1 | primary | default  |      100 | user=sophon password=passwd host=postgresql-db-node-1.postgresql-db-service dbname=sophon port=5432 connect_ti
meout=2 | sophon   | repmgr_slot_1001 | /etc/repmgr.conf
    1002 |             1001 | t      | postgresql-db-node-2 | standby | default  |      100 | user=sophon password=passwd host=postgresql-db-node-2.postgresql-db-service dbname=sophon port=5432 connect_ti
meout=2 | sophon   | repmgr_slot_1002 | /etc/repmgr.conf

sophon=# select * from pg_stat_replication;
 pid | usesysid | usename |   application_name   |  client_addr   | client_hostname | client_port |         backend_start         | backend_xmin |   state   | sent_lsn  | write_lsn | flush_lsn | replay_ls
n | write_lag | flush_lag | replay_lag | sync_priority | sync_state 
-----+----------+---------+----------------------+----------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------+----------
--+-----------+-----------+------------+---------------+------------
 682 |    16386 | sophon  | postgresql-db-node-2 | 100.111.40.224 |                 |       57974 | 2020-06-23 08:45:59.437912+00 |              | streaming | 0/50415E0 | 0/50415E0 | 0/50415E0 | 0/50415E0
  |           |           |            |             0 | async

sophon=# show pool_nodes;
 node_id |                  hostname                  | port | status | lb_weight |  role   | select_cnt | load_balance_node | replication_delay 
---------+--------------------------------------------+------+--------+-----------+---------+------------+-------------------+-------------------
 0       | postgresql-db-node-0.postgresql-db-service | 5432 | down   | 0.333333  | standby | 3          | false             | 0
 1       | postgresql-db-node-1.postgresql-db-service | 5432 | up     | 0.333333  | primary | 6          | true              | 0
 2       | postgresql-db-node-2.postgresql-db-service | 5432 | up     | 0.333333  | standby | 3          | false             | 0

-将旧master DB重新添加进cluster

将旧master DB重新添加进cluster需要人工干预。干预过程如下:

  • 将另一个slave所在节点的/var/project-deps/postgresql-data中所有文件使用ssh拷贝到旧master DB所在host对应的位置(先删除原来所有文件)。

下面命令在另一个slave DB所在host上执行,其中10.11.50.15为旧master DB所在host的IP。

$ cd /var/project_deps/postgresql-data/
$ scp -r ./* root@10.11.50.15:/var/project_deps/postgresql-data/
  • 修改拷贝过来的recovery.conf文件

Master DB下recovery.conf会被修改为recovery.done,recovery.conf文件只出现在slave DB中。将该文件中的primary_slot_name配置删除,并修改primary_conninfo中 的application_name=''postgresql-db-node-0'' 为合适的node name。

修改完,重启该节点的postgresql pod即可。

  • 验证旧master DB作为slave DB添加进cluster

连接新的master DB并执行下面的命令来验证旧master DB已经添加进集群。可知,postgresql-db-node-sophondeps2-0作为slave DB重新添加进cluster。

psql -h 10.11.24.18 -p 5432 -U rain rain 连接主node
sophon=# select * from pg_stat_replication;
  pid  | usesysid | usename |         application_name         | client_addr | client_hostname | client_port |         backend_start         | backend_xmin |   state   | sent_lsn  | write_lsn | flush_lsn 
| replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state 
-------+----------+---------+----------------------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------
+------------+-----------+-----------+------------+---------------+------------
 19046 |    16386 | sophon  | postgresql-db-node-sophondeps2-0 | 10.11.50.12 |                 |       50384 | 2020-07-08 07:34:06.353302+00 |          630 | streaming | 0/50480D0 | 0/50480D0 | 0/50480D0 
| 0/50480D0  |           |           |            |             0 | async
 13678 |    16386 | sophon  | postgresql-db-node-sophondeps2-2 | 10.11.24.12 |                 |       38052 | 2020-07-08 07:07:40.862402+00 |              | streaming | 0/50480D0 | 0/50480D0 | 0/50480D0 
| 0/50480D0  |           |           |            |             0 | async
(2 行记录)

sophon=# select * from repmgr.nodes;
 node_id | upstream_node_id | active |            node_name             |  type   | location | priority |                                                                   conninfo                        
                                            | repluser |    slot_name     |   config_file    
---------+------------------+--------+----------------------------------+---------+----------+----------+---------------------------------------------------------------------------------------------------
--------------------------------------------+----------+------------------+------------------
    1001 |                  | t      | postgresql-db-node-sophondeps2-1 | primary | default  |      100 | user=sophon password=passwd host=postgresql-db-node-sophondeps2-1.postgresql-db-service-sophondeps
2 dbname=sophon port=5432 connect_timeout=2 | sophon   | repmgr_slot_1001 | /etc/repmgr.conf
    1002 |             1001 | t      | postgresql-db-node-sophondeps2-2 | standby | default  |      100 | user=sophon password=passwd host=postgresql-db-node-sophondeps2-2.postgresql-db-service-sophondeps
2 dbname=sophon port=5432 connect_timeout=2 | sophon   | repmgr_slot_1002 | /etc/repmgr.conf
    1000 |             1001 | t      | postgresql-db-node-sophondeps2-0 | standby | default  |      100 | user=sophon password=passwd host=postgresql-db-node-sophondeps2-0.postgresql-db-service-sophondeps
2 dbname=sophon port=5432 connect_timeout=2 | sophon   | repmgr_slot_1000 | /etc/repmgr.conf
(3 行记录)

数据迁移

高可用的Postgresql cluster创建好之后,我们需要将之前的单个postgresql DB中的老数据迁移到新创建的Postgresql Cluster中。

数据迁移使用pg_dump和psql命令来完成。使用pg_dump命令来将旧数据库中的老数据dump到文件中,然后在新数据库中使用psql来导入老数据。

注意:pg_dump需要使用相同版本,否则不能dump。同时,数据导入应该使用postgresql cluster的master DB进行导入,slave DB不能执行写操作。

-老数据库的数据导出

下面示例,连接到老数据库并导出数据库rain中的所有数据到文件rain.sql中。

"host=100.120.8.201 port=5432 user=rain password=passwd dbname=rain" -f rain.sql

-新数据库导入数据

我们需要连接新数据库并导入所有数据。

$ psql -h 10.96.91.35 -p 5432 -U rain rain < rain.sql

至此,我们完成了Postgresql DB的高可用架构并完成老数据迁移。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值