本篇文章整顿自我司研发工程师杨可奥以及 PingCAP 工程效率负责人、Chaos Mesh 负责人周强在 GoCN 开源说上的演讲实录。
本文首先介绍了对混沌工程这一概念的形容,分享了混沌工程的动机和实际形式以及 Chaos Mesh 我的项目的倒退状况。在后半局部,介绍了 Chaos Mesh 我的项目自身的架构,并波及到在 Go 的生态环境中对容器等基本概念进行操作。干货十足,enjoy~ 本文首先介绍了对混沌工程这一概念的形容,分享了混沌工程的动机和实际形式以及 Chaos Mesh 我的项目的倒退状况。在后半局部,介绍了 Chaos Mesh 我的项目自身的架构,并波及到在 Go 的生态环境中对容器等基本概念进行操作。干货十足,enjoy~
混沌工程概述
当初的技术潮流在向着大规模集群、超简单的分布式系统与微服务架构演进。在演进的过程 当中,尽管给咱们带来了不少的便当,同时也带来了许多的麻烦。其中之一便是 —— 当一个节点产生谬误的时候,咱们无奈意料它将产生怎么的蝴蝶效应。它将只牵涉到局部服务还是会让所有服务解体?它可能自愈吗?更可怕的是,随着计算规模的扩充,故障产生的可能性也越来越大。对于一个个人电脑用户来说,可能用到更换电脑硬盘也未曾产生过损坏;而对于服务器集群来说,每天都可能会有数块磁盘损坏须要更换。
无论是云计算的领头羊 AWS,或是面向工程师们的 GitHub,还是互联网巨头 Google,都无奈逃离故障的命运。
混沌工程是一门新兴的技术学科,他的初衷是通过实验性的办法,让人们建设对于简单分布式系统在生产中抵挡突发事件能力的信念。
而混沌工程便是在这样蹩脚的环境下,让开发者、运维对简单零碎依然放弃信念的办法。
混沌工程历史
混沌工程曾经走过了十一个年头了。从最后 Netflix 提出这个概念,到 16 年 Gremlin 给出了 混动工程的商业产品,试图造成混沌工程服务的商业模式。Chaos Mesh 是在 2019 年末开源的,当初也成为了最受关注的混沌工程项目之一。
混沌工程步骤
如果想要为你治理的我的项目引入混沌工程,那么能够按照以下五步的循环:
一直进行这五步的循环,将对工程的稳定性产生显著的晋升。以混沌工程在 TiDB 上的试验为例:
1. 咱们立下冀望,TiDB 在删除一个节点之后应该可能在短时间内复原。
2. 进行了删除节点的混沌试验。
3. 发现前两次 TiDB 都在短时间内复原了,而第三次却花了很长的工夫才复原。
4. 考察这之中是否存在一些 Bug,修复之后再次进行试验,看看还有没有这个问题。
重复屡次地进行这样的步骤,都 TiDB 的稳定性就产生了一些帮忙。
Chaos Mesh 的社区
在短短的一年间,Chaos Mesh 的社区也曾经一直壮大了,当初领有了超过 3400 个星星,近 90 个贡献者,也是 CNCF Sandbox 我的项目。当初 Chaos Mesh 也曾经领有了波及 Pod 的生命 周期、IO、网络、资源压力、私有云等诸多方面的数十种不同类型的谬误注入形式;还领有一个功能丰富的仪表盘。这所有成就离不开来自社区的帮忙。无论是对 Bug 的报告、对工程、设计的意见,还是间接提交代码,都是对 Chaos Mesh 我的项目的巨大贡献。
Chaos Mesh 整体架构
Chaos Mesh 的整体架构如图中所展现,能够自上而下分为三个局部:
1. 用户输出、观测的局部。
2. 监听资源变动,进行注入 / 复原的 Controller 组件。
3. 在具体节点上进行故障注入的 Chaos Daemon。
这部分文章将按照这三个局部开展,自上而下梳理 Chaos Mesh 的架构。
用户输出、观测的局部
用户输出的局部总是以用户的操作为终点,以 Kubernetes API Server 为重点,不间接和 Chaos Mesh 的 Controller 交互。所有的用户操作最终都将反映为某个 Chaos 资源的变更(比方 NetworkChaos 资源的变更)。这保障了 Chaos Mesh Controller 的事件起源的单纯性。Chaos Mesh 提供了三种输出的形式:
1. 通过 kubectl 等命令行工具,将一个 YAML 文件提交至 Kubernetes 服务器这样做可能清晰地晓得提交的内容,通过 kubectl 这一 Kubernetes 用户都能纯熟应用的工具,将 Chaos Mesh 的资源操作形式和 Pod、Deployment 等其余原生资源的提交形式对立起来,不便用户上手、尝试。而除了应用 kubectl apply 命令来提交一个混沌试验之外,还能够通过 kubectl describe 命令来查问试验的状态和错误信息,应用 kubectl patch 来批改试验将试验暂停、复原。(如图,一个典型的形容网络分区的 NetworkChaos 资源文件)。
2. 通过 kubernetes/client-go
等包来对 Chaos 资源进行增删查改。这样做的益处是可能不便地集成进已有的测试流程。如果用户曾经搭建好一个可编程的测试平台,那么就能够通过这种形式在任何本人冀望的机会插入、复原 混沌试验,如果说将 Chaos Mesh 提供的丰盛的谬误注入能力作为测试的武器库,那么可编程的形式就是应用这些武器的最灵便的形式。
3. 通过 Chaos Dashboard 提供的 Web UI 界面对混沌试验进行操作和观测。Chaos Dashboard 提供了一套敌对的用户界面,同时也提供了按照 RBAC 的权限管控机制,用户须要输出本人的 Token 才可能进行操作。在这部分 UI 中用户可能治理和察看已有的谬误,同时也能将谬误注入归档并在未来复用。
监听资源变动的 Controller
Chaos Mesh 的 Controller 总是只承受来自 Kubernetes API Server 的事件 —— 这种事件会形容“什么资源产生了什么变动”,比方一个名为 network-test
的 NetworkChaos
资源被创立了。在工程实际中,对资源的具体变动进行响应太过简单了;而往往抉择应用“同步”的形式 —— 将资源中形容的情景向实在状态中同步,比方当 Pod 建设时会将这种形容给具象化为一个(或多个)容器;反过来的 “ 同步 ” 也是存在的,当容器意外死亡、启动失败的时候谬误状态也会同步至 Pod 中。所以无论是怎么的变动(无论是增删查改 中的哪一个),Controller 要做的都只是决定以下两件事件:
1. 当初应该注入还是应该复原还是要期待?
2. 如果须要注入 / 复原,应该怎么做?
对于第一个问题,Controller 将必要的信息存储在资源中,比方下一次要注入的工夫、下一次要复原的工夫。这样在响应变动的时候,就能通过以后工夫来推断应该注入还是复原还是期待。
而对于第二个问题,Controller 须要依据测试类型的不同进行不同的判断,比方删除 Pod 就能通过向 Kubernetes API Server 发送申请间接实现,对于私有云的混沌测试 ——比方起停虚拟机、移除磁盘则会通过每个私有云不同的 API 来实现,而其余稍稍简单的谬误注入(比方后文会提到的工夫偏移和网络提早)就须要 Chaos Daemon 的帮忙在每个节点上进行一些操作来注入。
Chaos Daemon 注入实现
要晓得如何在云环境下注入,第一个问题就是弄清楚咱们在注入什么 —— Pod 的实体是 Container 的话那么 Container 的实体是什么呢?在惯例的语境下,Container 的一个实体指的是一个过程与它所属的 Namespace 和 Cgroup。其中 Namespace 与 Cgroup 起到了一个与其余过程隔离的作用。Namespace 管制着可见性,主持着这个过程可能看到哪些货色(看到哪些文件、看到哪些过程);而 Cgroup 管制着资源分配:在一个 cgroup 内容许占用多少 CPU 工夫、占用多少内存、产生多少 Pid。而如果要进行谬误注入 —— 比方让一个容器内的 CPU 资源被吃满、让一个 Pod 与其余的 Pod 的网络连接断开,须要做的第一件事件就是侵入到对应的 Namespace 及 Cgroup 中去。
侵入 Namespace
侵入 Cgroup 的实现是颇为简略的,在过程启动之后将它退出到对应 Cgroup 的过程 名单中即可。而 Namespace 的注入在 Go 的独特线程环境下要艰难一些。Chaos Mesh 借鉴了 runc 的计划,本人实现了一个 chaos-mesh/nsexec
来侵入 Namespace。实现的原理是在过程启动的时候通过设置 LD_PRELOAD
环境变量来加载一个名为 nsexec.so
的动态链接库,这个动态链接库的 contructor
中调用 setns
零碎调用来将本身 设置到指标名字空间中去。执行流程如图所示:
这一工具相比 nsenter
这个现成工具的益处次要有两点:
1. 领有更适宜 Chaos Mesh 的信号处理机制,可能不便地管制子过程的死亡。
2. 可能和 mnt namespace
配合工作,只有是以后 mnt namespace 存在的二进制文件,都可能在指标名字空间中运行,而不须要指标名字空间中存在。在常见的 distroless 的镜像中,可能不存在任何常见的二进制文件,而 nsexec 可能应答这种状况。
具体注入实现的办法
当初 Chaos Mesh 曾经领有了十分丰盛的性能,受限于篇幅不可能独自介绍每一种注入的实现,更重要的可能是注入实现背地的办法和门路。在设计一个谬误注入的实现时,按照以下的流程进行思考被证实是十分有用的:
1. 思考失常状况下程序工作的形式。
2. 在失常的调用路径中有哪些可注入的中央。
3. 对这些可注入的中央进行注入。
以下咱们以工夫偏移为例,照着这种思考范式来设计它的实现:
1. 思考失常状况下程序工作的形式。失常状况下,一个程序是如何取得工夫的呢?大部分程序会抉择应用编程语言规范库中携带的函数,比方 Rust 程序会应用 Instant::now()
,Go 程序会应用 time.Now()
,C 生态的程序会应用 glibc
中的 clock_gettime
函数。而这些函数都会以 vDSO 的形式调用操作系统提供的 clock_gettime
。vDSO 也是由操作系统提供的函数性能。但与零碎调用不同,vDSO 由操作系统在过程启动的时候主动地加载入过程的内存空间,格局与动态链接库的 ELF 格局雷同。应用程序只须要解析这段 ELF 的一些段,就能找到 clock_gettime
函数 并调用它。
2. 在失常的调用路径中有哪些可注入的中央?因为 vDSO 是存在于指标过程的内存空间中的,如果咱们可能批改这部分内存空间就好了。而事实上 ptrace
零碎调用给了咱们批改另一过程的内存的能力。
3. 对这些可注入的中央进行注入,于是工夫偏移的实现形式跃然纸上——只须要筹备好一个有偏移的 clock_gettime
的实现,用它覆盖住原有的 clock_gettime
函数的实现就好了。
应用相似的办法,也同样可能破解其余类型注入的难题——比方对于文件系统的注入形式,咱们能够通过 FUSE
提供一个存在提早的文件系统,而这一文件系统以实在的磁 盘上的文件系统为存储后端即可(这样用户在复原之后依然能操作这些文件)。
以上为这篇文章介绍的全部内容了。如果读者对 Chaos Mesh 我的项目感兴趣,想要成为用户或贡献者,欢送退出咱们的 Slack Channel (CNCF Slack 的 #project-chaos-mesh 频道) 来一起探讨如何利用混沌工程、如何让 Chaos Mesh 变得更好。
结构一个稳固平安的软件服务应用环境,离不开社区的帮忙。无论是应用 Chaos Mesh 还是为 Chaos Mesh 提交代码,置信都是 在迈向这一美妙的指标。