Skip to content

etcd

etcd 是 CoreOS 基于 Raft 开发的分布式 key-value 存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。

在分布式系统中,如何管理节点间的状态一直是一个难题,etcd 像是专门为集群环境的服务发现和注册而设计,它提供了数据 TTL 失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便地跟踪并管理集群节点的状态。

  • 键值对存储:将数据存储在分层组织的目录中,如同在标准文件系统中
  • 监测变更:监测特定的键或目录以进行更改,并对值的更改做出反应
  • 简单:curl 可访问的用户的 API (HTTP+JSON)
  • 安全:可选的 SSL 客户端证书认证
  • 快速:单实例每秒 1000 次写操作,2000+ 次读操作
  • 可靠:使用 Raft 算法保证一致性

主要功能

  • 基本的 key-value 存储
  • 监听机制
  • key 的过期及续约机制,用于监控和服务发现
  • 原子 Compare And Swap 和 Compare And Delete,用于分布式锁和 leader 选举

使用场景

  • 也可以用于键值对存储,应用程序可以读取和写入 etcd 中的数据
  • etcd 比较多的应用场景是用于服务注册与发现
  • 基于监听机制的分布式异步系统

键值对存储

etcd 是一个键值存储的组件,其他的应用都是基于其键值存储的功能展开。

  • 采用 kv 型数据存储,一般情况下比关系型数据库快。
  • 支持动态存储(内存)以及静态存储(磁盘)。
  • 分布式存储,可集成为多节点集群。
  • 存储方式,采用类似目录结构。(B+tree)
    • 只有叶子节点才能真正存储数据,相当于文件。
    • 叶子节点的父节点一定是目录,目录不能存储数据。

服务注册与发现

  • 强一致性、高可用的服务存储目录。
    • 基于 Raft 算法的 etcd 天生就是这样一个强一致性、高可用的服务存储目录。
  • 一种注册服务和服务健康状况的机制。
    • 用户可以在 etcd 中注册服务,并且对注册的服务配置 key TTL,定时保持服务的心跳以达到监控健康状态的效果。

服务注册与发现

消息发布与订阅

  • 在分布式系统中,最适用的一种组件间通信方式就是消息发布与订阅。
  • 即构建一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅他们关心的主题,一旦主题有消息发布,就会实时通知订阅者。
  • 通过这种方式可以做到分布式系统配置的集中式管理与动态更新。
  • 应用中用到的一些配置信息放到 etcd 上进行集中管理。
  • 应用在启动的时候主动从 etcd 获取一次配置信息,同时,在 etcd 节点上注册一个 Watcher 并等待,以后每次配置有更新的时候,etcd 都会实时通知订阅者,以此达到获取最新配置信息的目的。

消息发布与订阅

etcd 的安装

下载安装包,参考 https://github.com/etcd-io/etcd/releases

安装

ETCD_VER=v3.4.17
DOWNLOAD_URL=https://github.com/etcd-io/etcd/releases/download
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test
curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1

运行

cd /tmp/etcd-download-test/
./etcd

清除

rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test

更多信息:https://github.com/cncamp/101/blob/master/module5/1.etcd-member-list.MD

第三方库和客户端工具

目前有很多支持etcd的库和客户端工具

  • 命令行客户端工具 etcdctl
    • 安装: sudo apt install etcd-client(默认已安装)
  • Go 客户端 go-etcd
  • Java 客户端 jetcd
  • Python 客户端 python-etcd

etcd 练习

cd /tmp/etcd-download-test/

查看集群成员状态

./etcdctl member list
8e9e05c52164694d, started, default, http://localhost:2380, http://localhost:2379, false
./etcdctl --endpoints=127.0.0.1:2379 member list --write-out=table
+------------------+---------+---------+-----------------------+-----------------------+------------+
|        ID        | STATUS  |  NAME   |      PEER ADDRS       |     CLIENT ADDRS      | IS LEARNER |
+------------------+---------+---------+-----------------------+-----------------------+------------+
| 8e9e05c52164694d | started | default | http://localhost:2380 | http://localhost:2379 |      false |
+------------------+---------+---------+-----------------------+-----------------------+------------+

基本的数据读写操作

写入数据

./etcdctl --endpoints=localhost:2379 put /version v1
OK

读取数据

./etcdctl --endpoints=localhost:2379 get /version
/version
v1
./etcdctl --endpoints=localhost:2379 get /version -wjson
{"header":{"cluster_id":14841639068965178418,"member_id":10276657743932975437,"revision":2,"raft_term":3},"kvs":[{"key":"L3ZlcnNpb24=","create_revision":2,"mod_revision":2,"version":1,"value":"djE="}],"count":1}

key 和 value 都是 base64 加密了的

echo L3ZlcnNpb24=|base64 -d
/version
echo djE=|base64 -d
v1

按 key 的前缀查询数据

