您当前的位置: 首页 >  容器

凌云时刻

暂无认证

  • 0浏览

    0关注

    1437博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

容器技术20年:容器引擎与江湖门派

凌云时刻 发布时间:2020-11-21 12:00:00 ,浏览量:0

 

凌云时刻 · 技术

导读:《容器技术20年》作者再聊容器技术。

作者 | 刘奖

来源 | 云巅论剑

背景

读过本系列第一篇文章《容器技术 20 年》的读者,可能已经理解了容器和云原生的关系,以及容器技术恒等式:

我们今天先聊执行引擎,后续将有一篇关于容器镜像的专题文章具体聊镜像格式和镜像加速。

从集装箱革命说起

容器技术需要解决的核心问题之一运行时的环境隔离。

有一本非常有名的书,叫《集装箱改变世界》,说的是看起来平淡无奇的铁箱子,如何从二十世纪起永久性的改变了这个世界,并促进了全球化和全球分工。集装箱的出现和发展是实体货物包装、运输、交付方式的一次革命。

《经济学家》杂志曾经评价说“没有集装箱,不可能有全球化”。集装箱为什么具有革命性?经济全球化的基础就是现代运输体系,而一个高度自动化、低成本和低复杂性的货物运输系统的核心就是集装箱。集装箱最大的成功在于其产品的标准化及由此建立的一整套运输体系。能够让一个载重几十吨的庞然大物实现标准化,并且以此为基础逐步实现全球范围内的船舶、港口、航线、公路、中转站、桥梁、隧道、多试联运相配套的物流系统,这的确堪称人类有史以来创造的伟大奇迹之一,而撬动这个系统的理念就是标准化和系统化。

改变世界的不仅仅是集装箱本身,还有一整套货物处理的新方法,包括港口、货船、起重机、卡车,还有发货人的自身操作方式等。容器技术对于 IT 领域的意义非常类似于集装箱,只是里面装载的不再是实体货物,而是虚拟世界的二进制代码和软件。

制作集装箱可以采用不同的材料与工艺流程,只需要符合相应的标准。同理,容器技术也有不同架构和实现,只要遵循相应的技术规范,就是合格的容器技术。在具体介绍容器执行引擎方案及技术路线之前,先上一张容器引擎技术江湖门派概览图,以飨读者。

提到容器引擎(Container Engine)、容器运行时(Container Runtime)这些名称,总是有点容易让人犯晕。为了让事情简化一些,我们关注在云原生、K8S 场景下的定义吧。从 CNCF Cloud Native Interactive Landscape 可以发现,容器引擎领域是一个很活跃的技术领域,该领域的大致发展(战争)史如下图。

相对较为正式的术语定义如下图,可以把容器管理系统分为三层:

1. High-level Container Management:容器管控的 UI 层。直接实现容器的管控和使用界面,也是用户最熟悉的子系统。

2. High-level Container Runtime:容器状态及资源供给。包括镜像管理、网络接入、容器状态、调用 Low Level Runtime 执行容器等功能。习惯上这层称之为容器引擎(Container Engine)。

3. Low-level Container Runtime:容器执行层。负责具体构建容器运行环境并执行容器进程。习惯上这层直接简称为容器运行时(Container Runtime)。

High-level Container Management 和 Container Engine 之间的接口规范是 CRI,Container Engine 和Container Runtime 之间的接口规范是 OCI。

下文将分别从容器引擎(High-level container runtime)和容器运行时(Low-level container runtime)两个纬度讨论容器执行引擎相关技术。

容器引擎

容器引擎的核心是准备运行容器所需要的资源以及管理容器生命周期。在 K8S 生态圈中,容器编排系统通过 CRI 接口来调用容器引擎。支持 CRI 接口的容器引擎主要有 docker、rkt、pouch、containerd 和 cri-o 等,其中活跃度比较高的是 containerd 和 CRI-O。

 containerd

