在Kubernetes架构中,Etcd作为Kubernetes的元数据存储。创建Pod时,Etcd背后是如何工作的?下面我们就通过具体案例来一步步分析吧!etcd存储概念Kubernetes资源存储格式/registry/deployments/default/nginx-deployment/registry/events/default/nginx-deployment-66b6c48dd5-gl7zl.16c35febf6e3c38c/registry/events/default/nginx-deployment-66b6c48dd5-gl7zl.16c35febfc6c35febfc6注册表/事件/默认/nginx-deployment-66b6c48dd5-gl7zl。registry/events/default/nginx-deployment-66b6c48dd5-gl7zl.16c35fec1fdba240/registry/events/default/nginx-deployment-66b6c48dd5-gl7zl.16c35fec249adf98/registry/events/default/nginx-deployment-66b6c48dd5-k55k7.16c35edbfa8784f8/registry/Events/default/nginx-deployment-66B6C48DD5-W9BR6.16C35EEB78BFA621/REGISTINC/gundistr/events/events/default/nginx-deployment-66b6c48dd55-w9ddd55-w9默认/nginx-deployment-66b6c48dd5-w9br6.16c35eeba7251388/registry/events/default/nginx-deployment-66b6c48dd5-w9br6.16c35fda25f0af06/registry/events/default/nginx-deploYMENT-66B6C48DD.16C35EEB788D245F/REGISTINC/EXTIST/events/default/nginx-deployment-66b6c48ddd.16c355.16c35febf6bb59a0/grautt/empert/evertsion/events/event/nginx-deployment.16c35ee/deft.16c35eeb7eeb7eeb77777.1777pllc35epllc225c2255.1pll.177plllly.b77eplllyepllly.177.plllyb77.plllay/nginx-deployment-66b6c48dd5-gl7zl/registry/replicasets/default/nginx-deployment-66b6c48dd5Kubernetes资源在etcd中的存储格式为前缀+“/”+资源类型+“/”+命名空间+“/”+具体资源名称存储模块kube-apiserver启动时,会把每个资源的APIGroup、Version、ResourceHandler注册到路由中。请求经过认证、限速、授权、访问控制等模块的校验后,将请求转发给相应的资源。处理逻辑。同时,kube-apiserver实现了类似数据库ORM机制的通用资源存储机制,提供资源创建、更新、删除前后的hook能力,封装成策略接口。当你添加一个新的资源时,只需要编写相应的创建、更新、删除等策略,而不需要编写任何etcdAPI。从图中可以看出,创建资源主要由三个步骤组成:BeforeCreate、Storage.Create、AfterCreate;当收到创建nginxDeployment的请求时,通用存储模块会先回调各个资源实现的BeforeCreate策略。资源被写入etcd来做一些初始化工作。下面是Deployment资源的创建策略实现,它执行诸如将deployment.Generation设置为1之类的操作。//PrepareForCreate清除不允许最终用户在creation.func(deploymentStrategy)PrepareForCreate(ctxcontext.Context,objruntime.Object){deployment:=obj.(*apps.Deployment)deployment.Status=apps.DeploymentStatus{}deployment.Generation=1pod.DropDisabledTemplateFields(&deployment.Spec.Template,nil)}执行完BeforeCreate策略后,会执行Storage.Create接口,即真正开始调用底层存储模块etcd3、将nginxDeployment资源对象写入etcd。资源安全创建和更新etcd提供了Put和Txn接口为业务添加key-value数据,但是如果Put接口在并发场景下接收到相同key的资源创建,会被覆盖。所以,Kubernetes显然不能直接通过etcd的Put接口写入数据。etcd事务接口Txn,为多key原子更新、并发操作安全等而生,提供了丰富的冲突检查机制。是Kubernetes集群用来防止并发创建、更新被覆盖等的事务Txn接口。执行完BeforeCreate策略后,kube-apiserver会调用Storage模块的Create接口写入资源。1.6版本以后Kubernetes集群默认使用的存储是etcd3,其创建接口简单实现如下://Createimplementsstorage.Interface.Create.func(s*store)Create(ctxcontext.Context,keystring,obj,outruntime.Object,ttluint64)error{......key=path.Join(s.pathPrefix,key)opts,err:=s.ttlOpts(ctx,int64(ttl))iferr!=nil{returnerr}newData,err:=s.transformer.TransformToStorage(data,authenticatedDataString(key))如果错误!=nil{returnstorage.NewInternalError(err.Error())}startTime:=time.Now()txnResp,err:=s.client.KV.Txn(ctx).If(notFound(key),).Then(clientv3.OpPut(key,string(newData),opts...),).Commit从上面的代码片段,我们可以得出结论,首先它会按照我们介绍的Kubernetes资源存储格式拼接key。那么如果TTL不为0,就会根据TTL从leaseManager中获取一个可复用的LeaseID。默认情况下,如果Kubernetes集群中不同key(如KubernetesEvent资源对象)的TTL差异在1分钟内不同,则可以复用同一个LeaseID,避免大量Lease影响etcd性能和稳定性。其次,如果启用了数据加密,数据在写入etcd之前会根据加密算法进行转换。最后使用etcd的Txn接口向etcd发起Txn请求创建部署资源。ResourceVersion资源版本是KubernetesAPI中一个非常重要的概念。顾名思义,它是一个Kubernetes资源的内部版本字符串,客户端可以通过它来判断资源是否发生了变化。同时,通过在Get、List、Watch接口指定ResourceVersion值,可以满足您对数据一致性和高性能的需求。未指定ResourceVersion,默认为空字符串。kube-apiserver收到此类读请求后,会向etcd发送共识读/线性读请求,获取etcd集群的最新数据。ResourceVersion="0",赋值字符串0。当kube-apiserver收到这样的请求时,可能会返回任意资源版本号的数据,但优先选择较新的版本。一般情况下,它直接从kube-apiserver缓存中获取数据返回给客户端。可以读取过期数据,适用于对数据一致性要求不高的场景。ResourceVersion是一个非零字符串。当kube-apiserver收到这样的请求时,会确保Cache中最新的ResourceVersion大于等于你传入的ResourceVersion,然后从Cache中查找你请求的资源对象key,并将数据返回给客户。基本原理是kube-apiserver为每个核心资源(如Pod)维护一个Cache,通过etcd的Watch机制实时更新Cache。当你的Get请求携带了一个非零的ResourceVersion时,它会等待缓存中最新的ResourceVersion大于等于你Get请求中的ResourceVersion。如果满足条件,就会从Cache中查询数据,返回给客户端。如果不满足条件,它将最多等待3秒。如果超过3秒,Cache中最新的ResourceVersion仍然小于Get请求中的ResourceVersion,则返回ResourceVersionTooLarge错误给客户端。etcd交互分析Kubernetes组件概览kube-apiserver负责对外提供集群各种资源的增删改查接口。它是Kubernetes集群中各组件之间数据交互和通信的枢纽。kube-apiserver设计为可横向扩展,一般多副本部署在一个高可用的Kubernetes集群中。当收到创建Pod的写入请求时,其基本流程是检查请求的鉴权、限速、授权、访问机制等,然后写入etcd。kube-scheduler是负责调度集群Pod的调度器组件。基本原理是通过监控kube-apiserver获取需要调度的pod,然后根据一系列的筛选和评估算法,将最好的Node节点分配给pod。kube-controller-manager包含一系列controller组件,如Deployment、StatefulSet等controller。控制器的核心思想是监控比较资源的实际状态是否与预期状态一致,如果不一致,则协调工作,使其最终一致。etcd组件,Kubernetes的元数据存储。Kubelet是部署在各个节点上的Agent组件,负责创建和运行Pod。基本原理是通过监听APIServer获取分配给其节点的Pod,然后调用运行时组件根据Pod的规范细节创建暂停和业务容器。首先,我们通过kubectlcreate-fnginx.yml命令创建Deployment资源,并向kube-apiserver发送请求。apiVersion:apps/v1kind:Deploymentmetadata:name:nginx-deploymentlabels:app:nginxspec:replicas:1选择器:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:-name:nginximage:nginx:1.14.2ports:-containerPort:80$kubectlcreate-fnginx.ymldeployment.apps/nginx-deploymentcreatedkube-apiserver会收到创建nginx部署资源的请求日志,通过Txn接口将数据成功写入etcd,执行kubectlcreate-fnginx.yml命令返回给客户端#etcd收到创建nginx部署资源的请求日志:2021-12-2311:49:16.320328D|etcdserver/api/v3rpc:开始时间=2021-12-2311:49:16.319917473+0000UTCm=+416.447615057,花费的时间=386.445μs,远程=127.0.0.1:45596,响应类型=/etcdserverpb.KV/Txn,请求计数=1,request769大小=1,响应计数=0,响应大小=41,请求内容=compare:
