关于云计算:万级K8s集群背后etcd稳定性及性能优化实践

45次阅读

共计 11793 个字符,预计需要花费 30 分钟才能阅读完成。

背景与挑战

随着腾讯自研上云及私有云用户的迅速增长,一方面,腾讯云容器服务 TKE 服务数量和核数大幅增长, 另一方面咱们提供的容器服务类型(TKE 托管及独立集群、EKS 弹性集群、edge 边缘计算集群、mesh 服务网格、serverless knative)也越来越丰盛。各类容器服务类型背地的外围都是 K8s,K8s 外围的存储 etcd 又对立由咱们基于 K8s 构建的 etcd 平台进行治理。基于它咱们目前治理了千级 etcd 集群,背地撑持了万级 K8s 集群。

在万级 K8s 集群规模下的咱们如何高效保障 etcd 集群的稳定性?

etcd 集群的稳定性危险又来自哪里?

咱们通过基于业务场景、历史遗留问题、现网经营教训等进行稳定性危险模型剖析,危险次要来自旧 TKE etcd 架构设计不合理、etcd 稳定性、etcd 性能局部场景无奈满足业务、测试用例笼罩有余、变更治理不谨严、监控是否全面笼罩、隐患点是否能主动巡检发现、极其劫难故障数据安全是否能保障。

后面所形容的 etcd 平台曾经从架构设计上、变更治理上、监控及巡检、数据迁徙、备份几个方面水平解决了咱们治理的各类容器服务的 etcd 可扩展性、可运维性、可观测性以及数据安全性,因而 本文将重点形容咱们在万级 K8s 场景下面临的 etcd 内核稳定性及性能挑战,比方:

  • 数据不统一
  • 内存泄露
  • 死锁
  • 过程 Crash
  • 大包申请导致 etcd OOM 及丢包
  • 较大数据量场景下启动慢
  • 鉴权及查问 key 数量、查问指定数量记录接口性能较差

本文将繁难形容咱们是如何发现、剖析、复现、解决以上问题及挑战,以及从以上过程中咱们取得了哪些教训及教训,并将之利用到咱们的各类容器服务存储稳定性保障中。

同时,咱们将解决方案全副奉献、回馈给 etcd 开源社区,截止目前咱们奉献的 30+ pr 已全副合并到社区。腾讯云 TKE etcd 团队是 etcd 社区 2020 年上半年最沉闷的奉献团队之一, 为 etcd 的倒退奉献咱们的一点力量, 在这过程中特别感谢社区 AWS、Google、Ali 等 maintainer 的反对与帮忙。

稳定性优化案例分析

从 GitLab 误删主库失落局部数据到 GitHub 数据不统一导致中断 24 小时,再到号称 ” 不沉航母 ” 的 AWS S3 故障数小时等,无一例外都是存储服务。稳定性对于一个存储服务、乃至一个公司的口碑而言至关重要,它决定着一个产品生与死。稳定性优化案例咱们将从数据不统一的严重性、两个 etcd 数据不统一的 bug、lease 内存泄露、mvcc 死锁、wal crash 方面论述,咱们是如何发现、剖析、复现、解决以上 case,并分享咱们从每个 case 中的取得的播种和反思,从中吸取教训,防患于未然。

数据不统一(Data Inconsistency)

谈到数据不统一导致的大故障,就不得不具体提下 GitHub 在 18 年一次因网络设备的例行保护工作导致的美国东海岸网络核心与东海岸次要数据中心之间的连贯断开。尽管网络的连通性在 43 秒内得以复原,然而短暂的中断引发了一系列事件,最终导致 GitHub 24 小时 11 分钟的服务降级,局部性能不可用。

GitHub 应用了大量的 MySQL 集群存储 GitHub 的 meta data, 如 issue、pr、page 等等,同时做了货色海岸跨城级别的容灾。故障外围起因是网络异样时 GitHub 的 MySQL 仲裁服务 Orchestrator 进行了故障转移,将写入数据定向到美国西海岸的 MySQL 集群(故障前 primary 在东海岸),然而美国东海岸的 MySQL 蕴含一小段写入,尚未复制到美国西海岸集群,同时故障转移后因为两个数据中心的集群当初都蕴含另一个数据中心中不存在的写入,因而又无奈平安地将主数据库故障转移回美国东海岸。

最终, 为了保障保障用户数据不失落,GitHub 不得不以 24 小时的服务降级为代价来修复数据一致性。

