容器运行时的内部结构和最新趋势(2023)下

简介: 容器运行时的内部结构和最新趋势(2023)

Cgroups

Cgroups(控制组)施加多种资源配额,例如 CPU 使用率、内存使用率、block I/O 以及容器中的进程数量。

Cgroup 还控制对设备节点的访问。Docker默认配置 允许无限制访问 /dev/null/dev/zero/dev/urandom 等,不允许访问 /dev/sda(磁盘设备)、/dev/mem(内存)等。

Capabilities

在 Linux 上,root 权限由 64-bit capability 标记。目前使用了 41 位

Docker 的默认配置删除了系统范围的管理功能,例如 CAP_SYS_ADMIN

保留的能力包括:

  • CAP_CHOWN:用于在容器内运行 chown
  • CAP_NET_BIND_SERVICE:用于绑定容器内 1024 以下的 TCP 和 UDP 端口。
  • CAP_NET_RAW:用于运行需要制作原始以太网数据包的旧版 ping 实现。这种功能非常危险,因为它允许在容器网络中进行ARP 欺骗和 DNS 欺骗。Docker 的未来版本可能会默认禁用它

(可选)Seccomp

Seccomp(安全计算)允许指定系统调用的显式允许列表(或拒绝列表)。Docker 的默认配置允许大约 350 个系统调用

Seccomp 用于纵深防御;对于容器来说这并不是硬性要求。为了向后兼容,Kubernetes 仍然默认不使用 seccomp,并且在可预见的将来可能永远不会改变默认配置。用户仍然可以通过 KubeletConfiguration 选择启用 seccomp 。

(可选)AppArmor 或 SELinux

AppArmorSELinux(安全增强型 Linux)是 LSM(Linux 安全模块),可提供更细粒度的配置旋钮。

这些是相互排斥的;由主机操作系统发行商(而不是容器镜像发行商)选择:

  • AppArmor:Debian、Ubuntu、SUSE 等选择的。
  • SELinux:由 Fedora、Red Hat Enterprise Linux 和类似的主机操作系统发行版选择。

为了进行纵深防御,Docker 的 默认 AppArmor 配置文件 几乎与其功能、挂载掩码等默认配置重叠。用户可以添加自定义设置以提高安全性。

但 SELinux 的情况则不同。要在 selinux-enabled 模式下运行容器,您必须在绑定挂载上附加选项 :z(小写字符)或 :Z(大写字符),或者自己运行复杂的 chcon 命令避免权限错误。

:z(小写字符)选项用于类型强制。类型强制通过为进程和文件分配“类型”来保护主机文件免受容器的影响。以 container_t 类型运行的进程可以读取 container_share_t 类型的文件,并读/写 container_file_t 类型的文件,但无法访问其他类型的文件。

:Z(大写字符)选项用于多类别安全性。多类别安全性通过为进程和文件分配类别号来保护一个容器免受另一个容器的影响。例如,类别 42 的进程无法访问标记为类别 43 的文件。

适用于 Mac/Win 的 Docker

Docker Desktop 产品支持在 Mac 和 Windows 上运行 Linux 容器,但它们只是在底层运行 Linux 虚拟机来在其上运行容器。这些容器不直接在 macOS 和 Windows 上运行。


3.容器运行时的最新趋势

Docker 的替代品(作为 Kubernetes 运行时)

Kubernetes 的第一个版本(2014 年)是专门为 Docker 制作的。Kubernetes v1.3 (2016) 添加了对名为 rkt 的替代容器运行时的临时支持,但 rkt 已于2019 年退役。支持替代容器运行时的努力在 Kubernetes v1.5 (2016) 中产生了容器运行时接口 CRI API。CRI 首次亮相后,业界已趋同于使用 containerdCRI-O 这两种运行时其中之一:。

Kubernetes 仍然内置了对 Docker 的支持,但最终在 Kubernetes v1.24(2022年)中被删除。Docker 仍然继续作为第三方运行时为 Kubernetes 工作(通过 cri-dockerd shim),但 Docker 现在在 Kubernetes 中的使用率越来越低。

业界知名大厂已经从 Docker 转向了 containerd 或者 CRI-O:

  • containerd 的采用者Amazon Elastic Kubernetes Service (EKS)Azure Kubernetes Service (AKS)Google Kubernetes Engine (GKE)k3s 等(很多)。
  • CRI-O 的采用者Red Hat OpenShiftOracle Container Engine for Kubernetes (OKE) 等。

Containerd 注重可扩展性,支持非 Kubernetes 工作负载以及 Kubernetes 工作负载。相比之下,CRI-O 注重简单性,并且仅支持 Kubernetes。

