etcd是一种响应快、分布式、一致的key-value存储。因为它是分布式的,因此可以运行多个etcd示例来获取高可用性和更好的性能。
etcd是存储Kubernetes集群状态和元数据的唯一的地方。
在集群中,所有组件都需要通过API服务器与其他组件通信,因此唯一与etcd通信的是API服务器。
乐观锁
API服务器并发的与etcd通信时,由于API服务器支持多实例并发,因此在访问资源时需要使用锁机制。
在这里,采用的是乐观锁的方式。
多实例使用乐观锁机制更新数据的过程如下:
从etcd中读取资源数据,每个资源都包含一个版本号,即metadata.resourceVersion
,假设为A;
提交更新数据时,再次读取该资源,并查看其版本号是否为A,若为A,则表示资源未被更新过,直接提交即可;否则,重新进入第1步;
资源存储在etcd中
现在新的Kubernetes版本已经开始使用etcd v3处理数据的存储。
之前提到过,etcd v3使用key-value存储,在Kubernetes中使用的是包含斜杠格式的key,类似于目录的形式,但是注意,etcd v3不支持目录形式的key,只是看起来像而已。
集群的数据的key都是以/registry
为前缀的,如下查看以/registry
为前缀的所有的key:
$ ETCDCTL_API=3 etcdctl --endpoints https://127.0.0.1:2379 --cacert ca.crt --cert server.crt --key server.key get /registry --prefix --keys-only
/registry/apiregistration.k8s.io/apiservices/v1.
/registry/apiregistration.k8s.io/apiservices/v1.admissionregistration.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1.apiextensions.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1.apps
/registry/apiregistration.k8s.io/apiservices/v1.authentication.k8s.io
...
从打印出的结果可以看到,key的组织结构为/registry/{资源类型}/{命名空间}/{资源实例名}
。
Kubernetes集群在将数据保存至etcd中时,会先将数据使用protobuf编码,再存入etcd中,从而压缩数据量,读取时再将数据进行解码,可以看下面这个例子,查看default
命名空间中,名为curl
的Pod:
$ ETCDCTL_API=3 etcdctl --endpoints https://127.0.0.1:2379 --cacert ca.crt --cert server.crt --key server.key get /registry/pods/default/curl
/registry/pods/default/curl
k8s
v1Pod�
�
curldefault"*$4c0d9790-d5ec-4a59-9de1-3a0d924a24822����b�
0kubectl.kubernetes.io/last-applied-configuration�{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"curl","namespace":"default"},"spec":{"containers":[{"command":["sleep","9999999"],"image":"tutum/curl","name":"main"}]}}
z�
1
default-token-67tb62
default-token-67tb6��
main
tutum/curlsleep9999999*BJJ
default-token-67tb6-/var/run/secrets/kubernetes.io/serviceaccount"2j/dev/termination-logrAlways����FileAlways 2
ClusterFirstBdefaultJdefaultminikubeX`hr���default-scheduler�6
node.kubernetes.io/not-readyExists" NoExecute(��8
node.kubernetes.io/unreachableExists" NoExecute(�����
Running#
InitializedTru����*2
ReadyTru����*2'
ContainersReadyTru����*2$
PodScheduledTru����*2"*192.168.99.1062
172.17.0.����B�
main
����o�Error�������:Idocker://dc7751dcdce4bcc3a85dd37ab32915fade2e4020a8acebf029322c15408f453e (
2tutum/curl:latest:ddocker-pullable://tutum/curl@sha256:b6f16e88387acd4e6326176b212b3dae63f5b2134e69560d0b0673cfb0fb976fBIdocker://dcb335aa117717980bfa42fb512e86a27a445a078afa1caea52e5a45abd2335bHJ
BestEffortZb
172.17.0.2"
确保etcd集群一致性
为了保持高性能,往往会运行多个etcd实例,多个etcd实例中的数据需要保持一致。
etcd使用RAFT一致性算法,确保在任何时间点,每个节点的状态要么是大部分(法定数量,通常是一半)节点的状态,要么是之前确认过的状态。
一致性算法,要求etcd集群大部分(法定数量,通常是一半)节点参与,才能进行到下一个状态。
假设由于某些原因,etcd集群分为两个不互联的节点组(这种情况称为脑裂),当有访问集群的请求时,有两种可能:
请求访问的是大部分节点组中的节点:由于节点组中有过半的节点,因此可以更改etcd集群的状态;
请求访问的是小部分节点组中的节点:由于节点组中的节点数量不过半,则无法参与状态变更;
当两个节点组之间恢复连接时,第二个组的节点会更新为第一个组的节点的状态。