数据不统一的故障严重性显而易见,然而 etcd 是基于 raft 协定实现的分布式高牢靠存储系统,咱们也并未做跨城容灾,按理数据不统一这种看起来高大上 bug 咱们是很难遇到的。然而幻想是美妙的,事实是残暴的,咱们不仅遇到了不堪设想的数据不统一 bug, 还一踩就是两个,一个是重启 etcd 有较低的概率触发,一个是降级 etcd 版本时如果开启了鉴权,在 K8s 场景下较大概率触发。在具体探讨这两个 bug 前,咱们先看看在 K8s 场景下 etcd 数据不统一会导致哪些问题呢?

  • 数据不统一最恐怖之处在于 client 写入是胜利的,但可能在局部节点读取到空或者是旧数据,client 无奈感知到写入在局部节点是失败的和可能读到旧数据
  • 读到空可能会导致业务 Node 隐没、Pod 隐没、Node 上 Service 路由规定隐没,个别场景下,只会影响用户变更的服务
  • 读到老数据会导致业务变更不失效,如服务扩缩容、Service rs 替换、变更镜像异样期待,个别场景下,只会影响用户变更的服务
  • 在 etcd 平台迁徙场景下,client 无奈感知到写入失败,若校验数据一致性也无异样时(校验时连贯到了失常节点),会导致迁徙后整个集群全面故障(apiserver 连贯到了异样节点),用户的 Node、部署的服务、lb 等会被全副删除,重大影响用户业务

首先第一个不统一 bug 是重启 etcd 过程中遇到的,人工尝试复现屡次皆失败,剖析、定位、复现、解决这个 bug 之路几经挫折,过程很乏味并充斥挑战,最终通过我对关键点减少 debug 日志,编写 chaos monkey 模仿各种异样场景、边界条件,实现复现胜利。最初的真凶居然是一个受权接口在重启后重放导致鉴权版本号不统一,而后放大导致多版本数据库不统一, 局部节点无奈写入新数据, 影响所有 v3 版本的 3 年之久 bug。

随后咱们提交若干个相干 pr 到社区, 并全副合并了, 最新的 etcd v3.4.9[1],v3.3.22[2]已修复此问题, 同时 google 的 jingyih 也曾经提 K8s issue 和 pr[3] 将 K8s 1.19 的 etcd client 及 server 版本升级到最新的 v3.4.9。此 bug 具体可参考超常同学写的文章三年之久的 etcd3 数据不统一 bug 剖析。

第二个不统一 bug 是在降级 etcd 过程中遇到的,因 etcd 短少要害的谬误日志,故障现场无效信息不多,定位较艰难,只能通过剖析代码和复现解决。然而人工尝试复现屡次皆失败,于是咱们通过 chaos monkey 模仿 client 行为场景,将测试环境所有 K8s 集群的 etcd 调配申请调度到咱们复现集群,以及比照 3.2 与 3.3 版本差别,在可疑点如 lease 和 txn 模块减少大量的要害日志,并对 etcd apply request 失败场景打印谬误日志。

通过以上措施,咱们比拟快就复现胜利了, 最终通过代码和日志发现是 3.2 版本与 3.3 版本在 revoke lease 权限上呈现了差别,3.2 无权限,3.3 须要写权限。当 lease 过期的时候,如果 leader 是 3.2,那么申请在 3.3 节点就会因无权限导致失败,进而导致 key 数量不统一,mvcc 版本号不统一,导致 txn 事务局部场景执行失败等。最新的 3.2 分支也已合并咱们提交的修复计划,同时咱们减少了 etcd 外围过程失败的谬误日志以进步数据不统一问题定位效率,欠缺了降级文档,具体阐明了 lease 会在此场景下引起数据不一致性,防止大家再次采坑。

  • 从这两个数据不统一 bug 中咱们取得了以下播种和最佳实际:

    • 算法实践数据一致性,不代表整体服务实现能保证数据一致性,目前业界对于这种基于日志复制状态机实现的分布式存储系统,没有一个外围的机制能保障 raft、wal、mvcc、snapshot 等模块合作不出问题,raft 只能保障日志状态机的一致性,不能保障应用层去执行这些日志对应的 command 都会胜利
    • etcd 版本升级存在肯定的危险,须要认真 review 代码评估是否存在不兼容的个性,如若存在是否影响鉴权版本号及 mvcc 版本号,若影响则降级过程中可能会导致数据不一致性,同时肯定要灰度变更现网集群
    • 对所有 etcd 集群减少了一致性巡检告警,如 revision 差别监控、key 数量差别监控等
    • 定时对 etcd 集群数据进行备份,再小概率的故障,依据墨菲定律都可能会产生,即使 etcd 自身虽具备齐备的自动化测试(单元测试、集成测试、e2e 测试、故障注入测试等),但测试用例仍有不少场景无奈笼罩,咱们须要为最坏的场景做筹备(如 3 个节点 wal、snap、db 文件同时损坏),升高极其状况下的损失, 做到可用备份数据疾速复原
    • etcd v3.4.4 后的集群灰度开启 data corruption 检测性能,当集群呈现不统一时,回绝集群写入、读取,及时止损,管制不统一的数据范畴
    • 持续欠缺咱们的 chaos monkey 和应用 etcd 自身的故障注入测试框架 functional,以帮助咱们验证、压测新版本稳定性(长时间继续跑),复现暗藏极深的 bug, 升高线上采坑的概率

