实战:CGroups与Namespaces-20211103

image-20211028105335512

本节我们来一起了解下容器背后的两个核心技术:CGroups 和 Namespace。

目录

1、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 节点,即可以绑定到特定的 CPU
  • cpu: 限制 cgroup 中进程的 CPU 使用份额
  • cpuacct: 统计 cgroup 中进程的 CPU 使用情况
  • memory: 限制 cgroup 中进程的内存使用,并能报告内存使用情况
  • devices: 控制 cgroup 中进程能访问哪些文件设备(设备文件的创建、读写)
  • freezer: 挂起或恢复 cgroup 中的 task
  • net_cls: 可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic contro)对数据包进行控制
  • blkio: 限制 cgroup 中进程的块设备 IO
  • perf_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.slicesystem.sliceuser.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 目录:

prometheus cgroup

📌 这其实可以理解为 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。

2、❤️实战:CGroup 测试

接下来我们来尝试手动设置下 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_uscpu.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.py
while True:
    pass

直接执行这个死循环脚本即可:

[root@containerd ~]#python cgroup.py  &
[1] 32416

使用 top 命令可以看到进程号32416的 CPU 使用率达到了 100%

image-20211028154304139

现在我们将这个进程ID写入到 /sys/fs/cgroup/cpu/ydzs.test/tasks 文件下面去,然后设置 /sys/fs/cgroup/cpu/ydzs.test/cpu.cfs_quota_us10000us,因为 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 命令查看该进程可以验证。

image-20211028154432762

记得最后杀死上面那个进程。

[root@containerd ~]#kill 32416

如果要限制内存等其他资源的话,同样去对应的子系统下面设置资源,并将进程 ID 加入 tasks 中即可。

如果要删除这个 cgroup,直接删除文件夹是不行的,需要使用 libcgroup 工具:

image-20211025210205786

[root@containerd ~]#yum install libcgroup libcgroup-tools
[root@containerd cpu]#cgdelete cpu:ydzs.test
[root@containerd cpu]#ls /sys/fs/cgroup/cpu/ydzs.test
ls: cannot access /sys/fs/cgroup/cpu/ydzs.test: No such file or directory
[root@containerd cpu]#

3、❤️实战:在容器中使用CGroups

上面我们测试了一个普通应用如何配置 cgroup,接下来我们在 Containerd 的容器中来使用 cgroup,比如使用 nerdctl 启动一个 nginx 容器,并限制其使用内存为50M:

image-20211025210634190

[root@containerd ~]#nerdctl run -d -m 50m --name nginx nginx:alpine
3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef
[root@containerd ~]#nerdctl ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED          STATUS    PORTS    NAMES
3543aa8676e8    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_bytes
52428800
[root@containerd ~]#

同样我们的 nginx 容器进程 ID 也会出现在上面的 tasks 文件中:

[root@containerd ~]#cat  /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef/tasks
27172
27266
27267
[root@containerd ~]#

我们可以通过如下命令过滤该进程号,可以看出第一行的 2686 就是 nginx 进程在主机上的进程 ID,下面几个是这个进程下的线程:

[root@containerd ~]#ps -ef|grep 27172
root      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 process
101       27267  27172  0 11:08 ?        00:00:00 nginx: worker process
root      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 ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED           STATUS    PORTS    NAMES
3543aa8676e8    docker.io/library/nginx:alpine    "/docker-entrypoint.…"    20 minutes ago    Up                 nginx
[root@containerd ~]#nerdctl rm -f nginx
nginx
[root@containerd ~]#ll /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef
ls: cannot access /sys/fs/cgroup/memory/default/3543aa8676e823726209bc24a78f14d7afbd09d13b06be3f80939c7cb84324ef: No such file or directory
[root@containerd ~]#nerdctl ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES
[root@containerd ~]#

实验结束。

4、❤️实战:Namespaces测试

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 COMMAND
4026531836 pid      109   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531837 user     109   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531838 uts      109   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531839 ipc      109   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531840 mnt      108   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
4026531856 mnt        1  18 root kdevtmpfs
4026531956 net      109   1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 22
[root@containerd ~]#

要查看一个进程所属的命名空间信息,可以到 /proc/<pid>/ns 目录下查看:

[root@containerd ~]#ps -ef|grep ssh
root       6587      1  0 05:05 ?        00:00:00 /usr/sbin/sshd -D
root      26963   6587  0 11:04 ?        00:00:00 sshd: root@pts/1
root      26965   6587  0 11:04 ?        00:00:00 sshd: root@notty
root      26967  26965  0 11:04 ?        00:00:00 /usr/libexec/openssh/sftp-server
root      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 ~]#

image-20211103140345630

