深入了解容器、其内部结构和相关工具
原文链接:equipintelligence.medium.com/containers-...

我是在 2018 年学习并开始使用 Docker 的,当时是为了在我那台老式非AVX 电脑上使用 TensorFlow。在我们大学课业学习云虚拟化之前,这感觉就像是在 Windows 上运行 Linux 实用程序的好方法。看到VMWare ESXi、VirtualBox和Docker 等工具如何使用不同的虚拟化策略令人着迷。其中,Docker 容器的工作方式更让我着迷,想要深入了解其内部结构。这篇博客以自下而上的方式汇集了我的所有心得,即从实现容器执行的最底层组件开始。在探索不同组件的过程中,我们可以把它想象成对运行docker run busybox
时发生的事情的深入分析。
虚拟化
容器与虚拟机。来源:Docker.com

- 操作系统(OS)虚拟化,也称为容器化,是一种轻量级的虚拟化形式,它允许多个孤立的用户空间实例(称为容器)在单个操作系统内核上运行。
- 传统的虚拟机(VM)会模拟整个硬件堆栈,每个实例都需要单独的客户操作系统,而容器则不同,它们共享主机操作系统内核。
- 这一本质区别使容器的效率、轻量级和部署速度大大提高。
- 容器利用主机操作系统的特性,如用于进程、网络和文件系统隔离的命名空间,以及用于资源分配的 cgroups(控制组),来提供隔离的环境。
从本质上讲,容器是操作系统级虚拟化的直接实现,它封装了应用程序及其所有依赖关系,以确保在不同环境中的一致执行,同时最大限度地减少开销。
容器和映像
本博客假定读者对容器和映像有一定的了解。
镜像就像一本菜谱--它是包含所有说明和配料的静态蓝图。你不能吃这本书本身。
而容器 则是你根据食谱烘焙出来的蛋糕。它是根据蓝图制作的可食用的有形实例。你可以用同一个食谱烤出很多蛋糕,每个蛋糕都是一个独立的容器。
映像作为一个自足的、不可变的、可执行的软件包,包含应用程序及其所有依赖项,如源代码、运行时、系统工具、库和配置。它是容器实例化的基础蓝图。
容器代表一个实时、隔离和可移植的运行时环境,由映像衍生而来,提供专用的文件系统、网络接口和进程空间。这一基本区别可在不同的计算环境中实现一致、可靠的应用部署。
Docker 映像与容器 - 应用程序部署技术之间的区别 - AWS
Docker 映像和容器之间有什么区别?如何在 AWS 中使用 Docker 映像和容器。
我们从 cgroups 开始讨论,它是 Linux 内核中的一项功能,可通过进程/资源隔离实现容器。
cgroup 和命名空间
cgroup
是一种分层结构,其中/sys/fs/cgroup
为根,分支为内存、磁盘 I/O 和 CPU 等资源控制器。这些控制器管理不同进程组的资源分配,将特定进程 ID(PID)与定义的资源限制相关联。来源:Red Hat Developers红帽开发人员

