作者：互联网容器团队-Chen Han、AI 研发团队 - Liu Dong Yang在大规模GPU容器集群与模型训练场景，面临稳定性和资源利用率等多重挑战。本文展示vivo GPU平台的总体架构，介绍容器平台在大规模GPU容器集群稳定性建设措施，以及探索多种GPU容器降本提效的解决方案。分享AI工程训练平台大规模训练稳定性建设，及GPU利用率提升实践经验。本文为2025年 vivo 开发者大会互联网技术专场分享内容之一，在微信公众号"vivo互联网技术"对话框回复【2025VDC】获取 2025VDC 互联网技术会场议题相关资料。1分钟看图掌握核心观点👇图1 VS 图2，您更倾向于哪张图来辅助理解全文呢？欢迎在评论区留言一、GPU平台架构vivo的GPU平台由物理层、容器平台层与AI工程层三方面构成。由多种GPU服务器和分布式存储以及高性能网络等基础设施，构成了可靠的物理层。容器平台层的GPU容器能力，主要包含了资源管理、编排调度、GPU虚拟化与多容器网络这四个方面。其中资源管理，表现为多种架构资源池的部署与管理能力。编排调度能力，由GPU弹性伸缩、训推潮汐部署以及多种卡调度策略组成。自研的GPU虚拟化囊括了业界主流的MIG虚拟化、内核层虚拟化以及CUDA层虚拟化三种技术。由传统的Underlay网络以及SRIOV的RDMA直通网络，组成了丰富的容器网络架构。容器平台提供了开放的API接口，为AI工程层的训练和推理平台，提供了坚实的算力底座。通过训练和推理平台，支撑公司内的智能计算业务。二、GPU容器能力实践GPU容器能力实践分为两个模块，首先是大规模容器集群稳定性建设，其次是GPU容器提效降本方案。先了解下容器平台在大规模容器集群场景，如何进行稳定性建设的。2.1 大规模容器集群稳定性集群稳定性是一切的基石。当集群规模大时，任务多，调度频繁，导致核心组件负载激增，极易发生集群崩溃。随着节点规模扩大，运维复杂度呈指数级增长，日常运维工作繁重，发现问题不及时。同时故障处理也面临严峻挑战，故障中涉及的复杂场景多，故障处理的难度大。稳定性建设需要解决上述问题。为了解决高频调度导致的核心组件高负载问题，我们针对Apiserver、etcd、CoreDNS，这3个核心组件进行了架构和性能优化，具体的方案如图所示。通过这些优化手段提升了组件性能，并且降低了组件负载，有利于大规模集群的平稳运行。为了减轻集群运维负担，我们重点建设了自动化节点管理方案。把重复性的运维事项自动化。同时我们还完善了监控告警体系，开发了自动化巡检功能，使运维人员能够及时发现集群问题，快速介入，处理潜在风险，保障集群能够长久稳定运行。故障处理是集群稳定的兜底措施，我们针对多个核心组件都做了，各类故障处理预案。结合可能存在的故障特点，构造故障场景，进行故障恢复演练，确保故障发生时，能够第一时间找到合理的解决方案，准确的处理问题。通过上述的措施，在集群稳定性方面取得了不错的效果，首先日常的集群可用性稳定保持在99.99%水平，其次平台的年度故障复盘数相较于上一年下降60%。核心组件方面的优化也达到了不错效果，其中Apiserver的CPU负载下降70%，etcd提交延迟，从秒级缩短到毫秒级，CoreDNS的毛刺现象消失了，并且负载量下降了90%左右。2.2 GPU容器提效降本实践容器平台的核心竞争力之一就是助力业务提效降本，我们从不同业务维度，对GPU容器提效降本方案进行了探索。首先在单卡维度，通过自研GPU虚拟化方案，使多个容器，互不干扰的共享一张卡资源。其次是在单服务维度，使业务能够自动应对，负载变化的GPU弹性扩缩容方案。在多服务维度，能够让推理服务和训练服务，分时复用整机资源的，训推潮汐部署方案。最后是在多机多卡的分布式场景中，让GPU容器搭配RDMA网络，来解决跨节点通信的瓶颈问题。2.2.1 单卡共享-GPU虚拟化如何让一张卡同时运行多个容器又不互相干扰，就涉及到GPU虚拟化技术。GPU虚拟化一直是AI云原生领域的热门话题之一，各大云厂商都有成熟的解决方案售卖。vivo容器平台的自研GPU虚拟化方案，主要为了解决业务的三大痛点，首先是部分推理业务负载偏低，无法有效用满整卡资源，需要通过共享部署方式，减少资源总量，降低业务成本，提升利用率。其次是不同业务共享同一张卡时，对于安全性以及隔离性的要求各不相同，就需要使用不同的GPU虚拟化技术来满足不同业务诉求。最后在Dev开发机场景，用户使用频率偏低，需要通过显存超售，来提升资源复用率，但显存超售后又需要避免某个用户将显存耗尽，导致OOM错误影响同卡的其他用户。自研GPU虚拟化方案包含MIG虚拟化、内核层虚拟化、CUDA层虚拟化这三种技术。结合业务场景，提供了丰富的卡调度策略，例如尽量聚集的Binpack策略、尽量分散的Spread策略，每个卡只有一个实例的CardOnlyOne策略，以及自定义节点和卡分配关系的CustomTopo策略。通过自研模块与组件，接入Kubernetes体系，对外提供统一调度能力。首先，MIG虚拟化技术，是基于Nvidia硬件提供的，切块组合能力，能够按规则把计算单元和显存单元进行组合，组成MIG实例挂载到容器内，提供完全独立的运行环境。MIG方案的优点是拥有Nvidia官方支持，可以集成到自研体系中。由于是在硬件层面实现的算力和显存限制，所以隔离性和安全性最好。缺点就是仅支持Ampere及以后架构的部分卡，而且限定了切分比例。主要应用场景是对算力隔离有强需求的线上业务。内核层虚拟化技术，是通过自研内核模块，创建虚拟字符设备替换原有的Nvidia字符设备，在内核态拦截IOCTL请求后，实现的算力和显存限制。优点是上层应用无感。并且内核态拥有良好的安全性。缺点是当前无开源方案，开发难度大，而且算力隔离的并不充分。主要应用场景是常规线上业务。CUDA层虚拟化技术的原理：使用拦截库替换Nvidia Driver的原始库，建立拦截库与原始库的，API函数映射关系，从而拦截调用函数，实现算力和显存的限制。优点是有开源方案，使用起来比较灵活。并且可以基于Nvidia提供的统一内存模型，开发显存超售能力。能够在显存不足时，使用内存替代，虽然处理速度下降，但是能够有效避免，显存OOM导致用户程序报错。缺点是用户态导致安全性不足，并且算力隔离能力偏弱，主要应用场景是Dev开发机场景。将自研的内核层虚拟化方案与业界方案，进行了自测性能对比，如图所示，可以看到自研方案在性能上，已经达到业界先进水平。业务使用该方案，与独占整卡部署相比较，平均单卡虚拟化率300%左右，就是把1张物理卡当3张卡使用，同时整机GPU利用率提升了30%+，成本优化超过50%。2.2.2 服务提效-GPU弹性扩缩容在单服务维度，如何帮助业务自动管理大量的GPU容器是提效的关键。我们引入了GPU弹性扩缩容方案。首先弹性扩缩容能力，能够快速响应负载变化，自动调节实例数量，减少人工干预次数，有利于业务在突发场景的平稳运行。其次是业务方在生产环境部署后，非生产环境的实例通常会闲置，这会浪费稀缺的GPU资源。最后由于Kubernetes原生，并不支持GPU维度的弹性扩缩容，需要寻找合适的方案来满足业务诉求。如图所示，我们是基于开源的KEDA框架，自研了GPU-Scaler组件，使用Prometheus中存储，来自DCGM-Exporter的GPU指标，汇聚为扩缩容事件，用于触发KEDA框架，调整实例个数，以此实现了GPU弹性扩缩容能力。由于KEDA框架支持将Workload实例数缩容到0，所以在非生产环境部署的GPU业务，默认开启无负载时，自动缩容到零的功能，以此自动回收，长期闲置的GPU资源。最终的使用效果，线上业务资源不足类告警，下降了80%，单业务平均减少约每周1小时的，扩缩容工作量，有效降低了GPU业务的运维成本。2.2.3 多服务降本-训推潮汐部署在多服务维度，训练服务的资源短缺问题，与推理服务，低峰时段资源空闲问题，相对突出。考虑让训练业务利用推理的空闲资源，即训推潮汐部署方案。首先推理和训练业务都需要稳定的运行环境。而且推理业务潮汐特征明显，夜晚负载低，资源空闲多，导致平均利用率偏低。并且多机多卡训练任务，需要整机资源，且资源需求日益增长，采购新设备，难且慢，导致训练资源缺口明显。训推潮汐部署就是整机资源分时复用的逻辑，如图所示，推理业务在白天高负载时，稳定运行，在夜晚低峰时段，自动腾挪出空闲整机资源，借给训练业务使用。在清晨时段，训练业务结束，把整机资源还给推理业务，如此达到分时复用的效果。如图所示。推理业务在部署前，需要评估保底负载容量。在部署时填入维持业务稳定的最少Pod数量。基于OpenKruise组件的，WorkloadSpread功能，管理不同的Subset，分别在稳定池和潮汐池中按需部署。同时配置CronHPA，定时缩容，自动调整副本数，到稳定Pod数量，优先删除潮汐池中的Pod。以此达到把潮汐池的节点整机腾空的效果。其中我们还针对Workload的缩容优先级进行了优化。当缩容发生时，结合Pod和节点的拓扑关系，把所在节点实例数少的Pod优先缩容，达到更快的腾空效果。通过上述方案，训推潮汐部署的降本效果明显，使推理业务，成本下降30%，同时整机GPU利用率提升20%多，有效缓解了训练资源短缺问题。2.2.4 多机多卡提效-容器RDMA高性能网络当前分布式训练和推理业务，对算力和显存的需求巨大，单节点资源不足，需要使用多机多卡资源，那么网络通信容易成为性能瓶颈。RDMA技术允许GPU直接访问支持RDMA设备中的数据，无需经过主机CPU或内存，实现跨节点的零拷贝数据传输，有效减少了CPU开销和网络延迟。所以从多机多卡维度，使用RDMA技术是网络提效的有效措施。从容器平台角度，GPU容器更加需要结合RDMA技术，提供简单高效的解决方案，方便业务使用。如图所示，RDMA容器有两个网卡，一个是使用Calico-CNI插件，通过veth创建的eth0网卡，对应的是Underlay网络。另一个是使用Sriov-CNI插件，通过VF创建的eth1网卡，对应的RoCE_v2或IB协议网络。我们引入了Multus-CNI组件，能够在单容器创建时，按需调用多种CNI插件。同时我们选择使用Spiderpool组件管理IP池，以及进行IP分配和路由策略配置。通过上述组件，在Kubernetes体系中实现了RDMA容器的全生命周期管理能力。基于容器RDMA能力，在大规模训练和推理场景，业务能够提速20%到30%之间，提升效果明显。三、AI工程训练平台实践3.1 训练平台整体架构VTraining训练平台是由vivo AI计算平台团队打造的一站式大模型训练方案，它面向算法工程师，提供模型开发、模型训练和海量样本存储等能力。VTraining底层是基于vivo容器平台、及高性能计算、网络、存储等基础设施之上构建，通过提供模型开发、模型训练、资产管理等能力，支撑公司的大模型训练业务。像vivo手机的蓝心小V、相册等核心产品的大模型训练，都是在VTraining平台进行迭代的。3.2 大规模训练稳定性实践3.2.1 稳定性背景稳定性问题是大规模训练的痛点。任务在大规模的同步训练过程中需要依赖计算、网络、存储、容器调度等复杂的基础设施环境，任何环节出问题都会导致任务中断，问题定位、恢复困难。例如知名头部公司千亿参数大模型的大规模训练任务，平均每3小时触发一次意外中断。3.2.2 稳定性实践-高频故障专项治理其中一个问题，是GPU集群投入使用初期机器故障率会很高，训练任务会经常被中断。为了降低机器故障率，我们进行了高频故障专项治理。首先对GPU集群进行了大规模测试诊断、然后进行高频故障统计和修复，把有问题的软硬件进行维修、替换、升级或修复。3.2.3 稳定性实践-故障处置流程完善另一个问题，是任务故障就是不可避免的，所以为了缩短任务中断时间，尽快恢复任务运行，我们对故障处置流程进行了重点建设完善。**训练前，**我们会针对GPU机器、网络通信等环境进行大规模检测，剔除异常节点、慢节点等问题节点，降低故障风险。**训练中，**会开启自动化容错机制，以便能及时发现基础设施或任务异常，快速进行故障定位和故障容错，自动隔离故障、自动重启恢复任务运行。**训练后，**对新问题进行搜集分析，完善异常特征库，增强问题诊断能力，以便后续遇到类似的故障可以快速容错。3.2.4 稳定性实践-效果与总结通过减少基础设施高频故障、完善任务故障处置流程两大措施，我们取得了良好效果：机器每天故障率由原来的百分之二下降到千分之一；千卡任务有效训练时长由原来的60%提升到99%，达到了行业一流水平。同时我们也积累了相关的稳定性经验：GPU集群由不稳定到稳定，需要一个软硬件磨合过程。因为不同的软硬件环境会触发不同的稳定性问题。大规模训练前，尽量剔除历史故障率高的机器。我们发现稳定的机器一般会一直很稳定，而故障率高的机器即使修复后出问题的概率也比较大。提升任务有效训练时长，需结合基础设施、训练框架、平台容错机制综合优化。例如秒级监控告警能力、checkpoint持久化策略等等都需要进行持续深入的优化。3.3 GPU利用率提升实践3.3.1 GPU利用率提升-业务背景及问题GPU是稀缺资源，但是差异化的业务场景下GPU难以高效利用，利用率提升困难。比如GPU场景常见业务形态：有训练任务、推理业务、数据生产、开发调试等等类型。他们各有特点：训练任务虽然GPU利用率高，但是偶尔也会出现碎片化空闲资源，比如算法同学偶尔会将任务停下来排查问题，调下参数之类的操作，任务并不是一直不间断地在跑的。推理业务有很明显的潮汐特性，白天流量高峰期GPU利用率高、到了夜间流量低峰期利用率会比较低。数据生产任务属于离线任务，GPU利用率高、资源需求大、但难申请到资源；开发调试任务的话GPU利用率低并且要长期占用资源，导致GPU利用率长期低下。所以不同的业务形态有不同的利用率特点，接下来介绍下我们的GPU利用率提升措施。3.3.2 利用率提升措施一：低优任务对于训练任务场景，偶现的碎片化空闲资源，我们 通过低优数据生产任务进行充分利用。如图所示，我们通过低优数据生产任务调度，复用训练场景偶现的碎片化资源，当正常任务需要资源时，可随时抢占低优资源，不会影响正常训练任务的调度。3.3.3 利用率提升措施二：训推潮汐部署对于推理业务场景，在夜间流量低峰期可以释放大量GPU资源，我们通过训推潮汐部署给离线业务复用。如图所示，通过训推潮汐部署机制，我们将夜间推理流量低峰期缩容的机器腾挪到了离线GPU资源池，给离线业务使用，白天再腾挪回在线GPU资源池进行扩容，达到潮汐复用的目的。3.3.4 利用率提升措施三：GPU虚拟化对于开发任务场景，长期独占GPU资源且利用率低，我们通过vivo自研VGPU虚拟化技术，减少开发任务占用的物理GPU卡数，释放冗余算力。如图所示，我们可以将单机4卡GPU机器，通过开启VGPU虚拟出16卡VGPU，相当于一卡顶四卡。VGPU的优点是：可支持1:2、1:4超卖、还可以用内存补充显存不足，所以对用户是无感知使用的，很适用于开发任务这种对性能要求低的场景。3.3.5 利用率提升总结与规划总之，训练平台通过低优任务、训推潮汐部署、GPU虚拟化等策略，深度适配差异化业务场景特性，实现了资源高效复用。AI整体GPU利用率均值绝对值提升了5个百分点，接近行业一流水平。未来训练平台也会持续对GPU利用率进行综合治理。例如进行低效任务治理、低效资源盘活、成本账单统计、奖励与惩罚措施的实施等等，让稀缺的GPU资源发挥更大价值！四、GPU平台未来展望在容器平台层面，我们会从多集群联邦调度、在离线GPU混部、国产异构计算芯片支持、GPU资源池化等方面能力进行综合建设，支撑上层平台业务对GPU资源的高效利用。训练平台层面，我们会增强故障预警、任务动态容错等能力，增强业务稳定性；同时完善对大模型训练全流程能力的支撑，以及对GPU资源进行更精细化的运营，从而让GPU业务更加稳定、资源利用更加高效！
今天跟大家分享一个etcd的内存大量占用的问题，这是前段时间在我们开源软件Easegress中遇到的问题，问题是比较简单的，但是我还想把前因后果说一下，包括，为什么要用etcd，使用etcd的用户场景，包括etcd的一些导致内存占用比较大的设计，以及最后一些建议。希望这篇文章不仅仅只是让你看到了一个简单的内存问题，还能让你有更多的收获。当然，也欢迎您关注我们的开源软件，给我们一些鼓励。为什么要用ETCD先说一下为什么要用etcd。先从一个我们自己做的一个API网关 – Easegress（源码）说起。Easegress 是我们开发并开源的一个API应用网关产品，这个API应用网关不仅仅只是像nginx那样用来做一个反向代理，这个网关可以做的事很多，比如：API编排、服务发现、弹力设计（熔断、限流、重试等）、认证鉴权（JWT，OAuth2，HMAC等）、同样支持各种Cloud Native的架构如：微服务架构，Service Mesh，Serverless/FaaS的集成，并可以用于扛高并发、灰度发布、全链路压力测试、物联网……等更为高级的企业级的解决方案。所以，为了达到这些目标，在2017年的时候，我们觉得在现有的网关如Nginx上是无法演进出来这样的软件的，必需重新写一个（后来其他人也应该跟我们的想法一样，所以，Lyft写了一个Envoy。只不过，Envoy是用C++写的，而我用了技术门槛更低的Go语言）另外，Easegress最核心的设计主要有三个： 一是无第三方依赖的自己选主组集群的能力 二是像Linux管道命令行那样pipeline式的插件流式处理（支持Go/WebAssembly） 三是内置一个Data Store用于集群控制和数据共享。 对于任何一个分布式系统，都需要有一个强一制性的基于Paxos/Raft的可以自动选主机制，并且需要在整个集群间同步一些关键的控制/配置和相关的共享数据，以保证整个集群的行为是统一一致的。如果没有这么一个东西的话，就没有办法玩分布式系统的。这就是为什么会有像Zookeeper/etcd这样的组件出现并流行的原因。注意，Zookeeper他们主要不是给你存数据的，而是给你组集群的。Zookeeper是一个很流行的开源软件，也被用于各大公司的生产线，包括一些开源软件，比如：Kafka。但是，这会让其它软件有一个依赖，并且在运维上带来很大的复杂度。所以，Kafka在最新的版本也通过内置了选主的算法，而抛弃了外挂zookeeper的设计。Etcd是Go语言社区这边的主力，也是kubernetes组建集群的关键组件。Easegress在一开始（5年前）使用了gossip协议同步状态（当时想的过于超前，想做广域网的集群），但是后发现这个协议太过于复杂，而且很难调试，而广域网的API Gateway也没遇到相应的场景。所以，在3年前的时候，为了稳定性的考量，我们把其换成了内嵌版本的etcd，这个设计一直沿用到今天。Easegress会把所有的配置信息都放到etcd里，还包括一些统计监控数据，以及一些用户的自定义数据（这样用户自己的plugin不但可以在一条pipeline内，还可以在整个集群内共享数据），这对于用户进行扩展来说是非常方便的。软件代码的扩展性一直是我们追求的首要目标，尤其是开源软件更要想方设法降低技术门槛让技术易扩展，这就是为什么Google的很多开源软件都会选使用Go语言的原因，也是为什么Go正在取代C/C++的做PaaS基础组件的原因。背景问题好了，在介绍完为什么要用etcd以后，我开始分享一个实际的问题了。我们有个用户在使用 Easegress 的时候，在Easegress内配置了上千条pipeline，导致 Easegress的内存飙升的非常厉害- 10+GB 以上，而且长时间还下不来。用户报告的问题是——在Easegress 1.4.1 上创建一个HTTP对象，1000个Pipeline，在Easegres初始化启动完成时的内存占用大概为400M，运行80分钟后2GB，运行200分钟后达到了4GB，这期间什么也没有干，对Easegress没有进行过一次请求。一般来说，就算是API再多也不应该配置这么多的处理管道pipeline的，通常我们会使用HTTP API的前缀把一组属于一个类别的API配置在一个管道内是比较合理的，就像nginx下的location的配置，一般来说不会太多的。但是，在用户的这个场景下配置了上千个pipeline，我们也是头一次见，应该是用户想做更细粒度的控制。经过调查后，我们发现内存使用基本全部来自etcd，我们实在没有想到，因为我们往etcd里放的数据也没有多少个key，感觉不会超过10M，但不知道为什么会占用了10GB的内存。这种时候，一般会怀疑etcd有内存泄漏，上etcd上的github上搜了一下，发现etcd在3.2和3.3的版本上都有内存泄露的问题，但都修改了，而 Easegress 使用的是3.5的最新版本，另外，一般来说内存泄漏的问题不会是这么大的，我们开始怀疑是我们哪里误用了etcd。要知道是否误用了etcd，那么只有一条路了，沉下心来，把etcd的设计好好地看一遍。大概花了两天左右的时间看了一下etcd的设计，我发现了etcd有下面这些消耗内存的设计，老实说，还是非常昂贵的，这里分享出来，避免后面的同学再次掉坑。首当其冲是——RaftLog。etcd用Raft Log，主要是用于帮助follower同步数据，这个log的底层实现不是文件，而是内存。所以，而且还至少要保留 5000 条最新的请求。如果key的size很大，这 5000条就会产生大量的内存开销。比如，不断更新一个 1M的key，哪怕是同一个key，这 5000 条Log就是 5000MB = 5GB 的内存开销。这个问题在etcd的issue列表中也有人提到过 issue #12548 ，不过，这个问题不了了之了。这个5000还是一个hardcode，无法改。（参看 DefaultSnapshotCatchUpEntries 相关源码）// DefaultSnapshotCatchUpEntries is the number of entries for a slow follower // to catch-up after compacting the raft storage entries. // We expect the follower has a millisecond level latency with the leader. // The max throughput is around 10K. Keep a 5K entries is enough for helping // follower to catch up. DefaultSnapshotCatchUpEntries uint64 = 5000另外，我们还发现，这个设计在历史上etcd的官方团队把这个默认值从10000降到了5000，我们估计etcd官方团队也意识到10000有点太耗内存了，所以，降了一半，但是又怕follwer同步不上，所以，保留了 5000条……（在这里，我个人感觉还有更好的方法，至少不用全放在内存里吧……）另外还有下面几项也会导致etcd的内存会增加 索引。etcd的每一对 key-value 都会在内存中有一个 B-tree 索引。这个索引的开销跟key的长度有关，etcd还会保存版本。所以B-tree的内存跟key的长度以及历史版本号数量也有关系。 mmap。还有，etcd 使用 mmap 这样上古的unix技术做文件映射，会把他的blotdb的内存map到虚拟内存中，所以，db-size越大，内存越大。 Watcher。watch也会占用很大的内存，如果watch很多，连接数多，都会堆积内存。 （很明显，etcd这么做就是为了一个高性能的考虑）Easegress中的问题更多的应该是Raft Log 的问题。后面三种问题我们觉得不会是用户这个问题的原因，对于索引和mmap，使用 etcd 的 compact 和 defreg （压缩和碎片整理应该可以降低内存，但用户那边不应该是这个问题的核心原因）。针对用户的问题，大约有1000多条pipeline，因为Easegress会对每一条pipeline进行数据统计（如：M1, M5, M15， P99, P90, P50等这样的统计数据），统计信息可能会有1KB-2KB左右，但Easegress会把这1000条pipeline的统计数据合并起来写到一个key中，这1000多条的统计数据合并后会导致出现一个平均尺寸为2MB的key，而5000个in-memory的RaftLog导致etcd要消耗了10GB的内存。之前没有这么多的pipeline的场景，所以，这个内存问题没有暴露出来。于是，我们最终的解决方案也很简单，我们修改我们的策略，不再写这么大的Value的数据了，虽然以前只写在一个key上，但是Key的值太大，现在把这个大Key值拆分成多个小的key来写，这样，实际保存的数据没有发生变化，但是RaftLog的每条数据量就小了，所以，以前是5000条 2M（10GB），现在是5000条 1K（500MB），就这样解决了这个问题。相关的PR在这里 PR#542 。总结要用好 etcd，有如下的实践 避免大尺寸的key和value，一方面会通过一个内存级的 Raft Log 占大量内存，另一方面，B-tree的多版本索引也会因为这样耗内存。 避免DB的尺寸太大，并通过 compact和defreg来压缩和碎片整理降低内存。 避免大量的Watch Client 和 Watch数。这个开销也是比较大的。 最后还有一个，就是尽可能使用新的版本，无论是go语言还是etcd，这样会少很多内存问题。比如：golang的这个跟LInux内核心相关的内存问题 —— golang 1.12的版sget的是 MADV_FREE 的内存回收机制，而在1.16的时候，改成了 MADV_DONTNEED ，这两者的差别是，FREE表示，虽然进程标记内存不要了，但是操作系统会保留之，直到需要更多的内存，而 DONTNEED 则是立马回收，你可以看到，在常驻内存RSS 上，前者虽然在golang的进程上回收了内存，但是RSS值不变，而后者会看到RSS直立马变化。Linux下对 MADV_FREE 的实现在某些情况下有一定的问题，所以，在go 1.16的时候，默认值改成了 MADV_DONTNEED 。而 etcd 3.4 是用 来1.12 编译的。 最后，欢迎大家关注我们的开源软件！ https://github.com/megaease/
基于 Patroni,Etcd,Haproxy,Keepalived 搭建 PG16 高可用集群
概述使用 postgresql + etcd + patroni + haproxy + keepalived 可以实现 PG 的高可用集群，其中，以 postgresql 做数据库，Patroni 监控本地的 PostgreSQL 状态，并将本地 PostgreSQL 信息 / 状态写入 etcd 来存储集群状态，所以，patroni 与 etcd 结合可以实现数据库集群故障切换（自动或手动切换），而 haproxy 可以实现数据库读写分离 + 读负载均衡（通过不同端口实现），keepalived 实现 VIP 跳转，对 haproxy 提供了高可用，防止 haproxy 宕机。Patroni 介绍Patroni 是一个基于 Python 的用于实现 PostgreSQL HA 解决方案的框架。为了最大程度的兼容性，它支持多种分布式配置存储，包括 ZooKeeper、etcd、Consul 或 Kubernetes。旨在帮助数据库工程师、DBA、DevOps 工程师和 SRE 快速部署数据中心（或任何地方）的 HA PostgreSQL 环境。当前支持的 PostgreSQL 版本从 9.3 到 16。支持自动化故障转移、物理复制和逻辑复制、提供 RESTful API 接口，允许外部应用或运维工具直接操作 PostgreSQL 集群，进行如启停、迁移等操作，与 Linux watchdog 集成，以避免脑裂现象。项目地址： GitHub - patroni/patroni: A template for PostgreSQL High Availability with Etcd, Consul, ZooKeeper, or KubernetesETCD 介绍etcd 是一个分布式键值存储数据库，支持跨平台，拥有强大的社区。etcd 的 Raft 算法，提供了可靠的方式存储分布式集群涉及的数据。etcd 广泛应用在微服务架构和 Kubernates 集群中，不仅可以作为服务注册与发现，还可以作为键值对存储的中间件。从业务系统 Web 到 Kubernetes 集群，都可以很方便地从 etcd 中读取、写入数据。etcd 完整的 cluster（集群）至少有三台，这样才能选举出一个 master 节点，两个 slave 节点。如果小于 3 台则无法进行选举，造成集群不可用。Etcd 使用 2379 和 2380 端口。2379 端口：提供 HTTP API 服务，和 etcdctl 交互2380 端口：集群中节点间通讯项目地址：GitHub - etcd-io/etcd: Distributed reliable key-value store for the most critical data of a distributed system环境说明服务器信息 服务器名ip 地址os数据库读写端口只读端口组件组件端口 k8s-mater01192.168.28.11ubuntu 24.101543325433PostgreSQL，Patroni、Etcd，haproxy、keepalived8008k8s-mater02192.168.28.12ubuntu 24.101543325433PostgreSQL，Patroni、Etcd，haproxy、keepalived8008k8s-mater01192.168.28.13ubuntu 24.101543325433PostgreSQL，Patroni、Etcd，haproxy、keepalived8008vip192.168.28.10PostgreSQL，Patroni、Etcd，haproxy、keepalived8008 软件信息 软件名版本 Patroni4.0.6Etcd3.5.16Keepalived2.3.1Haproxy2.9.10-1ubuntu1.2PostgreSQL16.9watchdog5.16python3.12.7 架构图这个架构中，PostgreSQL 提供数据服务，Patroni 负责主从切换，etcd 提供一致性存储，HAProxy 提供访问路由，Keepalived 提供网络 VIP 高可用，Watchdog 提供节点存活及脑裂防护机制。 六者协同组成一个企业级高可用数据库集群预先准备网络设置防火墙设置关闭防火墙 (相对简单，但是不安全)放行对应的端口（安全）安装 PostgreSQL通过 apt 安装 PostgreSQL (所有节点)sudo apt install curl ca-certificates sudo install -d /usr/share/postgresql-common/pgdg sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc . /etc/os-release sudo sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list" sudo apt update sudo apt -y install postgresql 数据库目录设置 (所有节点)mkdir -p /data/postgresql/pgdata/ mkdir -p /data/postgresql/pg_archive/ chown -R postgres:postgres /data/postgresql/ postgres 用户设置设置家目录mkdir -p /home/postgres/ chown -R postgres:postgres /home/postgres/ vim /etc/passwd #修改postgres 家目录为/home/postgres postgres:x:114:113:PostgreSQL administrator,,,:/home/postgres:/bin/bash 设置环境变量vim /home/postgres/.bashrc [ -f /etc/profile ] && source /etc/profile export PATH=/usr/lib/postgresql/16/bin:$PATH # If you want to customize your settings, # Use the file below. This is not overridden # by the RPMS. [ -f /var/lib/pgsql/.pgsql_profile ] && source /var/lib/pgsql/.pgsql_profile 设置 sudo 免密vim /etc/sudoers #行末新增 postgres ALL=(ALL) NOPASSWD: ALL 安装 ETCD 集群 (所有节点)安装 ectd下载 etcdwget https://github.com/etcd-io/etcd/releases/download/v3.6.4/etcd-v3.6.4-linux-amd64.tar.gz tar -xf etcd-v3.6.4-linux-amd64.tar.gz --strip-components=1 -C /usr/local/bin etcd-v3.6.4-linux-amd64/etcd etcd-v3.6.4-linux-amd64/etcdctl mkdir -p /etc/etcd/ mkdir -p /data/etcd/ 编辑 etcd 配置文件touch /etc/etcd/etcd-pg.config.yml #节点1配置文件 vim /etc/etcd/etcd-pg.config.yml #节点名 name: pg-etcd01 #数据目录 data-dir: /data/etcd snapshot-count: 5000 #选举和心跳参数 heartbeat-interval: 100 election-timeout: 1000 #存储新能优化 quota-backend-bytes: 8589934592 max-request-bytes: 10485760 max-concurrent-requests: 5000 #自动压缩与碎片整理 auto-compaction-mode: periodic auto-compaction-retention: "2h" #集群通信配置 listen-peer-urls: "http://192.168.28.11:12380" listen-client-urls: "http://192.168.28.11:12379,http://127.0.0.1:12379" max-snapshots: 3 max-wals: 5 cors: initial-advertise-peer-urls: "http://192.168.28.11:12380" advertise-client-urls: "http://192.168.28.11:12379" discovery: discovery-fallback: 'proxy' discovery-proxy: discovery-srv: initial-cluster: "pg-etcd01=http://k8s-etcd01:12380,pg-etcd02=http://k8s-etcd02:12380,pg-etcd03=http://k8s-etcd03:12380" initial-cluster-token: 'etcd-cluster-pg' initial-cluster-state: 'new' strict-reconfig-check: false enable-v2: true enable-pprof: true proxy: 'off' proxy-failure-wait: 5000 proxy-refresh-interval: 30000 proxy-dial-timeout: 1000 proxy-write-timeout: 5000 proxy-read-timeout: 0 #节点2配置文件 name: pg-etcd02 data-dir: /data/etcd snapshot-count: 5000 #选举和心跳参数 heartbeat-interval: 100 election-timeout: 1000 #存储新能优化 quota-backend-bytes: 8589934592 max-request-bytes: 10485760 max-concurrent-requests: 5000 #自动压缩与碎片整理 auto-compaction-mode: periodic auto-compaction-retention: "2h" #集群通信配置 listen-peer-urls: "http://192.168.28.12:12380" listen-client-urls: "http://192.168.28.12:12379,http://127.0.0.1:12379" max-snapshots: 3 max-wals: 5 cors: initial-advertise-peer-urls: "http://192.168.28.12:12380" advertise-client-urls: "http://192.168.28.12:12379" discovery: discovery-fallback: 'proxy' discovery-proxy: discovery-srv: initial-cluster: "pg-etcd01=http://k8s-etcd01:12380,pg-etcd02=http://k8s-etcd02:12380,pg-etcd03=http://k8s-etcd03:12380" initial-cluster-token: 'etcd-cluster-pg' initial-cluster-state: 'new' strict-reconfig-check: false enable-v2: true enable-pprof: true proxy: 'off' proxy-failure-wait: 5000 proxy-refresh-interval: 30000 proxy-dial-timeout: 1000 proxy-write-timeout: 5000 proxy-read-timeout: 0 #节点3配置文件 name: pg-etcd03 data-dir: /data/etcd snapshot-count: 5000 #选举和心跳参数 heartbeat-interval: 100 election-timeout: 1000 #存储新能优化 quota-backend-bytes: 8589934592 max-request-bytes: 10485760 max-concurrent-requests: 5000 #自动压缩与碎片整理 auto-compaction-mode: periodic auto-compaction-retention: "2h" #集群通信配置 listen-peer-urls: "http://192.168.28.13:12380" listen-client-urls: "http://192.168.28.13:12379,http://127.0.0.1:12379" max-snapshots: 3 max-wals: 5 cors: initial-advertise-peer-urls: "http://192.168.28.13:12380" advertise-client-urls: "http://192.168.28.13:12379" discovery: discovery-fallback: 'proxy' discovery-proxy: discovery-srv: initial-cluster: "pg-etcd01=http://k8s-etcd01:12380,pg-etcd02=http://k8s-etcd02:12380,pg-etcd03=http://k8s-etcd03:12380" initial-cluster-token: 'etcd-cluster-pg' initial-cluster-state: 'new' strict-reconfig-check: false enable-v2: true enable-pprof: true proxy: 'off' proxy-failure-wait: 5000 proxy-refresh-interval: 30000 proxy-dial-timeout: 1000 proxy-write-timeout: 5000 proxy-read-timeout: 0 创建 etcd 服务vim /etc/systemd/system/etcd-pg.service [Unit] Description=Etcd Server After=network.target After=network-online.target Wants=network-online.target [Service] Type=notify ExecStart=/usr/local/bin/etcd --config-file=/etc/etcd/etcd-pg.config.yml Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target 启动 etcd-pg 服务systemctl daemon-reload systemctl start etcd-pg.service systemctl enable etcd-pg.service 检查 etcd 集群健康状态root@k8s-master01:~# etcdctl --endpoints="k8s-etcd01:12379,k8s-etcd02:12379,k8s-etcd03:12379" member list -w=table +------------------+---------+-----------+-------------------------+----------------------------+------------+ | ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER | +------------------+---------+-----------+-------------------------+----------------------------+------------+ | 2bb79737c88dd84d | started | pg-etcd03 | http://k8s-etcd03:12380 | http://192.168.28.13:12379 | false | | 354b7a6aa8551f4a | started | pg-etcd02 | http://k8s-etcd02:12380 | http://192.168.28.12:12379 | false | | 84101e54de967367 | started | pg-etcd01 | http://k8s-etcd01:12380 | http://192.168.28.11:12379为了简化命令，可以通过 alisa 配置cd ~ vim .profile alias etcdctlpg="etcdctl --endpoints="k8s-etcd01:12379,k8s-etcd02:12379,k8s-etcd03:12379" " source .profile root@k8s-master01:~# etcdctlpg member list -w=table +------------------+---------+-----------+-------------------------+----------------------------+------------+ | ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER | +------------------+---------+-----------+-------------------------+----------------------------+------------+ | 2bb79737c88dd84d | started | pg-etcd03 | http://k8s-etcd03:12380 | http://192.168.28.13:12379 | false | | 354b7a6aa8551f4a | started | pg-etcd02 | http://k8s-etcd02:12380 | http://192.168.28.12:12379 | false | | 84101e54de967367 | started | pg-etcd01 | http://k8s-etcd01:12380 | http://192.168.28.11:12379 | false | Etcd 可视化工具 github.com GitHub - tzfun/etcd-workbench: A powerful ui client for ETCD v3. Provides desktop...A powerful ui client for ETCD v3. Provides desktop application and web packages. 安装 watchdog (所有节点)watchdog 防止脑裂。Patroni 支持通过 Linux 的 watchdog 监视 patroni 进程的运行，当 patroni 进程无法正常往 watchdog 设备写入心跳时，由 watchdog 触发 Linux 重启。# 安装软件，linux内置功能 sudo apt install -y watchdog # 初始化watchdog字符设备 sudo modprobe softdog # 修改/dev/watchdog设备权限 sudo chmod 666 /dev/watchdog sudo chown postgres:postgres /dev/watchdog # 启动watchdog服务 sudo systemctl start watchdog sudo systemctl enable watchdog 安装 Patroni (所有节点)安装1. pip3 install --break-system-packages psycopg2-binary 2. pip3 install --break-system-packages patroni[etcd] 3. pip3 install --break-system-packages python-json-logger 4. mkdir -p /etc/patroni 创建配置文件创建 Patroni 服务cat /etc/systemd/system/patroni-5433.service [Unit] Description=Patroni high-availability PostgreSQL After=syslog.target network.target etcd.service Requires=etcd-pg.service [Service] Type=simple User=postgres Group=postgres # 使用watchdog进行服务监控 ExecStartPre=-/usr/bin/sudo /sbin/modprobe softdog ExecStartPre=-/usr/bin/sudo /bin/chown postgres /dev/watchdog PermissionsStartOnly=true WorkingDirectory=/home/postgres/ ExecStart=/usr/local/bin/patroni /etc/patroni/patroni-5433.yaml ExecReload=/bin/kill -HUP KillMode=process Restart=always RestartSec=10 LimitNOFILE=65536 [Install] WantedBy=multi-user.target systemctl daemon-reload 配置免密登录su postgres cd ~ #备库从主库同步WAL日志使用，主备倒换后，主库降备库，新备库使用，所以备库也配置 touch .pgpass vim .pgpass 192.168.28.11:5433:*:replica:replica 192.168.28.12:5433:*:replica:replica 192.168.28.13:5433:*:replica:replica 创建 日志目录sudo mkdir -p /var/log/patroni/ sudo chown -R postgres:postgres /var/log/patroni/ 启动 Patroni根据节点依次启动 Patronisudo systemctl daemon-reload sudo systemctl restart patroni-5433.service sudo systemctl enable patroni-5433.service sudo systemctl status patroni-5433.service 查看服务状态，默认情况下，根据配置文件中，initdb 内容，Patroni 会自动对数据库进行初始化操作，并创建用户，配置文件，拉起数据库并建立主从关系及流复制查看状态root@k8s-master01:~# patronictl -c /etc/patroni/patroni-5433.yaml list + Cluster: pg_patroni_etcd () ---+-----------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | + | pg_patroni_5433_01 | 192.168.28.11:5433 | Leader | running | 17 | | | pg_patroni_5433_02 | 192.168.28.12:5433 | Replica | streaming | 17 | 0 | | pg_patroni_5433_03 | 192.168.28.13:5433 | Replica | streaming | 17 | 0 | + 通过 alisa 设置简易命令alias patr5433="patronictl -c /etc/patroni/patroni-5433.yaml" patroni 维护命令 (所有节点)列出节点信息patronictl -c /etc/patroni/patroni-5433.yaml list 重做备库reinit 先是移除了整个 data 目录。然后选择正确的节点进行备份恢复。patronictl -c /etc/patroni/patroni-5433.yaml reinit [nodename] 查看配置patronictl -c /etc/patroni/patroni-5433.yaml show-config 更改参数patronictl -c /etc/patroni/patroni-5433.yaml edit-config #重载参数 patronictl -c /etc/patroni/patroni-5433.yaml reload [nodename] 重启节点 / 关闭节点仅重启当前节点patronictl -c /etc/patroni/patroni-5433.yaml restart [clustername] [nodename] 如果节点是 pending 状态的，才会执行重启操作patronictl -c /etc/patroni/patroni-5433.yaml restart [clustername] --pending 重启所有成员patronictl -c /etc/patroni/patroni-5433.yaml restart [clustername] 维护模式 脱离 patroni 的集群管理patronictl pausepatronictl pause 暂时将 Patroni 集群置于维护模式并禁用自动在某些情况下，Patroni 需要暂时退出集群管理，同时仍然在 DCS 中保留集群状态。可能的用例是集群上不常见的活动，例如主要版本升级或损坏恢复。在这些活动期间，节点经常因为 Patroni 不知道的原因而启动和停止，有些节点甚至可以暂时提升，这违反了只运行一个主节点的假设。因此，Patroni 需要能够与正在运行的集群 “分离”，在 Pacemaker 中实现与维护模式相当的功能。patronictl resumepatronictl resume 将使 Patroni 集群退出维护模式，并重新启用自动故障转移。自动拉起所有数据库switchover 主备切换patronictl switchover# Switchover root@k8s-master01:~# patr5433 switchover Current cluster topology + Cluster: pg_patroni_etcd (7540584003720074567) ---+-----------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +--------------------+--------------------+---------+-----------+----+-----------+ | pg_patroni_5433_01 | 192.168.28.11:5433 | Leader | running | 22 | | | pg_patroni_5433_02 | 192.168.28.12:5433 | Replica | streaming | 22 | 0 | | pg_patroni_5433_03 | 192.168.28.13:5433 | Replica | streaming | 22 | 0 | +--------------------+--------------------+---------+-----------+----+-----------+ Primary [pg_patroni_5433_01]: Candidate ['pg_patroni_5433_02', 'pg_patroni_5433_03'] []: pg_patroni_5433_02 When should the switchover take place (e.g. 2025-08-26T12:26 ) [now]: now Are you sure you want to switchover cluster pg_patroni_etcd, demoting current leader pg_patroni_5433_01? [y/N]: y 2025-08-26 11:26:32.31189 Successfully switched over to "pg_patroni_5433_02" + Cluster: pg_patroni_etcd (7540584003720074567) ---+---------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +--------------------+--------------------+---------+---------+----+-----------+ | pg_patroni_5433_01 | 192.168.28.11:5433 | Replica | stopped | | unknown | | pg_patroni_5433_02 | 192.168.28.12:5433 | Leader | running | 22 | | | pg_patroni_5433_03 | 192.168.28.13:5433 | Replica | running | 22 | 0 | +--------------------+--------------------+---------+---------+----+-----------+ root@k8s-master01:~# patr5433 list + Cluster: pg_patroni_etcd (7540584003720074567) ---+-----------+----+-----------+ | Member | Host | Role | State | TL | Lag in MB | +--------------------+--------------------+---------+-----------+----+-----------+ | pg_patroni_5433_01 | 192.168.28.11:5433 | Replica | streaming | 23 | 0 | | pg_patroni_5433_02 | 192.168.28.12:5433 | Leader | running | 23 | | | pg_patroni_5433_03 | 192.168.28.13:5433 | Replica | streaming | 23 | 0 | +--------------------+--------------------+---------+-----------+----+-----------+ 接口切换 数据库从 pg_patroni_5433_01 switchover 到 pg_patroni_5433_02 [root@pgtest1 ~]# curl -s http://192.168.28.11:8008/switchover -XPOST -d '{"leader":"pg_patroni_5433_01","candidate":"pg_patroni_5433_02"}' Successfully switched over to "pg_patroni_5433_02" failover 切换patronictl failover# Failover [postgres@pgtest1 ~]$ patronictl -c /etc/patroni/patroni-5433.yaml failover Candidate ['pg_patroni_5433_01', 'pg_patroni_5433_02','pg_patroni_5433_03'] []: pg_patroni_5433_01 Current cluster topology ... ... Are you sure you want to failover cluster pg_cluster, demoting current master pg_patroni_5433_02? [y/N]: y 2021-10-28 03:47:56.13486 Successfully failed over to "pg_patroni_5433_01" ... ... 获取主节点 dsn 信息root@k8s-master01:~# patronictl -c /etc/patroni/patroni-5433.yaml dsn host=192.168.28.12 port=5433 安装 Haproxy (所有节点)安装 Haproxysudo apt install haproxy 编辑 Haproxy 配置文件sudo vim /etc/haproxy/haproxy.cfg global maxconn 2000 ulimit-n 16384 log 127.0.0.1 local0 err stats timeout 30s defaults log global mode http option httplog timeout connect 5000 timeout client 50000 timeout server 50000 timeout http-request 15s timeout http-keep-alive 15s listen status_page bind *:8888 stats enable stats uri /haproxy-status stats auth admin:admin stats realm "Welcome to the haproxy load balancer status page of k8s-master" frontend monitor-in bind *:33305 mode http option httplog monitor-uri /monitor # 主库读写端口 listen master bind *:15433 mode tcp option tcplog balance roundrobin option httpchk OPTIONS /master http-check expect status 200 default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions server pgtest1 192.168.28.11:5433 maxconn 1000 check port 8008 inter 5000 rise 2 fall 2 server pgtest2 192.168.28.12:5433 maxconn 1000 check port 8008 inter 5000 rise 2 fall 2 server pgtest3 192.168.28.13:5433 maxconn 1000 check port 8008 inter 5000 rise 2 fall 2 #从库读端口 listen replicas bind *:25433 mode tcp option tcplog balance roundrobin option httpchk OPTIONS /replica http-check expect status 200 default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions server pgtest1 192.168.28.11:5433 maxconn 1000 check port 8008 inter 5000 rise 2 fall 2 server pgtest2 192.168.28.12:5433 maxconn 1000 check port 8008 inter 5000 rise 2 fall 2 server pgtest3 192.168.28.13:5433 maxconn 1000 check port 8008 inter 5000 rise 2 fall 2 启动 haproxysudo systemctl enable haproxy sudo systemctl start haproxy HAProxy 监控页面登录地址：http://192.168.28.11:8888/haproxy-status （也可以通过各个节点 IP + 端口登录）默认用户密码：admin/admin安装 Keepalived (所有节点)安装sudo apt install -y keepalived 配置文件主服务配置文件vim /etc/keepalived/keepalived.conf global_defs { router_id LVS_DEVEL00 script_user root enable_script_security } vrrp_script check_haproxy { script "/etc/keepalived/check_haproxy.sh" interval 2 weight 5 fall 3 rise 5 timeout 2 } vrrp_instance VI_1 { state Master interface ens18 virtual_router_id 80 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 12345 } virtual_ipaddress { 192.168.28.10/24 } track_script { check_haproxy } } 备库节点 1 配置文件vim /etc/keepalived/keepalived.conf global_defs { router_id LVS_DEVEL01 script_user root enable_script_security } vrrp_script check_haproxy { script "/etc/keepalived/check_haproxy.sh" interval 2 weight 5 fall 3 rise 5 timeout 2 } vrrp_instance VI_1 { state BACKUP interface ens18 virtual_router_id 80 priority 90 advert_int 1 authentication { auth_type PASS auth_pass 12345 } virtual_ipaddress { 192.168.28.10/24 } track_script { check_haproxy } } 备库节点 2 配置文件vim /etc/keepalived/keepalived.conf global_defs { router_id LVS_DEVEL02 script_user root enable_script_security } vrrp_script check_haproxy { script "/etc/keepalived/check_haproxy.sh" interval 2 weight 5 fall 3 rise 5 timeout 2 } vrrp_instance VI_1 { state BACKUP interface ens18 virtual_router_id 80 priority 80 advert_int 1 authentication { auth_type PASS auth_pass 12345 } virtual_ipaddress { 192.168.24.15/24 } track_script { check_haproxy } } 检查脚本vim /etc/keepalived/check_haproxy.sh #!/bin/bash count=`ps aux | grep -v grep | grep haproxy | wc -l` if [ $count -eq 0 ]; then exit 1 else exit 0 fi 赋予执行权限chmod +x /etc/keepalived/check_haproxy.sh 依次启动sudo systemctl start keepalived sudo systemctl enable keepalived📌 转载信息来源： https://linux.do/t/topic/1485296原作者： xxdtb转载时间： 2026/1/19 17:52:08