内存泄露(OOM)

家喻户晓 etcd 是 golang 写的,而 golang 自带垃圾回收机制也会内存泄露吗?首先咱们得搞清楚 golang 垃圾回收的原理,它是通过后盾运行一个守护线程,监控各个对象的状态,辨认并且抛弃不再应用的对象来开释和重用资源,若你迟迟未开释对象,golang 垃圾回收不是万能的,不泄露才怪。比方以下场景会导致内存泄露:

  • goroutine 泄露
  • deferring function calls(如 for 循环外面未应用匿名函数及时调用 defer 开释资源,而是整个 for 循环完结才调用)
  • 获取 string/slice 中的一段导致长 string/slice 未开释(会共享雷同的底层内存块)
  • 利用内存数据结构治理不周导致内存泄露(如为及时清理过期、有效的数据)

接下来看看咱们遇到的这个 etcd 内存泄露属于哪种状况呢?事件起源于 3 月末的一个周末起床后收到现网 3.4 集群大量内存超过平安阈值告警,立即排查了下发现以下景象:

  • QPS 及流量监控显示都较低,因而排除高负载及慢查问因素
  • 一个集群 3 个节点只有两个 follower 节点出现异常,leader 4g,follower 节点高达 23G
  • goroutine、fd 等资源未呈现透露
  • go runtime memstats 指标显示各个节点申请的内存是统一的,然而 follower 节点 go_memstats_heap_release_bytes 远低于 leader 节点,阐明某数据结构可能长期未开释
  • 生产集群默认敞开了 pprof, 开启 pprof, 期待复现, 并在社区上搜寻开释有相似案例, 后果发现有多个用户 1 月份就报了,没引起社区器重,应用场景和景象跟咱们一样
  • 通过社区的 heap 堆栈疾速定位到了是因为 etcd 通过一个 heap 堆来治理 lease 的状态,当 lease 过期时须要从堆中删除,然而 follower 节点却无此操作,因而导致 follower 内存泄露, 影响所有 3.4 版本。
  • 问题剖析分明后,我提交的修复计划是 follower 节点不须要保护 lease heap, 当 leader 产生选举时确保新的 follower 节点能重建 lease heap, 老的 leader 节点则清空 lease heap.

此内存泄露 bug 属于内存数据结构治理不周导致的,问题修复后,etcd 社区立刻公布了新的版本(v3.4.6+)以及 K8s 都立刻进行了 etcd 版本更新。

从这个 内存泄露 bug 中咱们取得了以下播种和最佳实际:

  • 继续关注社区 issue 和 pr, 他人明天的问题很可能咱们今天就会遇到
  • etcd 自身测试无奈笼罩此类须要肯定工夫运行的能力触发的资源泄露 bug, 咱们外部须要增强此类场景的测试与压测
  • 继续欠缺、丰盛 etcd 平台的各类监控告警,机器留足足够的内存 buffer 以扛各种意外的因素。