Containerd 是从 dockerd 中抽离出来的容器管理的核心功能,是在社区影响下 dockerd 模块化的结果,也是现在最热门的容器引擎。由于 containerd 是从 dockerd 演化出来的,使用接口是针对容器管理而设计,内置管理对象是 Image 和 Container。K8S CRI 的管控对象是 Image/Pod/Container,为了让 containerd 支持 Pod 对象以及实现 CRI 接口,引入了 CRI-containerd 组件来粘合 Kubelet 和 containerd 两个子系统。现在 cri-containerd 组件已经作为一个功能模块内置到 containerd。Containerd 依赖后端 container runtime 来具体管理容器,最常见的 container runtime 是 runC。

 CRI-O

而 CRI-O 则是专门支持 K8S CRI 接口而设计实现的容器引擎,它抛弃了对 docker 的支持而专注于支持 K8S CRI,所以架构相对 containerd 要简洁不少。但是,虽然架构简洁优雅,但是从实际使用体验看 CRI-O 在成熟度、可扩展性方面相对 cantainerd 还是有一定差距。今年 CRI-O 也增加了对 shimv2 接口的支持,从而可以支持 runC 之外其它容器运行时,但是实际测试发现功能还不成熟。

进一步看 CRI-O 的架构,一个 CRI 实现需要实现的核心功能包括:镜像管理(Image service)、运行管理(runtime service、管理 Pod、container 生命周期)、容器网络(CNI)和对接 OCI 兼容的容器运行时。这儿有一个非常重要的细节,虽然 cri-containerd 和 CRI-O 都是同时x实现 CRI 的 image service 和 runtime service,但是 CRI 规范其实允许用不同的组件来分别实现 image service 和 runtime service。

容器运行时

runC 是目前使用最广泛的容器运行时,但是 runC 也不是完美的。业界对 runC 的担心主要集中在 runC 的隔离能力,众所周知 runC 容器共享一个 host 内核,利用 cgroup 和 namespace 机制来构建相互隔离的容器执行环境。Docker、LXC、RKT 和 runC 这类原生容器都是基于共享主机操作系统的,这些技术的优点是资源利用率高、弹性能力强,缺点则是受攻击面大和攻击后果严重。特别是在多租户的云环境中,不同客户的容器会被编排部署到同一个服务器系统,这种威胁就变得尤其明显了。

其实,容器引擎技术领域也存在类似 CAP 理论的三边关系:资源效率、安全隔离和标准通用。目前所有的容器运行时技术最多都只能满足资源效率、安全隔离和标准通用中的两项,还没有一个技术能做到同时满足三项。比如:runC 在资源效率和标准通用方面最强,但是在安全隔离方面却最弱。Kata containers/firecracker-containerd/runD 在安全隔离和标准通用上有优势,但是在资源效率方面却有不足。

基于共享内核的 OS 虚拟化技术总是让人不放心,“安全容器”便应运而生了。安全容器的核心思路是摒弃共享内核,为每个 Pod/Container 实例配置一个专用的容器 OS,再辅以硬件虚拟化、应用内核等技术以获得更好的隔离性。我们可以按是否使用共享内核、是否使用硬件虚拟化、是否使用 Linux Kernel 作为容器内核几个纬度来对容器运行时进行分类。从下面的分类表可以看出,基于硬件虚拟化+Linux 分离内核是目前行业的主流技术路线。

 runC

runC 的介绍,为了节省读者的时间,此处省略一万字……

 Kata Containers