Docker 的替代方案(作为 CLI)

尽管 Kubernetes 已成为多节点生产集群的标准,但用户仍然希望使用类似 Docker 的 CLI 在笔记本电脑上本地构建和测试容器。Docker 基本上满足了这个需求,但是社区中的运行时开发人员希望构建自己的“实验室”CLI,以先于 Docker 和 Kubernetes 孵化新功能,因为通常很难向 Docker 和 Kubernetes 提出新功能,由于一些技术/技术因素原因。

Podman(以前称为 kpod )是由 Red Hat 等公司创建的兼容 Docker 的独立容器引擎。它与 Docker 的主要区别在于它默认没有守护进程。此外,Podman 的独特之处在于它为管理 Pod(共享相同网络命名空间的容器组,通常共享同一主机上的数据卷以实现高效通信)以及容器提供一流的支持。然而,大多数用户似乎只将 Podman 用于非 Pod 容器。

nerdctl(我于 2020 年创立)是一个适用于 containerd 的兼容 Docker 的 CLI。nerdctl 最初是为了试验新功能,例如延迟拉取(稍后讨论),但它对于调试运行 containerd 的 Kubernetes 节点也很有用。

在 Mac 上运行容器

Docker Desktop 的 Mac 和 Windows 产品是专有的。Windows 用户可以在 WSL2 中运行 Docker 的 Linux 版本(Apache License 2.0,无图形界面),但迄今为止,Mac 用户还没有相应的解决方案。

Lima(也是我于 2021 年创立的)是一个命令行工具,用于在 macOS 上创建类似 WSL2 的环境来运行容器。Lima 默认使用 nerdctl,但它也支持 Docker 和 Podman。

Lima 还被 colima (2021)、Rancher Desktop (2021) 和 Finch (2022)等第三方项目采用。

Podman 社区发布了 Podman Machine(命令行工具,2021 年)和 Podman Desktop(GUI,2022 年)作为 Docker Desktop 的替代品。Podman Desktop 也支持 Lima(可选)。

Docker 正在重构

containerd 主要提供两个子系统:运行时子系统和镜像子系统。然而,后者并未被Docker使用。这是一个问题,因为 Docker 自身的传统镜像子系统远远落后于 containerd 的现代镜像子系统(这也导致我启动了nerdctl项目):

  • • 不支持 lazy-pulling 惰性拉取(按需镜像拉取)
  • 对多平台镜像的有限支持(例如 AMD64/ARM64 双平台镜像)
  • OCI 规范的有限合规性

这个长期存在的问题终于得到解决。Docker v24 (2023) 在 /etc/docker/daemon.json 中添加了对使用 containerd 的镜像子系统和 undocumented option 的实验性支持:

{"features":{"containerd-snapshotter": true}}

Docker 的未来版本(2024?2025?)很可能默认使用 containerd 的镜像子系统。

Lazy-pulling 惰性拉取

容器镜像中的大多数文件从未被使用:

“拉取包占容器启动时间的 76%,但其中只有 6.4% 的数据被读取” 摘自“ Slacker:使用 Lazy Docker 容器进行快速分发”(Harter 等人,FAST 2016)

“惰性拉取”是一种通过按需拉取部分镜像内容来减少容器启动时间的技术。对于 OCI 标准 tar.gz 镜像 来说这是不可能的,因为它们不支持 seek() 操作。人们提出了几种替代格式来支持惰性拉取:

  • eStargz (2019) :优化 seek() 能力的 gzip 粒度;向前兼容 OCI v1 tar.gz。
  • SOCI (2022):捕获 tar.gz 解码器状态的检查点;向前兼容 OCI v1 tar.gz。
  • Nydus (2022):另一种图像格式;与 OCI v1 tar.gz 不兼容。
  • OverlayBD (2021):将块设备作为容器镜像;与 OCI v1 tar.gz 不兼容。

下图显示了 eStargz 的基准测试结果。惰性拉动(+额外优化)可以将容器启动时间减少到 1/9。

扩大 User namespace 的采用

尽管 Docker 自 v1.9(2015)以来一直支持用户命名空间,但在 Docker 和 Kubernetes 生态系统中仍然很少使用。

原因之一是 “chowning” 容器 rootfs 作为伪根的复杂性和开销。Linux 内核 v5.12 (2021) 添加了 “idmapped mounts” 以消除 chown 的必要性。计划在 runc v1.2 中支持这一点。

