“容器”这一术语被严重过度使用。实际上,根据具体情况,它可能对不同的人意味着不同的东西。

传统的Linux容器实际上只是Linux系统上的普通进程。这些进程组使用资源约束(控制组[cgroups])、Linux安全约束(Unix权限、功能、SELinux、AppArmor、seccomp等)和命名空间(PID、网络、装载等)与其他进程组隔离。

如果你启动一个现代Linux系统并用cat / proc / PID / cgroup查看任何进程,你会看到该进程位于cgroup中。如果查看/ proc / PID / status,会看到功能。如果查看/ proc / self / attr / current,会看到SELinux标签。如果查看/ proc / PID / ns,会看到进程所在的命名空间列表。因此,如果你将容器定义为具有资源约束、Linux安全约束和命名空间的进程,那么根据定义,Linux上的每个进程都在一个容器中。这就是为什么我们经常说Linux是容器,容器是Linux。容器运行时是修改这些资源约束、安全性和命名空间并启动容器的工具。

Docker引入了容器镜像的概念。这是一个标准的TAR文件,它结合了:

——Rootfs(容器根文件系统):系统上看起来像操作系统的标准根(/)的目录。例如,带有/ usr,/ var,/ home等的目录。

—— JSON文件(容器配置):指定如何运行rootfs。例如,容器启动时在rootfs中运行的命令或入口点,为容器设置的环境变量,容器的工作目录,和一些其他设置。

Docker“tar up”rootfs和JSON文件来创建基本镜像。这使你可以在rootfs上安装其他内容,创建新的JSON文件,并使用更新的JSON文件来tar原始镜像与新镜像之间的差异。这会创建一个分层图像。

容器镜像的定义最终由Open Container Initiative(OCI)标准体标准化为OCI Image Specification。

用于创建容器镜像的工具称为容器镜像构建器。有时容器引擎执行此任务,也可以使用一些独立工具来构建容器镜像。

Docker采用了这些容器镜像(tarball)并将它们移动到一个Web服务(从中可以提取它们),开发了一个协议来提取它们,并将该Web服务称为容器注册表。

容器引擎是可以从容器注册表中提取容器镜像并将它们重新组装到容器存储上的程序。容器引擎也启动容器运行时。

容器存储通常是写时复制(COW)分层文件系统。从容器注册表中提取容器镜像时,首先需要解压rootfs并将其放在磁盘上。如果你有多个构成镜像的层,则会下载每个层并将其存储在COW文件系统的不同层上。COW文件系统允许每个层单独存储,这最大化了分层镜像的共享。容器引擎通常支持多种类型的容器存储,包括overlay、devicemapper、btrfs、aufs和zfs。

在容器引擎将容器镜像下载到容器存储之后,它需要创建容器运行时配置。运行时配置结合来自调用者/用户的输入以及容器镜像规范的内容。例如,调用者可能希望指定对正在运行的容器的安全性的修改,添加其他环境变量或将卷装入容器。

OCI标准组织还将容器运行时配置和rootfs布局标准化为OCI Runtime Specification。

最后,容器引擎启动一个读取容器运行时规范的容器运行时,修改Linux cgroups、Linux安全性约束和命名空间,并启动容器命令以创建容器的PID 1。此时,容器引擎可以将stdin / stdout中继回调用者并控制容器(如停止、启动、挂载)。

请注意,正在引入许多新的容器运行时以使用Linux的不同部分来隔离容器。人们现在可以使用KVM分离(比如迷你虚拟机)运行容器,或者可以使用其他虚拟机管理程序策略(例如拦截来自容器中进程的所有系统调用)。由于我们有一个标准的运行时规范,这些工具都可以由相同的容器引擎启动。甚至Windows也可以使用OCI Runtime Specification来启动Windows容器。

更高层次的是容器编排器。容器编排器是用于协调多个不同节点上的容器执行的工具。容器编排器与容器引擎通信以管理容器。编排器告诉容器引擎启动容器并将它们的网络连接在一起。编排器可以监控容器并在负载增加时启动其他容器。