容器是一种流行的虚拟化技术,它允许我们在同一台计算机上与其他进程在独立环境中运行进程。那么容器是如何做到这一点的呢?为此,容器是从 Linux 内核的一些新功能构建的,其中两个主要功能是“namespace”和“cgroup”。
Namespace(命名空间)技术是一种内核级别的特性,它允许将全局系统资源隔离成独立的视图,使得在不同 Namespace 中运行的进程看到的资源是不同的。这为容器化技术提供了基础,使得多个进程或容器可以在同一台主机上独立运行而不会相互干扰。
容器中的命名空间(Namespace)是一种用于隔离和分割不同容器之间和容器与主机操作系统之间资源的技术。命名空间是Linux内核提供的一种特性,容器技术(如Docker和Kubernetes)利用这些命名空间来实现容器的隔离和资源管理。每个命名空间都提供了一种不同类型的隔离,允许容器内的进程和资源在一个虚拟化的环境中运行,以便它们似乎独立于主机和其他容器。
对应到容器技术,为了隔离不同类型的资源,Linux 内核里面实现了以下几种不同类型的 namespace。
通过这些命名空间,容器技术可以提供隔离、安全性和资源管理,使多个容器可以在同一主机上运行而不会相互干扰。容器编排系统(如Kubernetes)可以有效地管理多个容器,并使用这些命名空间来确保它们之间的隔离和资源控制。这有助于实现更高效和可伸缩的应用程序部署和管理。
为了开始我们的探索,重要的是要注意,在 Linux 中,内核是程序和它们需要访问的资源之间的唯一抽象层。
每个进程进行系统调用:
由于容器是进程,它们也会进行系统调用:
内核为安全性、硬件和内部数据结构提供了抽象。例如,open系统调用通常用于获取文件处理程序并抽象文件操作。
如果我们用 ps 查看机器上的 nginx 进程,可以看到 master 和 worker,worker 的父进程是 master。
# ps -ef |grep nginx
root 58212 58195 0 01:43 ? 00:00:00 /bin/sh -c nginx -g "daemon off;"
root 58244 58212 0 01:43 ? 00:00:00 nginx: master process nginx -g daemon off;
33 58250 58244 0 01:43 ? 00:00:00 nginx: worker process
33 58251 58244 0 01:43 ? 00:00:05 nginx: worker process
33 58252 58244 0 01:43 ? 00:00:05 nginx: worker process
33 58253 58244 0 01:43 ? 00:00:05 nginx: worker process
在 /proc/pid/ns 里面,我们能够看到这个进程所属于的 6 种 namespace。我们拿出两个进程来,应该可以看出来,它们属于同一个 namespace。
# ls -l /proc/58212/ns
lrwxrwxrwx 1 root root 0 Jul 16 19:19 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Jul 16 19:19 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Jul 16 01:43 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Jul 16 19:19 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Jul 16 19:19 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jul 16 19:19 uts -> uts:[4026532277]# ls -l /proc/58253/ns
lrwxrwxrwx 1 33 tape 0 Jul 16 19:20 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 33 tape 0 Jul 16 19:20 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 33 tape 0 Jul 16 19:20 net -> net:[4026532281]
lrwxrwxrwx 1 33 tape 0 Jul 16 19:20 pid -> pid:[4026532279]
lrwxrwxrwx 1 33 tape 0 Jul 16 19:20 user -> user:[4026531837]
lrwxrwxrwx 1 33 tape 0 Jul 16 19:20 uts -> uts:[4026532277]
在容器技术中,控制组(Control Group,通常简称为cgroup)是一种用于资源限制、管理和隔离的 Linux 内核功能。Cgroup 允许你对容器内的资源进行精细的控制,确保容器在主机上共享的资源(如CPU、内存、磁盘I/O、网络带宽等)得到合理的分配和隔离。
首先,cgroup 定义了下面的一系列子系统,每个子系统用于控制某一类资源。
这些子系统允许你在容器内或进程组内对特定类型的资源进行精细的控制和限制。在容器编排系统(如Kubernetes)或容器管理工具(如Docker)中,这些子系统通常会被自动配置和管理,以确保容器能够在共享主机上安全、高效地运行。
第一步,系统初始化的时候,初始化 cgroup 的各个子系统的操作函数,分配各个子系统的数据结构。
第二步,mount cgroup 文件系统,创建文件系统的树形结构,以及操作函数。
第三步,写入 cgroup 文件,设置 cpu 或者 memory 的相关参数,这个时候文件系统的操作函数会调用到 cgroup 子系统的操作函数,从而将参数设置到 cgroup 子系统的数据结构中。
第四步,写入 tasks 文件,将进程交给某个 cgroup 进行管理,因为 tasks 文件也是一个 cgroup 文件,统一会调用文件系统的操作函数进而调用 cgroup 子系统的操作函数,将 cgroup 子系统的数据结构和进程关联起来。
第五步,对于 CPU 来讲,会修改 scheduled entity,放入相应的队列里面去,从而下次调度的时候就起作用了。对于内存的 cgroup 设定,只有在申请内存的时候才起作用。
创建cgroup
以下命令创建一个名为 v1 cgroup(您可以通过路径名格式判断)foo并将其内存限制设置为 50,000,000字节 (50 MB)。
root # mkdir -p /sys/fs/cgroup/memory/foo
root # echo 50000000 > /sys/fs/cgroup/memory/foo/memory.limit_in_bytes
现在我可以将一个进程分配给 cgroup,从而对其施加 cgroup 的内存限制。我编写了一个名为 的 shell 脚本test.sh,它打印cgroup testing tool到屏幕上,然后等待什么都不做。就我而言,这是一个持续运行的过程,直到我停止它为止。
我test.sh在后台启动,其 PID 报告为 2428。脚本生成其输出,然后通过将其 PID 通过管道传输到 cgroup 文件/sys/fs/cgroup/memory/foo/cgroup.procs ,将进程分配给 cgroup 。
root # ./test.sh &
[1] 2428
root # cgroup testing tool
root # echo 2428 > /sys/fs/cgroup/memory/foo/cgroup.procs
为了验证我的进程实际上受到我为 cgroup 定义的内存限制foo,我运行以下ps命令。该-o cgroup标志显示指定进程 (2428) 所属的 cgroup。输出确认其内存 cgroup 是foo。
为了验证我的进程实际上受到我为 cgroup 定义的内存限制foo,我运行以下ps命令。该-o cgroup标志显示指定进程 (2428) 所属的 cgroup。输出确认其内存 cgroup 是foo。我们在 /sys/fs/cgroup/ 下面能看到下面的目录结构。
drwxr-xr-x 5 root root 0 May 30 17:00 blkio
lrwxrwxrwx 1 root root 11 May 30 17:00 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 May 30 17:00 cpuacct -> cpu,cpuacct
drwxr-xr-x 5 root root 0 May 30 17:00 cpu,cpuacct
drwxr-xr-x 3 root root 0 May 30 17:00 cpuset
drwxr-xr-x 5 root root 0 May 30 17:00 devices
drwxr-xr-x 3 root root 0 May 30 17:00 freezer
drwxr-xr-x 3 root root 0 May 30 17:00 hugetlb
drwxr-xr-x 5 root root 0 May 30 17:00 memory
lrwxrwxrwx 1 root root 16 May 30 17:00 net_cls -> net_cls,net_prio
drwxr-xr-x 3 root root 0 May 30 17:00 net_cls,net_prio
lrwxrwxrwx 1 root root 16 May 30 17:00 net_prio -> net_cls,net_prio
drwxr-xr-x 3 root root 0 May 30 17:00 perf_event
drwxr-xr-x 5 root root 0 May 30 17:00 pids
drwxr-xr-x 5 root root 0 May 30 17:00 systemd
Deep into Container — Linux Namespaces and Cgroups: What are containers made from? | by Quân Huỳnh | FAUN — Developer Community