PostgreSQL 教程: 使用虚拟 IP 实现高可用

二月 27, 2025

摘要:在本教程中,我们将学习如何在 PostgreSQL 中使用虚拟 IP 实现高可用。

目录

在节点上设置虚拟 IP (VIP)

虚拟 IP 基本上是在服务器上配置的一个辅助 IP,可用于连接到实例。我们将使用这种 VIP 从应用端连接到主数据库。这会将通过 VIP 进来的连接重新路由到指向主数据库的网络接口,以完成切换。

如果发生灾难,repmgr 守护进程会记录主节点宕机。在确认无法访问主数据库后,它会将备用实例提升为主实例。在新的主节点启动后,我们将更新路由表,以重新路由 VIP 以连接到新的主节点,以便应用程序继续通过相同的 IP 地址获取数据。

要设置一个虚拟 IP,首先我们需要确保,用作 VIP 的 IP 不在底层 VPC 或虚拟网络允许的 IP 范围内,这样它就不会与 VPC 上的任何未来分配的 IP 发生冲突。为了定义 VIP,我们将使用 Linux 系统中管理网络的netplan工具。我们需要创建一个新的netplan配置,以便在复制集群中的所有服务器都附加上故障转移的 IP,如下所示:

  • 以 root 用户身份登录服务器。

  • 创建配置文件/etc/netplan/60-failover-ip.yaml,以设置故障转移的 IP。

  • 在配置文件中添加下面内容,其中的 virtual.ip 表示我们用作节点上故障转移 IP 的虚拟 IPv4 地址:

    network: version: 2 ethernets: eth0: addresses: - virtual.ip/32
    
  • 使用下面的命令应用网络配置:

    sudo netplan apply
    

您可以使用ifconfig验证新的 VIP 是否已添加到服务器,以检查网络配置。

在配置新的 VIP 后,我们需要创建路由,使此 VIP 指向路由表中主节点的网络接口。对于所有关联的路由表,您可以使用路由表服务下的 AWS 控制台,也可以使用下面的 AWS CLI 命令,来执行这个操作:

aws ec2 create-route --route-table-id $route_table_id --destination-cidr-block virtual.ip/32 --network-interface-id $network_interface_id

实施 VIP 故障转移设置时要检查的一个注意事项是,在 AWS 控制台上禁用 EC2 实例的 “来源地址/目的地址” 的检查。该检查可以确保 EC2 实例发送或接收的任何流量的 来源地址 或 目的地址。通过禁用该检查,我们可以允许 EC2 实例使用 VIP 发送/接收流量,而不是其原始的 IP。

以下是使用 AWS CLI 快速检查和更新 EC2 实例的 “来源地址/目的地址” 检查的一个方法:

  1. 检查 “来源地址/目的地址” 检查属性的当前状态:

    aws ec2 describe-instance-attribute --instance-id $AWS_INSTANCE_ID --attribute sourceDestCheck
    
  2. 禁用 “来源地址/目的地址” 检查:

    aws ec2 modify-instance-attribute --instance-id $AWS_INSTANCE_ID --no-source-dest-check
    

处理 repmgr 事件以执行 VIP 故障转移

在一次灾难或手动切换期间,我们需要将 VIP 切换到路由表中的新的主节点,以便应用程序可以继续访问数据。更新路由表和将弹性 IP 重新附加到新的主数据库的过程,可以使用守护进程记录和监控的 repmgr 事件来自动化。我们可以设置一个 repmgr 的钩子脚本来执行该脚本,该脚本将更新路由表以指向新的主节点的网络接口。我们可以使用下面的参数,在 repmgr 配置文件中配置 repmgr 事件钩子脚本:

event_notifications = 'standby_register, standby_promote, primary_register, node_rejoin, standby_switchover'
event_notification_command = '/etc/repmgr/repmgr-hook.sh %n %e %s "%t" "%d" >/tmp/repmgr-hook.out.$$ 2>&1'

此处,event_notifications 参数控制事件列表,repmgr 守护进程可以为这些事件调用 event_notification_command。

下面是 event_notification_command 的格式:

  • %n - 节点 ID
  • %e - 事件类型
  • %s - 成功(1)或失败(0)
  • %t - 时间戳
  • %d - 详细信息

使用事件通知,我们可以从钩子脚本调用 AWS CLI 命令,以便在 repmgr 观察到 standby_promote 事件时更新路由表和关联弹性 IP。如果数据库可以从外部访问,我们还可以使用钩子脚本中的 CLI 命令更新 DNS 中域名映射地址。

以下是使用 AWS CLI 更新路由表关联的示例命令,以将 VIP 重定向到新的主节点的网络接口:

aws ec2 replace-route --route-table-id $route_table_id --destination-cidr-block virtual.ip/32 --network-interface-id $network_interface_id_primary

