做者 | 王旭 蚂蚁金服资深技术专家linux
本文整理自《CNCF x Alibaba 云原生技术公开课》第 28 讲,点击直达课程页面。缓存
关注“阿里巴巴云原生”公众号,回复关键词“入门”,便可下载从零入门 K8s 系列文章 PPT。安全
Phil Karlton 有一句名言:“计算机科学界只有两个真正的难题——缓存失效和命名。”网络
对咱们容器圈而言,我相信「命名」绝对配得上这句话。这毫无疑问是一件让老开发者沉默、让新人落泪的事情。仅就系统软件而言,咱们当今比较通行地称为「Linux 容器技术」这个概念,它曾经用过的名字还有 Jail, Zone, Virtual Server, Sandbox 等。一样,在早期虚拟化的技术栈里也把一类虚拟机叫作容器,毕竟这个词自己就指代那些用来包容、封装和隔离的器物。它实在太过常见了,以致于以严谨著称的 Wikipedia,它的词条叫作「OS-Level Virtualization」(系统级虚拟化) ,从而回避了「什么是容器」这个问题。架构
在 2013 年,Docker 问世以后,容器这个概念伴随着不可变基础设施、云原生这一系列概念在随后的几年间以摧枯拉朽之势颠覆了基于“软件包+配置”这种细粒度组合的应用部署,用简单的声明式策略和不可变的容器就清爽地定义了软件栈。应用怎么部署,在这儿彷佛有点离题了,我在这里想要强调的是:框架
“云原生语境下的容器,实质是「应用容器」——是以标准格式封装的,运行于标准操做系统环境(经常是 Linux ABI)上的应用打包——或运行这一应用打包的程序/技术。”运维
这个定义是我下的,但它并非个人我的意志,是基于 OCI 规范这一共识写出来的。这个规范规定了容器之中应用被放到什么样的环境下、如何运行,好比说容器的根文件系统上哪一个可执行文件会被执行,是用什么用户执行,须要什么样的 CPU,有什么样的内存资源、外置存储,还有什么样的共享需求等等。性能
因此说,标准格式的封装、标准的操做系统环境在一块儿以应用为中心就构成了应用容器的打包。优化
以这个共识为基础,就能够来讲说安全容器了。当年,我和个人联合创始人赵鹏使用「虚拟化容器」这个名字来命名咱们的技术的,不过为了博人眼球,咱们用了「Secure as VM, Fast as Container」这样的 Slogan,因而,被容器安全性问题戳中心坎的人们马上用「Secure Container」或者说「安全容器」来称呼这种东西了,一发而不可收。虽然在咱们的心里里,这个技术是一层额外的隔离,它只是安全中的一环,可是呢,用户仍是愿意用安全容器这个名字来称呼它。咱们给安全容器下的定义就是:阿里云
安全容器是一种运行时技术,为容器应用提供一个完整的操做系统执行环境(经常是 Linux ABI),但将应用的执行与宿主机操做系统隔离开,避免应用直接访问主机资源,从而能够在容器主机之间或容器之间提供额外的保护。
这就是咱们的安全容器。
说安全容器的时候,就要提到「间接层」这个词。它出自于 Linus Torvalds 在 2015 年的 LinuxCon 上提出的:
“安全问题的惟一正解在于容许那些(致使安全问题的)Bug 发生,但经过额外的隔离层来阻挡住它们。”
为了安全,为何要引入隔离层呢?其实 Linux 自己这样的规模是很是大的,没法从理论上来验证程序是没有 Bug 的,因而,一旦合适的 Bug 被利用,安全性风险就变成安全性问题了。安全性的框架和修补并不能确保安全,因此咱们须要进行一些额外的隔离来减小漏洞以及由于这些漏洞形成的被完全攻破的风险。
这就是安全容器的由来。
2017 年 12 月,咱们在 KubeCon 上对外发布了 Kata Containers 的安全容器项目,这个项目有两个前身:由咱们以前开始的 runV 以及 Intel 的 Clear Container 项目。这两个项目都是 2015 年 5 月开始开展的,其实是早于 Linus 在 KubeCon 2015 说的那番话的。
它们的思路都很简单:
如今的问题是虚机不太够快,阻碍了它在容器环境中的应用,若是能拥有「speed of container」的话,那咱们就可能能够有一个用虚拟机来作隔离的安全容器技术了。这个也就是 Kata Containers 自己的一个思路,就是用虚拟机来作 Kubernetes 的 PodSandbox。在 Kata 里面被拿来作 VM 的前后有 qemu, firecracker, ACRN, cloud-hypervisor 等。
下图就是 Kata Containers 怎么去和 Kubernetes 集成的,这里的例子用的是 containerd,固然 CRI-O 也是同样的。
目前,Kata Containers 一般是在 Kubernetes 中使用。首先 Kubelet 经过 CRI 接口找到 containerd 或者 CRI-O,这个时候好比镜像这样的操做通常也是由 containerd 或者 CRI-O 来执行的。根据请求,它会把 runtime 部分的需求变成一个 OCI spec,并交给 OCI runtime 执行。好比说上图上半部分中的 kata-runtime,或者说下半部分精简事后的 containerd-shim-kata-v2。具体的过程是这样的:
能够看到,在咱们的 PodSandbox 里面,实际上只有一个 Guest Kernel 跑着一些容器自己的打包和容器应用,并不包含一个完整的操做系统。就是说,这个过程,它用起来并不像是传统的虚拟机,对于容器来讲,它只有容器的引擎,而且经过少用没必要要的内存、共享能共享的内存来进一步地下降内存的开销。
与传统的虚拟机比起来,开销更小、启动更轻快,对于大部分的场景来讲,它能够作到「secure as VM」、「fast as container」。同时,在安全性技术之外,相比传统的虚机,它有更多的弹性,更少了机器的那种物理操做的手感,好比说这里面说过的包括动态资源的插拔以及使用 virtio-fs 这样的技术等。它是一个专门为咱们这种场景、为像 kata 这样的场景来作的一个把 host 的基本文件系统的内容(好比说容器的 rootfs )共享给虚拟机的这样一个技术。
经过其中一些以前为非易失存储、非易失内存来作的 DAX 的技术,可以在不一样的 PodSandbox 之间,也就是不一样的 Pod 之间、不一样的容器之间,共享一些能够共享的只读的内存部分。这样能够在不一样的 PodSandbox 之间去节省不少的内存。同时全部的 Pod 的管理都是经过 Kubernetes 从外部进行的容器管理,而且从外部来获取 metrics 和 debug 信息,并无登录虚拟机这样一种手感。因此它看起来是一种很是容器化的操做,虽然从底层来看,它仍是一个虚拟机,可是实际上它是一个面向云原生的虚拟化。
gVisor,咱们又把它叫作进程级的虚拟化,它是和 kata 不同的另一种方式。
在 2018 年的 5 月份,哥本哈根的 KubeCon 上,Google 开源了他们内部开发了 5 年的 gVisor 安全容器做为对 kata containers 的回应,代表了他们有一种不一样的安全容器的解决方案。
若是说 Kata Containers 是经过对现有的隔离技术进行组合和改造来构建容器间的隔离层的话,那么 gVisor 的设计显然是更加简洁的。
如上图右侧所示,它是一个用 Go 语言重写的运行在用户态的操做系统内核,这个内核的名字叫作 sentry,它并不依赖于虚拟化和虚拟机技术,相反,它是借助一个它们内部叫作一个 Platform(平台)的能力,让宿主机的操做系统作一个操做,把应用全部的指望对操做系统的操做都转交给 sentry 来进行,sentry 作处理以后会把其中的一部分交给操做系统来帮它完成,大部分则由本身来完成。
gVisor 是一个纯粹的面向应用的隔离层,从一开始就不是一个彻底等同于虚拟机的东西,它就是用来在 Linux 上面跑一个 Linux 程序的。做为一个隔离层,它的安全性依据在于:
Linux 大概有 300 多个 Syscall,实际上 sentry 最后向操做系统发起的调用只会集中在 60 多个 Syscall 上。这个是源于 gVisor 的开发者们对操做系统的安全作了一些研究,他们发现,大多数对操做系统的成功的攻击都是来自于不经常使用的系统调用的。
这个很容易理解,由于不经常使用的系统调用,它的实现路径通常都是比较老的路径,也就是说这些部分的开发通常不是太积极,只有不多的开发者来维护,那些热门路径上的代码要更安全一些,由于那些代码被 review 的次数比较多。因此 gVisor 的设计就是让应用对那些并不经常使用的 Syscall 的访问根本就到不了操做系统层面,而只在 sentry 里就把它处理掉。
从 sentry 访问宿主机的,只使用那些被验证过的、比较成熟、比较热的路径上的系统调用,这样的话,安全性就会比原来看起来好不少。咱们如今 Syscall 是原来的 1/5,可是被攻击的可能性是并不到 1/5 的。
在 Unix 系统里面,大部分东西都是一些文件,因此 open 能够作太多的事情了,大部分的攻击都是经过 open 来进行的。gVisor 的开发者就单独地把 open 放到了一个独立的进程里面去实现,这个进程叫作 Gofer。一个独立的进程其实是更容器被 seccomp、被一些系统的限制、一些 "capbility drop" 来保护。Gofer 能够作更少的事情,能够用非 root 去执行,如此一来整个系统的安全性就被进一步地被提升了。
Go 语言自己是一个内存更安全的一个实现,所以整个 gVisor 就更不容易被攻击,更不容易发生一些内存上的问题。固然,Go 语言在有些地方仍是不够太系统级的,gVisor 的开发者也坦言,他们为了作这件事情,也对 Go Runtime 作了不少调整,并把这些东西也反馈回给了 Go 语言的社区。
能够说 gVisor 的架构很漂亮,有不少开发者跟我坦诚,他们其实很喜欢 gVisor 的架构,以为这个更简单、更纯粹、更干净。固然了,虽然它的架构很漂亮,但从新实现一个内核这件事情也只有 Google 这样的巨头能作得出来,相似的可能还有微软的 WSL 1。并且这个设计是比较超前的,它其实存在一些问题:
因此短期以内,gVisor 这样的解决方案并不能成为一个终极的解决方案,不过它能够适应一些特定的场景,而且它也带来一些启示性。我以为这个启示性对将来的操做系统、CPU 指令集的发展均可能会有一些做用。并且我相信,在将来,无论是 kata 仍是 gVisor,都会有一个演进,咱们期待着最后会有一个公共的解决方案来统一地解决应用的执行问题。
安全容器的名字虽叫安全,可是它提供的是一个隔离性。它的做用是不止于安全的。
安全容器经过隔离层让应用的问题——无论是来自于外部的恶意攻击仍是说意外的错误,都不至于影响主机,也不会在不一样的 Pod 之间相互影响,因此实际上,这个额外的隔离层,它所带来的影响不仅是安全,还有其它的方面。它对于系统的调度、服务质量,还有应用信息的保护都是有好处的。
咱们说传统的操做系统容器技术是内核进程管理的一个延伸,容器进程自己是一组相关联的进程,对于宿主机的调度系统来讲,它是彻底可见的,一个 Pod 里的全部容器或进程,同时也都被宿主机调度和管理。这就意味着,若是你有一个大量容器的环境,宿主机自己内核的负担就会很重,在不少实际环境中已经能够观察到这个负担带来的开销了。
尤为是如今计算机技术的不断发展,一个操做系统会有大量的内存,大量的 CPU,几百 G 的内存都是能够见到的。在这个状况下,若是分配的容器数量不少,调度系统就会有很是沉重的开销。在采纳安全容器以后,在宿主机上就看不到这些完整的信息了,这个隔离层同时承担了一些对隔离层上面应用的调度,因而在主机上面就只须要调度这些沙箱自己,下降了宿主机的调度开销,这也就是它为何会提升调度效率的缘由。
提升调度效率的同时,它会把全部的应用彼此隔离起来,这样就避免了容器之间、容器和主机之间的干扰,提升了服务质量。从另一个方向来看,咱们作安全容器的初衷是为了保护宿主机不受到容器内恶意或者有问题的应用的影响,反过来,做为一个云来讲,咱们有可能会面对有恶意的攻击,因此也是保护咱们本身。
同时用户也不肯意让咱们过多地去访问用户的资源,用户须要使用资源,但它并不须要咱们看到它的数据。安全容器能够把用户运行的东西彻底封装在容器里,这样的话可让主机的运维管理操做并不能访问到应用的数据,从而把应用的数据保护在沙箱里,不须要去碰到用户数据。若是咱们要访问用户数据,做为一个云的话,那就必须得让用户给你受权,这个时候,用户不肯定你是否是有什么恶意的操做,若是咱们的沙箱封装得很好的话,那也就不须要额外的对用户受权的要求,这对于保护用户的私密性是更好的。
当咱们把目光看向将来的时候,能够看到,安全容器不只仅是在作安全隔离,安全容器隔离层的内核相对于宿主机的内核是独立的,专门对应用服务,从这个角度来讲,主机和应用的功能之间其实是一个合理的功能分配与优化。它能够展示出不少的潜力,将来的安全容器,可能不只仅是隔离性能开销的下降,同时也是在提升应用的性能。隔离技术会让云原生基础设施更加完美
本文的主要内容就到此为止了,这里为你们简单总结一下: