谈到生产系统etcd高可用集群一般有几类,第一类为kubeadm搭建的K8s集群,这种的etcd我们通常通过kubelet扫描本地目录,这个本地目录我们可以将etcd的pod放进去
高可用etcd解决方案
谈到生产系统etcd高可用集群一般有几类,第一类为kubeadm搭建的K8s集群,这种的etcd我们通常通过kubelet扫描本地目录,这个本地目录我们可以将etcd的pod放进去,kubelet扫描到etcd的pod清单,然后它就会去启动etcd的pod,这个就是K8s控制平面怎么拉起自己的etcd。
有些时候我们可能使用另外的方式去搭建etcd集群,etcd-operator是基于k8s crd完成etcd的集群配置。
etcd operator
operator本身其实就是crd和controller的组合,所以etcd operator引入了几个核心对象,扩展对象。
第一个对象就是etcd cluster,用户可以定义一个第三方对象,扩展对象叫etcd cluster,在这个cluster里面可以定义什么呢?第一我的etcd版本是什么,第二我需要多少副本数。当你定义好这些对象之后,etcd-operator会运行一个控制器,这个控制器就会去解析etcd-cluster,然后创建k8s pod,这些pod就会运行etcd image,并且按照用户的输入来组成etcd集群。
etcd-operator作用就是按照用户的input定义用户的etcd的版本和副本数,我就将etcd的集群拉起来,它还支持其他两类对象,一类是banckup对象,一类是restore对象。
backup对象是用来保证数据的安全性的,那么它就可以定义一些备份的策略,备份的策略可以说要往哪备份,往GCS上备份还是往s3上面备份,这些公有云的存储都需要密码,在backup对象里面你可以去提供认证的credentials,那么它会按照你backup里面定义的频度和要保存备份的数量,那么它会按照你定义的时间周期一直去做snapshot,并且将这个snapshot存放在远端的存储里面去。
如果要去恢复就去定义etcd restore这样一个对象,他会关联volume,并且从volume拉下你的备份,并且通过etcd的operator来恢复整个etcd集群。
operator模式是什么?它其实就是crd + controller,crd是你的模型抽象,要把你做的事情,声明式的形式定义出来,controller行为是之前我自动化怎么做的,可能是自动化脚本,或者手敲的命令,希望可以通过控制器的模式以go语言的方式将它加载出来。
基于Bitnami安装etcd高可用集群
上面介绍了两种工具快速的去构建集群。
Kubernetes如何使用etcd的
api server对于etcd来说是一个客户端,它就要指定客户端的ca 文件 crt kety文件,同时要指定etcd server的地址。
Kubernets 对象在 etcd 中的存储路径
可以将整个namespace对象dump出来。所以api server去通过/api/v1/namespace/default去get一个namespace对象的时候,它就等价于在etcd里面get /registry/namspace/default,除了前缀不一样,拼装的逻辑还是一样的,它将整个路径组装为key,然后存储在etcd里面。
etcd在集群当中的位置
堆叠etcd集群:一个集群里面有3个master,每个master里面都有etcd这个pod,这个pod通常通过kubelet自管理。
kubelet会去读取一个config,这个config定义了static pod path,kubelet它本身的作用是维护pod的生命周期,他有几种方式来加载pod的清单,一种方式是监听api server,还有一种方式是它会去扫描本地的static pod path,扫描这个目录,它要去看这个目录里面有没有pod清单,如果有的话就直接加载。
这种模式就是etcd会在每个master上面部署,它是以静态的方式去部署。
堆叠式好处:apiserver和etcd之间是紧绑定的,所有apiserver的请求以loopback方式发送到etcd,读操作可以直接读走,读操作不需要经过master,读操作不需要经过网络调用的,本地就走了,其次这些组件放在一起好管理维护,不需要网络调用就可以读取数据。还有etcd是一个重落盘的,它对disk io是有要求的,其他的应用没有那么高的要求,那么放在一起也是ok的。
外部构建etcd高可用集群,那么apiserver指定路径的时候,就指定一个外部路径,而不是一个loopback地址,不是localhost。
实践 etcd集群高可用
etcd集群高可用是1 3 5节点?如果是偶数个节点会出现投票数均等的情况,投票数均等的情况会导致重新投票,会导致leader无主状态的时间变长,所以选择奇数个。
如果你选了1个节点作为master,那么它的性能最高,写任何数据确认就结束了,所以它性能是最高的,但是它的数据安全性是无法保证的,数据丢了就丢了,除非备份备份也不具有时效性,会丢数据。
3个就好处是有了最基本的冗余,3个里面有一个坏掉了,那么系统还是好的,运维人员要立刻上线修复,如果再崩掉一个,那么系统就出问题了。
5个性能首先是最差的,如果1个坏掉了还有4个,2个坏掉还有三个,如果1个出现问题不需要立刻修复,这样对运维的压力会小,多两个etcd又得多两台机器,这都是钱,得权衡。
强调一下,读操作都可以从follower去读的,写操作都要经过leader去写。
很多时候一个应用性能不够了,我们要去做横向扩展,对于etcd这样的应用来说其实是相反的,你peer越多,它写入数据的时候需要确认的人就越多,所以它做横向扩展会使得它的性能降低。
apiserver是etcd的唯一客户端,apiserver和etcd通信是grpc协议,grpc协议的一个特性就是连接复用,也就说不管多少个请求从同一个客户端来,它都会去复用一个连接到server,同一个group所有对象是共用一个tcp连接的,也就是pod node 这些等这些核心对象通过共用一个grpc连接到etcd端,
实践 etcd存储规划
挂网盘的好处是数据永远保存,它严重依赖于外部的网络存储设备,网络会经常出问题,当网络抖动etcd就会出现问题,最后就导致整个集群的稳定性非常的差,所以不要使用网络存储,使用本地存储。
能不能使用大部分的member都用local来保证其时效性,一个member挂网盘?让它跟着听就好了,实际上一样不行,在数据io高的时候,本地数据更新走的快,网络那个memb而永远跟不上,久而久之就掉队了,这样就会导致集群的状态不对,所以全部存储到本地是最好的选择。
最佳实践就是local ssd,一定要让etcd落盘快,使用local ssd,并且给它独立的盘,不让别人来干扰它,这样可以提高整个etcd的性能。
上面可以看到db size是不一致的,它和我们的compcat defrag是相关的。
在k8s里面有大部分对象是用来做声明的,比如pod期望它长的什么样,service是定义我希望怎么样去发布服务,但是有些对象纯属是用来做审计的,比如说event,希望了解一下这个对象发生过什么事情,每个控制器都会建立大量的event,所以量非常大,第二他们不那么重要,即使这些数据丢失也不会对集群造成破坏的影响,所以k8s允许你在api server里面去定义etcd-servers-overrrides ,这样可以再启动一个etcd的instance,然后将一部分对象转到新的etcd里面,这样好处是etcd数据量大了扛不住的时候,可以将一部分数据分出去,其次对于频繁变更的东西可以放到其他地方去,这样可以保证主的etcd写和etcd event落在不同的盘,那么整个的写性能会好一些。
etcd是个集群,网络效率对整个集群效率息息相关的,包发出去再回来看看需要多长时间。
etcd本身有election timeout,heartbeat timeout,它会有各种超时的时间限制,如果部署在不同的地域,那么heartbeat就要花很长时间。
虽然etc将客户端通信和peer通信分为不同的端口了,当客户端连接到leader连接数过多,那么leader和follower之间通信可能发生错误,发到follower就出错了,直接drop掉了,因为发送队列的缓冲区满了,所以发不出去,这个时候可以去节点上面做一些网络调优,通过tc的命令去调节peer通信的优先级,优先保证etcd的peer之间的通信。
减少磁盘io延时
这个影响是最高的,etcd要落盘,它的盘和其他的盘共享,那么就可能导致我的etcd的写入非常的慢,那么就进一步的降低了我的etcd的性能,在生产系统里要去做etcd落盘的时候,请给它划分单独的盘,而且尽可能的给ssd这种高性能的数据盘,如果要和其他业务共享落盘,那么你可以通过Ionice来提高etcd对磁盘写入的优先级,避免被其他影响,因为etcd是整个集群性能的根本,如果它的性能没法保证,那么集群的性能就会变差。
保持合理日志的文件的大小
etcd里面有wal log,wal会随着数据的修改而线性增长,修改数据它的reversion就会改变,虽然我key只有一个,只要我不停的去动它,那么数据就会很大。
etcd本身支持快照体系,当日志修改到一定次数的时候,参数通过sanpshot-count去指定,比如日志积累到1w次变更,它会自动生成snapshot,snapshot就是基于当前状态做个快照,然后将之前的wal log全部压缩掉。
如果etcd的内存和磁盘使用过高,那就要去分析是不是数据写入的频度太高了,大部分这种时候这种性能调优都要去找到bad user,日常运维集群的时候,最容易出问题的是apiserver和etcd,出问题往往就是bad user导致的,找到bad user并且修正他们的行为。
当然在etcd里面可以通过调节快照触发阈值,降低对磁盘内存的使用。
设置合理的存储配额
一般来说对于生产集群就配置8G,并且把auto-compact,dfrag这些参数设置好,来确保这8个G的空间不会被占满。
同时我们要做好监控,如果到了6 7G的时候,那么就要收到告警集群快要占满了,不能让它出现alarm,出现了alarm之后所有的写操作都不能成功,之前演示过,那么集群就变为一个只读集群了,就会导致某些控制器的行为异常。
自动压缩历史版本
如何保证磁盘的量小呢?有一个auto-compac这样一个参数,来允许自动的去压缩历史版本。
定期消除碎片化
其实就是dfrag,也就是配合dfrag和compact两个命令,我们可以有效的压缩历史版本,清除磁盘碎片,使得etcd更加高效的去利用磁盘。
优化运行参数
心跳默认100ms,选举超时时间1000ms也就是1s,这两个参数分别控制了,第一个leader以什么样的频次来发起心跳,第二就是follower多久接收不到leader过来的心跳,它要重新去选举。
这两个参数要配合使用,一般会把选举超时时间设置为心跳通知的这个周期的10倍,这是一个经验数据,也是etcd的一个推荐。
针对不同的网络环境我们就需要去做一些调优了,跨大陆的可能一个round-trip就要400ms,那么我心跳的interval就要改成很长,可能要改到800ms,所以针对于不同的网络环境,我需要去调节这些参数来适应网络延时。
心跳怎么设置呢,一般设置为RTT的0.5-1.5倍,选举超时时间设置为RTT的10倍。这是一些经验和建议。
etcd备份存储
它主要通过snapshot去备份的,snapshot是不能很频繁去做的,因为做snapshot的时候要去锁住磁盘空间,然后做一个快照,这个时候做的任何修改都要开辟新的空间来写,也就是snapshot频繁的时候,它就快速的让你磁盘占用的空间变的非常的大。
有了wal log,那么wal log存放的是增量数据的写入日志,它们两个结合到一起就是全局,
备份方案实践
上面是社区方案里面做了增强, 比如说在每个etcd member里面,会有一个备份的客户端,是一个sidecar形式,这个sidecar会去watch etcd leader,它每30分钟去做一次snapshot,这样snapshot的频度没有那么大,30分钟的数据备份一定是不足够的,因为极端的情况下可能是29.5秒的数据全部没有了,那么怎么减少snapshot频度又可以保证数据的时效性。
在这基础之上先做snapshot,然后去监听从快照结束reversion开始监听etcd事件,也就是通过etcd的客户端去做watch,watch所有对象,所有对象修改的信息,我们都可以通过每10s一次把这些watch到的event写入到磁盘,也就是通过snapshot的方式加上event这种方式去做增强备份。
30分钟snapshot+10s一次的event,这个event是实时去监听的,每10s写入一次,这个就是增强备份方案。