- cgroups(控制组)是 Linux 内核的一项功能,它可以为一组进程(如内存、I/O 控制器、用户 ID、进程树等)绑定/分配一定量的系统资源。
- 命名空间将全局系统资源抽象化,使命名空间内的进程看起来拥有自己的全局资源独立实例。
- 有了 cgroups 和命名空间,我们就可以将一组进程与系统的其他进程实际隔离开来。当我们继续前进时,就会发现 cgroups 和命名空间是在底层主机(即操作系统)上执行容器的手段。
如果运行容器需要 Linux 内核的功能,那么 Docker 或其他容器引擎又是如何在 Windows 和 macOS 上运行的呢?
对于 Windows 和 macOS,容器引擎不是直接在主机操作系统上运行,而是在从主机操作系统租用的虚拟机中执行。这种虚拟 Linux 环境由 Windows 上的WSL2(Windows Subsystem for Linux)和 macOS 上的LinuxKit提供。
OCI 规范
有了 cgroups 和命名空间,我们现在就有办法在 Linux 环境中执行程序,并将其限制在一定范围内的进程和计算资源集合中。在 Linux 和 macOS 中,可执行代码分别以 ELF 和 MachO 格式进行编码,而描述容器执行内容和支持执行所需的资源的规范或标准化布局的存在则至关重要。
OCI(开放容器倡议)图像规范描述了容器图像应如何以标准化布局组成。能够理解 OCI 容器映像的容器引擎可以读取映像并在容器中执行。该规范实现了容器图像格式的标准化,增强了引擎之间的互操作性。
除了镜像规范,OCI 还为容器运行时和分发策略提供了标准化程序。
现在,我们有了一种与系统其他部分隔离的程序运行方法(cgroups)和一种提供执行细节的格式。接下来,我们将探讨介于 cgroups 和 OCI 容器映像格式之间的一个组件。
runC 和 containerd
runC是一个读取 OCI 容器映像的引擎,它使用 cgroups 和命名空间来创建一个虚拟隔离环境,用于执行容器映像中的代码。执行资源(即网络、内存和文件系统)是利用从 OCI 映像中解析的详细信息配置的。此外,runC 实现本身也来自 OCI,并遵循 OCI 制定的运行时规范。
runC 需要一个 JSON 配置文件来定义容器的执行方式。该文件包括挂载、网络、I/O 设备、内存、CPU 等设置。
Docker 引擎、containerd 和 runc 之间的关系。来源:StackOverflowStackOverflow

containerd是一种容器运行时,可帮助在 Linux 和 Windows 上原生创建/执行容器。在 2017 年 3 月之前,它一直作为 Docker 引擎中的一个集成组件进行开发,之后被提取为一个单独的项目,捐赠给了云原生计算基金会(CNCF)。
它公开了一个gRPCAPI,由 Docker 引擎、Kubernetes Kubelet 或 Podman 用来管理容器。它从调用者那里抽象出了复杂的内核级细节,提供了一个通用的容器运行时 API。名称末尾的 "d "表示 containerd 在主机上作为守护进程执行。
它使用 Linux 上的runc和 Windows 上的hcsshim来执行容器。
映像注册表
映像注册中心是用于向容器运行时分发和版本映像的存储库。它们类似于几乎所有编程语言都有的软件包存储库,如 Java 的Maven Central、Rust 的crates.io、Python 的PyPI。
Docker Hub 是一个流行的镜像注册库。主要的云服务提供商也维护自己的容器镜像注册表,如Google Container Registry、Azure ContainerRegistry 和Amazon Elastic ContainerRegistry。
映像索引包含标签、版本和构建映像的 CPU 架构。
Docker
Docker 的客户端-服务器架构。来源:《什么是 Docker?什么是 Docker?

Docker 是一种将容器、卷和网络管理整合到统一 API 中的工具。
- Docker 引擎负责根据
Dockerfile
构建 OCI 容器映像。它还可以从配置的映像注册表中提取基础映像层。 - 命令行实用程序
docker
是与 Docker 守护进程(服务器)交互的 CLI 客户端,可用于管理映像、容器、卷和网络。
大多数应用程序由多个相互连接的组件组成,例如连接到数据库、缓存或反向代理的网络服务。每个组件都在自己的容器中执行。Docker Compose 是建立在 Docker 基础上的一种流行工具,用于管理此类应用程序。
Podman
来源:Redhat DevelopersRedhat 开发人员