存储层死锁(Mvcc Deadlock)

  • 死锁是指两个或两个以上的 goroutine 的执行过程中,因为竞争资源互相期待(个别是锁)或因为彼此通信(chan 引起)而造成的一种程序卡死景象,无奈对外提供服务。deadlock 问题因为往往是在并发状态下资源竞争导致的, 个别比拟难定位和复现, 死锁的性质决定着咱们必须保留好剖析现场,否则剖析、复现及其艰难。

    那么咱们是如何发现解决这个 deadlock bug 呢?问题起源于外部团队在压测 etcd 集群时,发现一个节点忽然故障了,而且始终无奈复原,无奈失常获取 key 数等信息。收到反馈后,我通过剖析卡住的 etcd 过程和查看监控,失去以下论断:

    • 不通过 raft 及 mvcc 模块的 rpc 申请如 member list 能够失常返回后果,而通过的 rpc 申请全副 context timeout
    • etcd health 衰弱监测返回 503,503 的报错逻辑也是通过了 raft 及 mvcc
    • 通过 tcpdump 和 netstat 排除 raft 网络模块异样,可疑指标放大到 mvcc
    • 剖析日志发现卡住的时候因数据落后 leader 较多,接管了一个数据快照,而后执行更新快照的时候卡住了,没有输入快照加载结束的日志,同时确认日志未失落
    • 排查快照加载的代码,锁定几个可疑的锁和相干 goroutine, 筹备获取卡住的 goroutine 堆栈
    • 通过 kill 或 pprof 获取 goroutine 堆栈,依据 goroutine 卡住的工夫和相干可疑点的代码逻辑,胜利找到两个相互竞争资源的 goroutine,其中一个正是执行快照加载, 重建 db 的主 goroutine,它获取了一把 mvcc 锁期待所有异步工作完结,而另外一个 goroutine 则是执行历史 key 压缩工作,当它收到 stop 的信号后,立即退出,调用一个 compactBarrier 逻辑,而这个逻辑又恰好须要获取 mvcc 锁,因而呈现死锁,堆栈如下。

这个 bug 也暗藏了很久,影响所有 etcd3 版本,在集群中写入量较大,某落后的较多的节点执行了快照重建,同时此时又恰好在做历史版本压缩,那就会触发。我提交的修复 PR 目前也曾经合并到 3.3 和 3.4 分支中,新的版本曾经公布(v3.3.21+/v3.4.8+)。

从这个 死锁 bug 中咱们取得了以下播种和最佳实际:

  • 多并发场景的组合的 etcd 自动化测试用例笼罩不到,也较难构建,因而也容易出 bug, 是否还有其余相似场景存在同样的问题?须要参加社区一起持续进步 etcd 测试覆盖率(etcd 之前官网博客介绍一大半代码曾经是测试代码),能力防止此类问题。
  • 监控尽管能及时发现异常节点宕机,然而死锁这种场景之前咱们不会主动重启 etcd, 因而须要欠缺咱们的衰弱探测机制(比方 curl /health 来判断服务是否失常),呈现死锁时可能保留堆栈、主动重启复原服务。
  • 对于读申请较高的场景,需评估 3 节点集群在一节点宕机后,残余两节点提供的 QPS 容量是否可能反对业务,若不够则思考 5 节点集群。

Wal crash(Panic)

panic 是指呈现重大运行时和业务逻辑谬误,导致整个过程退出。panic 对于咱们而言并不生疏,咱们在现网遇到过几次,最早遭逢的不稳定性因素就是集群运行过程中 panic 了。

虽说咱们 3 节点的 etcd 集群是能够容忍一个节点故障,然而 crash 霎时对用户仍然有影响,甚至呈现集群拨测连贯失败。

