大家好!我是大聪明-PLUS!
首先是物理服务器 ------价格昂贵且效率低下。后来出现了虚拟机,使得在单个硬件上运行多个相互隔离的操作系统成为可能。但隔离的成本仍然很高:需要完整的操作系统副本、数GB的磁盘空间,以及几分钟的启动时间。
容器是下一个发展阶段。既然可以使用内核内置机制隔离 进程本身,为什么还要虚拟化整个硬件并运行完整的操作系统呢?这种方法要轻量级得多,速度更快,效率也更高。
图像和容器
-
镜像文件 是一种模板或蓝图。它是一组不可变的、只读的文件、指令和依赖项,用于定义你的应用程序。
-
容器 是镜像的运行实例。它提供了一个隔离的环境,应用程序就在这个环境中实际运行。当从镜像创建容器时,会添加一个轻量级的可写层。应用程序运行时发生的所有更改都会保存到该层中。
但这个盒子里面究竟藏着什么呢?让我们一探究竟。
第一部分:容器的惊天秘密......它们根本不存在
事实上,从Linux内核的角度来看, 并不存在所谓的"容器 "。存在的只是普通的Linux进程。只不过,这是一个在特殊条件下运行的进程。
这些条件是由两种内核机制产生的:
- 命名空间 。
命名空间限制了容器内部可见的资源和进程。本质上,它们为每组进程创造了一种错觉,即它们拥有全局资源的独立实例,无论是网络协议栈、进程树还是文件系统。
-
PID 命名空间: 容器内的进程认为自己是第一个也是主要的进程(PID 1),并且看不到容器外的进程。
-
网络命名空间: 容器拥有自己的虚拟网卡、路由表和 iptables。
-
挂载命名空间: 容器拥有自己的文件系统树,与主机系统隔离。
-
......以及其他(UTS、IPC、用户)。
- 控制组(Cgroups
)它们并不隔离,而是 限制和控制 资源。控制组回答"多少人?"这个问题。
-
限制 CPU、内存和磁盘 I/O 的使用。
-
他们不允许一个"贪婪"的容器拖垮整个系统。
结论: 容器是一个非常普通的进程(例如 nginx 或 bash),只是通过 命名空间 与其他进程隔离,并通过 cgroups设置限制。
第二部分:图像剖析
如果容器是一个进程,那么镜像就是运行该进程的指令。
容器镜像由两个主要部分组成:
-
根文件系统(rootfs) -- 一个目录树,其中包含运行应用程序所需的全部软件:二进制文件、库和设置。不多不少。
-
一个包含元数据(指令)的 JSON 文件 。它描述了要运行的进程(CMD)、要设置的环境变量、要打开的端口以及要挂载卷的位置。
它是如何存储和传输的? 很简单!
该工具 还会tar 将 rootfs JSON 文件打包成一个单独的归档文件。这种格式也是镜像存储在 Docker 镜像仓库(例如 Docker Hub)中的格式。每个镜像层也是 tar一个包含文件系统更改的归档文件。
以下是实际操作中的样子**------nginx镜像清单:**
`[
{
`"RepoTags"`: [`"nginx:latest"`],
`"Layers"`: [
`"blobs/sha256/eb5f13bce993..."`,
`"blobs/sha256/dab69e9f41e9..."`,
],
`"LayerSources"`: {
`"sha256:129b375526fc..."`: {
`"mediaType"`: `"application/vnd.oci.image.layer.v1.tar"`,
`"size"`: `5120`,
`"digest"`: `"sha256:129b375526fc..."`
}
}
}
]`
这样做时 docker pull,你只需下载并解压这些归档文件,Docker Engine就会从中构建一个现成的容器文件系统。
第三部分:如何协同工作:Docker架构
当你输入 docker run nginx 命令时,后台会启动一个由四个组件组成的庞大系统。
- Docker引擎
-
这是一个高级守护进程,您可以通过 CLI(docker run、docker build)与其通信。
-
它处理你的命令,管理网络和卷,但 它本身并不运行容器。它将这项任务委托给底层服务器。
- containerd
-
这是管理容器生命周期的主要运行时环境。
-
它负责下载图像、管理存储,并将启动指令传递给下一个链接。
- runc
-
正是这个工具可以访问 Linux 内核,从而将进程、文件系统、网络和其他容器资源与主机系统隔离。
-
在收到来自 containerd(config.json 和 rootfs)的指令后,runc 直接与 Linux 内核交互。
-
它创建新的命名空间,配置 cgroups,最后在这个隔离环境中启动我们的目标进程(例如 nginx)。
重要提示: runc 进程启动后,会认为其工作已完成并退出。
- containerd-shim
- 当容器进程的父进程(runc)终止时,该容器进程会发生什么?它由 containerd-shim 负责处理。
-
这是一个轻量级进程,它将成为容器进程的新父进程。
为什么有必要这样做?
-
Containerd-shim 允许您在不停止正在运行的容器的情况下重启或升级 containerd。
-
将标准输入/输出流(stdin、stdout、stderr)重定向回 Docker Engine。
-
监控流程状态并报告其完成情况。
-
⠀
结果
你的docker run命令 至关重要:
这样最终你的机器上就只会有一个独立的Linux进程。
现在你掌握了这个技巧,Docker 就变成了一个强大的工程系统。祝你容器化之旅一切顺利 :-)