㈠ etcd是什么东西它和ZooKeeper有什么区别
etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性,在Raft中,任何一个节点都可能成为Leader。Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。
etcd 集群的工作原理基于 raft 共识算法 (The Raft Consensus Algorithm)。etcd 在 0.5.0 版本中重新实现了 raft 算法,而非像之前那样依赖于第三方库 go-raft 。raft 共识算法的优点在于可以在高效的解决分布式系统中各个节点日志内容一致性问题的同时,也使得集群具备一定的容错能力。即使集群中出现部分节点故障、网络故障等问题,仍可保证其余大多数节点正确的步进。甚至当更多的节点(一般来说超过集群节点总数的一半)出现故障而导致集群不可用时,依然可以保证节点中的数据不会出现错误的结果。
㈡ 笔记本etcd什么意思
键值存储仓库,用于配置共享和服务发现。
扩展知识:etcd(读作 et-see-dee)是一种开源的分布式统一键值存储,用于分布式系统或计算机集群的共享配置、服务发现和的调度协调。etcd 有助于促进更加安全的自动更新,协调向主机调度的工作,并帮助设置容器的覆盖网络。
etcd 是许多其他项目的核心组件。最值得注意的是,它是 Kubernetes 的首要数据存储,也是容器编排的实际标准系统。使用 etcd, 云原生应用可以保持更为一致的运行时间,而且在个别服务器发生故障时也能正常工作。应用从 etcd 读取数据并写入到其中;通过分散配置数据,为节点配置提供冗余和弹性。
㈢ kubernetes控制平面组件:etcd
--listen-peer-urls
--listen-client-urls
--initial-advertise-peer-urls
--initial-cluster
--initial-cluster-state
--advertise-client-urls
1.code
headless svc, 像DNS RR ClusterIP:None
kubectl -n stg1 get endpoints
client 怎么访问:
2.配置文件
3.apply
官方的code有两个问题
本地访问
扩容
利用反亲和性 分布etcd pod到不同节点
~ ❯❯❯ etcdctl get / --prefix
从 etcd 的架构图中我们可以看到,etcd 主要分为四个部分。
etcd 目前支持 V2 和 V3 两个大版本,这两个版本在实现上有比较大的不同,一方面是对外提供接口的方式,另一方面就是底层的存储引擎,V2 版本的实例是一个纯内存的实现,所有的数据都没有存储在磁盘上,而 V3 版本的实例就支持了数据的持久化。
v3默认boltdb
consortium etcd2+mysql
数据默认会存放在 /var/lib/etcd/default/ 目录。我们会发现数据所在的目录,会被分为两个文件夹中,分别是 snap 和 wal目录。
解决三个问题:节点选举、日志复制以及安全性 。
每一个 Raft 集群中都包含多个服务器,在任意时刻,每一台服务器只可能处于 Leader 、 Follower 以及 Candidate 三种状态;在处于正常的状态时,集群中只会存在一个 Leader 状态,其余的服务器都是 Follower 状态。
所有的 Follower 节点都是被动的,它们不会主动发出任何的请求 ,只会响应 Leader 和 Candidate 发出的请求。对于每一个用户的可变操作,都会被路由给 Leader 节点进行处理,除了 Leader 和 Follower 节点之外,Candidate 节点其实只是集群运行过程中的一个临时状态。
每一个服务器都会存储当前集群的最新任期,它就像是一个单调递增的逻辑时钟,能够同步各个节点之间的状态,当前节点持有的任期会随着每一个请求被传递到其他的节点上。Raft 协议在每一个任期的开始时都会从一个集群中选出一个节点作为集群的 Leader 节点,这个节点会负责集群中的日志的复制以及管理工作。
客户端通过 监听指定的key可以迅速感知key的变化并作出相应处理 ,watch机制的实现依赖于 资源版本号revision的设计 ,每一次key的更新都会使得revision原子递增,因此根据不同的版本号revision的对比就可以感知新事件的发生。etcd watch机制有着广泛的应用,比如利用etcd实现分布式锁; k8s中监听各种资源的变化 ,从而实现各种controller逻辑等。
watch机制的实现主要可分为三个部分
client使用 watchClient 的watch接口发起watch请求,与server端建立一个 gRPCStream 连接。
server端会为每个client生成唯一一个watch id,并记录每个client也就是watcher监听的key或者key range,通过recvLoop接收client请求,通过sendLoop发送请求,server端只负责收发请求和响应。
主要的实现都放在了watchalbStore层,watchalbStore会监听key的变化,然后通过syncWatchersLoop和syncVictimsLoop两个处理流程将key的更新变化包装成event,通过channel发送给gRPC server。
MVCC(Multiversion Concurrency Control)多版本并发控制机制
场景1:
这就是悲观锁
悲观锁:悲观得认为并发事务会冲突,所以要先拿锁,拿到锁的作修改操作
场景2
数据库:写回磁盘,A写好了。哎,B和C都是version 13,我咋写?算了,报错吧。。
就是乐观锁,默认不加锁,你尽管写,冲突我认怂!乐观锁其实不是锁,只是相对悲观锁来定义,适合读多写少。
乐观锁:乐观得认为数据不会冲突,但发生冲突时要能检测到。
场景3
这就是MVCC,在 MVCC 数据库中,你更新一个 key-value 数据的时候,它并不会直接覆盖原数据,而是 新增一个版本来存储新的数据,每个数据都有一个版本号 ,版本号是一个逻辑时钟,不会因为服务器时间的差异而受影响。
MVCC不等于乐观锁!
--rev 查的是main
在底层boltdb里,实际分布是这样的:
底层的key是revision,/奥特曼是用户key,“他很帅”就是用户value
删除
之前有delete动作,但是依然有版本记录。为什么?
删除这个动作,其实etcd是在blotdb里写了一条,“删除用户/奥特曼”
此时有个问题:用户说我的确删除了啊,真的不要了!请把空间还给我啊!
回收 compact(压缩)
etcdctl compact {version}
compact 需要一个版本号。这个版本号就是写事务递增的那个版本号,compact 12345,就是说把版本12345以前的 标记删除了的数据 释放掉,用户没删除的数据肯定不能回收。
如何压缩:
注意修改go.mod
Watch
服务发现要解决的也是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并建立连接。本质上来说,服务发现就是想要了解集群中是否有进程在监听 udp 或 tcp 端口,并且通过名字就可以查找和连接。
需要实现的功能;
discover.go
eBay payment
ebay kubernetes 控制面架构
问题
㈣ k8s etcd 与持久化存储
1、是什么
2、etcd架构及工作原理
(1) 数据流程
一个用户的请求发送过来,会经过HTTP Server转发给store进行具体事务处理,如果涉及到节点的修改,则需要交给raft模块进行状态的变更,日志的记录,然后再同步给别的etcd节点确认数据提交,最后进行数据提交,再次同步
(2)工作原理
Etcd使用 Raft协议 来维护集群内各个节点状态的 一致性 。简单说,ETCD集群是一个分布式系统,由多个节点相互通信构成整体对外服务, 每个节点都存储了完整的数据 ,并且通过Raft协议保证每个节点维护的数据是一致的
(3) 主要组成部分
(4)etcd集群中的术语
3、k8s中的etcd
(1)etcd在k8s中的作用: etcd在kubernetes集群是用来存放数据并通知变动的
(2)为什么k8s选择etcd:
PV 目前支持的类型包括:gcePersistentDisk 、AWSElasticBlockStore 、AzureFile 、AzureDisk 、FC ( Fibre Channel ) 、Flocker、NFS 、iSCSI 、RBD (Rados Block Device )、CephFS 、Cinder、GlusterFS 、V sphere Volume 、Quobyte Volumes 、VMware Photon 、Portwonc
Volumes 、ScaleIO Volumes 和HostPath (仅供单机测试)。
如果某个Pod 想申请某种类型的PY ,则首先需要定义一个PersistentVolurneClaim ( PVC )对象,然后,在Pod 的Volume 定义中引用上述PVC 即可:
㈤ etcd的应用场景
提到etcd很多人第一反应就是一个键值存储仓库。不过etcd官方文档的定义却是这样的:
etcd作为一个受到ZooKeeper与doozer启发而催生的项目,除了拥有与之类似的功能外,更专注于以下四点。
接下来将针对剖析一些etcd的经典使用场景,来看看etcd这个基于Raft强一致性算法的分布式存储仓库能给我们带来哪些帮助。
在分布式系统中“服务发现”也是比较常见的问题:在同一个集群环境中不同的应用或服务,如何能够找到对方并建立连接,来完成后续的行为。本质上来说,服务发现就是想要知道集群中是否有进程在监听udp或tcp端口,并能通过名字就可以查找和连接。而要解决服务发现的问题,需要满足如下三个方面,缺一不可。
来看服务发现对应的具体场景:
在分布式系统中,最适用的一种组件间通信方式就是消息发布与订阅。即构建一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅他们关心的主题,一旦主题有消息发布,就会实时通知订阅者。通过这种方式可以做到分布式系统配置的集中式管理与动态更新。
在场景一中也提到了负载均衡,本文所指的负载均衡均为软负载均衡。分布式系统中,为了保证服务的高可用以及数据的一致性,通常都会把数据和服务部署多份,以此达到对等服务,即使其中的某一个服务失效了,也不影响使用。由此带来的坏处是数据写入性能下降,而好处则是数据访问时的负载均衡。因为每个对等服务节点上都存有完整的数据,所以用户的访问流量就可以分流到不同的机器上。
这里说到的分布式通知与协调,与消息发布和订阅有些相似。都用到了etcd中的Watcher机制,通过注册与异步通知机制,实现分布式环境下不同系统之间的通知与协调,从而对数据变更做到实时处理。实现方式通常是这样:不同系统都在etcd上对同一个目录进行注册,同时设置Watcher观测该目录的变化(如果对子目录的变化也有需要,可以设置递归模式),当某个系统更新了etcd的目录,那么设置了Watcher的系统就会收到通知,并作出相应处理。
因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。
分布式队列的常规用法与场景五中所描述的分布式锁的控制时序用法类似,即创建一个先进先出的队列,保证顺序。
另一种比较有意思的实现是在保证队列达到某个条件时再统一按顺序执行。这种方法的实现可以在/queue这个目录中另外建立一个/queue/condition节点。
通过etcd来进行监控实现起来非常简单并且实时性强。
这样就可以第一时间检测到各节点的健康状态,以完成集群的监控要求。
另外,使用分布式锁,可以完成Leader竞选。这种场景通常是一些长时间CPU计算或者使用IO操作的机器,只需要竞选出的Leader计算或处理一次,就可以把结果复制给其他的Follower。从而避免重复劳动,节省计算资源。
这个的经典场景是搜索系统中建立全量索引。如果每个机器都进行一遍索引的建立,不但耗时而且建立索引的一致性不能保证。通过在etcd的CAS机制同时创建一个节点,创建成功的机器作为Leader,进行索引计算,然后把计算结果分发到其它节点。
etcd实现的这些功能,ZooKeeper都能实现。那么为什么要用etcd而非直接使用ZooKeeper呢?
相较之下,ZooKeeper有如下缺点:
而etcd作为一个后起之秀,其优点也很明显。
最后,etcd作为一个年轻的项目,真正告诉迭代和开发中,这既是一个优点,也是一个缺点。优点是它的未来具有无限的可能性,缺点是无法得到大项目长时间使用的检验。然而,目前CoreOS、Kubernetes和CloudFoundry等知名项目均在生产环境中使用了etcd,所以总的来说,etcd值得你去尝试。
从etcd的架构图中我们可以看到,etcd主要分为四个部分。
通常,一个用户的请求发送过来,会经由HTTP Server转发给Store进行具体的事务处理,如果涉及到节点的修改,则交给Raft模块进行状态的变更、日志的记录,然后再同步给别的etcd节点以确认数据提交,最后进行数据的提交,再次同步。
㈥ 【K8s 精选】CKA - 管理高可用性 Kubernetes 集群
创建 HA 集群的一般步骤:
● 设置负载均衡器。有许多开源选项可用于负载均衡:HAproxy、Envoy 或来自云提供商的类似负载均衡器效果很好。
● 在第一个控制平面节点上运行 kubeadm init ,并进行以下修改:
① 创建 kubeadm 配置文件
② 在配置文件中,将 controlPlaneEndpoint 字段设置为可以访问负载均衡器的位置。
③ 运行 init,带有如下 --upload-certs 标志: sudo kubeadm init --config=kubeadm-config.yaml --upload-certs
● kubeadm join –control-plane 在您想要扩展控制平面节点集时随时运行。控制平面和普通节点可以随时以任何顺序加入。
① 3 台机器作为控制面节点、3 台机器作为工作节点、3 台机器作为外部 etcd 集群(外部 etcd 拓扑方案)
② 在集群中,确保所有计算机之间存在全网络连接(公网或私网)
③ 在所有机器上具有 sudo 权限
④ 从某台设备通过 ssh 访问系统中所有节点的能力
⑤ 所有机器上已经安装 kubeadm 和 kubelet , kubectl 是可选的
每个控制平面节点创建一个本地 etcd 成员(member) 。该 etcd 成员只与该节点的 kube-apiserver 、 kube-controller-manager 、 kube-scheler 实例通信。
堆叠(stacked) etcd 方案,在控制平面设置负载均衡后,自动管理 etcd 的 external 对象下带有 endpoints 的配置文件。
etcd 分布式数据存储集群在独立于控制平面节点的其他节点上运行 。每个控制平面节点都运行 kube-apiserver , kube-scheler 和 kube-controller-manager 实例。 每个 etcd 主机与每个控制平面节点的 kube-apiserver 通信 。
● 利用 kubeadm 创建高可用 etcd 集群
● 配置 SSH 。
● 将以下文件从集群中的任何 etcd 节点复制到第一个控制平面节点:
㈦ kubernetes的master节点和node节点
kubernetes集群中的master节点是集群的控制节点, 负责整个集群的管理和控制。执行的控制命令都是发送给master节点。 Master节点上运行的主要组件如下:
Node节点时kubernetes集群中的工作负责节点,Node上的工作负载由master分配, 当某个Node宕机时,Master会将上面的工作负载转移到其他节点上去, Node节点上运行的主要组件如下:
上图为kubernetes中所有组件一起工作时的示意图,由此我们可以得出以下几个通信流程,
根据上面节点通信的介绍我们会产生一个疑问, 这个看起来好复杂的样子。 臣妾看不懂呀。 如果想进一步了解集群中详细的工作流程请移驾 kubectl创建pod背后发生了什么 探索背后的秘密
要求结果为所有的 Kubernetes Master 服务器没有单点故障,任何一台服务器当机均不影响Kubernetes的正常工作。
为了实现没有单点故障的目标,需要为以下几个组件建立高可用方案:
etcd是Kubernetes当中唯一带状态的服务,也是高可用的难点。Kubernetes选用etcd作为它的后端数据存储仓库正是看重了其使用分布式架构,没有单点故障的特性。
虽然单节点的etcd也可以正常运行。但是推荐的部署方案均是采用3个或者5个节点组成etcd集群,供Kubernetes使用。
etcd的高可用基本有三种思路:
一是使用独立的etcd集群,使用3台或者5台服务器只运行etcd,独立维护和升级。甚至可以使用CoreOS的 update-engine 和 locksmith ,让服务器完全自主的完成升级。这个etcd集群将作为基石用于构建整个集群。 采用这项策略的主要动机是etcd集群的节点增减都需要显式的通知集群,保证etcd集群节点稳定可以更方便的用程序完成集群滚动升级,减轻维护负担。
二是在Kubernetes Master上用static pod的形式来运行etcd,并将多台Kubernetes Master上的etcd组成集群。 在这一模式下,各个服务器的etcd实例被注册进了Kubernetes当中,虽然无法直接使用 kubectl 来管理这部分实例,但是监控以及日志搜集组件均可正常工作。在这一模式运行下的etcd可管理性更强。
三是使用CoreOS提出的 self-hosted etcd方案 ,将本应在底层为Kubernetes提供服务的etcd运行在Kubernetes之上。 实现Kubernetes对自身依赖组件的管理。在这一模式下的etcd集群可以直接使用 etcd-operator 来自动化运维,最符合Kubernetes的使用习惯。
这三种思路均可以实现etcd高可用的目标,但是在选择过程中却要根据实际情况做出一些判断。简单来讲预算充足但保守的项目选方案一, 想一步到位并愿意承担一定风险的项目选方案三。折中一点选方案二。各个方案的优劣以及做选择过程中的取舍在这里就不详细展开了,对这块有疑问的朋友可以私下联系交流。
apiserver本身是一个无状态服务,要实现其高可用相对要容易一些,难点在于如何将运行在多台服务器上的apiserver用一个统一的外部入口暴露给所有Node节点。
说是难点,其实对于这种无状态服务的高可用,我们在设计业务系统的高可用方案时已经有了相当多的经验积累。需要注意的是apiserver所使用的SSL证书要包含外部入口的地址,不然Node节点无法正常访问apiserver。
apiserver的高可用也有三种基本思路:
一是使用外部负载均衡器,不管是使用公有云提供的负载均衡器服务或是在私有云中使用 LVS 或者 HaProxy 自建负载均衡器都可以归到这一类。 负载均衡器是非常成熟的方案,在这里略过不做过多介绍。如何保证负载均衡器的高可用,则是选择这一方案需要考虑的新问题。
二是在网络层做负载均衡。比如在Master节点上用 BGP 做 ECMP ,或者在Node节点上用 iptables 做NAT都可以实现。采用这一方案不需要额外的外部服务,但是对网络配置有一定的要求。
三是在Node节点上使用反向代理对多个Master做负载均衡。这一方案同样不需要依赖外部的组件,但是当Master节点有增减时,如何动态配置Node节点上的负载均衡器成为了另外一个需要解决的问题。
从目前各个集群管理工具的选择来看,这三种模式都有被使用,目前还没有明确的推荐方案产生。建议在公有云上的集群多考虑第一种模式,在私有云环境中由于维护额外的负载均衡器也是一项负担,建议考虑第二种或是第三种方案。
这两项服务是Master节点的一部分,他们的高可用相对容易,仅需要运行多份实例即可。这些实例会通过向apiserver中的 Endpoint 加锁的方式来进行leader election, 当目前拿到leader的实例无法正常工作时,别的实例会拿到锁,变为新的leader。
严格来说kube-dns并不算是Master组件的一部分,因为它是可以跑在Node节点上,并用 Service 向集群内部提供服务的。但在实际环境中, 由于默认配置只运行了一份kube-dns实例,在其升级或是所在节点当机时,会出现集群内部dns服务不可用的情况,严重时会影响到线上服务的正常运行。
为了避免故障,请将kube-dns的 replicas 值设为2或者更多,并用 anti-affinity 将他们部署在不同的Node节点上。这项操作比较容易被疏忽,直到出现故障时才发现原来是kube-dns只运行了一份实例导致的故障。
上面介绍了Kubernetes Master各个组件高可用可以采用的策略。其中etcd和kube-apiserver的高可用是整个方案的重点。由于存在多种高可用方案,集群管理员应当根据集群所处环境以及其他限制条件选择适合的方案。
这种没有绝对的通用方案,需要集群建设者根据不同的现状在多个方案中做选择的情况在Kubernetes集群建设过程中频频出现, 也是整个建设过程中最有挑战的一部分。容器网络方案的选型作为Kubernetes建设过程中需要面对的另外一个大问题也属于这种情况,今后有机会再来分享这个话题。
在实际建设过程中,在完成了上述四个组件的高可用之后,最好采取实际关机检验的方式来验证高可用方案的可靠性,并根据检验的结果不断调整和优化整个方案。
此外将高可用方案与系统自动化升级方案结合在一起考虑,实现高可用下的系统自动升级,将大大减轻集群的日常运维负担,值得投入精力去研究。
虽然本篇主要在讲Kubernetes Master高可用的方案,但需要指出的是,高可用也并不是必须的,为了实现高可用所付出的代价并不低, 需要有相应的收益来平衡。对于大量的小规模集群来说,业务系统并没有实现高可用,贸然去做集群的高可用收益有限。这时采用单Master节点的方案,做好etcd的数据备份,不失为理性的选择。
㈧ 【知识总结】6.服务注册发现框架比较(Consul/Zookeeper/etcd/Eureka)
服务发现就是服务提供者将自己提供的地址post或者update到服务中介,服务消费者从服务中介那里get自己想要的服务的地址。
但是有两个问题:
第一个问题:如果有一个服务提供者宕机,那么中介的key/value中会有一个不能访问的地址,该怎么办?
心跳机制: 服务提供者需要每隔5秒左右向服务中介汇报存活,服务中介将服务地址和汇报时间记录在zset数据结构的value和score中。服务中介需要每隔10秒左右检查zset数据结构,踢掉汇报时间严重落后的地址。这样就可以保证服务列表中地址的有效性。
第二个问题是服务地址变动时如何通知消费者。有两种解决方案。
第一种是轮询,消费者每隔几秒查询服务列表是否有改变。如果服务地址很多,查询会很慢。这时候可以引入服务版本号机制,给每个服务提供一个版本号,在服务变动时,递增这个版本号。消费者只需要轮询这个版本号的变动即可知道服务列表是否发生了变化。
第二种是采用pubsub。这种方式及时性要明显好于轮询。缺点是每个pubsub都会占用消费者一个线程和一个额外的连接。为了减少对线程和连接的浪费,我们使用单个pubsub广播全局版本号的变动。所谓全局版本号就是任意服务列表发生了变动,这个版本号都会递增。接收到版本变动的消费者再去检查各自的依赖服务列表的版本号是否发生了变动。这种全局版本号也可以用于第一种轮询方案。
CAP理论
CAP理论是分布式架构中重要理论
关于P的理解,我觉得是在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用,而可用性是,某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求,CAP 不可能都取,只能取其中2个。原因是
(1)如果C是第一需求的话,那么会影响A的性能,因为要数据同步,不然请求结果会有差异,但是数据同步会消耗时间,期间可用性就会降低。
(2)如果A是第一需求,那么只要有一个服务在,就能正常接受请求,但是对与返回结果变不能保证,原因是,在分布式部署的时候,数据一致的过程不可能想切线路那么快。
(3)再如果,同事满足一致性和可用性,那么分区容错就很难保证了,也就是单点,也是分布式的基本核心,好了,明白这些理论,就可以在相应的场景选取服务注册与发现了。
平时经常用到的服务发现的产品进行下特性的对比,首先看下结论:
补充:
(1)运维和开发如果是 Java 更熟,也更多 Java 的应用,那毫无疑问应该用 ZK;如果是搞 Go 的,那么还是 etcd 吧,毕竟有时候遇到问题还是要看源码的。
(2)在创建一百万个或更多键时,etcd可以比Zookeeper或Consul稳定地提供更好的吞吐量和延迟。此外,它实现了这一目标,只有一半的内存,显示出更高的效率。但是,还有一些改进的余地,Zookeeper设法通过etcd提供更好的最小延迟,代价是不可预测的平均延迟。
(3)
一致性协议: etcd 使用 Raft 协议,Zookeeper 使用 ZAB(类PAXOS协议),前者容易理解,方便工程实现;
运维方面:etcd 方便运维,Zookeeper 难以运维;
数据存储:etcd 多版本并发控制(MVCC)数据模型 , 支持查询先前版本的键值对
项目活跃度:etcd 社区与开发活跃,Zookeeper 感觉已经快死了;
API:etcd 提供 HTTP+JSON, gRPC 接口,跨平台跨语言,Zookeeper 需要使用其客户端;
访问安全方面:etcd 支持 HTTPS 访问,Zookeeper 在这方面缺失;
与 Eureka 有所不同,Apache Zookeeper 在设计时就紧遵CP原则,即任何时候对 Zookeeper 的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是 Zookeeper 不能保证每次服务请求都是可达的。
从 Zookeeper 的实际应用情况来看,在使用 Zookeeper 获取服务列表时,如果此时的 Zookeeper 集群中的 Leader 宕机了,该集群就要进行 Leader 的选举,又或者 Zookeeper 集群中半数以上服务器节点不可用(例如有三个节点,如果节点一检测到节点三挂了 ,节点二也检测到节点三挂了,那这个节点才算是真的挂了),那么将无法处理该请求。所以说,Zookeeper 不能保证服务可用性。
当然,在大多数分布式环境中,尤其是涉及到数据存储的场景,数据一致性应该是首先被保证的,这也是 Zookeeper 设计紧遵CP原则的另一个原因。
但是对于服务发现来说,情况就不太一样了,针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不尽相同,也并不会造成灾难性的后果。
因为对于服务消费者来说,能消费才是最重要的,消费者虽然拿到可能不正确的服务实例信息后尝试消费一下,也要胜过因为无法获取实例信息而不去消费,导致系统异常要好(淘宝的双十一,京东的618就是紧遵AP的最好参照)。
当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,而且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署环境下, 因为网络问题使得zk集群失去master节点是大概率事件,虽然服务能最终恢复,但是漫长的选举事件导致注册长期不可用是不能容忍的。
Spring Cloud Netflix 在设计 Eureka 时就紧遵AP原则。Eureka是在Java语言上,基于Restful Api开发的服务注册与发现组件,由Netflix开源。遗憾的是,目前Eureka仅开源到1.X版本,2.X版本已经宣布闭源。
Eureka Server 也可以运行多个实例来构建集群,解决单点问题,但不同于 ZooKeeper 的选举 leader 的过程,Eureka Server 采用的是Peer to Peer 对等通信。这是一种去中心化的架构,无 master/slave 之分,每一个 Peer 都是对等的。在这种架构风格中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。每个节点都可被视为其他节点的副本。
在集群环境中如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点上,当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会在节点间进行复制(replicate To Peer)操作,将请求复制到该 Eureka Server 当前所知的其它所有节点中。
当一个新的 Eureka Server 节点启动后,会首先尝试从邻近节点获取所有注册列表信息,并完成初始化。Eureka Server 通过 getEurekaServiceUrls() 方法获取所有的节点,并且会通过心跳契约的方式定期更新。
默认情况下,如果 Eureka Server 在一定时间内没有接收到某个服务实例的心跳(默认周期为30秒),Eureka Server 将会注销该实例(默认为90秒, eureka.instance.lease-expiration-ration-in-seconds 进行自定义配置)。
当 Eureka Server 节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我保护模式。
Eureka的集群中,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
Eureka不再从注册表中移除因为长时间没有收到心跳而过期的服务;
Eureka仍然能够接受新服务注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用);
当网络稳定时,当前实例新注册的信息会被同步到其它节点中;
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使得整个注册服务瘫痪。
Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X)。
Consul采用主从模式的设计,使得集群的数量可以大规模扩展,集群间通过RPC的方式调用(HTTP和DNS)。
Consul 内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其他工具(比如 ZooKeeper 等),使用起来也较为简单。
Consul 遵循CAP原理中的CP原则,保证了强一致性和分区容错性,且使用的是Raft算法,比zookeeper使用的Paxos算法更加简单。虽然保证了强一致性,但是可用性就相应下降了,例如服务注册的时间会稍长一些,因为 Consul 的 raft 协议要求必须过半数的节点都写入成功才认为注册成功 ;在leader挂掉了之后,重新选举出leader之前会导致Consul 服务不可用。
默认依赖于SDK
Consul本质上属于应用外的注册方式,但可以通过SDK简化注册流程。而服务发现恰好相反,默认依赖于SDK,但可以通过Consul Template(下文会提到)去除SDK依赖。
Consul Template
Consul,默认服务调用者需要依赖Consul SDK来发现服务,这就无法保证对应用的零侵入性。
所幸通过 Consul Template ,可以定时从Consul集群获取最新的服务提供者列表并刷新LB配置(比如nginx的upstream),这样对于服务调用者而言,只需要配置一个统一的服务调用地址即可。
Consul强一致性(C)带来的是:
Eureka保证高可用(A)和最终一致性:
其他方面,eureka就是个servlet程序,跑在servlet容器中; Consul则是go编写而成。
etcd是一个采用http协议的分布式键值对存储系统,因其易用,简单。很多系统都采用或支持etcd作为服务发现的一部分,比如kubernetes。但正事因为其只是一个存储系统,如果想要提供完整的服务发现功能,必须搭配一些第三方的工具。
比如配合etcd、Registrator、confd组合,就能搭建一个非常简单而强大的服务发现框架。但这种搭建操作就稍微麻烦了点,尤其是相对consul来说。所以etcd大部分场景都是被用来做kv存储,比如kubernetes。
etcd 比较多的应用场景是用于服务发现,服务发现 (Service Discovery) 要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务如何才能找到对方并建立连接。和 Zookeeper 类似,etcd 有很多使用场景,包括:
配置管理
服务注册发现
选主
应用调度
分布式队列
分布式锁
按照官网给出的数据, 在 2CPU,1.8G 内存,SSD 磁盘这样的配置下,单节点的写性能可以达到 16K QPS, 而先写后读也能达到12K QPS。这个性能还是相当可观。
etcd 提供了 etcdctl 命令行工具 和 HTTP API 两种交互方法。etcdctl命令行工具用 go 语言编写,也是对 HTTP API 的封装,日常使用起来也更容易。所以这里我们主要使用 etcdctl 命令行工具演示。
(1)注册中心ZooKeeper、Eureka、Consul 、Nacos对比
https://zhuanlan.hu.com/p/165217227?utm_source=wechat_session
(2)常用的服务发现对比(Consul、zookeeper、etcd、eureka)
https://blog.csdn.net/gaohe7091/article/details/101197107
㈨ ETCD——基础原理
一个ETCD集群一般由3个或者5个节点组成,两个quorum一定存在交集,则
即:3个节点容忍1个节点故障,5个节点容忍2个节点故障,以此类推。
首先,所有的数据都保存在B+树(灰色),当我们指定了版本信息之后,会直接到灰色B+树中去获取相关的数据;同时,还有另外一个B+树,它维护了key和revions的映射关系,查询key的数据时候,会根据key查询到revision,再通过revision查询到相应的key。
etcd 使用 raft 协议来维护集群内各个节点状态的一致性。简单说,etcd 集群是一个分布式系统,由多个节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过 Raft 协议保证每个节点维护的数据是一致的。
每个 etcd 节点都维护了一个状态机,并且,任意时刻至多存在一个有效的主节点。主节点处理所有来自客户端写操作,通过 Raft 协议保证写操作对状态机的改动会可靠的同步到其他节点。
etcd 的设计目标是用来存放非频繁更新的数据,提供可靠的 Watch插件,它暴露了键值对的历史版本,以支持低成本的快照、监控历史事件。这些设计目标要求它使用一个持久化的、多版本的、支持并发的数据数据模型。
当 etcd 键值对的新版本保存后,先前的版本依然存在。从效果上来说,键值对是不可变的,etcd 不会对其进行 in-place 的更新操作,而总是生成一个新的数据结构。为了防止历史版本无限增加,etcd 的存储支持压缩(Compact)以及删除老旧版本。
逻辑视图
从逻辑角度看,etcd 的存储是一个扁平的二进制键空间,键空间有一个针对键(字节字符串)的词典序索引,因此范围查询的成本较低。
键空间维护了多个修订版本(Revisions),每一个原子变动操作(一个事务可由多个子操作组成)都会产生一个新的修订版本。在集群的整个生命周期中,修订版都是单调递增的。修订版同样支持索引,因此基于修订版的范围扫描也是高效的。压缩操作需要指定一个修订版本号,小于它的修订版会被移除。
一个键的一次生命周期(从创建到删除)叫做 “代 (Generation)”,每个键可以有多个代。创建一个键时会增加键的版本(version),如果在当前修订版中键不存在则版本设置为1。删除一个键会创建一个墓碑(Tombstone),将版本设置为0,结束当前代。每次对键的值进行修改都会增加其版本号 — 在同一代中版本号是单调递增的。
当压缩时,任何在压缩修订版之前结束的代,都会被移除。值在修订版之前的修改记录(仅仅保留最后一个)都会被移除。
物理视图
etcd 将数据存放在一个持久化的 B+ 树中,处于效率的考虑,每个修订版仅仅存储相对前一个修订版的数据状态变化(Delta)。单个修订版中可能包含了 B+ 树中的多个键。
键值对的键,是三元组(major,sub,type)
键值对的值,包含从上一个修订版的 Delta。B+ 树 —— 键的词法字节序排列,基于修订版的范围扫描速度快,可以方便的从一个修改版到另外一个的值变更情况查找。
etcd 同时在内存中维护了一个 B 树索引,用于加速针对键的范围扫描。索引的键是物理存储的键面向用户的映射,索引的值则是指向 B+ 树修该点的指针。
元数据存储——Kubernetes
Service Discovery(Name Service)
Distributed Coordination: Leader Election
㈩ etcd工作原理和部署指南
etcd是由CoreOS团队发的一个分布式一致性的KV存储系统,可用于服务注册发现和共享配置,随着CoreOS和Kubernetes等项目在开源社区日益火热,它们项目中都用到的etcd组件作为一个高可用强一致性的服务发现存储仓库,渐渐为开发人员所关注。在云计算时代,如何让服务快速透明地接入到计算集群中,如何让共享配置信息快速被集群中的所有机器发现,更为重要的是,如何构建这样一套高可用、安全、易于部署以及响应快速的服务集群,已经成为了迫切需要解决的问题。etcd为解决这类问题带来了福音,本文将从etcd的应用场景开始,深入解读etcd的实现方式,以供开发者们更为充分地享用etcd所带来的便利。
etcd推荐使用奇数作为集群节点个数。因为奇数个节点和其配对的偶数个节点相比,容错能力相同,却可以少一个节点。综合考虑性能和容错能力,etcd官方文档推荐的etcd集群大小是3,5,7。由于etcd使用是Raft算法,每次写入数据需要有2N+1个节点同意可以写入数据,所以部分节点由于网络或者其他不可靠因素延迟收到数据更新,但是最终数据会保持一致,高度可靠。随着节点数目的增加,每次的写入延迟会相应的线性递增,除了节点数量会影响写入数据的延迟,如果节点跟接节点之间的网络延迟,也会导致数据的延迟写入。
结论:
1.节点数并非越多越好,过多的节点将会导致数据延迟写入。
2.节点跟节点之间的跨机房,专线之间网络延迟,也将会导致数据延迟写入。
3.受网络IO和磁盘IO的延迟
4.为了提高吞吐量,etcd通常将多个请求一次批量处理并提交Raft,
5.增加节点,读性能会提升,写性能会下降,减少节点,写性能会提升。
参数说明:
这种方式就是 利用一个已有的 etcd 集群来提供 discovery 服务,从而搭建一个新的 etcd 集群。
假设已有的 etcd 集群的一个访问地址是: myetcd.local ,那么我们首先需要在已有 etcd 中创建一个特殊的 key,方法如下:
其中 value=3 表示本集群的大小,即: 有多少集群节点。而 就是用来做 discovery 的 token。
接下来你在 3 个节点上分别启动 etcd 程序,并加上刚刚的 token。
加 token 的方式同样也有 命令行参数 和 环境变量 两种。
命令行参数:
环境变量
以 命令行参数 启动方式为例:
可以使用etcd附带的 基准 CLI工具完成基准测试etcd性能。
对于一些基准性能数字,我们考虑具有以下硬件配置的三个成员的etcd集群:
有了这个配置,etcd可以大致写出:
示例命令是:
可线性读取请求通过集群成员的法定人数达成一致以获取最新数据。可序列化的读取请求比线性读取要便宜,因为它们由任何单个etcd成员提供,而不是成员法定人数,以换取可能的陈旧数据。etcd可以阅读:
示例命令是:
我们鼓励在新环境中首次安装etcd集群时运行基准测试,以确保集群达到足够的性能; 群集延迟和吞吐量可能会对较小的环境差异敏感。
以上测试部分翻译自官方文档( https://coreos.com/etcd/docs/latest/op-guide/performance.html )