runc v1.2 发布后,用户命名空间预计将在 Docker 和 Kubernetes 中更加流行,而 Docker 和 Kubernetes 刚刚在 v1.25(2022)中添加了对用户命名空间的 初步支持。出于兼容性考虑,Kubernetes 不太可能默认启用用户命名空间。然而,Docker 将来 仍有可能默认启用用户命名空间。不过,一切还没有决定。

Rootless 容器

Rootless 容器 是一种将容器运行时以及容器放置在由非 root 用户创建的用户命名空间中的技术,以减轻运行时的潜在漏洞。

即使容器运行时存在允许攻击者逃离容器的错误,攻击者也无法拥有对其他用户的文件、内核、固件和设备的特权访问权限。

以下是 rootless 容器的简史:

  • 2014LXC v1.0 引入了对 rootless 容器的支持。当时 rootless 容器被称为“非特权容器”。LXC 的非特权容器与现代 rootless 容器略有不同,因为它们需要 SETUID 二进制文件启动网络
  • 2017:runc v1.0-rc4 获得对 rootless容器的初步支持。
  • 2018:一些工具已经开始支持,containerdBuildKitdocker build的后端)、DockerPodmanslirp4netns 被我自己创建,以通过转换以太网来允许 SETUID-less 网络数据包发送至非特权套接字系统调用。
  • 2019:Docker v19.03 发布,对 rootless 容器提供实验性支持。Podman v1.1 也在今年发布,具有相同的功能,略领先于 Docker v19.03。
  • 2020:Docker v20.10 发布,rootless 容器全面可用。

从 2020 年到 2022 年,我们还致力于 bypass4netns,通过在容器内挂钩套接字文件描述符并在容器外重建它们来消除 slirp4netns 的开销。所实现的吞吐量甚至比 “rootful” 容器更快。

Rootless 容器已经成功普及,但也有人对 rootless 容器提出批评。特别是,是否应该允许非root用户创建运行无根容器所需的用户命名空间是有争议的。对于容器用户,我的回答是“是”,因为无根容器至少比以根身份运行所有内容要安全得多。但是,对于不使用容器的人,我宁愿回答“否”,因为用户命名空间也可能是攻击面。例如,CVE-2023–32233 漏洞:“Privilege escalation in Linux Kernel due to a Netfilter nf_tables vulnerability.”

社区已经在寻求解决这一困境的方法。Ubuntu(自 13.10 起)和 Debian 提供了一个 sysctl 设置 kernel.unprivileged_userns_clone=<bool> 来指定是否允许或禁止创建非特权用户命名空间。然而,他们的补丁并没有合并到上游 Linux 内核中。

相反,上游内核在 Linux v6.1 (2022) 中引入了新的 LSM(Linux 安全模块)钩子 userns_create ,以便 LSM 可以动态决定是否允许或禁止创建用户命名空间。该钩子可从 eBPF (bpf_program__atttach_lsm()) 调用,因此预计将有一个不依赖于 AppArmor 或 SELinux 的细粒度且非特定于发行版的旋钮。然而,eBPF + LSM 的用户空间实用程序尚未成熟,无法为此提供良好的用户体验。

更多 LSM

Landlock LSM 已合并到 Linux v5.13 (2021) 中。Landlock 与 AppArmor 类似,它通过路径(LANDLOCK_ACCESS_FS_EXECUTELANDLOCK_ACCESS_FS_READ_FILE 等)限制文件访问,但 Landlock 不需要 root 权限来设置新配置文件。Landlock 也与 OpenBSD 的 promise(2) 非常相似。

Landlock 仍然 不受 OCI Runtime Spec 支持,但我猜它可以包含在 OCI Runtime Spec v1.2 中。

Kata Containers

正如我在第一部分中提到的,“容器”并不是一个定义明确的术语。任何东西只要能与现有的容器生态系统提供良好的兼容性,就可以称为“容器”。

Kata Containers (2017) 就是这样一种“容器”,实际上并不是狭义上的容器。Kata 容器实际上是虚拟机,但支持 OCI 运行时规范。Kata 容器比 runc 容器安全得多,但是它们在性能方面存在缺陷,并且在不支持嵌套虚拟化的典型非裸机 IaaS 实例上无法正常工作。

Kata Containers 作为一个 containerd 运行时插件,并接收与 runc 容器相同的镜像和运行时配置。它的用户体验与 runc 容器几乎没有区别。

gVisor

