Patroni中对主备切换、故障转移和命令行的流程整理

Patroni中对主备切换、故障转移和命令行的流程整理

1. 主备切换

主备切换(Switchover)是高可用性(High Availability,HA)系统中的一个重要操作,常见于数据库集群或分布式系统中。在主备架构中,通常有一个主节点和一个或多个备节点,备节点会复制主节点的数据并保持同步。

主备切换通常有两种操作模式:手动主备切换和自动主备切换,具体取决于故障情况或系统配置。

1.1 手动

1.1.1 介绍

手动主备切换是由管理员主动执行的操作,通常用于非故障情况下的维护、升级或负载均衡。

场景

  • 维护和升级:当管理员需要对主节点进行维护或升级时,可以手动将备节点提升为主节点。
  • 负载均衡:当主节点负载过高时,管理员可以主动进行主备切换,将负载均衡到其他节点。
  • 灾难恢复演练:在测试或演练灾难恢复流程时,也需要手动触发主备切换。

操作流程

  1. 监控状态:首先确认当前主节点的健康状态,确保备节点已同步。
  2. 切换操作:管理员通过集群管理工具(如 Patronipg_ctlsystemctl 等)手动发起主备切换。
  3. 验证切换:验证新的主节点是否正常工作,确认系统切换成功。

优点

  • 控制性强:管理员可以灵活选择切换时机。
  • 低风险:在非故障情况下进行切换,可以避免不必要的风险。

缺点

  • 需要人工干预,可能会出现操作延迟或人为失误。

1.1.2 流程解析

1.1.2.1 请求发送和解析

手动进行主备切换即使用switchover命令。

patronictl -c /usr/local/patroni/patroni_pg.yml switchover demo
patronictl -c /usr/local/patroni/patroni_pg.yml switchover demo --group 0

进入ctl.py文件的switchover函数,其中调用_do_failover_or_switchover函数来实现故障转移和主备切换。在其中整理当前主节点信息、要切换的候选节点信息和调度时间等信息,再向 REST API 发送请求,会发送/switchover或者/failover请求路径。在api.py中发现两者都是使用do_POST_failover函数来处理这两个请求。

发送请求的代码如下:

        member = cluster.leader.member if cluster.leader else candidate and cluster.get_member(candidate, False)
        if TYPE_CHECKING:  # pragma: no cover
            assert isinstance(member, Member)
        r = request_patroni(member, 'post', action, failover_value)

        # probably old patroni, which doesn't support switchover yet
        if r.status == 501 and action == 'switchover' and b'Server does not support this operation' in r.data:
            r = request_patroni(member, 'post', 'failover', failover_value)

do_POST_failover函数会对switchoverfailover请求解析请求体的信息,然后做一系列的验证,如果验证都通过,则根据请求体中提供的信息执行手动的主备切换或者故障转移操作,并且注册到 DCS 中。其中调用 DCS 中的manual_failover将操作信息注册到 DCS 中。在is_failover_possible函数中进行检查故障转移或主备切换操作的状态,直到操作完成或超时,返回相应的状态信息。

将操作注册到 DCS 的代码如下:

            if self.server.patroni.dcs.manual_failover(leader, candidate, scheduled_at=scheduled_at):
                self.server.patroni.ha.wakeup()
                if scheduled_at:
                    data = action.title() + ' scheduled'
                    status_code = 202
                else:
                    status_code, data = self.poll_failover_result(cluster.leader and cluster.leader.name,
                                                                  candidate, action)

主备切换的请求发送流程图如下:

1.1.2.2 ha循环中切换

在发送和解析完请求之后,会将信息注册到 DCS 中,在patroni开启的主循环中,使用ha类的_run_cycle循环函数遍历节点信息,来进行切换操作。

_run_cycle函数中会对各种条件进行判断从而判断当前节点应该处于什么操作中,在这个函数中可以对新节点进行初始,也会让当前用于领导者锁的故障节点释放领导者锁从而触发一个新的主节点提升,从而完成故障转移的流程,并且在这个函数中也调用了recover函数来恢复 pg 实例。在其中也有一段对集群状态的判断和切换,这代应该是对不健康集群进行主备切换或故障转移的部分。代码如下:

