认识Docker底层原理:Linux内核的Namespace、Cgroup和UnionFS
2024-05-27 14:55 阅读(356)

The underlying technology

Docker is written in the Go programming language and takes advantage of several features of the Linux kernel to deliver its functionality. Docker uses a technology called namespaces to provide the isolated workspace called the container. When you run a container, Docker creates a set of namespaces for that container.


Docker使用一种名为命名空间的技术来提供被称为容器的隔离工作空间。当您运行一个容器时,Docker会为该容器创建一组命名空间。


These namespaces provide a layer of isolation. Each aspect of a container runs in a separate namespace and its access is limited to that namespace.


这些命名空间提供了一层隔离。容器的每个方面都在一个独立的命名空间中运行,其访问权限仅限于该命名空间。


Docker容器技术的核心架构建立在Linux内核提供的三大关键技术之上:Namespace、Cgroup和UnionFS。这些技术共同确保了容器在隔离性、资源管理和文件系统效率方面的高效运作。Namespace通过创建隔离的环境,为容器提供了独立的网络、文件系统、用户空间和进程空间。Cgroup则通过资源限制和监控,确保了对容器资源使用的精细控制。而UnionFS作为一种创新的文件系统技术,通过层叠文件系统结构,不仅提高了存储效率,还实现了镜像的快速分发和容器的只读性与写入隔离。


Namespace

Linux Namespaces是Linux内核提供的一种资源隔离方案。Namespaces之间的资源相互独立。通过使用Namespace,进程可以拥有独立的资源副本,如网络、文件系统等,从而实现不同进程间的资源隔离。目前Linux中提供七种namespace。

func init() {
     namespaceList = Namespaces{
          {Key: "NEWNS", Value: syscall.CLONE_NEWNS, File: "mnt"},
          {Key: "NEWUTS", Value: syscall.CLONE_NEWUTS, File: "uts"},
          {Key: "NEWIPC", Value: syscall.CLONE_NEWIPC, File: "ipc"},
          {Key: "NEWUSER", Value: syscall.CLONE_NEWUSER, File: "user"},
          {Key: "NEWPID", Value: syscall.CLONE_NEWPID, File: "pid"},
          {Key: "NEWNET", Value: syscall.CLONE_NEWNET, File: "net"},
     }
}

在Linux操作系统中,创建新进程通常涉及fork等系统调用。fork系统调用负责创建子进程,它会复制父进程的task_struct结构体到子进程。在调用fork时,可以传递多种与命名空间(namespace)相关的参数,这些参数通过标志位(flags)来设置。随后,Linux使用exec系列函数来启动子进程中的新程序。


对于容器技术,由于容器本质上是基于进程的,Docker守护进程(Docker Daemon)在创建容器时,会传递特定的命名空间相关参数。这些参数作为标志位,用于在创建过程中配置命名空间。这样,当容器内的进程开始运行时,它们就会受到命名空间隔离的影响,确保容器拥有独立的系统资源视图和操作环境。


Cgroup

Cgroups(控制组)是 Linux 内核提供的一种资源管理框架,它允许系统管理员限制、监控和隔离一组进程使用的资源。通过 Cgroups,可以有效地控制进程组对 CPU、内存、磁盘 I/O 等资源的访问,确保容器化应用不会消耗过多主机资源。此外,Cgroups 还能够记录进程组的资源使用情况,为资源规划和性能监控提供数据支持。


资源限制:Cgroups 可以限制进程组可以使用的资源量,如 CPU 时间、内存、磁盘 I/O 等。这有助于防止单个进程或进程组消耗过多资源,从而影响系统上其他进程或应用的性能。

资源分配:Cgroups 允许管理员为不同的进程组分配资源,确保关键任务获得足够的资源,同时限制非关键任务的资源使用。

资源监控:Cgroups 提供了监控工具,可以跟踪和记录进程组的资源使用情况,帮助系统管理员了解系统资源的使用模式和性能瓶颈。

进程隔离:通过将进程分配到不同的 Cgroups,可以实现进程间的隔离,使得一个进程组中的进程无法影响其他进程组。

层次化管理:Cgroups 支持层次化结构,允许将进程组织成树状结构,方便对具有相似特性的进程进行统一管理。

系统稳定性:通过限制进程资源使用,Cgroups 有助于提高系统的稳定性和可靠性,防止因资源耗尽导致的系统崩溃。

cgroups已经被自动挂载到/sys/fs/cgroup目录下。

docker run -it --name d1 -c 512 centos

cat /sys/fs/cgroup/cpu/cpu.shares

启动一个容器限制内存为300M,交换空间为400M

 cat  /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes  //查看确认,单位为bytes

docker会为该容器在每个子系统目录下创建docker/$container_id目录。这样cgroups就能对该容器的资源进行管理和限制了。


容器镜像提供了容器运行所需的全部依赖文件的“静态快照”,包括应用程序及其依赖环境。尽管镜像是不可变的,但容器运行时可以对其文件进行修改。这种灵活性得益于 UnionFS 技术,它将容器的文件系统与镜像文件系统联合挂载。在容器内部,镜像文件表现为只读(类似于 CD-ROM),而所有的修改都被隔离在容器自己的写入层中,保持镜像本身的不变性。


镜像本身由多个只读层构成,这些层叠加在一起形成了完整的文件系统。每当你基于镜像启动新容器时,系统会在这些只读层之上添加一个新的可写层。容器中的任何文件更改或新增操作都发生在这一层,从而确保了镜像的原始状态得以保留。


UnionFS(联合文件系统)

UnionFS 是一种文件系统服务,允许容器共享宿主机的文件系统,同时可以有自己独立的文件系统层:


联合挂载:UnionFS 可以将多个目录的内容叠加在一起,形成一个统一的文件系统视图。在这个视图中,上层目录的内容会覆盖下层目录中的同名文件或目录。联合视图:UnionFS 通过联合挂载(union mount)的方式,将所有分支堆叠起来,形成一个单一的文件系统视图。优先级:在联合视图中,位于上层的分支会覆盖下层分支中的同名文件或目录。也就是说,如果多个分支中存在同名文件,只有最上层分支中的文件可见。

只读与可写层:通常,基础层被设置为只读,以保持镜像的不变性。当需要写入文件时,更改会被写入一个额外的可写层。多个分支:在 UnionFS 中,数据被组织成多个分支(branch),每个分支可以代表文件系统中的一个层(layer)。只读分支:通常,除了最顶层分支外,其他分支都是只读的。这意味着底层分支中的数据不可更改,从而保持了文件系统的一致性和不可变性。

容器化技术中的应用

Docker 镜像:在 Docker 中,镜像由多个只读层组成,每一层代表 Dockerfile 中的一个指令。当容器启动时,一个新的可写层(通常称为容器层)被添加到顶部。

写时复制:当容器需要修改文件时,UnionFS 执行写时复制操作。这意味着只有在文件被修改时,它才会被复制到容器的可写层,保持了存储效率。

UnionFS 为容器提供了文件系统级别的隔离,确保容器内的更改不会影响宿主机或其他容器