etcd
用于分布式系统的最重要数据的分布式的、可靠的键值存储。
提示:
- 在阅读本文之前,建议先阅读 raft 算法
ectd 是强一致的、分布式的键值存储,它提供一种可靠的方式,来存储分布式系统或机器集群需要访问的数据。它在网络分区期间优雅地处理 Leader 选举,并且可以容忍机器故障,即使是在 Leader 节点中。
使用标准的 HTTP 工具,比如 curl,读写值。
与标准文件系统一样,在层次结构组织的目录中,存储数据。
监听特定键或目录的变化,对值的变化做出反应。
关于从预构建二进制文件(pre-built binaries)或源码安装 etcd 的细节,请参阅官方文档。接下来讲述如何安装预构建二进制文件。
安装 etcd 的最简单方式是从预构建二进制文件:
解压归档文件。这将生成包含二进制文件的目录
将可执行的二进制文件添加到 PATH 中。比如,将二进制文件重命名和/或移动到 PATH 的某个目录中(比如 /usr/local/bin
),或将前一步创建的目录添加到 PATH 中
在 Shell 中测试,etcd
在 PATH 中:
$ etcd --version
etcd Version: 3.5.0
...
官网教程:https://etcd.io/docs/v3.5/tutorials/how-to-setup-cluster/
在 etcd 中设置集群指南
在每个 etcd 节点上,指定集群成员:
CLUSTER_STATE=new
NAME_1=machine-1
NAME_2=machine-2
NAME_3=machine-3
HOST_1=192.168.56.121
HOST_2=192.168.56.122
HOST_3=192.168.56.123
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
在每台机器上运行:
# 在第一台机器上运行
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}
# 在第二台机器上运行
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}
# 在第三台机器上运行
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}
现在 etcd 集群已经就绪,使用 etcdctl 连接 etcd:
x
export ETCDCTL_API=3
HOST_1=192.168.56.121
HOST_2=192.168.56.122
HOST_3=192.168.56.123
ENDPOINTS=$HOST_1:2379,$HOST_2:2379,$HOST_3:2379
etcdctl --endpoints=$ENDPOINTS member list
etcd 集群访问指南
put
命令用于写:
etcdctl --endpoints=$ENDPOINTS put foo "Hello World!"
get
用于从 etcd 读:
etcdctl --endpoints=$ENDPOINTS get foo
etcdctl --endpoints=$ENDPOINTS --write-out="json" get foo
根据前缀提取 etcd 键的指南
etcdctl --endpoints=$ENDPOINTS put web1 value1
etcdctl --endpoints=$ENDPOINTS put web2 value2
etcdctl --endpoints=$ENDPOINTS put web3 value3
etcdctl --endpoints=$ENDPOINTS get web --prefix
描述删除 etcd 键的方式
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 --prefix
进行事务性写入的指南
txn
将多个请求包装进单个事务:
xxxxxxxxxx
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
监视 etcd 键的指南
watch
用于获取未来变更的通知:
etcdctl --endpoints=$ENDPOINTS watch stock1
etcdctl --endpoints=$ENDPOINTS put stock1 1000
etcdctl --endpoints=$ENDPOINTS watch stock --prefix
etcdctl --endpoints=$ENDPOINTS put stock1 10
etcdctl --endpoints=$ENDPOINTS put stock2 20
在 etcd 中创建租约(lease)的指南
lease
用于带 TTL 的写:
xxxxxxxxxx
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
在 etcd 中创建分布式锁的指南
lock
用于分布式锁:
etcdctl --endpoints=$ENDPOINTS lock mutex1
# another client with the same name blocks
etcdctl --endpoints=$ENDPOINTS lock mutex1
在 etcd 集群中,实施主节点选举的指南
elect
用于主节点选举:
xxxxxxxxxx
etcdctl --endpoints=$ENDPOINTS elect one p1
# another client with the same name blocks
etcdctl --endpoints=$ENDPOINTS elect one p2
检查 etcd 集群状态的指南
为每台机器指定初始集群配置:
xxxxxxxxxx
etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint status
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 10.240.0.17:2379 | 4917a7ab173fabe7 | 3.5.0 | 45 kB | true | false | 4 | 16726 | 16726 | |
| 10.240.0.18:2379 | 59796ba9cd1bcd72 | 3.5.0 | 45 kB | false | false | 4 | 16726 | 16726 | |
| 10.240.0.19:2379 | 94df724b66343e6c | 3.5.0 | 45 kB | false | false | 4 | 16726 | 16726 | |
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------|
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.025451ms
关于 etcd 数据库快照的指南
snapshot
用于保存 etcd 数据库的时间点(point-in-time)快照:
快照只能从一个 etcd 节点请求,因此 --endpoints
标志应该只包含一个端点。
ENDPOINTS=$HOST_1:2379
etcdctl --endpoints=$ENDPOINTS snapshot save my.db
Snapshot saved at my.db
xxxxxxxxxx
etcdctl --write-out=table --endpoints=$ENDPOINTS snapshot status my.db
+---------+----------+------------+------------+
| HASH | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| c55e8b8 | 9 | 13 | 25 kB |
+---------+----------+------------+------------+
处理 etcd 集群成员的指南
memeber
用于添加、移除、更新成员:
在每台机器上执行:
x
# 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=192.168.56.121
HOST_2=192.168.56.122
HOST_3=192.168.56.123
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
在 node 1 上执行:
x
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}
在 node 2 上执行:
xxxxxxxxxx
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}
在 node3 上执行:
x
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}
然后使用 member remove
和 member add
命令替换成员:
x
# 获取 member ID
export ETCDCTL_API=3
HOST_1=192.168.56.121
HOST_2=192.168.56.122
HOST_3=192.168.56.123
etcdctl --endpoints=${HOST_1}:2379,${HOST_2}:2379,${HOST_3}:2379 member list
# 移除成员
MEMBER_ID=300774bba1f38564
etcdctl --endpoints=${HOST_1}:2379,${HOST_2}:2379,${HOST_3}:2379 \
member remove ${MEMBER_ID}
# 添加新成员 node 4
export ETCDCTL_API=3
NAME_1=etcd-node-1
NAME_2=etcd-node-2
NAME_4=etcd-node-4
HOST_1=192.168.56.121
HOST_2=192.168.56.122
HOST_4=192.168.56.124 # 新成员
etcdctl --endpoints=${HOST_1}:2379,${HOST_2}:2379 \
member add ${NAME_4} \
--peer-urls=http://${HOST_4}:2380
下面,使用 --initial-cluster-state existing
标记启动新成员:
xxxxxxxxxx
# [警告]如果从相同的磁盘空间启动新成员,
# 确保移除旧成员的数据目录
#
# 使用 “existing” 标记重启
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=192.168.56.121
HOST_2=192.168.56.122
HOST_4=192.168.56.124 # 新成员
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}