if self.cluster.is_unlocked():
    ret = self.process_unhealthy_cluster()
else:
    msg = self.process_healthy_cluster()
    ret = self.evaluate_scheduled_restart() or msg

并且在主备切换或故障转移中对复制槽进行复制。代码如下:

            is_promoting = self._async_executor.scheduled_action == 'promote'
            if (not self._async_executor.busy or is_promoting) and not self.state_handler.is_starting():
                create_slots = self._sync_replication_slots(False)

                if not self.state_handler.cb_called:
                    if not is_promoting and not self.state_handler.is_primary():
                        self._rewind.trigger_check_diverged_lsn()
                    self.state_handler.call_nowait(CallbackAction.ON_START)

                if not is_promoting and create_slots and self.cluster.leader:
                    err = self._async_executor.try_run_async('copy_logical_slots',
                    								self.state_handler.slots_handler.copy_logical_slots,
                                                    args=(self.cluster, self.patroni, create_slots))

其中重点实现的函数为process_unhealthy_clusterprocess_healthy_cluster。这也是实现自动主备切换和故障转移的函数。

ha主循环的流程图如下:

  • process_unhealthy_cluster

处理不健康的集群所调用的函数,主要进行故障转移的操作。通过判断当前节点是否是最健康的节点,来判断是否来进行故障转移,然后获取主节点锁,尝试将当前节点提升为主节点(调用enforce_primary_role函数),如果无法提升就会降级为备节点跟随其他节点。

enforce_primary_role函数则是当一个节点赢得领导者选举,满足提升为主节点的条件时执行相关的操作。最终调用 pg 的promote函数提升为主节点。

下图是process_unhealthy_cluster函数的大致流程图。

  • process_healthy_cluster

处理健康汲取你所调用的函数,主要进行角色的变更处理,可以进行主备切换或故障转移。根据集群的健康程度和锁的状态来决定是否需要进行角色变更(提升为主节点、降级为备节点)。其中重要的函数处理即为process_manual_failover_from_leader函数,来处理普通的故障转移和主备切换。

下图是process_healthy_cluster函数的大致流程图。

1.1.2.3 流程分析

经过代码分析整理,后使用不含mpp处理器的配置文件启动patroni,部署一个一主一从的集群,然后执行手动主备切换,发现会经过以下流程:

  1. 发送switchover请求:将请求体内容写入 DCS 中

    [zk: localhost:2181(CONNECTED) 2] get /pgsql/demo/failover
    {"leader":"pgsql1","member":"pgsql2"}
    

    如果在switchover命令行中没有对Primary、Candidate指定而采用默认值的话,不会有member,只会有pgsql1。

    并且这种情况会调用process_unhealthy_cluster函数来处理switchover请求,会当成一个故障转移来处理。

  2. 如果pgsql1要从主库转换为备库,在process_healthy_cluster函数中对pgsql1进行降级。调用process_manual_failover_from_leader函数。下面则是调用process_manual_failover_from_leader函数返回的消息,表明原主pgsql1将会降级自己。

    'switchover: demoting myself'
    

    如果是pgsql1从备库转换为主库,则会在process_healthy_cluster中进行提升操作。会调用其中的enforce_primary_role函数将pgsql1提升为主库,然后在_run_cycle函数中同步复制槽信息,之后会在开启的线程中去执行 pg 中的promote函数使用pg_ctl promote命令提升。

  3. 调用 ha 中的demote函数,对pgsql1进行降级操作,在这个函数中先会停止 pg 数据库,调用 pg 中的stop函数来停止pgsql1的数据库。然后将数据库设置为demoted调用 pg 中的follow函数将pgsql1转换为备库。在这个函数中将会调用start函数来启动数据库实例并将数据库角色设置为replica

  4. 最后在process_healthy_cluster或者是demote函数中,让降级之后的主跟随现在集群的主节点即可。