咱们遇到的第一个 crash bug,是发现集群链接数较多的时候有肯定的概率呈现 crash, 而后依据堆栈查看社区已有人报 grpc crash(issue)[4], 起因是 etcd 依赖的组件 grpc-go 呈现了grpc crash(pr)[5],而最近咱们遇到的crash bug[6] 是 v3.4.8/v3.3.21 新版本公布引起的,这个版本跟咱们有很大关系,咱们奉献了 3 个 PR 到这个版本,占了一大半以上, 那么这个 crash bug 是如何产生以及复现呢?会不会是咱们本人的锅呢?

  • 首先 crash 报错是 walpb: crc mismatch, 而咱们并未提交代码批改 wal 相干逻辑,排除本人的锅。
  • 其次通过 review 新版本 pr, 指标锁定到 google 一位大佬在修复一个 wal 在写入胜利后,而 snapshot 写入失败导致的 crash bug 的时候引入的.
  • 然而具体是怎么引入的?pr 中蕴含多个测试用例验证新加逻辑,本地创立空集群和应用存量集群 (数据比拟小) 也无奈复现.
  • 谬误日志信息太少,导致无奈确定是哪个函数报的错,因而首先还是加日志,对各个可疑点减少谬误日志后,在咱们测试集群轻易找了个老节点替换版本,而后很容易就复现了,并确定是新加的验证快照文件合法性的锅,那么它为什么会呈现 crc mismatch 呢? 首先咱们来简略理解下 wal 文件。
  • etcd 任何通过 raft 的模块的申请在写入 etcd mvcc db 前都会通过 wal 文件长久化,若过程在 apply command 过程中呈现被杀等异样, 重启时可通过 wal 文件重放将数据补齐,防止数据失落。wal 文件蕴含各种申请命令如成员变动信息、波及 key 的各个操作等,为了保障数据完整性、未损坏,wal 每条记录都会计算其的 crc32,写入 wal 文件。重启后解析 wal 文件过程中,会校验记录的完整性,如果数据呈现损坏或 crc32 计算算法呈现变动则会呈现 crc32 mismatch.
  • 硬盘及文件系统并未出现异常,排除了数据损坏,通过深刻排查 crc32 算法的计算,发现是新增逻辑未解决 crc32 类型的数据记录,它会影响 crc32 算法的值,导致呈现差别,而且只有在当 etcd 集群创立产生后的第一个 wal 文件被回收才会触发,因而对存量运行一段时间的集群,100% 复现。
  • 解决方案就是通过减少 crc32 算法的解决逻辑以及减少单元测试笼罩 wal 文件被回收的场景,社区已合并并公布了新的 3.4 和 3.3 版本(v3.4.9/v3.3.22).

尽管这个 bug 是社区用户反馈的,但从这个crash bug 中咱们取得了以下播种和最佳实际:

  • 单元测试用例十分有价值,然而编写齐备的单元测试用例并不容易,须要思考各类场景。
  • etcd 社区对存量集群降级、各版本之间兼容性测试用例简直是 0,须要大家一起来为其舔砖加瓦,让测试用例笼罩更多场景。
  • 新版本上线外部流程标准化、自动化, 如测试环境压测、混沌测试、不同版本性能比照、优先在非核心场景应用(如 event)、灰度上线等流程必不可少。

配额及限速(Quota&QoS)

etcd 面对一些大数据量的查问(expensive read)和写入操作时(expensive write),如全 key 遍历(full keyspace fetch)、大量 event 查问, list all Pod, configmap 写入等会耗费大量的 cpu、内存、带宽资源,极其容易导致过载,乃至雪崩。

然而,etcd 目前只有一个极其简略的限速爱护,当 etcd 的 commited index 大于 applied index 的阈值大于 5000 时,会回绝所有申请,返回 Too Many Request,其缺点很显著,无奈准确的对 expensive read/write 进行限速,无奈无效避免集群过载不可用。

为了解决以上挑战,防止集群过载目前咱们通过以下计划来保障集群稳定性:

  • 基于 K8s apiserver 下层限速能力,如 apiserver 默认写 100/s, 读 200/s
  • 基于 K8s resource quota 管制不合理的 Pod/configmap/crd 数
  • 基于 K8s controller-manager 的 -terminated-Pod-gc-threshold 参数管制有效 Pod 数量(此参数默认值高达 12500,有很大优化空间)
  • 基于 K8s 的 apiserver 各类资源可独立的存储的个性, 将 event/configmap 以及其余外围数据别离应用不同的 etcd 集群,在进步存储性能的同时,缩小外围主 etcd 故障因素
  • 基于 event admission webhook 对读写 event 的 apiserver 申请进行速率管制
  • 基于不同业务状况,灵便调整 event-ttl 工夫,尽量减少 event 数
  • 基于 etcd 开发 QoS 个性,目前也曾经向社区提交了初步设计计划,反对基于多种对象类型设置 QoS 规定(如按 grpcMethod、grpcMethod+ 申请 key 前缀门路、traffic、cpu-intensive、latency)
  • 通过多维度的集群告警(etcd 集群 lb 及节点自身出入流量告警、内存告警、精细化到每个 K8s 集群的资源容量异样增长告警、集群资源读写 QPS 异样增长告警)来提前防备、躲避可能呈现的集群稳定性问题

多维度的集群告警在咱们的 etcd 稳定性保障中施展了重要作用,屡次帮忙咱们发现用户和咱们本身集群组件问题。用户问题如外部某 K8s 平台之前呈现 bug, 写入大量的集群 CRD 资源和 client 读写 CRD QPS 显著偏高。咱们本身组件问题如某旧日志组件,当集群规模增大后,因日志组件不合理的频繁调用 list Pod,导致 etcd 集群流量高达 3Gbps, 同时 apiserver 自身也呈现 5XX 谬误。

