最近因为工作内容调整,需要对etcd进行学习。随手记录了一些docker部署etcd集群的方式。
etcd是一个高可用的分布式键值(KV)数据库。可以用来实现各种分布式协同服务。内部采用raft协议作为一致性算法。基于go语言实现
简单:
支持RESTful风格的HTTP+JSON的API
v3版本增加了对gRPC的支持 同时也提供rest gateway进行转化
Go语言编写,跨平台,部署和维护简单
使用Raft算法保证强一致性,Raft算法可理解性好
安全:支持SSL证书验证、支持TLS客户端安全认证
性能:单实例(V3)支持每秒10KQps Qps = 并发量/平均相应时间 并发量 = Qps * 平均相应时间
可靠:使用 Raft 算法充分保证了分布式系统数据的强一致性 etcd 集群是一个分布式系统,由多个节点相互通信构成整体的对外服务,每个节点都存储了完整的数据,并且通过 Raft 协议保证了每个节点维护的数据都是一致的。
etcd可以扮演两大角色:
持久化的键值存储系统
分布式系统数据一致性服务提供者
etcd 大体可以分为 网络层(http(s) server)、raft算法模块、复制状态机(RSM)和存储模块
网络层:提供网络数据读写功能,监听服务端口,完成集群节点之间数据通信,收发客户端数据。
Raft模块:Raft强一致性算法的具体实现。
存储模块:涉及KV存储、WAL文件、Snapshot管理等,用户处理etcd支持的各类功能的事务,包括数据索引 节点状态变更、监控与反馈、事件处理与执行 ,是 etcd 对用户提供的大多数 API 功能的具体实现。
复制状态机:这是一个抽象的模块,状态机的数据维护在内存中,定期持久化到磁盘,每次写请求都会持久化到 WAL 文件,并根据写请求的内容修改状态机数据。
通常,一个用户的请求发送过来,会经由 HTTP ( S) Server 转发给存储模块进行具体的事务处理,如果涉及节点状态的更新,则交给 Raft 模块进行仲裁和日志的记录,然后再同步给别的 etcd 节点,只有当半数以上的节点确认了该节点状态的修改之后,才会进行数据的持久化。
etcd 集群的各个节点之间需要通过网络来传递数据,具体表现为如下几个方面:
Leader Follower 发送心跳包, Follower Leader 回复消息
Leader Follower 发送日志追加信息
Leader Follower 发送 Snapshot 数据
Candidate 节点发起选举,向其他节点发起投票请求
Follower 将收到的写操作转发给 Leader
CAP原则(CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。)
zk和etcd都是顺序一致性的,满足CAP中的CP。任何时间访问节点都会获取到最终一致的数据视图。因为zk的paxos和etcd使用的raft算法都是大多数同意原则。所以部分节点可能会延迟接收到更新,但数据最终将一致。
zk从逻辑上来讲是一种目录结构,etcd从逻辑上来说就是一个k-v结构
但是etcd的key可以是任意字符串,同时在存储上实现了key的有序排列
所以etcd是可以模拟出树桩的结构的 key = /A/B/C 、/A/B、/A
本质上来说etcd是一个有序的kv存储
临时节点
在实现服务发现时,我们一般都会用到zookeeper的临时节点。当客户端掉线一段时间,对应的zookeeper session会过期,那么对应的临时节点就会被自动删除。
在etcd中对应的是lease租约机制
,通过该机制实现了key的自动删除。
可以在set key的同时携带lease ID,当lease过期后所有关联的key都将被自动删除
事件模型
在我们用zookeeper实现服务发现时,我们一般会getChildrenAndWatch
来获取一个目录下的所有在线节点,这个API会先获取当前的孩子列表并同时原子注册了一个观察器。
每当zookeeper发现孩子有变动的时候,就会发送一个通知事件给客户端(同时关闭观察器),此时我们会再次调用getChildrenAndWatch再次获取最新的孩子列表并重新注册观察器。
简单的来说,zookeeper提供了一个原子API,它先获取当前状态,同时注册一个观察器,当后续变化发生时会发送一次通知到客户端:获取并观察->收到变化事件->获取并观察->收到变化事件->….,如此往复。
zookeeper的事件模型非常可靠,不会出现发生了更新而客户端不知道的情况,但是特点也很明显:
事件不包含数据,仅仅是通知变化。
多次连续的更新,通知会合并成一个;即,客户端收到通知再次拉取数据,会跳过中间的多个版本,只拿到最新数据。
这些特点并不是缺点,因为一般应用只关注最新状态,并不关注中间的连续变化。
相反etcd的事件是包含数据的,并且通常情况下连续的更新不会被合并通知,而是逐条通知到客户端。
vmwarm 16 pro 本地虚拟机
198.168.234.128~130
##通过docker pull 拉取3.5.0镜像版本 [root@localhost docker]# docker pull quay.io/coreos/etcd:v3.5.0 ##拉取成功 [root@localhost docker]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE quay.io/coreos/etcd v3.5.0 a7908fd5fb88 11 months ago 110MB ##官网下载docker-compose镜像 [root@localhost data]# cd /usr/local [root@localhost local]# cd bin [root@localhost bin]# ll 总用量 25912 -rw-r--r--. 1 root root 26533888 5月 23 09:44 docker-compose-linux-x86_64 [root@localhost bin]# mv docker-compose-linux-x86_64 docker-compose [root@localhost bin]# ll 总用量 25912 -rw-r--r--. 1 root root 26533888 5月 23 09:44 docker-compose [root@localhost bin]# chmod 777 docker-compose [root@localhost bin]# ll 总用量 25912 -rwxrwxrwx. 1 root root 26533888 5月 23 09:44 docker-compose ##验证docker-compose 版本 [root@localhost bin]# ./docker-compose version Docker Compose version v2.5.1 [root@localhost bin]# vim ~/.bash_profile ##配置环境变量 PATH=$PATH:$HOME/bin:/usr/local/bin [root@localhost bin]# source ~/.bash_profile [root@localhost bin]# cd / [root@localhost /]# docker-compose version Docker Compose version v2.5.1
docker-compose.yml文件编写
version: '3' networks: myetcd_single: services: etcd: image: quay.io/coreos/etcd container_name: etcd_single command: etcd -name etcd1 -advertise-client-urls http://0.0.0.0:2379 -listen-client-urls http://0.0.0.0:2379 -listen-peer-urls http://0.0.0.0:2380 ##基本etcd配置 通过2380端口进行通信 通过2379端口监听 ports: - 12379:2379 - 12380:2380 volumes: - ./data:/etcd-data ## 挂载数据卷 networks: - myetcd_single etcdkeeper: image: deltaprojects/etcdkeeper container_name: etcdkeeper_single ports: - 8088:8080 networks: - myetcd_single
执行docker-compose.yml文件
[root@localhost data]# docker-compose up -d [root@localhost data]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 45edb1a12ada quay.io/coreos/etcd "etcd -name etcd1 -a…" About a minute ago Up About a minute 0.0.0.0:12379->2379/tcp, 0.0.0.0:12380->2380/tcp etcd_single af5c7cb06080 deltaprojects/etcdkeeper "/bin/sh -c './etcdk…" About a minute ago Up About a minute 0.0.0.0:8088->8080/tcp etcdkeeper_single [root@localhost data]# docker exec -it 45e etcdctl member list b063ce376278837c: name=etcd1 peerURLs=http://172.18.0.3:2380 clientURLs=http://0.0.0.0:2379 isLeader=true
集群部署
准备好三台虚机之后,调整docker-compose文件
## 其他docker-compose替换地址即可 version: '3' services: etcd: container_name: etcd image: quay.io/coreos/etcd ports: - "12379:2379" - "12380:2380" network_mode: "host" restart: always stdin_open: true tty: true command: etcd --name etcd-128 --data-dir /data/app/etcd/ --listen-peer-urls http://192.168.234.128:2380 --listen-client-urls http://127.0.0.1:2379,http://192.168.234.128:2379 --initial-advertise-peer-urls http://192.168.234.128:2380 --advertise-client-urls http://127.0.0.1:2379,http://192.168.234.128:2379 --initial-cluster-token etcd-cluster --initial-cluster "etcd-128=http://192.168.234.128:2380,etcd-129=http://192.168.234.129:2380,etcd-130=http://192.168.234.130:2380" --auto-compaction-retention 24 --max-wals 3 --max-snapshots 3 --snapshot-count 30000 --heartbeat-interval 700 --election-timeout 3500 --initial-cluster-state new volumes: - /data/app/etcd/:/data/app/etcd ##执行docker-compose文件 [root@localhost etcd]# docker-compose up -d [+] Running 1/1 ⠿ Container etcd Started 0.2s [root@localhost etcd]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2ec977d8e222 quay.io/coreos/etcd "etcd --name etcd-13…" About a minute ago Up About a minute etcd ##执行成功后 进入docker容器中 [root@localhost etcd]# docker exec -it 2ec /bin/sh / # etcdctl member list 136956e961af78a1: name=etcd-128 peerURLs=http://192.168.234.128:2380 clientURLs=http://127.0.0.1:2379,http://192.168.234.128:2379 isLeader=true dec5185119cc78dc: name=etcd-129 peerURLs=http://192.168.234.129:2380 clientURLs=http://127.0.0.1:2379,http://192.168.234.129:2379 isLeader=false ea5b7cd8877afcc2: name=etcd-130 peerURLs=http://192.168.234.130:2380 clientURLs=http://127.0.0.1:2379,http://192.168.234.130:2379 isLeader=false
通过docker-compose进行容器创建
docker-compose up -d #集群内容器全部拉起后,执行 docker exec -it container #进入容器后执行 put key命令发现报错 etcdctl put name "i am lipeng" #需要执行环境变量 export ETCDCTL_API=3 #再次执行成功 / # etcdctl put name "i am *" OK #在etcd-129上执行 / # etcdctl get name name i am *