总结:

  • 故障转移和主备切换有些类似,如果在patronictl switchover中没有指定候选节点的话,就会导致集群的领导节点释放领导者锁之后需要进入process_unhealthy_cluster函数找到最健康的节点提升为主节点(因为此时的集群中leader_name会变为None)。这个主备切换也会被转换为一个特殊的故障转移来处理。
  • 如果指定了候选节点,那么会向当前的主节点发送请求,然后主节点发生降级动作,是否领导者锁,而候选节点则得到领导者锁,并且leader_name变更为候选节点,此时进入process_healthy_cluster对备节点进行提升,然后让降级后的原主跟随提升的现主即可完成一次主备切换过程。
  • 在这个过程调用的各种前后处理函数,可以让数据库在主备切换的时候也让mpp处理器进行切换。

流程整理后的手动主备切换的流程图如下:

1.2 定时切换

switchover命令中指定scheduled参数即可。

查看请求参数:

{'candidate': 'pgsql2', 'leader': 'pgsql1', 'scheduled_at': '2024-11-23T23:20:00+08:00'}

DCS 中存储的failover值:

{"leader":"pgsql1","member":"pgsql2","scheduled_at":"2024-11-23T23:20:00+08:00"}

这个scheduled_at值在process_healthy_cluster函数中调用process_manual_failover_from_leader来解析

        if (failover.scheduled_at and not
            self.should_run_scheduled_action(bare_action, failover.scheduled_at, lambda:
                                             self.dcs.manual_failover('', '', version=failover.version))):
            return

should_run_scheduled_action函数中会计算出预定时间和现在时间的差值,如果十分接近就会睡眠后执行switchover,如果较远就会继续执行主循环而不执行switchover,如果已经过去一段时间,就会清理掉 DCS 中failover中的旧数据。

下面是定时主备切换的执行流程图:

2. 故障转移

2.1 手动

2.1 介绍

手动故障转移是指管理员手动触发故障转移过程。在这种模式下,管理员能够根据当前的系统状况决定何时进行故障转移以及选择哪个节点作为新的主节点。

手动故障转移的流程

  • 管理员介入:管理员通过命令行工具或管理接口触发故障转移。例如,使用 patronictl 命令或 API 手动切换主节点。
  • 选择新主节点:管理员选择哪个备用节点应该成为新的主节点。通常,这个选择是基于节点的健康状况、同步状态和数据一致性来决定的。
  • 故障转移执行:在管理员的控制下,Patroni 会执行角色切换,并将选定的备用节点升级为新的主节点。
  • 恢复操作:类似于自动故障转移,手动故障转移后也可以对故障节点进行恢复操作,使其重新加入集群。

手动故障转移的优点

  • 完全控制:管理员可以完全控制故障转移的时机和选定的节点。这对于确保数据一致性和最小化潜在风险是很重要的。
  • 防止错误的自动切换:如果自动故障转移触发时的情况不理想(例如,主节点不是完全宕机,只是短暂的网络故障),手动故障转移可以防止不必要的切换。
  • 预防数据丢失:管理员可以在进行故障转移前检查集群状态,确保数据已经充分同步,从而避免数据丢失。

手动故障转移的缺点

  • 需要人工干预:手动故障转移需要人工介入,可能会导致故障恢复的时间延迟,特别是在管理员不在现场时。
  • 更高的操作风险:如果管理员不小心选择了错误的节点,可能会导致数据不一致或其他问题。

2.1.2 流程解析

手动进行主备切换即使用failover命令。

patronictl -c /usr/local/patroni/patroni_pg.yml failover demo
patronictl -c /usr/local/patroni/patroni_pg.yml failover demo --group 3

手动failover的过程与手动switchover是的大部分的内容是相同的,即流程基本上差不多。主动故障转移是因为主节点故障,但是从节点服务转换为主节点导致需要手动切换,此时需要选择一个候选节点提升为主节点。手动故障也会发送一个failover请求到api,解析这个请求体也会讲请求体的信息放入DCS中,如果是自动则不会将failover的信息放入DCS。

具体的failover请求的发送和解析请查看1.1.1节。手动故障转移的流程图如下所示:

2.2 自动

2.2.1 介绍