尽管通过以上措施,咱们能极大的缩小因 expensive read 导致的稳定性问题,然而从线上实际成果看,目前咱们依然比拟依赖集群告警帮忙咱们定位一些异样 client 调用行为,无奈自动化的对异样 client 的进行精准智能限速,。etcd 层因无奈辨别是哪个 client 调用,如果在 etcd 侧限速会误杀失常 client 的申请, 因而依赖 apiserver 精细化的限速性能实现。社区目前已在 1.18 中引入了一个API Priority and Fairness[7],目前是 alpha 版本,期待此个性早日稳固。

性能优化案例分析

etcd 读写性能决定着咱们能撑持多大规模的集群、多少 client 并发调用,启动耗时决定着咱们当重启一个节点或因落后 leader 太多,收到 leader 的快照重建时,它从新提供服务须要多久?性能优化案例分析咱们将从启动耗时缩小一半、明码鉴权性能晋升 12 倍、查问 key 数量性能晋升 3 倍等来简略介绍下如何对 etcd 进行性能优化。

启动耗时及查问 key 数量、查问指定记录数性能优化

当 db size 达到 4g 时,key 数量百万级别时,发现重启一个集群耗时居然高达 5 分钟, key 数量查问也是超时,调整超时工夫后,发现高达 21 秒,内存暴涨 6G。同时查问只返回无限的记录数的场景(如业务应用 etcd grpc-proxy 来缩小 watch 数,etcd grpc proxy 在默认创立 watch 的时候,会发动对 watch 门路的一次 limit 读查问),仍然耗时很高且有微小的内存开销。于是周末闲暇的时候我对这几个问题进行了深入调查剖析,启动耗时到底花在了哪里?是否有优化空间?查问 key 数量为何如何耗时,内存开销如此之大?

带着这些问题对源码进行了深入分析和定位,首先来看查问 key 数和查问只返回指定记录数的耗时和内存开销极大的问题,剖析论断如下:

  • 查问 key 数量时 etcd 之前实现是遍历整个内存 btree, 把 key 对应的 revision 寄存在 slice 数组外面
  • 问题就在于 key 数量较多时,slice 扩容波及到数据拷贝,以及 slice 也须要大量的内存开销
  • 因而优化计划是新增一个 CountRevision 来统计 key 的数量即可,不须要应用 slice,此计划优化后性能从 21s 升高到了 7s, 同时无任何内存开销
  • 对于查问指定记录数据耗时和内存开销十分大的问题,通过剖析发现是 limit 记录数并未下推到索引层,通过将查问 limit 参数下推到索引层,大数据场景下 limit 查问性能晋升百倍,同时无额定的内存开销。

再看启动耗时问题过高的问题,通过对启动耗时各阶段减少日志,失去以下论断:

  • 启动的时候机器上的 cpu 资源 etcd 过程未能充分利用
  • 9% 耗时在关上后端 db 时,如将整个 db 文件 mmap 到内存
  • 91% 耗时在重建内存索引 btree 上。当 etcd 收到一个申请 Get Key 时,申请被层层传递到了 mvcc 层后,它首先须要从内存索引 btree 中查找 key 对应的版本号,随后从 boltdb 外面依据版本号查出对应的 value, 而后返回给 client. 重建内存索引 btree 数的时候,恰好是相同的流程,遍历 boltdb, 从版本号 0 到最大版本号一直遍历,从 value 外面解析出对应的 key、revision 等信息,重建 btree,因为这个是个串行操作,所以操作及其耗时
  • 尝试将串行构建 btree 优化成高并发构建,尽量把所有核计算力利用起来,编译新版本测试后发现成果甚微,于是编译新版本打印重建内存索引各阶段的具体耗时剖析,后果发现瓶颈在内存 btree 的插入上,而这个插入领有一个全局锁,因而简直无优化空间
  • 持续剖析 91% 耗时发现重建内存索引居然被调用了两次,第一处是为了获取一个 mvcc 的要害的 consistent index 变量,它是用来保障 etcd 命令不会被反复执行的要害数据结构,而咱们后面提到的一个数据不统一 bug 恰好也是跟 consistent index 有密切关系。
  • consistent index 实现不合理,封装在 mvcc 层,因而我后面提了一个 pr 将此个性重构,做为了一个独立的包,提供各类办法给 etcdserver、mvcc、auth、lease 等模块调用。
  • 个性重构后的 consistent index 在启动的时候就不再须要通过重建内存索引数等逻辑来获取了,优化成调用 cindex 包的办法疾速获取到 consistent index,就将整个耗时从 5min 从缩短到 2 分 30 秒左右。因而优化同时依赖的 consistent index 个性重构,改变较大暂未 backport 到 3.4/3.3 分支,在将来 3.5 版本中、数据量较大时能够享受到启动耗时的显著晋升。

