- 云原生
- 后端
- 微服务
- DevOps
- AI 应用
【免费下载链接】rainbond
无需学习 Kubernetes 的容器平台,在 Kubernetes 上构建、部署、组装和管理应用,无需 K8s 专业知识,全流程图形化管理
本文以 Rainbond 仓库内置的 etcd(位于 third_party/coreos-etcd)官方 Demo 文档为核心骨架,系统讲解从零搭建 etcd 多节点集群、用 etcdctl 完成读写、前缀查询、事务、监听、租约、分布式锁、选举、快照、迁移、成员管理与认证授权的完整流程,并结合 util/etcd 下的客户端封装源码,说明这些操作在 Rainbond 平台中的真实落地方式。读完本文,你将具备独立搭建并运维一套生产级 etcd 集群、熟练使用 etcdctl 完成日常键值管理与故障排查的实战能力。
Rainbond 是一款"无需学习 Kubernetes"的应用管理平台,而 etcd 作为其底层分布式协调组件,承担着服务发现、配置存储、任务分发与主节点选举等关键职责。仓库在 third_party/coreos-etcd 中完整保留了 etcd 官方文档与源码,其中 Documentation/demo.md 是一份面向实操的端到端演示指南——从集群搭建到 API 的每个核心操作都有可复制的命令行示例。本文将该文档的每一步命令展开讲解,并对照 Rainbond 源码中真实的 etcd 客户端封装,让你"既会敲命令,也懂底层原理"。
一、etcd 在 Rainbond 中的角色与源码封装概览
在进入 Demo 操作之前,先建立整体认知。从源码结构看,Rainbond 对 etcd 的封装集中在 util/etcd 目录:
| 文件 | 职责 |
|---|---|
| client.go | 封装 etcd v3 客户端创建,支持多 Endpoint、TLS 证书、自动同步与重试 |
| etcd.go | 基于 v3 事务与租约实现键值抽象(RemoteKV / EphemeralKV) |
| error.go | 统一 etcd 错误处理与日志输出 |
| etcdlock/master.go | 基于concurrency.Election实现主节点选举(Master 锁) |
其中 client.go 的NewClient只支持 v3 协议(注释明确写着 "do not support v2"),并对连接参数做了生产级默认值处理:
DialTimeout小于等于 5 秒时使用默认 5 秒;AutoSyncInterval小于等于 30 秒时使用默认 10 秒;- 额外设置
DialKeepAliveTime: 2s与DialKeepAliveTimeout: 6s保持长连接存活; - 当
CaFile、CertFile、KeyFile三者同时非空时,自动构建 TLS 客户端配置; - 连接失败时每 10 秒重试一次,直到成功,保证平台核心组件对 etcd 的强依赖。
而在业务层面,api/handler/etcd_handler.go 展示了 Rainbond 实际使用的三类 etcd 键空间:/servicecheck/(源码检测结果)、/rainbond/shareresult/(应用分享结果)、/rainbond/backup_restore/(备份恢复任务)。这些前缀键的清理逻辑即依赖下文将要讲到的"前缀删除"能力。理解这些落地场景后,再来看 Demo 中的每一条命令,含义会更加清晰。
二、搭建 etcd 集群:静态配置方式
Demo 文档给出的第一种集群搭建方式是静态指定集群成员。在每台 etcd 节点上先定义统一的集群变量:
TOKEN=token-01 CLUSTER_STATE=new NAME_1=machine-1 NAME_2=machine-2 NAME_3=machine-3 HOST_1=10.240.0.17 HOST_2=10.240.0.18 HOST_3=10.240.0.19 CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380关键变量说明:
TOKEN:集群初始化令牌,同一集群的所有节点必须一致,用于区分不同集群;CLUSTER_STATE:初始状态为new,表示这是一个全新集群;扩容时改为existing(见"成员管理"章节);CLUSTER:节点名=节点peer地址的逗号分隔列表,是全集群共享的成员拓扑。
然后在每台机器上分别执行(三台机器仅THIS_NAME与THIS_IP不同):
# For machine 1 THIS_NAME=${NAME_1} THIS_IP=${HOST_1} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} # For machine 2 THIS_NAME=${NAME_2} THIS_IP=${HOST_2} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} # For machine 3 THIS_NAME=${NAME_3} THIS_IP=${HOST_3} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}端口约定(全平台通用):
| 端口 | 用途 |
|---|---|
| 2379 | 客户端通信端口(--listen-client-urls/--advertise-client-urls) |
| 2380 | 节点间 Peer 通信端口(--listen-peer-urls/--initial-advertise-peer-urls) |
参数含义补充:
--data-dir:数据目录,Raft 日志、WAL 与键值快照均存储于此;--name:当前节点在集群内的唯一名字,必须与CLUSTER中对应的名字一致;--initial-advertise-peer-urls:向其他节点广播的 Peer 地址;--listen-peer-urls:本节点监听 Peer 请求的地址;--advertise-client-urls:向客户端广播的可访问地址(客户端通过它连接);--listen-client-urls:本节点监听客户端请求的地址;--initial-cluster:初始成员拓扑;--initial-cluster-state:new或existing;--initial-cluster-token:集群唯一性令牌,防止不同集群的数据混淆。
三、搭建 etcd 集群:使用公共 Discovery 服务
当节点 IP 事先不确定、不便手写成员拓扑时,可以使用 etcd 官方提供的公共发现服务discovery.etcd.io:
curl https://discovery.etcd.io/new?size=3 https://discovery.etcd.io/a81b5818e67a6ea83e9d4daea5ecbc92 # grab this token TOKEN=token-01 CLUSTER_STATE=new NAME_1=machine-1 NAME_2=machine-2 NAME_3=machine-3 HOST_1=10.240.0.17 HOST_2=10.240.0.18 HOST_3=10.240.0.19 DISCOVERY=https://discovery.etcd.io/a81b5818e67a6ea83e9d4daea5ecbc92工作流程是:curl /new?size=3向发现服务申请一个容量为 3 的集群令牌,获得一个唯一的发现 URL;然后每台节点启动时都携带--discovery指向该 URL,由发现服务负责把各节点互相"介绍"给对方,从而自动完成成员汇聚。
三台节点分别执行(同样仅THIS_NAME与THIS_IP不同):
THIS_NAME=${NAME_1} THIS_IP=${HOST_1} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \ --discovery ${DISCOVERY} \ --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} THIS_NAME=${NAME_2} THIS_IP=${HOST_2} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \ --discovery ${DISCOVERY} \ --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} THIS_NAME=${NAME_3} THIS_IP=${HOST_3} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \ --discovery ${DISCOVERY} \ --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}与静态配置的唯一区别是:用--discovery ${DISCOVERY}取代了--initial-cluster ${CLUSTER}。注意 Discovery 模式同样需要--initial-cluster-token标识集群,且发现服务注册的节点数量达到size=3后即开始组集群。该方式适合私有云、跨网段等不便预知全部地址的场景。
四、连接集群:etcdctl 与 Endpoints 约定
集群就绪后,用 etcdctl 连接。Demo 文档约定使用 v3 API 与多端点列表:
export ETCDCTL_API=3 HOST_1=10.240.0.17 HOST_2=10.240.0.18 HOST_3=10.240.0.19 ENDPOINTS=$HOST_1:2379,$HOST_2:2379,$HOST_3:2379 etcdctl --endpoints=$ENDPOINTS member list说明:
ETCDCTL_API=3:显式声明使用 v3 API(Rainbond 的 util/etcd/client.go 也只支持 v3,与之一致);--endpoints传入逗号分隔的多个节点,客户端会轮流尝试、自动故障转移;member list输出集群成员 ID 与地址,可用来验证集群是否搭建成功。
五、键值读写:put 与 get
5.1 写入(put)
etcdctl --endpoints=$ENDPOINTS put foo "Hello World!"put写入一个键值对。在 etcd v3 中,每次写入都会产生一个新的版本号(revision),历史版本会被保留直至压缩。
5.2 读取(get)
etcdctl --endpoints=$ENDPOINTS get foo etcdctl --endpoints=$ENDPOINTS --write-out="json" get foo- 默认输出纯文本形式;
--write-out="json"输出完整的 JSON 结构,包含header(集群 ID、成员 ID、revision)与kvs(键值及版本信息),是排查与脚本化处理的常用方式。
Rainbond 侧的对应用法可参考 util/etcd/etcd.go:其newSequentialKV通过kv.Get(prefix, WithLastKey())先读取前缀下最后一个键,再通过事务写入新键,这正是"前缀查询 + 事务"组合实现队列分配的典型模式。
六、前缀查询:get --prefix
etcd 的键是扁平的字节串,前缀查询是最常用的"目录式"组织手段:
etcdctl --endpoints=$ENDPOINTS put web1 value1 etcdctl --endpoints=$ENDPOINTS put web2 value2 etcdctl --endpoints=$ENDPOINTS put web3 value3 etcdctl --endpoints=$ENDPOINTS get web --prefixget web --prefix会返回所有以web为前缀的键(web1、web2、web3)。Raft 的线性读保证该查询返回一致视图。
Rainbond 正是利用前缀键来组织业务数据:如 api/handler/etcd_handler.go 中定义的/servicecheck/%s、/rainbond/shareresult/%s、/rainbond/backup_restore/%s三类前缀,并通过DeleteWithPrefix一次清理某应用的全部相关键。前缀设计是 etcd 键空间组织的第一原则。
七、删除操作:del
单键删除与前缀批量删除:
etcdctl --endpoints=$ENDPOINTS put key myvalue etcdctl --endpoints=$ENDPOINTS del key etcdctl --endpoints=$ENDPOINTS put k1 value1 etcdctl --endpoints=$ENDPOINTS put k2 value2 etcdctl --endpoints=$ENDPOINTS del k --prefixdel key:删除单个键;del k --prefix:删除所有以k开头的前缀键(此处会删除 k1、k2)。
删除操作同样会产生 revision 变更,可通过get验证键已不存在。
八、事务写入:txn(比较-交换)
txn将多个请求包装进一个原子事务,按"比较条件成立与否"分别执行成功/失败分支:
etcdctl --endpoints=$ENDPOINTS put user1 bad etcdctl --endpoints=$ENDPOINTS txn --interactive compares: value("user1") = "bad" success requests (get, put, delete): del user1 failure requests (get, put, delete): put user1 good执行逻辑:事务首先比较user1当前值是否等于bad;若成立则执行成功分支(del user1),否则执行失败分支(put user1 good)。整个比较与执行在服务端原子完成,不会被打断。--interactive表示交互式输入 compares / success / failure 三段内容,以空行结束一段。
事务是构建分布式协调逻辑的基石。Rainbond 源码中大量使用事务保证原子性,例如 util/etcd/etcd.go 的putNewKV:
cmp := v3.Compare(v3.Version(key), "=", 0) // 仅当键尚不存在(Version == 0) req := v3.OpPut(key, val, v3.WithLease(leaseID)) txnresp, err := kv.Txn(ctx).If(cmp).Then(req).Commit()它通过"比较 Version 为 0"实现键不存在才创建的原子语义;etcd.go 的deleteRevKey则比较ModRevision(key) == rev后再删除,实现仅删除指定版本的安全回收。这正是txn在真实系统中的典型用法。
九、监听变更:watch
watch用于订阅键的未来变化,是实现事件驱动、配置热更新的核心机制:
etcdctl --endpoints=$ENDPOINTS watch stock1 etcdctl --endpoints=$ENDPOINTS put stock1 1000第一个终端执行watch stock1进入阻塞监听;第二个终端执行put stock1 1000后,监听终端立即收到变更事件(包含键、值、版本号)。
前缀监听可同时关注多个键:
etcdctl --endpoints=$ENDPOINTS watch stock --prefix etcdctl --endpoints=$ENDPOINTS put stock1 10 etcdctl --endpoints=$ENDPOINTS put stock2 20watch stock --prefix会捕获 stock1、stock2 的所有写入。在分布式系统中,watch 通常配合租约与锁使用,作为状态变更的"通知总线"。
十、租约:lease(带 TTL 的键)
租约(Lease)为键附加存活时间(TTL),到期后键自动失效:
etcdctl --endpoints=$ENDPOINTS lease grant 300 # lease 2be7547fbc6a5afa granted with TTL(300s) etcdctl --endpoints=$ENDPOINTS put sample value --lease=2be7547fbc6a5afa etcdctl --endpoints=$ENDPOINTS get sample etcdctl --endpoints=$ENDPOINTS lease keep-alive 2be7547fbc6a5afa etcdctl --endpoints=$ENDPOINTS lease revoke 2be7547fbc6a5afa # or after 300 seconds etcdctl --endpoints=$ENDPOINTS get sample流程拆解:
lease grant 300:申请一个 TTL 为 300 秒的租约,返回租约 ID(如2be7547fbc6a5afa);put ... --lease=...:将键绑定到该租约,键的存活周期即租约剩余时间;lease keep-alive:周期性续约,防止键到期(守护进程/选举持锁者必备);lease revoke:主动撤销租约,绑定的键立即全部删除;- 既不续约也不撤销时,300 秒后
get sample将为空——这是"临时/注册类数据自动过期"的经典实现。
Rainbond 中EphemeralKV(util/etcd/etcd.go)正是"键 + 会话租约"的封装:通过concurrency.Session获取租约,再创建绑定该租约的键值,配合心跳自动续约,实现节点注册与自动摘除。etcdlock/master.go 中Lease.Grant后以租约 ID 创建 Session,同样是这一机制的运用。
十一、分布式锁:lock
lock基于租约 + 前缀键实现分布式互斥锁:
etcdctl --endpoints=$ENDPOINTS lock mutex1 # another client with the same name blocks etcdctl --endpoints=$ENDPOINTS lock mutex1第一个客户端持有mutex1锁后命令阻塞(持续持有);第二个客户端请求同名锁时同样阻塞,直到第一个客户端释放(中断命令或租约过期)。锁实现的核心是"同一前缀下创建唯一键 + 按 revision 排序取最小者持锁",util/etcd/etcd.go 的newUniqueKV用前缀/UnixNano时间戳生成唯一键的写法正是此类锁的键构造方式。
十二、选举:elect(主节点竞选)
elect提供基于 etcd 的领导者选举原语:
etcdctl --endpoints=$ENDPOINTS elect one p1 # another client with the same name blocks etcdctl --endpoints=$ENDPOINTS elect one p2elect one p1让当前进程以候选人p1身份参与名为one的选举;竞选成功则命令持续持有领导权并阻塞。第二个客户端elect one p2竞选同一名称时进入等待,直到当前领导者退出才接任。
Rainbond 将这一机制封装在 util/etcd/etcdlock/master.go 中:
CreateMasterLock(endpoints, election, prop, ttl)创建选举锁(master.go):默认 Endpoints 为http://127.0.0.1:2379,先Lease.Grant申请租约,再用concurrency.NewElection(session, election)建立选举对象;campaign()(master.go)调用election.Campaign参与竞选,并通过election.Observe监听结果:当选后发出MasterAdded事件,否则持续观察当前主节点;会话过期时发出MasterError并自动退出;- 事件类型
MasterAdded / MasterDeleted / MasterModified / MasterError通过EventsChan()通知调用方(master.go),上层据此判断"本节点是否成为主节点"。
这就是"多个 worker 实例同时启动,只有一个能成为主节点执行任务"的标准实现,与 Demo 中elect命令的语义完全一致。
十三、集群状态检查:endpoint status / health
13.1 查看各节点状态
etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint status +------------------+------------------+---------+---------+-----------+-----------+------------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX | +------------------+------------------+---------+---------+-----------+-----------+------------+ | 10.240.0.17:2379 | 4917a7ab173fabe7 | 3.0.0 | 45 kB | true | 4 | 16726 | | 10.240.0.18:2379 | 59796ba9cd1bcd72 | 3.0.0 | 45 kB | false | 4 | 16726 | | 10.240.0.19:2379 | 94df724b66343e6c | 3.0.0 | 45 kB | false | 4 | 16726 | +------------------+------------------+---------+---------+-----------+-----------+------------+表格列含义:
ENDPOINT:被检查的客户端地址;ID:该成员在 Raft 集群内的唯一 ID;VERSION:etcd 版本号(Demo 环境为 3.0.0);DB SIZE:当前键值数据库大小;IS LEADER:是否为 Raft 领导者(示例中 10.240.0.17 为 Leader);RAFT TERM:当前任期号,每次领导者变更递增;RAFT INDEX:已提交日志索引,三个节点一致说明数据同步正常。
13.2 健康检查
etcdctl --endpoints=$ENDPOINTS endpoint health 10.240.0.17:2379 is healthy: successfully committed proposal: took = 3.345431ms 10.240.0.19:2379 is healthy: successfully committed proposal: took = 3.767967ms 10.240.0.18:2379 is healthy: successfully committed proposal: took = 4.025451msendpoint health会向每个端点发起提案提交测试,能成功提交并返回耗时即判定为 healthy。这是日常巡检与故障定位(如节点掉线、网络分区)最常用的命令。
十四、快照:snapshot save / status
snapshot用于保存 etcd 数据库的时间点一致性快照,是备份与恢复的基础:
etcdctl --endpoints=$ENDPOINTS snapshot save my.db Snapshot saved at my.db查看快照文件信息:
etcdctl --write-out=table --endpoints=$ENDPOINTS snapshot status my.db +---------+----------+------------+------------+ | HASH | REVISION | TOTAL KEYS | TOTAL SIZE | +---------+----------+------------+------------+ | c55e8b8 | 9 | 13 | 25 kB | +---------+----------+------------+------------+字段说明:HASH为快照校验哈希(用于完整性校验),REVISION为快照时的最新修订号,TOTAL KEYS为键总数,TOTAL SIZE为文件大小。注意官方建议从非 Leader 节点执行snapshot save,以减轻 Leader 负载;恢复时需用同一哈希校验快照完整性。快照 + WAL 共同构成 etcd 的持久化层,对应--data-dir下的member/snap与member/wal目录。
十五、数据迁移:migrate(v2 → v3)
migrate用于将 etcd v2 存储中的数据转换到 v3 存储(注意:v2 与 v3 使用不同的存储引擎与 API,数据不互通):
# write key in etcd version 2 store export ETCDCTL_API=2 etcdctl --endpoints=http://$ENDPOINT set foo bar # read key in etcd v2 etcdctl --endpoints=$ENDPOINTS --output="json" get foo # stop etcd node to migrate, one by one # migrate v2 data export ETCDCTL_API=3 etcdctl --endpoints=$ENDPOINT migrate --data-dir="default.etcd" --wal-dir="default.etcd/member/wal" # restart etcd node after migrate, one by one # confirm that the key got migrated etcdctl --endpoints=$ENDPOINTS get /foo操作要点:
- 用
ETCDCTL_API=2配合 v2 命令set foo bar写入 v2 存储; --data-dir指向原 v2 数据目录,--wal-dir指向 WAL 子目录;- 逐节点执行:先停止节点 → 迁移 → 重启,再处理下一个节点,保证集群始终有可用成员(Raft 多数派存活);
- 迁移后键会带上
/前缀(示例中foo变为/foo),这是 v2 键在 v3 存储中的命名规则。
Rainbond 仅使用 v3 API(见 util/etcd/client.go 的注释 "do not support v2"),因此该迁移命令主要适用于从旧版 etcd 升级的场景。
十六、成员管理:member add / remove
成员管理用于集群扩容、故障节点替换。先按前文静态配置方式启动一个三节点集群(此例地址为 10.240.0.13~15):
# For each machine TOKEN=my-etcd-token-1 CLUSTER_STATE=new NAME_1=etcd-node-1 NAME_2=etcd-node-2 NAME_3=etcd-node-3 HOST_1=10.240.0.13 HOST_2=10.240.0.14 HOST_3=10.240.0.15 CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380 # For node 1 THIS_NAME=${NAME_1} THIS_IP=${HOST_1} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 \ --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 \ --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} \ --initial-cluster-token ${TOKEN} # For node 2 THIS_NAME=${NAME_2} THIS_IP=${HOST_2} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 \ --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 \ --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} \ --initial-cluster-token ${TOKEN} # For node 3 THIS_NAME=${NAME_3} THIS_IP=${HOST_3} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 \ --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 \ --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} \ --initial-cluster-token ${TOKEN}16.1 移除成员
先列出成员拿到目标成员的 ID,再执行移除:
# get member ID export ETCDCTL_API=3 HOST_1=10.240.0.13 HOST_2=10.240.0.14 HOST_3=10.240.0.15 etcdctl --endpoints=${HOST_1}:2379,${HOST_2}:2379,${HOST_3}:2379 member list # remove the member MEMBER_ID=278c654c9a6dfd3b etcdctl --endpoints=${HOST_1}:2379,${HOST_2}:2379,${HOST_3}:2379 \ member remove ${MEMBER_ID}16.2 添加新成员
# add a new member (node 4) export ETCDCTL_API=3 NAME_1=etcd-node-1 NAME_2=etcd-node-2 NAME_4=etcd-node-4 HOST_1=10.240.0.13 HOST_2=10.240.0.14 HOST_4=10.240.0.16 # new member etcdctl --endpoints=${HOST_1}:2379,${HOST_2}:2379 \ member add ${NAME_4} \ --peer-urls=http://${HOST_4}:2380member add只需连接任意一个现存成员即可把新节点登记进集群元数据,并返回该新节点启动所需的--initial-cluster参数。
16.3 以 existing 状态启动新成员
新节点加入既有集群时,CLUSTER_STATE必须改为existing:
# [WARNING] If the new member starts from the same disk space, # make sure to remove the data directory of the old member # # restart with 'existing' flag TOKEN=my-etcd-token-1 CLUSTER_STATE=existing NAME_1=etcd-node-1 NAME_2=etcd-node-2 NAME_4=etcd-node-4 HOST_1=10.240.0.13 HOST_2=10.240.0.14 HOST_4=10.240.0.16 # new member CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_4}=http://${HOST_4}:2380 THIS_NAME=${NAME_4} THIS_IP=${HOST_4} etcd --data-dir=data.etcd --name ${THIS_NAME} \ --initial-advertise-peer-urls http://${THIS_IP}:2380 \ --listen-peer-urls http://${THIS_IP}:2380 \ --advertise-client-urls http://${THIS_IP}:2379 \ --listen-client-urls http://${THIS_IP}:2379 \ --initial-cluster ${CLUSTER} \ --initial-cluster-state ${CLUSTER_STATE} \ --initial-cluster-token ${TOKEN}两条运维红线:
--initial-cluster必须填写包含新成员在内的完整成员列表(node-1、node-2、node-4);- 若新成员复用了旧成员的磁盘/数据目录,必须先删除旧数据目录(
data.etcd),否则可能因残留数据导致成员加入失败或数据不一致。
十七、认证与权限:auth / role / user
etcd 提供基于角色的访问控制(RBAC),保护集群数据不被未授权客户端读写:
export ETCDCTL_API=3 ENDPOINTS=localhost:2379 etcdctl --endpoints=${ENDPOINTS} role add root etcdctl --endpoints=${ENDPOINTS} role grant-permission root readwrite foo etcdctl --endpoints=${ENDPOINTS} role get root etcdctl --endpoints=${ENDPOINTS} user add root etcdctl --endpoints=${ENDPOINTS} user grant-role root root etcdctl --endpoints=${ENDPOINTS} user get root etcdctl --endpoints=${ENDPOINTS} auth enable # now all client requests go through auth etcdctl --endpoints=${ENDPOINTS} --user=root:123 put foo bar etcdctl --endpoints=${ENDPOINTS} get foo etcdctl --endpoints=${ENDPOINTS} --user=root:123 get foo etcdctl --endpoints=${ENDPOINTS} --user=root:123 get foo1逐步说明:
role add root:创建root角色;role grant-permission root readwrite foo:授予该角色对foo键的读写权限(注意此权限只覆盖foo本身,不含前缀范围,因此后续get foo1会因无权限被拒绝);role get root:查看角色权限;user add root:创建用户(交互式设置密码,示例密码为123);user grant-role root root:把root角色授予root用户;auth enable:开启认证,此后所有客户端请求都必须携带凭据;- 未带
--user的get foo会被拒绝;带--user=root:123的读写正常;get foo1因超出foo键的授权范围同样被拒——体现了"认证 + 键级授权"的双重约束。
在生产环境中,开启认证后所有应用客户端(包括 Rainbond 各组件)都需要在连接时配置用户名密码或 TLS 证书。Rainbond 的 util/etcd/client.go 已支持通过CaFile / CertFile / KeyFile构建 TLS 客户端,即为此类安全连接场景预留的能力。
十八、从 Demo 到生产:关键要点小结
综合上述全部操作,将 etcd 集群从搭建到运维的关键要点归纳如下:
- 搭建:静态
--initial-cluster或 Discovery 服务二选一;三节点起步,TOKEN与CLUSTER_STATE全集群一致; - 端口:2379 面向客户端,2380 面向节点间通信,切勿混淆;
- 读多写少:查询善用
--prefix与--write-out=json;写操作可组合txn保证原子性; - 高可用:用
lock/elect实现互斥与选举,用lease管理临时数据生命周期,用watch驱动配置与状态同步; - 运维:
endpoint status/health做日常巡检;snapshot save定期备份;成员变更(member add/remove)配合existing状态扩容替换; - 安全:
auth enable前先完成角色与用户规划,生产环境建议配合 TLS 双向认证; - 升级:跨大版本(v2 → v3)按节点逐个
migrate,始终保证 Raft 多数派存活。
若希望进一步深入,可继续阅读仓库内的相关材料:
- etcd 完整文档与源码:third_party/coreos-etcd(含 Documentation/demo.md、
Documentation/op-guide/运维指南、Documentation/dev-guide/开发指南等); - Rainbond etcd 客户端封装:util/etcd/client.go、util/etcd/etcd.go、util/etcd/error.go;
- 基于 etcd 的主节点选举实现:util/etcd/etcdlock/master.go;
- etcd 键空间在平台中的实际划分:api/handler/etcd_handler.go。
- 云原生
- 后端
- 微服务
- DevOps
- AI 应用
【免费下载链接】rainbond
无需学习 Kubernetes 的容器平台,在 Kubernetes 上构建、部署、组装和管理应用,无需 K8s 专业知识,全流程图形化管理
相关推荐
rainbond 内置 etcd v3.3 的 etcdctl 命令行实战:从 KV 操作、租约、集群维护到快照恢复全解
rainbond 内置 etcd v3.3 的 etcdctl 命令行实战:从 KV 操作、租约、集群维护到快照恢复全解 本文基于 rainbond 仓库内 v
云原生后端微服务DevOpsAI 应用rainbond 内置 etcd 本地发现集群一键启动:insta-discovery 实战与源码原理
rainbond 内置 etcd 本地发现集群一键启动:insta discovery 实战与源码原理 本文聚焦 rainbond 仓库内嵌的 etcd 源码(
云原生后端微服务DevOpsAI 应用Rainbond 内置 etcd 集群从 3.0 滚动升级到 3.1 全流程指南
Rainbond 内置 etcd 集群从 3.0 滚动升级到 3.1 全流程指南 导读:本指南围绕 etcd 官方升级文档( upgrade_3_1.md ht
云原生后端微服务DevOpsAI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考