这些 namespace 都是链接文件, 格式为 namespaceType:[inode number]inode number 用来标识一个 namespace,可以理解为 namespace id,如果两个进程的某个命名空间的链接文件指向同一个,那么其相关资源在同一个命名空间中,也就没有隔离了。

比如同样针对上面运行的 nginx 容器,我们查看其命名空间:

[root@containerd ~]#nerdctl run -d -p 80:80 --name nginx nginx:alpine
e4e500fed73578233fa73d76468df21a476a39dd9d81be0fa1696cc633b651a8
[root@containerd ~]#lsns |grep nginx
4026532503 mnt        3 27582 root nginx: master process nginx -g daemon off
4026532504 uts        3 27582 root nginx: master process nginx -g daemon off
4026532505 ipc        3 27582 root nginx: master process nginx -g daemon off
4026532506 pid        3 27582 root nginx: master process nginx -g daemon off
4026532508 net        3 27582 root nginx: master process nginx -g daemon off
[root@containerd ~]#

可以看出 nginx 容器启动后,已经为该容器自动创建了单独的 mtnutsipcpidnet 命名空间,也就是这个容器在这些方面是独立隔离的,其他容器想要和该容器共享某一个命名空间,那么就需要指向同一个命名空间。

只有用户是没被隔离开的:

[root@containerd ~]#ps -ef|grep nginx
root      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 process
101       27697  27582  0 14:09 ?        00:00:00 nginx: worker process
root      27721  26980  0 14:11 pts/1    00:00:00 grep --color=auto nginx
[root@containerd ~]#ps -ef|grep ssh
root       6587      1  0 05:05 ?        00:00:00 /usr/sbin/sshd -D
root      26963   6587  0 11:04 ?        00:00:00 sshd: root@pts/1
root      26965   6587  0 11:04 ?        00:00:00 sshd: root@notty
root      26967  26965  0 11:04 ?        00:00:00 /usr/libexec/openssh/sftp-server
root      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 ~]#

image-20211103141210952

参考文档

  • https://kubernetes.io/zh/docs/setup/production-environment/container-runtimes/
  • https://www.infoq.cn/article/docker-resource-management-cgroups/

需要注意的问题

1.注意:kubepods.slice目录

/sys/fs/cgroup/kubepods.slice

注意,这个kubepods.slice,我们会后面和大家说,它呢,就是k8s当中那个pods,每个pods它创建完成后,它的cgroups控制话,其实都是把他放在这个子目录下面去的,如果你要限制内存的话。

image-20211025200755925

Hierarchy: cgroup 的层级组织关系,cgroup 以树形层级组织,每个 cgroup 子节点默认继承其父 cgroup 节点的配置属性,这样每个 Hierarchy 在初始化会有 root cgroup。

2.问题:cgroup和cgroup v2差别大吗?

其实是因为cgroup v1版本增加了一些功能,特别特别多,后期,cgroup v1版本就特变难维护了。然后,就相当于重新做了一个cgroup v2版本,也不能说差别大,只能说cgroup v2功能更强大,它可以去做cgroup的隔离。

3.注意:容器的本质是宿主机上的一个进程。

4.注意:宿主机上的容器都是共享宿主机的内核的。

5.命令总结

  • 查看当前系统支持的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 ~]#
  • 我们可以使用 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 ~]#
  • 注意:cat /proc/cgroup命令
[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

[root@containerd ~]#nerdctl ps -a
CONTAINER ID    IMAGE                             COMMAND                   CREATED        STATUS    PORTS                 NAMES
a4afb3adda0d    docker.io/library/nginx:alpine    "/docker-entrypoint.…"    9 hours ago    Up        0.0.0.0:80->80/tcp    nginx

[root@containerd ~]#nerdctl rm -f nginx #删除这个nginx容器后,立马可看到相应的cgroup 子系统下层级关系都减少了。
nginx
[root@containerd ~]#cat /proc/cgroups
#subsys_name    hierarchy       num_cgroups     enabled
cpuset  5       3       1
cpu     6       57      1
cpuacct 6       57      1
memory  3       57      1
devices 9       57      1
freezer 2       3       1
net_cls 4       3       1
blkio   7       57      1
perf_event      11      3       1
hugetlb 10      3       1
pids    8       57      1
net_prio        4       3       1
[root@containerd ~]#

关于我

我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!

各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:

  1. 个人微信二维码:x2675263825 (舍得), qq:2675263825。

    image-20211002091450217

  2. 个人博客地址:www.onlyonexl.cn

    image-20211002092057988

  3. 个人微信公众号:云原生架构师实战

    image-20211002141739664

  4. 个人csdn

    https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

    image-20211002092344616

总结

​ 好了,关于CGroups与Namespaces实验就到这里了,感谢大家阅读,最后贴上我的美圆photo一张,祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20211103143937344

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值