一旦一切都配置完毕,并且您能够使用虚拟 IP 连接到主数据库,我们就可以更新应用端的连接字符串,以指向 VIP 以连接到数据库。在完成这个操作后,您的应用程序仍将使用相同的 VIP 连接到主节点,并且 repmgr 守护进程会调用 repmgr 钩子脚本,来处理将 VIP 指向当前主节点的任务。

处理脑裂的情况

在脑裂场景中,在故障转移完成后再次启动旧的主数据库,应用程序仍会指向正确的新的主节点,因为 VIP 正在将连接路由到新的主节点的网络接口。这可以防止应用程序连接到重启的故障节点产生数据完整性的问题。

使用 repmgr 检查,我们也可以监控这种脑裂情况,其中 repmgr 检测到复制已损坏,并且两个节点都作为主节点工作。在这种情况下,旧的主节点中的repmgr service status命令,会将备用节点的当前状态显示为 “running as primary”,如下所示:

postgres@test_node_1:~$ repmgr service status -f /etc/repmgr/repmgr.conf
 ID | Name        | Role    | Status               | Upstream | repmgrd | PID  | Paused? | Upstream last seen
----+-------------+---------+----------------------+----------+---------+------+---------+--------------------
 1  | Test_node_1 | primary | * running            |          | running | 4000 | no      | n/a
 2  | Test_node_2 | standby | ! running as primary |          | running | 2011 | no      | n/a

WARNING: following issues were detected
  - node "Test_node_2" (ID: 2) is registered as standby but running as primary

当复制集群处于这种状态时,我们可以使用repmgr node rejoin强制旧的主节点作为备用节点重新加入复制集群。以下是使用node rejoin命令将旧的主节点重新添加为备用节点的示例:

postgres@test_node_1:~$ repmgr node rejoin --force-rewind -f /etc/repmgr/repmgr.conf -h test_node_2 -U repmgr
NOTICE: rejoin target is node "Test_node_2" (ID: 2)
NOTICE: pg_rewind execution required for this node to attach to rejoin target node 2
DETAIL: rejoin target server's timeline 29 forked off current database system timeline 28 before current recovery point 0/56000028
NOTICE: executing pg_rewind
DETAIL: pg_rewind command is "/usr/lib/postgresql/11/bin/pg_rewind -D '/var/lib/postgresql/11/main' --source-server='host=test_node_2 user=repmgr dbname=repmgr port=5432 connect_timeout=2'"
NOTICE: 0 files copied to /var/lib/postgresql/11/main
NOTICE: setting node 1's upstream to node 2
WARNING: unable to ping "host=test_node_1 user=repmgr dbname=repmgr port=5432 connect_timeout=2"
DETAIL: PQping() returned "PQPING_NO_RESPONSE"
NOTICE: starting server using "sudo systemctl start postgresql@11-main"
INFO: executing notification command for event "node_rejoin"
DETAIL: command is: /etc/repmgr/repmgr-agent.sh 1 node_rejoin 1 "2022-12-31 16:59:19.837178+00" "node 1 is now attached to node 2" >/tmp/repmgr-agent.out.$$ 2>&1
WARNING: unable to execute event notification command
DETAIL: parsed event notification command was: /etc/repmgr/repmgr-agent.sh 1 node_rejoin 1 "2022-12-31 16:59:19.837178+00" "node 1 is now attached to node 2" >/tmp/repmgr-agent.out.$$ 2>&1
NOTICE: NODE REJOIN successful
DETAIL: node 1 is now attached to node 2

执行完成后,您应该会看到旧的主节点作为备用节点重新加入到复制集群中。

postgres@test_node_1:~$ repmgr service status -f /etc/repmgr/repmgr.conf
 ID | Name        | Role    | Status    | Upstream    | repmgrd | PID  | Paused? | Upstream last seen
----+-------------+---------+-----------+-------------+---------+------+---------+--------------------
 1  | Test_node_1 | standby |   running | Test_node_2 | running | 4000 | no      | 1 second(s) ago
 2  | Test_node_2 | primary | * running |             | running | 2011 | no      | n/a

在脑裂场景期间监控和实施节点的重新加入也可以自动进行,以支持 HA 集群的容错能力,使其成为关键业务负载的可靠解决方案。

至此,我们已经在此集群中成功实现了带容错的高可用。此集群可以自动检测并快速从任何类型的故障中恢复,同时将对最终用户的干扰最小化。该集群还具有高度可靠性,它可以检测集群上的危险场景(如脑裂)并成功恢复,而不会影响用户体验。该集群现在已进行了足够的强化,可提供接近 100% 的可用性,并在处理关键业务负载时将中断降至最低。

了解更多

PostgreSQL 管理

使用 repmgr 实现自动故障转移