gVisor (2018) 是另一个奇特的容器运行时。gVisor 捕获系统调用并在 Linux 兼容的用户模式内核中执行它们以减轻攻击。gVisor 目前具有 三种 捕获系统调用的模式:

  • KVM 模式:很少使用,但是裸机主机的最佳选择
  • ptrace 模式:最常见的选项,但速度较慢
  • SIGSYS trap 模式(自 2023 年起):预计最终取代 ptrace 模式

gVisor 已用于 Google 的多个产品中,包括 Google Cloud Run。然而,Google Cloud Run 已于 2023 年从 gVisor 转向 microVM。这意味着 gVisor 的性能和兼容性问题对于他们的业务来说是不可忽视的。

WebAssembly

WebAssembly (WASM) 是一种独立于平台的字节代码格式,最初于 2015 年 为 Web 浏览器设计。WebAssembly 与 Java applet (1995) 有点相似,但它更注重可移植性和安全性。WebAssembly 的一个有趣的方面是它将代码地址空间与数据地址空间分开;没有像 JMP <immediate>JMP *<reg> 这样的指令。它仅支持 跳转到在编译时解析的标签。这种设计减少了任意代码执行错误,尽管它也牺牲了 JIT 将其他字节代码格式编译为 WebAssembly 的可行性。

WebAssembly 作为容器的潜在替代品也受到关注。为了在浏览器之外运行 WebAssembly,WASI(WebAssembly 系统接口)于 2019 年提出,提供低级 API(例如 fd_read()fd_write()sock_recv()sock_send())可用于在其上实现类似 POSIX 的层。containerd 在 2022 年添加了 runWASI 插件,将 WASI 工作负载视为容器。

2023年,WASIX 被提议扩展 WASI 以提供更方便(也有些争议)的功能:

  • 线程thread_spawn(), thread_join()`, ...
  • 进程:proc_fork(), proc_exec(), ...
  • 套接字sock_listen(), sock_connect(), ...

最终,这些技术可能会取代很大一部分(但不是 100%)的容器。Docker 的创始人 Solomon Hykes 表示:“如果 WASM+WASI 在 2008 年就存在,我们就不需要创建 Docker 了 ”。

总结

  • • 容器比虚拟机更高效,但安全性往往也更低。人们正在引入许多安全技术来强化容器。(用户命名空间、无根容器、Linux 安全模块……)
  • • Docker 的替代品不断涌现(containerd、CRI-O、Podman、nerdctl、Finch 等),但 Docker 并没有消失。
  • • “Non-container” 容器也是趋势。(Kata:基于 VM,gVisor:用户模式内核,runWASI:WebAssembly,...)

下图显示了著名的运行时的概况。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
1月前
|
Oracle 关系型数据库 数据库
|
3月前
|
Docker 容器
Docker学习笔记三:如何运行一个容器?
Docker学习笔记三:如何运行一个容器?
Docker学习笔记三:如何运行一个容器?
|
5月前
|
Kubernetes Java 容器
如何获取k8s容器里运行的jar包
如何获取k8s容器里运行的jar包
162 0
|
5月前
|
Linux Docker 容器
Docker容器运行Linux
Docker容器运行Linux
|
5月前
|
存储 传感器 物联网
如何在Docker中配置Mosquitto MQTT代理,以便在容器化环境中运行和管理MQTT通信
如何在Docker中配置Mosquitto MQTT代理,以便在容器化环境中运行和管理MQTT通信
269 0
如何在Docker中配置Mosquitto MQTT代理,以便在容器化环境中运行和管理MQTT通信
|
3月前
|
Linux 开发者 Docker
如何构建在 Docker 容器中运行命令?
【1月更文挑战第6天】
61 0
|
1月前
|
JavaScript Shell Docker
|
2月前
|
Kubernetes 关系型数据库 数据库
运行在容器中 Postgres 数据库数据损坏后如何恢复?
运行在容器中 Postgres 数据库数据损坏后如何恢复?
|
3月前
|
文件存储 Docker Python
记录一次 nas docker 运行出错 使用Python脚本检查错误并重启对应容器 npc 运行出错 导致无法连接
记录一次 nas docker 运行出错 使用Python脚本检查错误并重启对应容器 npc 运行出错 导致无法连接
30 1
|
5月前
|
Cloud Native 数据库 Docker
Docker是一个流行的容器化平台,用于构建、部署和运行应用程序。
Docker容器已经成为现代应用程序开发和部署的核心技术之一。它们的轻量性、可移植性和可伸缩性使其成为各种不同领域的理想选择,从Web应用程序到微服务架构,再到云原生应用程序。本文将探讨Docker容器在各个领域中的实际应用,以及它们为开发人员和组织带来的好处。

热门文章

最新文章