2、Cgroup和NameSpace
CGroups与Namespaces
本节我们来一起了解下容器背后的两个核心技术:CGroups 和 Namespace。
CGroups概述
2007年,谷歌把这个cgroup给开发出来了,并且把这个技术给集成到了linux内核里面去了;
CGroups
全称为 Linux Control Group
,其作用是限制一组进程使用的资源(CPU、内存等)上限,CGroups
也是 Containerd 容器技术的核心实现原理之一(不仅仅是containerd,是目前基本all容器的核心技术),首先我们需要先了解几个 CGroups
的基本概念:
- Task: 在 cgroup 中,task 可以理解为一个进程,但这里的进程和一般意义上的操作系统进程不太一样,实际上是进程 ID 和线程 ID 列表(不是一个真正物理上的进程)。
- CGroup: 即控制组,一个控制组就是一组按照某种标准(cpu或者内存)划分的 Tasks,可以理解为资源限制是以进程组为单位实现的,一个进程加入到某个控制组后,就会受到相应配置的资源限制。
- Hierarchy: cgroup 的层级组织关系,cgroup 以树形层级组织,每个 cgroup 子节点默认继承其父 cgroup 节点的配置属性,这样每个 Hierarchy 在初始化会有 root cgroup。
- Subsystem: 即子系统,子系统表示具体的资源配置,如CPU使用,内存占用等,Subsystem附加到Hierarchy上后可用。
CGroups
支持的子系统包含以下几类,即为每种可以控制的资源定义了一个子系统:
cpuset
: 为 cgroup 中的进程分配单独的 CPU 节点,即可以绑定到特定的 CPUcpu
: 限制 cgroup 中进程的 CPU 使用份额cpuacct
: 统计 cgroup 中进程的 CPU 使用情况memory
: 限制 cgroup 中进程的内存使用,并能报告内存使用情况devices
: 控制 cgroup 中进程能访问哪些文件设备(设备文件的创建、读写)freezer
: 挂起或恢复 cgroup 中的 tasknet_cls
: 可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic contro)对数据包进行控制blkio
: 限制 cgroup 中进程的块设备 IOperf_event
: 监控 cgroup 中进程的 perf 时间,可用于性能调优hugetlb
: hugetlb 的资源控制功能pids
: 限制 cgroup 中可以创建的进程数net_prio
: 允许管理员动态的通过各种应用程序设置网络传输的优先级
通过上面的各个子系统,可以看出使用 CGroups
可以控制的资源有: CPU、内存、网络、IO、文件设备等。CGroups
具有以下几个特点:
- CGroups 的 API 以一个**伪文件系统(/sys/fs/cgroup/)**的实现方式,用户的程序可以通过文件系统实现 CGroups 的组件管理
- CGroups 的组件管理操作单元可以细粒度到线程级别,用户可以创建和销毁 CGroups,从而实现资源载分配和再利用
- 所有资源管理的功能都以子系统(cpu、cpuset 这些)的方式实现,接口统一子任务创建之初与其父任务处于同一个 CGroups 的控制组
我们可以通过查看 /proc/cgroups
文件来查找当前系统支持的 CGroups
子系统:
[root@containerd ~]#cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 5 4 1
cpu 6 58 1
cpuacct 6 58 1
memory 3 58 1
devices 9 58 1
freezer 2 4 1
net_cls 4 4 1
blkio 7 58 1
perf_event 11 4 1
hugetlb 10 4 1
pids 8 58 1
net_prio 4 4 1
在使用 CGroups
时需要先挂载,我们可以使用 df -h | grep cgroup
命令进行查看:
[root@containerd ~]#df -h|grep cgroup
tmpfs 910M 0 910M 0% /sys/fs/cgroup
[root@containerd ~]#
可以看到被挂载到了 /sys/fs/cgroup
,cgroup 其实是一种文件系统类型,所有的操作都是通过文件来完成的,我们可以使用 mount --type cgroup
命令查看当前系统挂载了哪些 cgroup:
[root@containerd ~]#mount --type cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
[root@containerd ~]#
/sys/fs/cgroup
目录下的每个子目录就对应着一个子系统,cgroup 是以目录形式组织的,/
是 cgroup 的根目录,但是这个根目录可以被挂载到任意目录,例如 CGroups 的 memory 子系统的挂载点是 /sys/fs/cgroup/memory
,那么 /sys/fs/cgroup/memory/
对应 memory 子系统的根目录,我们可以列出该目录下面的文件:
[root@containerd ~]#ll /sys/fs/cgroup/memory/
total 0
drwxr-xr-x 2 root root 0 Oct 25 16:21 buildkit
-rw-r--r-- 1 root root 0 Oct 23 13:52 cgroup.clone_children
--w--w--w- 1 root root 0 Oct 23 13:52 cgroup.event_control
-rw-r--r-- 1 root root 0 Oct 23 13:52 cgroup.procs
-r--r--r-- 1 root root 0 Oct 23 13:52 cgroup.sane_behavior
drwxr-xr-x 3 root root 0 Oct 25 16:25 default
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.failcnt
--w------- 1 root root 0 Oct 23 13:52 memory.force_empty
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.numa_stat
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.oom_control
---------- 1 root root 0 Oct 23 13:52 memory.pressure_level
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.stat
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.swappiness
-r--r--r-- 1 root root 0 Oct 23 13:52 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Oct 23 13:52 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Oct 23 13:52 notify_on_release
-rw-r--r-- 1 root root 0 Oct 23 13:52 release_agent
drwxr-xr-x 54 root root 0 Oct 25 16:25 system.slice
-rw-r--r-- 1 root root 0 Oct 23 13:52 tasks
drwxr-xr-x 2 root root 0 Oct 23 17:48 user.slice
[root@containerd ~]#
上面包含 kubepods.slice
、system.slice
、user.slice
等目录,这些目录下可能还会有子目录,相当于一颗有层级关系的树来进行组织:
/
├── kubepods.slice
├── system.slice
└── user.slice
例如我在节点上使用 systemd 管理了一个 Prometheus 的应用,我们可以使用 systemctl status prometheus
命令查看 Prometheus 进程所在的 cgroup 为 /system.slice/prometheus.service
: (也可以用sshd服务演示)
➜ ~ systemctl status prometheus
● prometheus.service - prometheus service
Loaded: loaded (/etc/systemd/system/prometheus.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2021-10-21 10:10:12 CST; 1h 40min ago
Docs: https://prometheus.io
Main PID: 1065 (prometheus)
Tasks: 10
Memory: 167.4M
CGroup: /system.slice/prometheus.service
└─1065 /root/p8strain/prometheus-2.30.2.linux-amd64/prometheus --config.file=/root/p8strain/prometheu...
上面显示的 CGroup 只是一个相对的路径,实际的文件系统目录是在对应的子系统下面,比如
/sys/fs/cgroup/cpu/system.slice/prometheus.service
、/sys/fs/cgroup/memory/system.slice/prometheus.service
目录:
这其实可以理解为 cpu 和 memory 子系统被附加到了 /system.slice/prometheus.service
这个 cgroup 上。
注意理解下下面这段话:
如果 linux 系统使用 systemd 初始化系统,初始化进程会生成一个 root cgroup,每个
systemd unit
都将会被分配一个cgroup,同样可以配置容器运行时如 containerd 选择使用 cgroupfs 或 systemd 作为 cgroup 驱动,containerd 默认使用的是 cgroupfs,但对于使用了 systemd 的 linux 发行版来说就同时存在两个 cgroup 管理器,对于该服务器上启动的容器使用的是cgroupfs,而对于其他 systemd 管理的进程使用的是 systemd,这样在服务器资源负载高的情况下可能会变的不稳定。因此对于使用了 systemd 的 linux 系统,推荐将容器运行时的 cgroup 驱动使用 systemd。
CGroup 测试
💘 实践:CGroup 测试(测试成功)-2022.5.17 |
---|
接下来我们来尝试手动设置下 cgroup,以 CPU 这个子系统为例进行说明,首先我们在 /sys/fs/cgroup/cpu
目录下面创建一个名为 ydzs.test
的目录:
[root@containerd ~]#mkdir -p /sys/fs/cgroup/cpu/ydzs.test
[root@containerd ~]#ls /sys/fs/cgroup/cpu/ydzs.test
cgroup.clone_children cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release
cgroup.event_control cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks
[root@containerd ~]#
我们可以看到目录创建完成后,下面就会已经自动创建 cgroup 的相关文件。
这里我们重点关注 cpu.cfs_period_us
和 cpu.cfs_quota_us
这两个文件,前面一个是用来配置 CPU 时间周期长度的,默认为 100000us
,后者用来设置在此时间周期长度内所能使用的 CPU 时间数,默认值为-1,表示不受时间限制。
[root@containerd ~]#cat /sys/fs/cgroup/cpu/ydzs.test/cpu.cfs_period_us
100000
[root@containerd ~]#cat /sys/fs/cgroup/cpu/ydzs.test/cpu.cfs_quota_us
-1
[root@containerd ~]#
现在我们写一个简单的 Python 脚本来消耗 CPU:
[root@containerd ~]#vim cgroup.py# cgroup.pywhile True: pass
直接执行这个死循环脚本即可:
[root@containerd ~]#python cgroup.py &[1] 32416
使用 top 命令可以看到进程号32416的 CPU 使用率达到了 100%
现在我们将这个进程ID写入到 /sys/fs/cgroup/cpu/ydzs.test/tasks
文件下面去,然后设置 /sys/fs/cgroup/cpu/ydzs.test/cpu.cfs_quota_us
为 10000us
,因为 cpu.cfs_period_us
默认值为 100000us
,所以这表示我们要限制 CPU 使用率为10%:
[root@containerd ~]#echo 32416 > /sys/fs/cgroup/cpu/ydzs.test/tasks[root@containerd ~]#echo 10000 > /sys/fs/cgroup/cpu/ydzs.test/cpu.cfs_quota_us
设置完过后上面我们的测试进程 CPU 就会被限制在 10% 左右了,再次使用 top 命令查看该进程可以验证。
记得最后杀死上面那个进程。
[root@containerd ~]#kill 32416
如果要限制内存等其他资源的话,同样去对应的子系统下面设置资源,并将进程 ID 加入 tasks 中即可。
如果要删除这个 cgroup,直接删除文件夹是不行的,需要使用 libcgroup
工具:
[root@containerd ~]#yum install libcgroup libcgroup-tools[root@containerd cpu]#cgdelete cpu:ydzs.test[root@containerd cpu]#ls /sys/fs/cgroup/cpu/ydzs.testls: cannot access /sys/fs/cgroup/cpu/ydzs.test: No such file or directory[root@containerd cpu]#
在容器中使用CGroups
💘 实践:在容器中使用CGroups(测试成功)-2022.5.17 |
---|
上面我们测试了一个普通应用如何配置 cgroup,接下来我们在 Containerd 的容器中来使用 cgroup,比如使用 nerdctl 启动一个 nginx 容器,并限制其使用内存为50M:
[root@containerd ~]#nerdctl run -d -m 50m --name nginx nginx:alpine3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef[root@containerd ~]#nerdctl psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES3543aa8676e8 docker.io/library/nginx:alpine "/docker-entrypoint.…" 5 seconds ago Up nginx[root@containerd ~]#
在使用 nerdctl run
启动容器的时候可以使用 -m
或 --memory
参数来限制内存,启动完成后该容器的 cgroup 会出现在 名为 default
的目录下面,比如查看内存子系统的目录: 例如:我们可以看下这个default目录 这个目录就是以后容器所做资源限制的地方:
[root@containerd ~]#ll /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef/total 0-rw-r--r-- 1 root root 0 Nov 3 11:08 cgroup.clone_children--w--w--w- 1 root root 0 Nov 3 11:08 cgroup.event_control-rw-r--r-- 1 root root 0 Nov 3 11:08 cgroup.procs-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.failcnt--w------- 1 root root 0 Nov 3 11:08 memory.force_empty-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.failcnt-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.limit_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.max_usage_in_bytes…………
上面我们启动的 nginx 容器 ID 的目录会出现在 /sys/fs/cgroup/memory/default/
下面,该文件夹下面有很多和内存相关的 cgroup 配置文件,要进行相关的配置就需要在该目录下对应的文件中去操作:
[root@containerd ~]#ll /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef/total 0-rw-r--r-- 1 root root 0 Nov 3 11:08 cgroup.clone_children--w--w--w- 1 root root 0 Nov 3 11:08 cgroup.event_control-rw-r--r-- 1 root root 0 Nov 3 11:08 cgroup.procs-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.failcnt--w------- 1 root root 0 Nov 3 11:08 memory.force_empty-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.failcnt-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.limit_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.max_usage_in_bytes-r--r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.slabinfo-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.tcp.failcnt-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.tcp.limit_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.tcp.max_usage_in_bytes-r--r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.tcp.usage_in_bytes-r--r--r-- 1 root root 0 Nov 3 11:08 memory.kmem.usage_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.limit_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.max_usage_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.memsw.failcnt-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.memsw.limit_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.memsw.max_usage_in_bytes-r--r--r-- 1 root root 0 Nov 3 11:08 memory.memsw.usage_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.move_charge_at_immigrate-r--r--r-- 1 root root 0 Nov 3 11:08 memory.numa_stat-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.oom_control---------- 1 root root 0 Nov 3 11:08 memory.pressure_level-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.soft_limit_in_bytes-r--r--r-- 1 root root 0 Nov 3 11:08 memory.stat-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.swappiness-r--r--r-- 1 root root 0 Nov 3 11:08 memory.usage_in_bytes-rw-r--r-- 1 root root 0 Nov 3 11:08 memory.use_hierarchy-rw-r--r-- 1 root root 0 Nov 3 11:08 notify_on_release-rw-r--r-- 1 root root 0 Nov 3 11:08 tasks[root@containerd ~]#
我们这里需要关心的是 memory.limit_in_bytes
文件,该文件就是用来设置内存大小的,正常应该是 50M 的内存限制:
[root@containerd ~]#cat /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef/memory.limit_in_bytes52428800[root@containerd ~]#
同样我们的 nginx 容器进程 ID 也会出现在上面的 tasks
文件中:
[root@containerd ~]#cat /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef/tasks271722726627267[root@containerd ~]#
我们可以通过如下命令过滤该进程号,可以看出第一行的 2686 就是 nginx 进程在主机上的进程 ID,下面几个是这个进程下的线程:
[root@containerd ~]#ps -ef|grep 27172root 27172 27145 0 11:08 ? 00:00:00 nginx: master process nginx -g daemon off;101 27266 27172 0 11:08 ? 00:00:00 nginx: worker process101 27267 27172 0 11:08 ? 00:00:00 nginx: worker processroot 27306 26980 0 11:27 pts/1 00:00:00 grep --color=auto 27172[root@containerd ~]#
我们删除这个容器后,/sys/fs/cgroup/memory/default/
目录下的容器 ID 文件夹也会自动删除。
[root@containerd ~]#nerdctl psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES3543aa8676e8 docker.io/library/nginx:alpine "/docker-entrypoint.…" 20 minutes ago Up nginx[root@containerd ~]#nerdctl rm -f nginxnginx[root@containerd ~]#ll /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324efls: cannot access /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef: No such file or directory[root@containerd ~]#nerdctl psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES[root@containerd ~]#
实验结束。
Namespaces测试
💘 实践:Namespaces测试(测试成功)-2022.5.17 |
---|
namespace
也称命名空间,是 Linux 为我们提供的用于隔离进程树、网络接口、挂载点以及进程间通信等资源的方法。在日常使用个人 PC 时,我们并没有运行多个完全分离的服务器的需求,但是如果我们在服务器上启动了多个服务,这些服务其实会相互影响的,每一个服务都能看到其他服务的进程,也可以访问宿主机器上的任意文件,一旦服务器上的某一个服务被入侵,那么入侵者就能够访问当前机器上的所有服务和文件,这是我们不愿意看到的,我们更希望运行在同一台机器上的不同服务能做到完全隔离,就像运行在多台不同的机器上一样。而我们这里的容器其实就通过 Linux 的 Namespaces 技术来实现的对不同的容器进行隔离。
linux 共有6(7)种命名空间:
ipc namespace
: 管理对 IPC 资源(进程间通信(信号量、消息队列和共享内存)的访问net namespace
: 网络设备、网络栈、端口等隔离mnt namespace
: 文件系统挂载点隔离pid namespace
: 用于进程隔离user namespace
: 用户和用户组隔离(3.8以后的内核才支持)uts namespace
: 主机和域名隔离cgroup namespace
:用于 cgroup 根目录隔离(4.6以后版本的内核才支持)
我们可以通过 lsns
命令查看当前系统已经创建的命名空间:
[root@containerd ~]#lsns NS TYPE NPROCS PID USER COMMAND4026531836 pid 109 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 224026531837 user 109 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 224026531838 uts 109 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 224026531839 ipc 109 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 224026531840 mnt 108 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 224026531856 mnt 1 18 root kdevtmpfs4026531956 net 109 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22[root@containerd ~]#
要查看一个进程所属的命名空间信息,可以到 /proc/<pid>/ns
目录下查看:
[root@containerd ~]#ps -ef|grep sshroot 6587 1 0 05:05 ? 00:00:00 /usr/sbin/sshd -Droot 26963 6587 0 11:04 ? 00:00:00 sshd: root@pts/1root 26965 6587 0 11:04 ? 00:00:00 sshd: root@nottyroot 26967 26965 0 11:04 ? 00:00:00 /usr/libexec/openssh/sftp-serverroot 27527 26980 0 14:03 pts/1 00:00:00 grep --color=auto ssh[root@containerd ~]#ll /proc/6587/ns/*lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/ipc -> ipc:[4026531839]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/mnt -> mnt:[4026531840]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/net -> net:[4026531956]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/pid -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/uts -> uts:[4026531838][root@containerd ~]#
这些 namespace 都是链接文件, 格式为 namespaceType:[inode number]
,inode number
用来标识一个 namespace,可以理解为 namespace id,如果两个进程的某个命名空间的链接文件指向同一个,那么其相关资源在同一个命名空间中,也就没有隔离了。
比如同样针对上面运行的 nginx 容器,我们查看其命名空间:
[root@containerd ~]#nerdctl run -d -p 80:80 --name nginx nginx:alpinee4e500fed73578233fa73d76468df21a476a39dd9d81be0fa1696cc633b651a8[root@containerd ~]#lsns |grep nginx4026532503 mnt 3 27582 root nginx: master process nginx -g daemon off4026532504 uts 3 27582 root nginx: master process nginx -g daemon off4026532505 ipc 3 27582 root nginx: master process nginx -g daemon off4026532506 pid 3 27582 root nginx: master process nginx -g daemon off4026532508 net 3 27582 root nginx: master process nginx -g daemon off[root@containerd ~]#
可以看出 nginx 容器启动后,已经为该容器自动创建了单独的 mtn
、uts
、ipc
、pid
、net
命名空间,也就是这个容器在这些方面是独立隔离的,其他容器想要和该容器共享某一个命名空间,那么就需要指向同一个命名空间。
只有用户是没被隔离开的:
[root@containerd ~]#ps -ef|grep nginxroot 27582 27551 0 14:09 ? 00:00:00 nginx: master process nginx -g daemon off;101 27696 27582 0 14:09 ? 00:00:00 nginx: worker process101 27697 27582 0 14:09 ? 00:00:00 nginx: worker processroot 27721 26980 0 14:11 pts/1 00:00:00 grep --color=auto nginx[root@containerd ~]#ps -ef|grep sshroot 6587 1 0 05:05 ? 00:00:00 /usr/sbin/sshd -Droot 26963 6587 0 11:04 ? 00:00:00 sshd: root@pts/1root 26965 6587 0 11:04 ? 00:00:00 sshd: root@nottyroot 26967 26965 0 11:04 ? 00:00:00 /usr/libexec/openssh/sftp-serverroot 27723 26980 0 14:11 pts/1 00:00:00 grep --color=auto ssh[root@containerd ~]#ll /proc/27582/ns/*lrwxrwxrwx 1 root root 0 Nov 3 14:09 /proc/27582/ns/ipc -> ipc:[4026532505]lrwxrwxrwx 1 root root 0 Nov 3 14:09 /proc/27582/ns/mnt -> mnt:[4026532503]lrwxrwxrwx 1 root root 0 Nov 3 14:09 /proc/27582/ns/net -> net:[4026532508]lrwxrwxrwx 1 root root 0 Nov 3 14:09 /proc/27582/ns/pid -> pid:[4026532506]lrwxrwxrwx 1 root root 0 Nov 3 14:09 /proc/27582/ns/user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Nov 3 14:09 /proc/27582/ns/uts -> uts:[4026532504][root@containerd ~]#ll /proc/6587/ns/*lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/ipc -> ipc:[4026531839]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/mnt -> mnt:[4026531840]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/net -> net:[4026531956]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/pid -> pid:[4026531836]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/user -> user:[4026531837]lrwxrwxrwx 1 root root 0 Nov 3 14:01 /proc/6587/ns/uts -> uts:[4026531838][root@containerd ~]#
FAQ
容器的本质是宿主机上的一个进程
注意:宿主机上的容器都是共享宿主机的内核的。
cgroup和cgroup v2差别大吗?
其实是因为cgroup v1版本增加了一些功能,特别特别多,后期,cgroup v1版本就特变难维护了。然后,就相当于重新做了一个cgroup v2版本,也不能说差别大,只能说cgroup v2功能更强大,它可以去做cgroup的隔离。
kubepods.slice目录
/sys/fs/cgroup/kubepods.slice
注意,这个kubepods.slice,我们会后面和大家说,它呢,就是k8s当中那个pods,每个pods它创建完成后,它的cgroups控制话,其实都是把他放在这个子目录下面去的,如果你要限制内存的话。
Hierarchy: cgroup 的层级组织关系,cgroup 以树形层级组织,每个 cgroup 子节点默认继承其父 cgroup 节点的配置属性,这样每个 Hierarchy 在初始化会有 root cgroup。
参考文档
- https://kubernetes.io/zh/docs/setup/production-environment/container-runtimes/
- https://www.infoq.cn/article/docker-resource-management-cgroups/
容器运行时介绍是k8s底层的一个基础
注意:关于容器运行时介绍的一些概念很容易混淆,需要理清一点!
docker、docker daemon(守护进程)、containerd、containerd-shim(可以翻译成垫片或者中间件。)、runc(oci规范的一个实现)
libcontainer:Docker 被逼无耐将 libcontainer 捐献出来改名为 runc 的;
OCI、CRI--规范;
cri-docker-shim
符合CRI的容器运行时介绍如下: containerd:containerd与Docker相兼容,相比Docker轻量很多,目前较为成熟,部分大厂已经在向containerd这方面切了; cri-o,podman:都是红帽(RedHat)项目,目前红帽主推podman; kata; gVisor;