OpenZiti HA 快速启动实战:三节点本地集群搭建、故障演练与源码级原理解析

发布时间:2026/10/6 22:22:06
OpenZiti HA 快速启动实战:三节点本地集群搭建、故障演练与源码级原理解析
零信任网络后端认证鉴权【免费下载链接】zitiThe parent project for OpenZiti. Here you will find the executables for a fully zero-trust, programmable network OpenZiti项目地址https://gitcode.com/gh_mirrors/zi/ziti点击查看免费下载导读本文基于 OpenZiti 仓库 doc/ha/quickstart.md 编写完整讲解如何在单台机器上用三个独立 TCP 端口跑起一套自包含的 OpenZiti 高可用HA集群涵盖首个成员创建集群、后续成员加入、Agent 工具查看集群状态、模拟成员故障与恢复的全过程。读完本文你将能独立复现这套交互式演练并理解ziti edge quickstart命令背后的集群初始化、Raft 选主与自动加入机制同时掌握如何用ziti agent cluster list定位与验证集群领导者。一、背景为什么需要快速启动一套 HA 集群OpenZiti 的控制器Controller是一个零信任网络的“大脑”负责身份认证、服务授权与网络策略编排。在生产环境中控制器以多副本方式构成一个 Raft 集群实现状态机复制与领导者选举——当某个成员宕机时其余成员仍能继续提供服务这正是 HAHigh Availability的核心价值。仓库中的 doc/ha/dev-setup.md 明确标注“HA 目前处于 beta 阶段”因此在开发、测试与学习场景下我们希望用最小成本验证 HA 行为。ziti edge quickstart提供的 HA 模式正好满足这一诉求它不需要预先准备 PKI、不需要手写配置文件一个命令即可拉起“控制器 内嵌路由器”再通过join子命令把多个实例加入同一集群。从源码结构看quickstart 的 HA 能力集中在 ziti/run/quickstart.go单实例快速启动与集群加入与 ziti/run/quickstart_cluster.go一键拉起整簇后文会结合这两处实现逐一展开。二、环境准备干净的工作目录整个演练是**自包含self-contained**的所有数据、PKI、配置都产生在当前工作目录内前后无需任何额外步骤即可得到一个可用的本地集群。cd $(mktemp -d)mktemp -d会创建一个临时目录并把工作目录切换进去。之后的所有命令都默认在该目录下执行。演练结束后可以随时删除这个目录完成清理。三、启动第一个成员创建集群第一个成员ctrl1负责创建集群因此使用不带join子命令的 quickstartnohup ziti edge quickstart \ --instance-idctrl1 \ --ctrl-port1281 \ --router-port3021 \ --home${PWD} \ --ctrl-address127.0.0.1 \ --router-address127.0.0.1 \ --trust-domainha-quickstart \ ctrl1.log 各参数的含义如下参数解析实现见 ziti/run/quickstart.go 中的addCommonQuickstartFlags与addQuickstartHaFlags参数含义本演练取值源码中的处理--instance-idHA 模式下每个实例的唯一标识会写入 SPIFFE ID 与实例目录名ctrl1缺省时使用instance-1源码NewQuickStartCmd默认值join模式强制要求--ctrl-port控制面与 Edge API 使用的端口1281默认值取自constants包中的DefaultCtrlEdgeAdvertisedPort--router-port内嵌路由器监听端口3021默认值取自constants.DefaultZitiEdgeRouterPort--home数据目录数据库、PKI、配置文件的根目录${PWD}缺省时创建临时目录并在退出时删除显式指定则永久保留--ctrl-address控制面/API 对外通告地址127.0.0.1启动时写入ZITI_CTRL_ADVERTISED_ADDRESS、ZITI_CTRL_EDGE_ADVERTISED_ADDRESS等环境变量--router-address内嵌路由器通告地址127.0.0.1写入ZITI_EDGE_ROUTER_ADVERTISED_ADDRESS--trust-domainSPIFFE ID 中使用的信任域ha-quickstart缺省值为quickstart加入集群时强制要求其他常用参数还包括--username/-u与--password/-p管理员账号默认均为admin、--no-router不启动内嵌路由器、--zac下载并挂载 Ziti Admin Console 管理界面、--configure-and-exit完成配置后即退出等。启动后立即检查进程与日志tail ctrl1.log; echo; jobs预期输出大致如下日志被省略. . . ... logs ... . . . [1] running nohup ziti edge quickstart --instance-idctrl1 --ctrl-port1281 从源码看第一个成员启动时会依次完成生成最小 PKICreateMinimalPki创建根 CA、中间 CA、服务器/客户端证书SPIFFE ID 形如spiffe://ha-quickstart/controller/ctrl1→ 生成控制器配置文件ctrl.yaml→ 启动控制器并轮询等待其 HTTP 端点可用waitForController→ 通过本进程的 ops-agent 发起agent cluster init初始化集群源码中对初始化失败最多重试 5 次→ 创建并注册内嵌路由器。这也是为什么第一个成员必须由它自己“初始化”集群而后续成员只需“加入”。四、启动第二、三个成员加入集群第二个与第三个成员使用join子命令加入同一个集群其余参数与第一个成员保持一致仅更换端口与实例 IDnohup ziti edge quickstart join \ --instance-idctrl2 \ --ctrl-port1282 \ --router-port3022 \ --home${PWD} \ --ctrl-address127.0.0.1 \ --router-address127.0.0.1 \ --trust-domainha-quickstart \ --cluster-membertls:127.0.0.1:1281 \ ctrl2.log nohup ziti edge quickstart join \ --instance-idctrl3 \ --ctrl-port1283 \ --router-port3023 \ --home${PWD} \ --ctrl-address127.0.0.1 \ --router-address127.0.0.1 \ --trust-domainha-quickstart \ --cluster-membertls:127.0.0.1:1281 \ ctrl3.log 新增参数--cluster-member指定已有集群成员的控制面地址本例为tls:127.0.0.1:1281即 ctrl1新成员将通过它发起加入请求。在 ziti/run/quickstart.go 的NewQuickStartJoinClusterCmd中join模式还额外支持--cluster-member/-m必填集群成员的tls:host:port地址--non-voting将新成员以非投票成员non-voter身份加入集群不参与 Raft 投票。分别确认两个后台任务运行正常tail ctrl2.log; echo; jobs tail ctrl3.log; echo; jobs预期输出日志省略[1] - running nohup ziti edge quickstart --instance-idctrl1 --ctrl-port1281 [2] running nohup ziti edge quickstart join --instance-idctrl2 --ctrl-port1282 [1] running nohup ziti edge quickstart --instance-idctrl1 --ctrl-port1281 [2] - running nohup ziti edge quickstart join --instance-idctrl2 --ctrl-port1282 [3] running nohup ziti edge quickstart join --instance-idctrl3 --ctrl-port1283加入过程的源码细节join模式下 quickstart 不会重新生成根 CA源码注释明确指出“dont emit a root-ca, expect itll be there or error”即复用第一个成员生成的根 CA 来创建本实例的服务器 PKI随后通过本进程 ops-agent 反复执行agent cluster add直到成功或 90 秒超时。源码中有一段针对性的重试逻辑加入请求提交后集群成员关系已通过 AddPeer Raft 命令落盘但管理员认证数据可能尚未复制到本地 FSM导致一次性登录返回 401因此loginWithRetry会以 1 秒间隔重试最多 60 秒。可选如果想在另一个终端同时观察三个实例的日志可以合并追踪所有*.logtail -F -n 1 *.log五、观察集群Agent 工具与领导者定位OpenZiti 为控制器提供了基于 gops agent 的运维通道。先列出本机所有 agent 应用ziti agent list预期输出╭────────┬────────────┬────────┬─────────────────────────────┬────────────┬─────────────┬───────────╮ │ PID │ EXECUTABLE │ APP ID │ UNIX SOCKET │ APP TYPE │ APP VERSION │ APP ALIAS │ ├────────┼────────────┼────────┼─────────────────────────────┼────────────┼─────────────┼───────────┤ │ 276912 │ ziti │ ctrl1 │ /tmp/gops-agent.276912.sock │ controller │ v0.0.0 │ │ │ 277714 │ ziti │ ctrl2 │ /tmp/gops-agent.277714.sock │ controller │ v0.0.0 │ │ │ 281490 │ ziti │ ctrl3 │ /tmp/gops-agent.281490.sock │ controller │ v0.0.0 │ │ ╰────────┴────────────┴────────┴─────────────────────────────┴────────────┴─────────────┴───────────╯其中APP ID对应--instance-idAPP TYPE均为controller每个进程在/tmp下暴露独立的gops-agent.pid.sock源码中 quickstart 正是通过unix:/tmp/gops-agent.pid.sock这个 socket 直接发起集群操作避免按 PID 枚举全部 agent。查看 ctrl1 视角下的集群成员ziti agent cluster list --app-id ctrl1预期输出╭───────┬────────────────────┬───────┬────────┬─────────┬───────────╮ │ ID │ ADDRESS │ VOTER │ LEADER │ VERSION │ CONNECTED │ ├───────┼────────────────────┼───────┼────────┼─────────┼───────────┤ │ ctrl1 │ tls:127.0.0.1:1281 │ true │ true │ v0.0.0 │ true │ │ ctrl2 │ tls:127.0.0.1:1282 │ true │ false │ v0.0.0 │ true │ │ ctrl3 │ tls:127.0.0.1:1283 │ true │ false │ v0.0.0 │ true │ ╰───────┴────────────────────┴───────┴────────┴─────────┴───────────╯此时ctrl1 是集群领导者LEADER 列为 true三个成员均为投票成员VOTER 为 true且全部连接正常。该命令的底层实现见 ziti/cmd/agentcli/agent_cluster_list.go它向目标 controller agent 发送RaftListMembersRequestType管理消息接收RaftMemberListResponse再以圆角表格渲染Id / Address / Voter / Leader / Version / Connected / Preferred七列。Preferred列表示是否为优先领导者——从源码推断集群在故障转移时会优先尝试将领导权交给 preferred 成员。六、故障演练终止领导者并观察集群自愈HA 的核心价值在于成员故障时的自动选主。现在模拟一次可用性事故把当前领导者 ctrl1 杀掉。先确认各后台任务的编号——领导者不一定对应%1务必以jobs实际输出为准jobs示例输出任务编号顺序可能与启动顺序不同[1] running nohup ziti edge quickstart --instance-idctrl1 --ctrl-port1281 [2] - running nohup ziti edge quickstart join --instance-idctrl3 --ctrl-port1283 [3] running nohup ziti edge quickstart join --instance-idctrl2 --ctrl-port1282本例中任务 1 属于 ctrl1当前领导者将其终止kill %1然后换一个幸存的成员查看集群状态ziti agent cluster list --app-id ctrl2预期输出╭───────┬────────────────────┬───────┬────────┬─────────────────┬───────────╮ │ ID │ ADDRESS │ VOTER │ LEADER │ VERSION │ CONNECTED │ ├───────┼────────────────────┼───────┼────────┼─────────────────┼───────────┤ │ ctrl1 │ tls:127.0.0.1:1281 │ true │ false │ not connected │ false │ │ ctrl2 │ tls:127.0.0.1:1282 │ true │ true │ v0.0.0 │ true │ │ ctrl3 │ tls:127.0.0.1:1283 │ true │ false │ v0.0.0 │ true │ ╰───────┴────────────────────┴───────┴────────┴─────────────────┴───────────┘两个关键变化值得注意ctrl1 显示not connected/false被终止的成员失去了控制面连接领导者已从 ctrl1 转移到 ctrl2剩余的两个投票成员ctrl2、ctrl3构成多数派3 节点集群的法定人数为 2Raft 完成重新选举ctrl2 接管领导权。七、成员恢复重启断连成员任意断连成员都可以用 quickstart 命令重启。此时只需要提供启动参数无需再指定集群成员地址——数据目录仍在它会以既有 Raft 成员身份自动回归集群nohup ziti edge quickstart \ --instance-idctrl1 \ --home${PWD} \ ctrl1.log 注意两点这里只传了--instance-id与--home其余端口/地址参数会从已持久化的配置home/ctrl1/ctrl.yaml中读取。源码中applyConfiguredEndpoints正是负责解析已生成的配置文件并恢复通告地址与端口而noteIgnoredSetupFlags会提示哪些仅在首次创建时生效的参数--ctrl-address、--ctrl-port、--router-address、--router-port、--trust-domain在本次重启中被忽略日志使用追加写入保留历史输出。重启完成后再次查看集群ziti agent cluster list --app-id ctrl2预期输出╭───────┬────────────────────┬───────┬────────┬─────────────────┬───────────╮ │ ID │ ADDRESS │ VOTER │ LEADER │ VERSION │ CONNECTED │ ├───────┼────────────────────┼───────┼────────┼─────────────────┼───────────┤ │ ctrl1 │ tls:127.0.0.1:1281 │ true │ false │ v0.0.0 │ true │ │ ctrl2 │ tls:127.0.0.1:1282 │ true │ false │ v0.0.0 │ true │ │ ctrl3 │ tls:127.0.0.1:1283 │ true │ true │ v0.0.0 │ true │ ╰───────┴────────────────────┴───────┴────────┴─────────────────┴───────────╯ctrl1 已恢复连接true但并未重新成为领导者——新的领导者是 ctrl3。这正是 Raft 选主机制的正常表现领导者是当前任期内的选举结果成员重启后不会自动“抢回”领导权。这一事实告诉我们不要对“哪个节点是领导者”做任何静态假设客户端与运维脚本都应动态查询。八、清理停止全部后台进程演练结束后停止所有后台任务。BASH 环境kill $(jobs -p)ZSH 环境kill ${${(v)jobstates##*:*:}%*}预期输出[1] done nohup ziti edge quickstart --instance-idctrl1 --ctrl-port1281 [2] done nohup ziti edge quickstart join --instance-idctrl2 --ctrl-port1282 [3] done nohup ziti edge quickstart join --instance-idctrl3 --ctrl-port1283随后可以删除演练目录即第一步mktemp -d创建的目录彻底完成清理。九、进阶ziti run quickstart cluster一键拉起整簇除了逐个nohup启动仓库还提供了更高层的封装ziti run quickstart cluster实现见 ziti/run/quickstart_cluster.go。它以一个父进程启动 N 个子进程每个子进程即一个 quickstart 节点自动完成“节点 1 初始化、其余节点依次 join”的编排按 Ctrl-C 即可优雅停止全部节点。该命令的核心参数参数说明默认值--size集群节点数量3合法范围 39更多成员会拖慢写操作延迟--ctrl-port基础控制面端口节点 i 监听 basei默认取自constants.DefaultCtrlEdgeAdvertisedPort--router-port基础路由器端口节点 i 监听 basei默认取自constants.DefaultZitiEdgeRouterPort--home数据目录缺省使用临时目录并在退出时删除无--ctrl-address/--router-address所有节点统一使用的通告地址环境默认值--trust-domainSPIFFE 信任域quickstart--shutdown-grace停止节点时的最长等待时间30s--zac下载 ZAC 并为每个节点挂载控制台false从源码实现看这个命令还做了几件值得注意的事情端口范围预校验--ctrl-port/--router-port加上--size后不得超过 65535且两条端口段不能重叠否则在拉起前直接报错quickstart_cluster.go中run开头的一段校验逻辑重启语义若--home下所有实例的db目录都已存在isFullRestart判定说明是既有集群重启必须同时启动全部节点以重新形成法定人数否则单节点无法选出领导者按实例隔离 CLI 配置目录每个子进程通过ZITI_CONFIG_DIR指向独立的 CLI 配置目录避免多个节点并发登录时争用同一份会话文件就绪哨兵父进程监听子进程输出中“Quickly add another member”字样以此判定节点已完全就绪领导者选举/加入完成、路由器运行再启动下一个节点。对于希望快速验证 HA、又不想手工管理多个终端窗口的场景这一命令是最省事的入口。十、对照手写配置的 HA 开发环境dev-setup快速启动quickstart自动生成了所有配置而 doc/ha/dev-setup.md 给出了另一条更贴近生产形态的路径——手工 PKI 手写控制器配置适合需要定制参数的开发环境。注意该文档同样标注 HA 处于 beta 阶段。10.1 生成三节点 PKI执行 doc/ha/create-pki.sh需先安装好zitiCLI./create-pki.sh脚本内容展示了 HA 场景下的信任链设计一个共享的根 CA--trust-domain ha.test作为集群信任根每个控制器拥有独立的中级签名证书并用--spiffe-id controller/ctrlN绑定各自的 SPIFFE 身份ziti pki create ca --trust-domain ha.test --pki-root ./pki --ca-file ca --ca-name HA Example Trust Root ziti pki create intermediate --pki-root ./pki --ca-name ca --intermediate-file ctrl1 --intermediate-name Controller One Signing Cert ziti pki create server --pki-root ./pki --ca-name ctrl1 --dns localhost --ip 127.0.0.1 --server-name ctrl1 --spiffe-id controller/ctrl1 ziti pki create client --pki-root ./pki --ca-name ctrl1 --client-name ctrl1 --spiffe-id controller/ctrl1 # ... 其余两个控制器以此类推10.2 启动三个控制器配置文件 doc/ha/ctrl1.yml以及同目录的ctrl2.yml、ctrl3.yml中包含了相对路径因此必须在doc/ha目录下运行ziti controller run ctrl1.yml ziti controller run ctrl2.yml ziti controller run ctrl3.yml以 ctrl1 的配置为例HA 相关的关键结构包括cluster.dataDir本节点 Raft 数据目录./data/ctrl1identity节点自身的服务端证书、私钥与 CA 链对应 10.1 生成的 PKIctrl.listener与options.advertiseAddress控制面监听与通告地址tls:127.0.0.1:6262/tls:localhost:6262events.jsonLogger订阅connect、cluster事件并写入json格式日志文件便于观察集群事件edge.api/edge.enrollmentEdge API 监听地址与注册签名证书、enrollment 时长web绑定多个 APIhealth-checks、fabric、edge-management、edge-client、edge-oidc并限定 TLS 版本。10.3 初始化集群并加入成员三个控制器启动后利用 Agent 通道完成集群初始化与成员加入# 初始化第一个控制器用户名/密码均为 admin显示名 Default Admin ziti agent cluster init -i ctrl1 admin admin Default Admin # 将另外两个节点加入集群地址取各自 ctrl 通告地址 ziti agent cluster add -i ctrl1 tls:localhost:6363 ziti agent cluster add -i ctrl1 tls:localhost:6464至此三节点集群已就绪可分别登录各控制器验证ziti edge login localhost:1280 ziti edge -i ctrl2 login localhost:1380 ziti edge -i ctrl3 login localhost:1480随后可在任意控制器上创建模型数据并跨节点查询ziti demo setup echo client ziti demo setup echo single-sdk-hostedziti edge login localhost:1280 ziti edge ls services ziti edge login -i ctrl2 localhost:1380 ziti edge -i ctrl2 ls services ziti edge login -i ctrl3 localhost:1480 ziti edge -i ctrl3 ls services因为 Raft 会在各成员间复制状态三个控制器的查询结果应保持一致。10.4 HA 场景下的 SDK 兼容性dev-setup 文档同时说明自 Golang SDK v1.2.3 起SDK 无需特殊改动即可接入 HA 系统。这意味着客户端只需使用常规的 Edge API 登录与拨号流程控制器集群的领导者转移对 SDK 是透明的。十一、测试佐证HA 集群在仓库中的验证方式快速启动脚本并非孤证仓库的端到端测试基建同样覆盖了多控制器集群场景。tests/ha_cluster.go 中peerController表示一个次要集群成员控制器主控制器保留在TestContext字段上使既有测试辅助函数无需改动即可继续工作StartHaCluster会先清空 Raft 数据目录启动主控制器与所有 peer把它们加入同一个 Raft 集群并等待每个成员都成为投票者且注册进 Controller store——与本文演练的“初始化 join”流程一一对应NewEdgeClientApiForHost支持将 Edge API 客户端指向集群中任意成员用来验证跨节点读写一致性。此外 tests/cli_tests/cluster_test.go 与 quickstart/test/ha-test.sh 也提供了 CLI 层与脚本层的 HA 验证入口读者可结合这些文件深入了解测试细节。十二、小结HA 快速启动的关键事实清单三节点是 HA 的最小形态3 个投票成员的法定人数为 2任一成员宕机后集群仍可选出新领导者节点数越多写操作Raft 提交延迟越高这也是quickstart cluster把--size限制在 39 的原因。领导者不固定成员故障会触发重新选举重启后的原领导者不会自动夺回领导权所有客户端与脚本都应通过ziti agent cluster list动态定位领导者。quickstart 自动完成全部编排PKI 生成、配置生成、集群初始化agent cluster init、成员加入agent cluster add、内嵌路由器注册与启动全部内置于 ziti/run/quickstart.go这也是它能“一个命令拉起一个节点”的原因。重启成员只需最小参数数据目录持久化后--home与--instance-id即足以让节点以既有 Raft 成员身份回归其余端点参数从已生成配置读取。两条上手路径追求效率用本文的ziti edge quickstartjoin或ziti run quickstart cluster需要定制配置则参考 doc/ha/dev-setup.md 的手工 PKI 配置文件方案配置模板可对照 doc/ha/ctrl1.yml 学习。赞分享零信任网络后端认证鉴权【免费下载链接】zitiThe parent project for OpenZiti. Here you will find the executables for a fully zero-trust, programmable network OpenZiti项目地址https://gitcode.com/gh_mirrors/zi/ziti点击查看免费下载相关推荐Redis集群节点故障模拟终极指南CacheCloud测试环境快速搭建与实战演练Redis集群节点故障模拟终极指南CacheCloud测试环境快速搭建与实战演练 CacheCloud是搜狐视频开发的Redis私有云平台支持Standal后端运维MAS 激活脚本完整教程4 条路线 10 分钟搞定 Windows 与 Office 永久激活MAS 激活脚本完整教程4 条路线 10 分钟搞定 Windows 与 Office 永久激活 多数激活问题缺的不是密钥而是选错了通道。开源项目 Micr操作系统minikube 本地 Kubernetes 集群实战指南从快速启动到多集群、Addons 与源码级解析minikube 本地 Kubernetes 集群实战指南从快速启动到多集群、Addons 与源码级解析 minikube 是 Kubernetes 官方社区云原生容器编排CLI开发工具上一篇5个场景让你彻底告别PDF处理难题在线PDF工具全攻略下一篇DependenciesWindows DLL依赖分析终极指南 - 快速解决应用程序启动问题创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考