ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

etcd contrib 目录深度指南:分布式锁 Fencing 示例、Grafana 监控 Mixin 与 systemd 部署实践

etcd contrib 目录深度指南:分布式锁 Fencing 示例、Grafana 监控 Mixin 与 systemd 部署实践 etcd contrib 目录深度指南分布式锁 Fencing 示例、Grafana 监控 Mixin 与 systemd 部署实践【免费下载链接】etcdDistributed reliable key-value store for the most critical data of a distributed system项目地址: https://gitcode.com/GitHub_Trending/et/etcdetcd 仓库中的 contrib 目录 收集了四类“有用但不属于核心项目”的配套资源分布式锁租约过期问题的可运行示例lock、可定制的 Grafana 看板与 Prometheus 告警集合mixin、面向 systemd 发行版的示例服务单元文件systemd以及基于 systemd 的三节点集群部署流程systemd/etcd3-multinode。读完本文你能够复现并理解“基于租约的分布式锁为何不保证互斥、如何用 fencing token 补救”掌握 etcd 监控 mixin 的配置项、编译与告警单测方法并能在 systemd 系统上完成从单节点到三节点集群的部署、日志查看与启停操作。一、contrib 目录的定位contrib/README.md 的开篇定义非常明确这里的脚本和文件“可能有用但不属于 etcd 核心项目”。其内容索引包括lock —— 演示并解决 etcd 分布式锁中租约过期问题的示例mixin —— 可定制的 etcd Grafana 看板与 Prometheus 告警集合systemd —— 基于 systemd 的发行版上部署 etcd 的示例单元文件systemd/etcd3-multinode —— 基于 systemd 的多节点集群搭建指南。下面逐个展开并结合作用户端、服务端源码印证其工作机制。二、lock用 Fencing Token 破解“租约过期”的分布式锁示例2.1 问题背景基于租约的锁为什么不够contrib/lock/README.md 指出一个基于租约lease的锁服务无法为进程间提供严格的互斥因为租约机制依赖锁服务与客户端双方的物理时钟。语言运行时的 Stop-The-World GC 暂停、网络抖动、进程卡顿等都可能造成“已被授予的租约被错误地判定为过期”——此时第二个客户端可能拿到锁而第一个客户端以为自己仍持有锁两者并发访问共享资源就会破坏一致性。该目录引用的正是 Martin Kleppmann 关于分布式锁的经典论述解决办法是在共享资源侧引入版本号校验fencing token。每次持锁者写入资源时都必须带上自己的“版本”本示例中即 etcd 的 lease ID资源端拒绝任何版本与当前记录不匹配的写入请求从而让“已经失去租约的旧客户端”无法再污染数据。2.2 两个可执行程序storage 与 client示例由两个独立程序构成storage一个极简的内存 KV 存储通过 HTTP 和一个自定义 JSON 协议{op,key,val,version}提供读写接口client模拟客户端进程借助 etcd 的锁机制协调对storage的写入。storage 的核心校验逻辑见 contrib/lock/storage/storage.go。它用map[string]*value保存数据每个value同时记录val和version。写请求的处理流程是} else if strings.Compare(req.Op, write) 0 { if val, ok : data[req.Key]; ok { if req.Version ! val.version { // 版本不匹配拒绝写入返回错误 writeResponse(response{, -1, fmt.Sprintf( given version (%x) is different from the existing version (%x), req.Version, val.version)}, w) } else { data[req.Key].val req.Val data[req.Key].version req.Version ...关键就在req.Version ! val.version这一判断键已存在且请求携带的版本与存储中记录的版本不一致时写入被直接拒绝——这就是 fencing token 校验。服务默认监听:8080http.ListenAndServe(:8080, nil)。client 的加锁与写数据流程见 contrib/lock/client/client.go其调用链为client, err : clientv3.New(clientv3.Config{ Endpoints: []string{http://127.0.0.1:2379, http://127.0.0.1:22379, http://127.0.0.1:32379}, }) ... session, err : concurrency.NewSession(client, concurrency.WithTTL(1)) ... locker : concurrency.NewLocker(session, /lock) locker.Lock() defer locker.Unlock() version : session.Lease() log.Printf(acquired lock, version: %x, version)几个值得注意的实现细节连接预检NewSession前先调用client.MemberList(ctx)做 5 秒超时的连接检查注释说明否则newSession可能无限期挂起1 秒 TTLconcurrency.WithTTL(1)把租约存活时间压到 1 秒便于快速制造“租约过期”的场景fencing token 即 lease IDversion : session.Lease()直接取会话租约 ID 作为版本令牌写storage时原样携带write(key0, ..., int64(version))模式参数./client 1与./client 2分别扮演两个客户端——client 1 在拿到锁后等待人工按键制造“租约已过期但仍尝试写入”的旧客户端client 2 则正常完成写入etcd 端点从源码看端点写死为2379/22379/32379三个端口即预期运行在本地三节点 etcd 集群单节点实例也可连通因为 gRPC 会轮转可用端点。2.3 完整复现步骤以下流程完整继承自 contrib/lock/README.md。第 1 步构建。分别在contrib/lock/client与contrib/lock/storage目录下执行go build。注意 client 依赖go.etcd.io/etcd/client/v3的concurrency包需在仓库 go workspace见 go.work或能解析该模块的环境中构建。第 2 步启动 etcd。$ make # build etcd $ bin/etcd # start etcd第 3 步启动 storage。$ ./storage第 4 步启动 client 1扮演“Client 1”。$ ./client 1输出类似client 1 starts created etcd client and session acquired lock, version: 694d82254d5fa305 please manually revoke the lease using etcdctl lease revoke 694d82254d5fa305 or wait for it to expire, then start executing client 2 and hit any key...第 5 步验证租约已创建。$ bin/etcdctl lease list found 1 leases 694d82254d5fa305第 6 步手动撤销租约模拟“第一个客户端实际已失去锁”。$ bin/etcdctl lease revoke 694d82254d5fa305 lease 694d82254d5fa305 revoked第 7 步启动 client 2。它应顺利取锁并写入。$ ./client 2 client 2 starts created etcd client and session acquired lock, version: 694d82254e18770a this is client 2, continuing第 8 步回到 client 1 按任意键恢复其执行观察 fencing 生效resuming client 1 expected fail to write to storage with old lease version: error: given version (694d82254d5fa305) is different from the existing version (694d82254e18770a)client 1 持有旧 lease ID 作为版本令牌被 storage 的版本校验拒绝——这正是该示例想演示的结论锁只负责协调真正的互斥必须靠资源端的 token 校验兜底。这套“session Locker lease ID 作 fencing token”的用法与 etcd 客户端库client/v3/concurrency包的设计是一致的可直接迁移到真实业务中。三、mixinetcd 的 Grafana 看板与 Prometheus 告警集合3.1 组成与状态说明contrib/mixin/README.md 声明这是一套“可定制的 etcd Grafana 看板与 Prometheus 告警集合”并特别提示该项目处于 alpha 阶段标志位、配置、行为与设计可能在后续版本中大幅变化。从 contrib/mixin/mixin.libsonnet 可以看到其结构由三部分组成(import ./config.libsonnet) (import ./dashboards/dashboards.libsonnet) (import ./alerts/alerts.libsonnet)即全局配置 看板定义contrib/mixin/dashboards/ 下有etcd.libsonnet、etcd-grafana7x.libsonnet、panels.libsonnet、variables.libsonnet等 7 个 jsonnet 文件 告警定义contrib/mixin/alerts/alerts.libsonnet。外部依赖通过 contrib/mixin/jsonnetfile.json 声明——使用 Grafana 官方的 grafonnetv10.0.0库用jbjsonnet-bundler管理。3.2 核心配置项所有定制点集中在 contrib/mixin/config.libsonnet配置项默认值说明grafana7xfalse设为true生成兼容 Grafana 7.x 的看板默认生成兼容 Grafana 8.x 及更新版本etcd_selectorjob~.*etcd.*筛选 etcd 实例的 PromQL 选择器可按自己的 relabel 规则调整etcd_instance_labelsinstance唯一标识一个 etcd 实例的标签名列表集群级告警需要按这些标签聚合。若 etcd 部署在 K8s 上通常要改为instance, podscrape_interval_seconds30全局抓取间隔用于动态调整告警中 rate 窗口dashboard_var_refresh2看板变量刷新策略0 从不刷新1 加载时刷新2 时间范围变化时刷新clusterLabeljob用于识别集群的标签名3.3 编译看板与告警、跑告警单测前置工具安装README 说明与 kubernetes-mixin 用法一致make toolscontrib/mixin/Makefile 中tools目标会安装jsonnet、gojsontoyaml、jb三个 Go 工具并下载 prometheus v2.33.1 的promtool到 GOPATH/bin。第一步编译 mixin 为告警规则 YAML供 promtool 读取make manifests其实现为jsonnet -e (import mixin.libsonnet).prometheusAlerts | gojsontoyaml manifests/etcd-prometheusRules.yaml第二步运行告警单元测试。promtool test rules test.yaml测试用例 contrib/mixin/test.yaml 构造了三个up{jobetcd,...}实例序列分别从第 12、9、5 分钟开始掉线验证两条关键告警的触发时机与内容etcdInsufficientMembers集群存活成员数不足以维持 quorum 时触发3 个实例场景下3 分钟后 2 个掉线即触发etcdMembersDown成员掉线时触发预期注解为description: etcd cluster etcd: members are down (3).、summary: etcd cluster members are down.并带severity: warning标签。这套“jsonnet 编译 promtool 规则测试”的流程使告警逻辑具备可重复验证的回归保障而不只是配置即止。四、systemd面向 systemd 发行版的示例单元文件contrib/systemd/etcd.service 提供了一份可直接参考的单元文件核心字段如下[Unit] Descriptionetcd key-value store Afternetwork-online.target local-fs.target remote-fs.target time-sync.target Wantsnetwork-online.target local-fs.target remote-fs.target time-sync.target [Service] Useretcd Typenotify EnvironmentETCD_DATA_DIR/var/lib/etcd EnvironmentETCD_NAME%m ExecStart/usr/bin/etcd Restartalways RestartSec10s LimitNOFILE40000 [Install] WantedBymulti-user.target几个值得注意的设计Typenotifyetcd 服务就绪后主动向 systemd 发送 sd_notify 通知这与 etcd 内嵌的 systemd 通知实现相呼应见 client/pkg/systemd 包确保依赖方不会在 etcd 真正就绪前访问它After/Wantsnetwork-online.target ... time-sync.targetetcd 对网络与时间同步敏感单元声明了在网络在线、本地/远程文件系统就绪、时间同步之后再启动EnvironmentETCD_NAME%m利用 systemd 的%m机器 ID 环境变量生成器让每个节点的 etcd 成员名默认以机器 ID 区分ETCD_DATA_DIR/var/lib/etcd则配合 etcd 读取ETCD_*环境变量的启动配置机制无需在ExecStart里堆参数RestartalwaysRestartSec10sLimitNOFILE40000崩溃自动拉起、放宽文件描述符上限这是生产部署的常规要求。配套的 contrib/systemd/sysusers.d/20-etcd.conf 通过 systemd 的 sysusers 机制创建专用运行账户避免 etcd 以 root 运行#Type Name ID GECOS Home u etcd - etcd user /var/lib/etcd即自动创建名为etcd的无固定 UID 系统用户家目录数据目录为/var/lib/etcd。五、etcd3-multinode用 systemd 搭建三节点集群contrib/systemd/etcd3-multinode/README.md 给出了一份完整的三节点集群部署流程以下按步骤继承原文并补充说明。5.1 准备数据目录etcd 需要宿主机上的数据目录先创建并授权给 systemd 下的 etcd 进程sudo mkdir -p /var/lib/etcd sudo chown -R root:$(whoami) /var/lib/etcd sudo chmod -R arw /var/lib/etcd5.2 编写 systemd 服务文件在每台机器上写入对应的服务文件以my-etcd-1为例${IP_1}替换为该节点实际 IP2、3 节点同理仅--name与 IP 不同cat /tmp/my-etcd-1.service EOF [Unit] Descriptionetcd Conflictsetcd.service Conflictsetcd2.service [Service] Typenotify Restartalways RestartSec5s LimitNOFILE40000 TimeoutStartSec0 ExecStartetcd --name my-etcd-1 \ --data-dir /var/lib/etcd \ --listen-client-urls http://${IP_1}:2379 \ --advertise-client-urls http://${IP_1}:2379 \ --listen-peer-urls http://${IP_1}:2380 \ --initial-advertise-peer-urls http://${IP_1}:2380 \ --initial-cluster my-etcd-1http://${IP_1}:2380,my-etcd-2http://${IP_2}:2380,my-etcd-3http://${IP_3}:2380 \ --initial-cluster-token my-etcd-token \ --initial-cluster-state new [Install] WantedBymulti-user.target EOF sudo mv /tmp/my-etcd-1.service /etc/systemd/system/my-etcd-1.service参数要点--initial-cluster必须列出全部三个成员的名字peer 地址且三台机器上该值完全一致--initial-cluster-state new表示创建新集群若集群已存在扩容、恢复场景应使用existing否则会触发“集群已存在”的冲突错误--initial-cluster-token用于区分不同集群的初始化 token避免历史集群混淆--listen-*用本机实际可路由 IP--advertise-*/--initial-advertise-*声明对外的客户端地址与 peer 地址Conflictsetcd.service/etcd2.service防止与发行版自带的旧 etcd 服务同时运行原文文件中的Documentation行指向 etcd 的 GitHub 项目主页此处从略。5.3 启动服务每台机器先 enable 再 start保证重启后自动拉起sudo systemctl daemon-reload sudo systemctl enable my-etcd-1.service sudo systemctl start my-etcd-1.service对my-etcd-2、my-etcd-3重复同样三步。5.4 查看日志systemd 通过 journald 保存 etcd 日志sudo systemctl status my-etcd-1.service -l --no-pager sudo journalctl -u my-etcd-1.service -l --no-pager | less sudo journalctl -f -u my-etcd-1.servicejournalctl -f可持续跟踪成员加入集群、选举 leader 的过程三个节点日志都出现选举成功且各自member数量变为 3即表示集群组建完成。5.5 停止 etcdsudo systemctl stop my-etcd-1.service sudo systemctl disable my-etcd-1.service六、小结与延伸阅读contrib 目录虽非 etcd 核心代码却是理解与运维 etcd 的高质量素材contrib/lock 用约 250 行 Go 代码完整演示了“租约锁 fencing token”的分布式锁正确用法源码在 client/client.go 与 storage/storage.go可作为业务中集成client/v3/concurrency包时的参考实现contrib/mixin 提供带单测的告警规则与 Grafana 看板生成链路配置项、编译命令与测试方法均可直接复用contrib/systemd 与 三节点部署指南 覆盖了 systemd 环境下从单元文件、专用用户到多节点集群的完整部署面。如需进一步了解 etcd 的服务端行为如快照、Raft、存储后端可继续参阅仓库中的 Documentation 目录本文所述内容均以当前仓库实际文件为准mixin 部分请注意其 alpha 状态的演进风险。【免费下载链接】etcdDistributed reliable key-value store for the most critical data of a distributed system项目地址: https://gitcode.com/GitHub_Trending/et/etcd创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表