./etcdctl --endpoints=localhost:2379 put /version/java 1.8
./etcdctl --endpoints=localhost:2379 put /version/hadoop 2.7.0
./etcdctl --endpoints=localhost:2379 put /version/spark 3.2
./etcdctl get --prefix /version
/version
v2
/version/hadoop
2.7.0
/version/java
1.8
/version/spark
3.2

只显示键值

./etcdctl get --prefix / --keys-only
/version

/version/hadoop

/version/java

/version/spark

查看 debug 信息

类似于 k8s 中的 -v=9

./etcdctl get --prefix / --keys-only --debug
ETCDCTL_CACERT=
ETCDCTL_CERT=
ETCDCTL_COMMAND_TIMEOUT=5s
ETCDCTL_DEBUG=true
ETCDCTL_DIAL_TIMEOUT=2s
ETCDCTL_DISCOVERY_SRV=
ETCDCTL_DISCOVERY_SRV_NAME=
ETCDCTL_ENDPOINTS=[127.0.0.1:2379]
ETCDCTL_HEX=false
ETCDCTL_INSECURE_DISCOVERY=true
ETCDCTL_INSECURE_SKIP_TLS_VERIFY=false
ETCDCTL_INSECURE_TRANSPORT=true
ETCDCTL_KEEPALIVE_TIME=2s
ETCDCTL_KEEPALIVE_TIMEOUT=6s
ETCDCTL_KEY=
ETCDCTL_PASSWORD=
ETCDCTL_USER=
ETCDCTL_WRITE_OUT=simple
WARNING: 2022/11/14 21:45:14 Adjusting keepalive ping interval to minimum period of 10s
WARNING: 2022/11/14 21:45:14 Adjusting keepalive ping interval to minimum period of 10s
INFO: 2022/11/14 21:45:14 parsed scheme: "endpoint"
INFO: 2022/11/14 21:45:14 ccResolverWrapper: sending new addresses to cc: [{127.0.0.1:2379  <nil> 0 <nil>}]
/version

/version/hadoop

/version/java

/version/spark

监听数据

./etcdctl --endpoints=localhost:2379 watch /version
./etcdctl --endpoints=localhost:2379 put /version v2
PUT
/version
v2
  • 监听前缀
./etcdctl --endpoints=localhost:2379 watch --prefix /version

核心:TTL & CAS

TTL (time to live)指的是给一个 key 设置一个有效期,到期后这个 key 就会被自动删掉,这在很多分布式锁的实现上都会用到,可以保证锁的实时有效性。

Atomic Compare-and-Swap (CAS) 指的是在对 key 进行赋值的时候,客户端需要提供一些条 件,当这些条件满足后,才能赋值成功。这些条件包括:

  • prevExist: key 当前赋值前是否存在
  • prevValue: key 当前赋值前的值
  • prevlndex: key 当前赋值前的 Index

这样的话,key 的设置是有前提的,需要知道这个 key 当前的具体情况才可以对其设置。

查看 k8s 在 etcd 中存储的数据

进入 etcd 容器

ks get pod|grep etcd
ks exec -it etcd-cjx sh

查看启动参数

ps -ef|grep etcd
root        2431    2372  2 14:23 ?        00:00:08 etcd --advertise-client-urls=https://192.168.1.168:2379 --cert-file=/etc/kubernetes/pki/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/etcd --experimental-initial-corrupt-check=true --initial-advertise-peer-urls=https://192.168.1.168:2380 --initial-cluster=cjx=https://192.168.1.168:2380 --key-file=/etc/kubernetes/pki/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://192.168.1.168:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://192.168.1.168:2380 --name=cjx --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/etc/kubernetes/pki/etcd/peer.key --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
  • endpoints: --advertise-client-urls=https://192.168.1.168:2379
  • cert: --cert-file=/etc/kubernetes/pki/etcd/server.crt
  • key: --key-file=/etc/kubernetes/pki/etcd/server.key
  • cacert: --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

操作

member list

etcdctl --endpoints https://127.0.0.1:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt member list --write-out=table

output

+------------------+---------+------+----------------------------+----------------------------+------------+
|        ID        | STATUS  | NAME |         PEER ADDRS         |        CLIENT ADDRS        | IS LEARNER |
+------------------+---------+------+----------------------------+----------------------------+------------+
| 37646e37976003c2 | started |  cjx | https://192.168.1.168:2380 | https://192.168.1.168:2379 |      false |
+------------------+---------+------+----------------------------+----------------------------+------------+

get

etcdctl --endpoints https://127.0.0.1:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --prefix / --keys-only

output

......
/registry/pods/default/nginx
......
/registry/pods/kube-system/kube-proxy-kwnx7
......

/registry/services/specs/calico-system/calico-typha

/registry/services/specs/default/kubernetes

/registry/services/specs/kube-system/kube-dns
获取细节
etcdctl --endpoints https://127.0.0.1:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get /registry/pods/default/nginx
etcdctl --endpoints https://127.0.0.1:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get /registry/pods/kube-system/kube-proxy-kwnx7

结果以 protobuf 格式存储