出于对传统容器安全性的担忧,Intel 在 2015 年启动了它们以虚拟机为基础的容器技术:Clear Container。Clear Container 依赖 Intel VT 的硬件虚拟化技术以及高度定制的 QEMU-KVM(qemu-lite)来提供高性能的基于虚拟机的容器。在 2017 年,Clear container 项目加入了 Hyper RunV,这是一个基于 hypervisor 的 OCI 运行时,从而启动了 Kata 容器项目(https://clearlinux.org/news-blogs/kata-containers-next-evolution-clear-containers?spm=ata.13261165.0.0.217a57c62rMc0r)。Kata containers 的核心思路是:

1. 操作系统本身的容器机制没办法解决安全性问题,需要一个隔离层;

2. 虚拟机是一个现成的隔离层,云服务已经让全世界相信,对户来说,“*secure of VM*”是可以满足需求的;

3. 虚机里面只要有个内核,就可以支持 OCI 规范的语义,在内核上跑个 Linux 应用这并不太难实现;

4. 虚机可能不够快,阻碍了它在容器环境的应用,那么可不可以拥有“speed of container”呢?

所以,Kata containers 核心之一是把 VM 变得轻快稳,使之能满足容器高密弹性的需求。同时,作为一个容器运行时,必须深度融入生态,支持相关规范。

Kata containers 最大的特点是它专注于实现一个开放的符合 OCI 标准的安全容器 runtime 实现,对于对接什么样的虚拟化方案,它抽象了一套 hypervisor 接口,如今已经对接了多种虚拟化实现,比如 qemu、nemu、firecracker、cloud-hypervisor 等。

2019 年,Kata containers 有个非常重要的技术进步,和 containerd 社区共同制定了 shimv2 接口规范,并率先在 Kata containers 支持了该规范。通过 containerd-shim-v2 和 vsock 技术,kata 精简了大量的组件,配合轻量级 hypervisor 和精简内核,kata 可以大幅降低内存开销和容器启动时间。更关键的是,降低系统部署复杂度还大幅提高了稳定性,特别是在系统重载情况下的稳定性。从实际使用体感看,阿里云沙箱容器 1.0 从 shimv1 升级到 shimv2 后,稳定性得到大幅提升,缺陷数量大幅下降。一个技术,同时服务于“轻”、“快”、“稳”三个目标,当之无愧重要的技术进步!

第二个热点则是 Kata containers 2.0 架构。从 2019 年 8 月开始,阿里云、蚂蚁和 intel 三方共同推动 Kata containers 2.0 架构定义及设计,核心是进一步提升多租隔离能力及可观测性。现在蚂蚁可信原生团队、阿里云容器团队和阿里云操作系统团队三方正在合力推进 Kata containers 2.0 架构。

回头再聊聊 Kata Containers 和 runC 的关系吧!Kata Containers 和 runC 不是替代与被替代,而是青出于蓝而胜于蓝。Kata Containers 把 runC 一层基于 OS 虚拟化的隔离机制扩展为三层隔离:Guest OS 虚拟化(等效于 runC),硬件虚拟化和 Host OS 虚拟化。所以 Kata Containers 通过引入更多的间接层来提升系统的隔离能力,但是需要付出“更多间接层”的代价。

 Firecracker-containerd

Firecracker-containerd 是 firecracker 和 containerd 的合体。Firecracker 是 AWS 基于 Google crosvm 开发的、配合 KVM 使用的一个轻量级安全 VMM,用以支持和实现 MicroVM。Firecracker MicroVM 同时具备传统虚拟机的安全性和工作负载隔离能力以及容器的速度和资源利用率。Firecracker 具有如下一些特色:

1. 抛弃 QEMU 使用的 C 语言,选择内存安全的 Rust 作为开发语言。

2. 基于 crosvm 使用极简设备模型,模拟尽可能少的必要设备,减小暴露的攻击面。

3. 高性能和低开销:得益于极简的设备模型, Firecracker 取消了 SeaBIOS (开源的 X86 BIOS),移除了 PCI 总线,取消了 VGA 显示等等硬件模拟,严格的说它甚至不是一台完整的虚拟计算机。而 Firecracker 运行的 GuestOS 使用的也是 AWS 定制过的精简 Linux 内核,同样裁剪掉了对应的设备驱动程序、子系统等等。因此叫它 MicroVM,其启动步骤和加载项要远远少于传统虚拟机。因此 Firecracker 目前已经能提供小于 125ms 的 MircroVM 启动速度,每秒 150 台的启动能力,小于 5MiB 的内存开销,并发运行 4000 台的极限承载容量(AWS i3.metal EC2 作为宿主机),以及热升级能力等。这些都是传统虚拟机所遥不可及,但现代化弹性工作负载又有强烈需求的性能指标。

但是,Firecracker 毕竟只是一个 VMM,还必须配合上 containerd 才能支持容器生态。所以,AWS 又开源了 firecracker-containerd 项目,用于对接 K8S 生态。本质上 Firecracker-containerd 是另外一个私有化、定制化的 Kata containers,整体架构和 Kata containers 类似,只是放弃了一些兼容性换取更简化的实现。

 gVisor

Google gVisor 是 GCP App Engine、Cloud Functions Cloud Run 和 CloudML 中使用的沙箱技术,正式商用名称是 Google Sandbox。Google 意识到在公有云基础设施中运行不受信容器的风险,以及虚拟机沙箱的低效,因此开发了用户空间的内核作为沙箱来运行不受信应用。gVisor 是沿着 libdune 的系统调用拦截思路发展而来的用户态内核或进程虚拟化技术。gVisor 通过拦截所有从应用到主机内核的系统调用,并使用用户空间中 gVisor 的内核实现来处理这些调用。

本质上来说,gVisor 是 VMM 和客户内核的组合,或者说 gVisor 是 syscall 虚拟化。基于 lock-in-pop 理论,假设 Linux 内核被高频访问的syscall是安全的,这部分可以开放给容器进程直接使用而不会导致严重的安全风险;对于冷僻 Linux syscall 则需单独处理,要不就是sentry模拟实现,要不就是在一个专用受限环境中执行(gofer)。sentry 实现了多数的 Linux 系统调用,尤其是内核功能,例如信号分发、内存管理、网络栈以及线程模型。gVisor 和 Nabla 有很相似的策略:保护主机。它们都使用了不到 10%的系统调用来和主机内核通信。gVisor 创建通用内核,而 Nabla 依赖的是 Unikernel,它们都是在用户空间运行特定的客户内核来支持沙箱应用的运行。

gVisor 还在婴儿期,也一样有一些限制。gVisor 要拦截和处理沙箱应用中的系统调用,总要有一定开销,因此不适合系统调用繁重的应用。gVisor 没有直接的硬件访问(透传),所以如果应用需要硬件(例如 GPU)访问,就无法在 gVisor 上运行。最后,gVisor 没有实现所有的系统调用,因此使用了未实现系统调用的应用是无法在 gVisor 上运行的。

出于对使用 C 语言开发系统软件导致的安全风险和软件缺陷的担忧,Google 开创了使用安全语言开发系统软件的先河。2013 年开发的 gVisor 选择了 Golang,2017 年开源的 crosvm 则使用 Rust。从目前状态看,Rust 更适合开发底层系统软件,Golang 则更适合开发上层便应用管理的系统软件。也许 Rust 早成熟几年 gVisor 就会有不同的选择啦!

 Nabla Containers

Nabla Containers 的核心思想是用 libOS 作为容器运行时的隔离机制。通过增加一个隔离层,libOS kernel,把容器应用和 host OS给隔离开来。nabla 是继承于 unikernel 的隔离方式,应用采用 rumprun 打包成一个 unikernel 镜像,直接运行在一个专为运行 unikernel 定制虚拟机(ukvm)中。应用直接打包首先可以降低很多内核态和用户态转换的开销,另外通过 ukvm 暴露非常有限的主机上的 syscall(只剩 7 个),可以大大缩小主机的攻击面。它是这些安全容器实现中,最安全的。

这条技术路径从学术理论角度看是很好的路线,但是 libOS 一二十年发展不起来是有原因的。它要求应用打包成 unikernel 镜像,因此和当前 docker 的镜像标准是不兼容的。另外,unikernel 应用在诸如支持创建子进程等一些常规操作上都有很难解决的问题。核心原因是应用侵入性没法解决,需要修改应用来适配容器运行时,和 K8S 部分语义也是冲突。

最近 Nabla containers 项目发生了一个有意思的转变,从采用 rumpkernel 切换到 user model linux。估计背后的原因是 rumpkernel 已经有 6 年没有更新了,背后的 NetBSD 活跃度也不高,Linux compatibility layer 的质量有限。但是,即使切换到 UML 解决部分 syscall ABI 兼容兼容问题,这条路前途也很渺茫。

我的观点是用 libOS 做容器运行时是条死路,我们在这个上面有血的教训!2018 年 5 月加入阿里云就开始 unikernel,基本思路和软件架构和 Nabla 基本一摸一样,rumpkernel + miniOS + ukvm + OCI 接口实现。搞了四五个月之后 rumpkernel + miniOS + ukvm 的核心架构基本 ready,但是当我们计划支持 OCI 时,发现模拟 Linux namespace、支持多进程难度太大,如果一步一步做下去最后就会做成另外一个简化版的 KVM + linux 实现。进一步评估发现解决 OS ABI 兼容或应用改造的代价都很大,所以这条技术路线不具有广泛落地生产的可能性。所以,当 IBM 宣布 Nabla Containers 项目时,我们已经基本放弃这条路线了。

 Windows 容器

受 Windows 操作系统的限制导致 Windows 容器天然有臃肿及生态的问题,所以 Windows 容器一直相对比较小众。但是从软件架构上看,Windows 容器架构反而是一个非常优秀的案例,值得探讨一番。Windows 2016 开始支持 Windows Containers,具有两种形态:Windows Server Containers 和 Hyper-v Containers,其具体架构如下图。

直观的理解,Windows Server Container 相当于 runC,而 Hypver-V Containers 则相当于 Kata Containers。也就是从诞生的一天开始,Windows 就提供了两种不同的 Container Runtime,以提供不同的特性来满足不同场景的需求。

具体到 Windows Server Containers 的架构细节,基本上和 Linux 容器技术类似,没有太多的惊喜。内核支持容器技术的关键子系统名称都是一致的:Control Groups、Namespaces、Layer Capabilities。比较特殊的是 Windows 内核子系统Host Compute Service 实现了 containerd + runc 的能力,也就是说 Windows 内核内置支持容器对象。

为了更好地与 K8S 生态融合,Windows 2019 在 docker 之外增加了对 Containerd/CRI 的支持,以及相关组件 runhcs(run Host Computer Service,对应 Linux runC)。Windows 容器技术整体架构升级为下图的架构。至此,Windows 容器通过支持 OCI 规范基本无缝融入了 K8S 生态。

Windows 容器技术架构中有两个很有意思的组件。一是 Host Compute Service(hcs),这个是专门为容器服务的 service。相对Linux直接暴露支撑容器的底层技术(cgroup、namespace、seccomp 等),Windows 选择了对底层基础技术进行一层封装由内核直接容器暴露容器兑现而不暴露底层技术。阿里云操作系统团队内部也进行过类似的技术探讨,Linux kernel 是否应该内置支持 container object,目前还没结论。二是轻量化的 Hyper-V 虚拟机,Microsoft 基于轻量化的 Hyper-V 虚拟机长出了 Hyper-V container,Windows Subsystem for Linux 2 和 Windows Sandbox 三个技术。最近 Redhat 基于 rust-vmm 搞的 runK 类似 Windows Sandbox 的思路,值得关注。

 VMware Project Pacific

相对其它家的技术路线,VMware Project Pacific 走了一条与众不同的路线来融入 K8S 生态。Project Pacific 没有采用常规路线实现一个 runP 之类的容器运行时,而是选择了全链路技术改造。Project Pacific 通过 pherelet、CRX 等组件完全重构 kubelet、containerd、runc 等组件,直接对接到 K8S API server。从下图的架构可以看出,Project Pacific 核心是在节点上实现容器和虚拟机的混合部署,同时支持虚拟机管理系统 vCenter 和容器编排系统 K8S。

进一步展开 Project Pacific 节点级的架构细节。为了支持 K8S 容器生态,ESXi 节点上增加了 Spherelet、Image Service、Spherelet Agent 等组件。ESXi 上同时部署了 hostd(相当于 pync + libvirt)和 Spherelet(相当于 Kubelet)两种管控系统以支持容器虚拟机混合部署,同时还为容器专门部署了 Image Service 用以管理容器镜像。

VMware  Project Pacific 和 ECI 使用场景和部署形态有很多共通之处,部分设计理念值得借鉴。

Alibaba Cloud Sandbox - runD 敬请期待下回分解!

小结

结尾一张图,功法全靠悟!

END

往期精彩文章回顾

如何保障“双11”期间亿万买家和卖家愉快地聊天

ECS自助服务之智能诊断和自动化修复

AI 云原生浅谈:好未来 AI 中台实践

云上高弹性、低成本解决方案

我眼中的解决方案架构师

饿了么技术往事(中)

饿了么技术往事(上)

阿里云落地全球最大云原生实践:双11核心系统全面云原生化

云上自动化部署和运维的正确姿势

【cherry键盘白送】有人在云上送来一波双十一福利

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

关注
打赏
1663816507
查看更多评论
立即登录/注册

微信扫码登录

0.0523s