自动故障转移的流程

  • 故障检测:Patroni 会周期性地通过 H/A(High Availability)检测机制 监控主节点的健康状态。如果主节点无法响应,或者无法提供写操作,Patroni 会认为主节点已经宕机。
  • 选举新主节点:当主节点故障后,Patroni 会选择一个健康的备用节点(Replica)作为新的主节点。这个选择的过程是通过集群中的节点达成共识(通常基于领导者选举机制),并根据某些策略(例如,节点的时间线、数据的同步状态等)来决定最合适的候选节点。
  • 角色切换:一旦选举出新的主节点,Patroni 会自动更新节点角色,并通知其他节点进行同步,使其跟随新主节点。
  • 恢复模式:在故障转移完成后,Patroni 会尝试恢复故障节点(如果可能的话),并将其重新加入集群。这个过程通常是通过回滚(rewind)或者重新初始化数据库来完成。

自动故障转移的优点

  • 无人工干预:自动故障转移是完全自动化的,适合需要高可用性、最小化人工干预的场景。
  • 快速恢复:因为自动化完成故障转移,通常能够迅速恢复集群的高可用性。
  • 配置灵活:可以通过配置参数来调整自动故障转移的策略和容忍度。

自动故障转移的缺点

  • 潜在的数据丢失:如果集群中的数据同步策略没有配置得当(如 synchronous replication),自动故障转移可能会导致某些数据丢失,特别是在主节点宕机后没有足够的时间进行数据同步的情况下。
  • 无法预知的选举行为:在某些情况下,自动故障转移的选举过程可能不是理想的,导致不适当的节点被选为新的主节点。

2.2.2 流程解析

将集群中的主节点的patroni进程关闭,即可触发自动故障转移。

故障转移也依赖于1.1.2 节中介绍的process_unhealthy_clusterprocess_healthy_cluster函数。当主节点关闭时,对应的变化会被 watchdog或DCS(zookeeper) 监控到主节点失效,因此导致集群成为不健康集群,在process_unhealthy_cluster函数中选举出最健康的节点,如果可以提升该节点,就调用promote函数将该节点提升为主节点。提升成为主节点之后才会同步复制槽。

当主节点正常关闭时,会执行ha中的shutdown函数来关闭数据库,在这个函数中会对能否进行故障转移进行检查,如果可以在关闭主节点的数据库之后就会释放主节点锁,但是不会显示调用demote函数对主节点进行降级。在原主节点启动之后,在 ha 的主循环中会调用recover函数对原主进行恢复,然后在其中发现原主并没有持有主节点锁了因此会把其作为一个备节点,异步调用 pg 的follow函数跟随现在的主节点(修改原主节点的配置文件)。

        if self._async_executor.try_run_async('restarting after failure', self.state_handler.follow,
                                              args=(node_to_follow, role, timeout)) is None:
            self.recovering = True

自动故障转移的流程图如下所示:

3. 命令行

命令行的命令基本上可以分为两类,一类是发送request请求以完成某些操作,另一类则是通过获取DCS中的信息来从而展示或进行修改。

3.1 管理信息命令

这类命令有list、remove、topology、dsn、edit-config、history、query、show-config、version。

  • list

    list命令的作用是输出指定条件的集群信息。其中output_members函数是重要的输出函数,获取的集群信息都是调用这个函数输出在命令行中。其中便是调用工具模块(utils.py)中的cluster_as_json函数,将一个集群信息转换为处理过后的json字符串,然后从这个json字符串中获取所有的信息输出展示即可。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml list demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml list demo --group 1
    

    list命令的流程图如下所示:

  • remove

    remove命令的作用是删除 DCS 中保存的指定集群的信息。在其中会验证是否使用的是分布式数据库,如果是分布式数据库必须要传入group参数才能删除,删除调用的是 DCS 中实现的删除集群函数delete_cluster。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    remove命令的流程图如下所示:

  • topology

    topology命令的作用是打印给定集群的拓扑结构信息,根据传入的参数不同而展示形态不同。这个命令的实现主要是依赖于list命令,是对list命令输出集群信息的格式的改变。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml topology demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml topology demo --group 1
    

    topology命令的流程图如下所示:

  • dsn

    dsn命令的作用是获取Patroni集群的一个成员的连接字符串。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml dsn demo -r primary
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml dsn demo -r primary --group 1
    

    dsn命令的流程图如下所示:

  • edit-config

    edit-config命令的作用是更改集群的动态配置并更新DCS。这个命令修改的配置是在保存在DCS中,其路径为/namespace/scope/group/config。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml edit-config demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml edit-config --group 1 --pg max_connections="150" demo
    

    edit-config命令的流程图如下所示:

  • history

    history命令的作用是展示集群的历史记录。这个命令查看的信息是保存在 DCS 中的,其路径为/namespace/scope/group/history。获取到这些信息之后,还会创建一个表头,并且根据实际信息来扩展表头的信息以便输出内容的匹配。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml history demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml history demo --group 1
    

    history命令的流程图如下所示:

  • query

    query命令的作用是针对Patroni集群的成员执行SQL命令或脚本。在其中会对 SQL 进行处理,并且根据传入的参数构造连接参数,所以要注意连接参数的传递。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml query demo -U fbase -c "SELECT now()"
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml query demo --group 0 -r primary -U fbase -c "SELECT now()"
    

    query命令的流程图如下所示:

  • show-config

    show-config命令的作用是展示zk中存储的配置信息。其路径为/namespace/scope/group/config。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml show-config demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml show-config demo --group 1
    

    show-config命令的流程图如下所示:

3.2 发送请求命令

这类命令有restart、switchover、failover、pause、resume、reinit、reload、flush。

  • restart

    restart命令的作用是对指定的成员或者集群进行重启数据库操作(可以预定计划)。在其中如果之前有预定的重启计划,会删除之前的重启计划,再发送当前的重启计划。

            if 'schedule' in content:
                if force and member.data.get('scheduled_restart'):
                    r = request_patroni(member, 'delete', 'restart')
                    check_response(r, member.name, 'flush scheduled restart', True)
    
            r = request_patroni(member, 'post', 'restart', content)
    

    使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml restart demo pgsql1
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    restart命令的流程图如下所示:

  • switchover

    switchover命令的作用是对指定的集群进行主备切换操作(可以预定计划)。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml switchover demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml switchover demo --group 1
    

    switchover命令的流程在1.1.2中已经详细介绍。

  • failover

    failover命令的作用对指定的集群进行故障转移操作(可以预定计划)。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    failover命令的流程在1.1.2中已经详细介绍。

  • pause

    pause命令的作用是暂时将指定集群置于维护模式并禁用自动故障转移。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    pause命令的流程图如下所示:

  • resume

    resume命令的作用是用于恢复指定集群的自动故障转移功能。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    resume命令的流程图与pause一致,只是调用toggle_pause传递的参数不同,pause传递的pauseTrueresume传递的为False

  • reinit

    reinit命令的作用是将指定的备库进行重新初始化。这个命令只针对备库,只有备库才能重新初始化,在这个命令行会对符合条件的备库都发送一个重新初始化的请求,等待所有接单重新初始化之后,会发送一个get请求查看是否初始化完成。

    	r = request_patroni(member, 'post', 'reinitialize', body)
           
    	data = json.loads(request_patroni(member, 'get', 'patroni').data.decode('utf-8'))
    

    使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    reinit命令的流程图如下所示:

  • reload

    reload命令的作用是当修改了某个节点patroni的配置文件时,使用该命令来重新加载配置文件。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
    

    reload命令的流程图如下所示:

  • flush

    flush命令的作用是丢弃计划的事件(如果有)。这个命令目前只支持放弃restart或者switchover计划,会先遍历找到的所有符合条件的成员(switchover计划会首选主节点),然后判断该成员是否有这些计划,有的话就会发送一个delete形式的request来放弃这些计划,在放弃switchover计划之后还会清理DCS中保存的信息。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml flush demo restart -r replica
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml flush demo switchover
    

    flush命令的流程图如下所示:

  • version

    version命令的作用是展示指定成员的patronictlpostgresql的版本。在其中会先输出当前patronictl的版本,然后查找到指定成员,发送request请求来获取成员的 pg 的版本信息。使用命令如下所示:

    patronictl -c /usr/patroni/conf/patroni_postgresql.yml version demo
    patronictl -c /usr/patroni/conf/patroni_postgresql.yml version demo --group 1
    

    version命令的流程图如下所示:

posted @   零の守墓人  阅读(152)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示