- Podman 是一种无守护进程的容器引擎,与 Docker 等其他容器平台不同的是,它的运行不需要持续运行的后台服务。
- Podman 不需要一个中央守护进程来管理所有容器操作,而是直接与 Linux 内核的 cgroups 和命名空间等功能交互,以创建和管理容器。
- 这种设计通过支持 "无根 "容器来增强安全性,允许用户在没有提升权限的情况下运行容器,从而减少了攻击面。
Docker Compose
Docker Compose 是一种使用简单的 YAML 配置文件轻松管理多容器应用程序的工具。该文件允许开发人员声明容器并配置挂载卷、端口、网络和健康检查策略。
以一个与数据库交互的简单网络服务为例,如果没有 Docker Compose,开发人员就必须使用docker run
来启动网络服务和数据库容器,并确保它们连接到同一个网络。有了 Docker Compose,docker compose up
这一条命令就能执行两个容器,并根据需要对它们进行配置。
yaml
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80"
depends_on:
- redis
networks:
- my-app-network
volumes:
- ./nginx-conf:/etc/nginx/conf.d:ro
redis:
image: redis:6-alpine
ports:
- "6379:6379"
networks:
- my-app-network
networks:
my-app-network:
driver: bridge
Docker Compose 可以对容器执行与传入流量或其他动态因素无关的基本操作。再进一步,我们来探索一种能跨多台主机智能管理容器的工具。
Kubernetes
Kubernetes (K8s)是一个开源平台,用于自动化部署、扩展和管理容器化应用程序。Docker Compose 用于单个主机上的多容器应用,而 Kubernetes 则用于跨多个主机协调容器。
K8S 的核心用例之一是管理微服务--确保高可用性并简化大规模操作。其主要功能包括自动推出和回滚、自愈功能(重启故障容器、在健康节点上重新调度)、基于需求的水平自动扩展、服务发现和负载平衡以实现无缝通信,以及强大的存储协调。
这使得 Kubernetes 成为企业在从内部部署到多云等各种环境中高效构建、部署和管理云原生应用程序的重要工具。
AWS 等云服务提供商提供AWS EKS等托管 K8S 解决方案,以及AWS ECS 等自己的 "原生 "容器解决方案。
最新发展
无分布式映像
无发行版 Azul JDK 17 映像的 Dockerfile。请注意,ubuntu:jammy 被用作安装 Java 特定组件的构建镜像。安装完成后,这些组件将从构建镜像中复制。如 "FROM scratch "所示,没有基础镜像。来源:zulu-openjdk/distroless

无发行版映像是轻量级的容器映像,只包含支持特定语言运行时所需的基本依赖项。例如,Python、NodeJS 和 Java 的无发行版镜像不包含软件包管理器、shell 或构建工具,只包含执行所需的运行时。
在查看无发行版 Azul JDK 17 镜像的 Dockerfile 时,我们很快就会发现它只复制了 Java 运行时(JVM)所需的基本组件,而没有继承自任何基础镜像。
在构建只需支持应用程序执行的超小型容器时,无发行版映像非常有用。容器可以快速删除或生成,允许开发人员根据目标吞吐量迅速改变部署规模。
苹果容器
最近在 WWDC 2025 上推出的Apple Containerization框架旨在优化容器在 Apple Silicon 设备上的执行。每个容器都在轻量级 Linux VM 中执行,支持网络端口、卷挂载和符合 OCI 标准的映像生成。
WASM 容器
WebAssembly (WASM) 是一种可移植的二进制格式,用于将本地应用程序(用 C++、C、Rust 等语言编写)移植到网络应用程序中。现代浏览器包含一个 WASM 运行时和一个 JS 运行时,用于执行.wasm
模块。
Docker Desktop 也可以配置为包含 WASM 运行时,如 WasmEdge、Wasmer 和 WasmTime。仅包含 WASM 运行时作为执行应用程序平台的特殊容器被称为 "WASM 容器"。
许多语言都支持编译到 WASM,从而使 WASM 容器可移植到不同的应用程序中。
结束语
我希望我们从 cgroups 到 Kubernetes 的讨论能引人入胜,并能让读者对容器的执行方式有更广泛的了解。
从实现隔离执行的 cgroups 和命名空间等基础 Linux 内核特性,到 OCI 提供的标准化,以及 Docker 和 Kubernetes 的实用协调能力,我们探索了使容器成为现代软件开发基石的错综复杂的层次。随着无发行版镜像、苹果容器化和 WASM 容器等创新技术的不断发展,这种轻量级、高效的虚拟化模式显然将继续推动应用部署的未来。