明码鉴权性能晋升 12 倍

某外部业务服务始终跑的好好的,某天 client 稍微增多后,忽然现网 etcd 集群呈现大量超时,各种折腾,切换云盘类型、切换部署环境、调整参数都不发挥作用,收到求助后,索要 metrics 和日志后,通过一番排查后,失去以下论断:

  • 景象确实很诡异,db 延时相干指标显示没任何异样, 日志无任何无效信息
  • 业务反馈大量读申请超时,甚至能够通过 etcdctl 客户端工具简略复现,可是 metric 对应的读申请相干指标数居然是 0
  • 疏导用户开启 trace 日志和 metrics 开启 extensive 模式,开启后发现无任何 trace 日志,然而开启 extensive 后,我发现耗时居然全副花在了 Authenticate 接口,业务反馈是通过明码鉴权,而不是基于证书的鉴权
  • 尝试让业务同学短暂敞开鉴权测试业务是否复原,业务同学找了一个节点敞开鉴权后,此节点立即复原了失常,于是抉择长期通过敞开鉴权来复原现网业务
  • 那鉴权为什么耗时这么慢?咱们对可疑之处减少了日志,打印了鉴权各个步骤的耗时,后果发现是在期待锁的过程中呈现了超时,而这个锁为什么耗时这么久呢?排查发现是因为加锁过程中会调用 bcrpt 加密函数计算明码 hash 值,每次消耗 60ms 左右,数百并发下期待此锁的最高耗时高达 5s+。
  • 于是咱们编写新版本将锁的范畴缩小,升高持锁阻塞工夫,用户应用新版本后,开启鉴权后,业务不再超时,恢复正常。
  • 随后咱们将修复计划提交给了社区,并编写了压测工具,测试晋升后的性能高达近 12 倍(8 核 32G 机器,从 18/ s 晋升到 202/s),然而仍然是比较慢,次要是鉴权过程中波及明码校验计算, 社区上也有用户反馈明码鉴权慢问题, 目前最新的 v3.4.9 版本曾经蕴含此优化, 同时能够通过调整 bcrpt-cost 参数来进一步晋升性能。

本文简略形容了咱们在治理万级 K8s 集群和其余业务过程中遇到的 etcd 稳定性和性能挑战,以及咱们是如何定位、剖析、复现、解决这些挑战,并将解决方案奉献给社区。

同时,详细描述了咱们从这些挑战中播种了哪些贵重的教训和教训,并将之利用到后续的 etcd 稳定性保障中,以反对更大规模的单集群和总集群数。

最初咱们 面对万级 K8s 集群数, 千级的 etcd 集群数, 10 几个版本散布,其中不少低版本蕴含重要的潜在可能触发的重大 bug, 咱们还须要投入大量工作一直优化咱们的 etcd 平台 ,使其更智能、变更更加高效、平安、可控(如反对自动化、可控的集群降级等), 同时数据安全也至关重要, 目前腾讯云 TKE 托管集群咱们曾经全面备份,独立集群的用户后续将疏导通过利用市场的 etcd 备份插件开启定时备份到腾讯云对象存储 COS 上。

将来咱们将持续严密融入 etcd 的社区,为 etcd 社区的倒退奉献咱们的力量,与社区一块晋升 etcd 的各个性能。

参考资料

[1]v3.4.9: https://github.com/etcd-io/et…

[2]v3.3.22: https://github.com/etcd-io/et…

[3]K8s issue 和 pr: https://github.com/kubernetes…

[4]grpc crash(issue): https://github.com/etcd-io/et…

[5]grpc crash(pr): https://github.com/grpc/grpc-…

[6]crash bug : https://github.com/etcd-io/et…

[7]API Priority and Fairness: https://github.com/kubernetes…

【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!

正文完
 0