容器底层原理之namespace和cgroups

容器底层原理之namespace和cgroups

Linux内核中的namespace和cgroups技术实现了各种资源的隔离与控制。

Namespace

Namespace 是将内核的全局资源做封装,使得每个namespace 都有一份独立的资源,因此不同的进程在各自的namespace内对同一种资源的使用互不干扰。

1
Namespace技术实现了各种资源的隔离。
Namespace 名称作用内核版本
Mount(mnt)隔离挂载点2.4.19
Process ID(pid)隔离进程ID2.6.24
Network(net)隔离网络设备、端口号等2.6.29
Interprocess Communication (ipc)隔离进程间通信 System V IPC 和 POSIX message queues2.6.19
UTS Namespace(uts)隔离主机名和域名2.6.19
User Namespace(user)隔离用户和用户组3.8
Control group(cgroups)Namespace隔离cgroups根目录4.6
Time Namespace隔离系统时间5.6

3.8 的内核开始,/proc/[pid]/ns 目录下会包含进程所属的 namespace 信息. 使用下面的命令可以查看当前进程所属的 namespace 信息:ll /proc/$$/ns ex:

[email protected]:~$ ll /proc/$$/ns
total 0
dr-x--x--x 2 vagrant vagrant 0 Aug 23 03:38 ./
dr-xr-xr-x 9 vagrant vagrant 0 Aug 23 03:38 ../
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 net -> 'net:[4026531992]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 user -> 'user:[4026531837]'
lrwxrwxrwx 1 vagrant vagrant 0 Aug 23 03:38 uts -> 'uts:[4026531838]'

隔离挂载点(mount)

Mount Namespace 实现了不同进程可以看到不同的挂载信息。

1
换句话说说,容器内的挂载操作不会影响到主机。

ex: 使用unshare命令新建一个mount namespace

1
sudo unshare --mount --fork /bin/bash

创建一个临时挂载目录

1
mkdir /tmp/tmpfs

使用tmpfs挂载一个目录

1
mount -t tmpfs -o size=1024k tmpfs /tmp/tmpfs

当前窗口查看挂载信息

[email protected]:~# mkdir /tmp/tmpfs
[email protected]:~# mount -t tmpfs -o size=1024k tmpfs /tmp/tmpfs
[email protected]:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        39G  1.3G   38G   4% /
udev            977M     0  977M   0% /dev
tmpfs           994M     0  994M   0% /dev/shm
tmpfs           199M  924K  198M   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           199M     0  199M   0% /run/user/1000
tmpfs           994M     0  994M   0% /sys/fs/cgroup
/dev/loop1       56M   56M     0 100% /snap/core18/2128
/dev/loop0       71M   71M     0 100% /snap/lxd/21029
/dev/loop2       33M   33M     0 100% /snap/snapd/12704
tmpfs           1.0M     0  1.0M   0% /tmp/tmpfs
[email protected]:~$ 

新开一个窗口查看挂载信息,看到没有/tmp/tmpfs挂载信息

Last login: Mon Aug 23 03:38:34 2021 from 10.0.2.2
[email protected]:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            977M     0  977M   0% /dev
tmpfs           199M  932K  198M   1% /run
/dev/sda1        39G  1.3G   38G   4% /
tmpfs           994M     0  994M   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           994M     0  994M   0% /sys/fs/cgroup
/dev/loop1       56M   56M     0 100% /snap/core18/2128
/dev/loop0       71M   71M     0 100% /snap/lxd/21029
/dev/loop2       33M   33M     0 100% /snap/snapd/12704
tmpfs           199M     0  199M   0% /run/user/1000
[email protected]:~$ 

隔离进程ID(pid)

用于实现不同PID Namespace内的进程拥有相同的ID.

创建一个pid namespace

1
sudo unshare --pid --fork --mount-proc /bin/bash

查看进程信息,1号进程为bash

[email protected]:~#
[email protected]:~# sudo unshare --pid --fork --mount-proc /bin/bash 
[email protected]:~# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1   8960  3892 pts/0    S    03:52   0:00 /bin/bash
root           8  0.0  0.1  10616  3440 pts/0    R+   03:52   0:00 ps aux
[email protected]:~#

隔离网络设备、端口号(net)

net namespace实现网络设备的隔离。

查看主机网络信息

[email protected]:~# 
[email protected]:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:14:28:cf:54:43 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 85336sec preferred_lft 85336sec
    inet6 fe80::14:28ff:fecf:5443/64 scope link 
       valid_lft forever preferred_lft forever
[email protected]:~# 

创建一个net namespace

1
sudo unshare --net --fork /bin/bash

查看此net namespace下网络信息

[email protected]:~# 
[email protected]:~# sudo unshare --net --fork /bin/bash
[email protected]:~# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[email protected]:~# 

隔离主机名(uts)

UTS Namespace主要是用来隔离主机名的,它允许每个UTS Namespace拥有一个独立的主机名。

创建一个 UTS Namespace

1
sudo unshare --uts --fork /bin/bash

创建完namespace后当前终端已经处于一个独立的UTS Namespace中了。 先看下主机名、然后在修改主机名、最后查看主机。

[email protected]:~# sudo unshare --uts --fork /bin/bash
[email protected]:~# hostname
ubuntu20-04
[email protected]:~# hostname -b changehostname
[email protected]:~# hostname
changehostname
[email protected]:~# 

上面输出已经看到成功修改了主机名。 打开新的bash终端查看主机名

Last login: Mon Aug 23 03:43:22 2021 from 10.0.2.2
[email protected]:~$ sudo -i
[email protected]:~# hostname
ubuntu20-04
[email protected]:~# 

主机名并未被修改,验证UTS Namespace隔离主机名。

隔离进程间通信(ipc)

IPC Namespace主要是用来隔离进程间通信的。 ex:

1
PID Namespace 和 IPC Namespace 一起使用可以实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信。

使用unshare命令来创建一个IPC Namespace

1
sudo unshare --ipc --fork /bin/bash
  • ipcs -q 命令用于查看系统间通信队列列表。
  • ipcmk -Q 命令用于创建系统间通信队列。
[email protected]:~# 
[email protected]:~# sudo unshare --ipc --fork /bin/bash
[email protected]:~# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

[email protected]:~# ipcmk -Q
Message queue id: 0
[email protected]:~# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0xf350f2cb 0          root       644        0            0           

[email protected]:~# 

打开新新窗口终端,查看系统通信队列。

Last login: Mon Aug 23 04:07:29 2021 from 10.0.2.2
[email protected]:~$ sudo -i
[email protected]:~# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

[email protected]:~# 

结果可以看到,在单独的IPC Namespace内创建的系统通信队列在主机上无法查看,即IPC Namespace实现了系统间通信对列的隔离。

隔离用户和用户组(user)

User Namespace 主要是用来隔离用户和用户组的。

一个比较典型的应用场景就是在主机上以非root用户运行的进程可以在一个单独的User Namespace中映射成root用户。 使用User Namespace可以实现进程在容器内拥有root权限,而在主机上却只是普通用户。

User Namesapce的创建是可以不使用 root 权限的。 下面我们以普通用户的身份创建一个User Namespace,命令如下:

1
unshare --user -r /bin/bash
[email protected]:~$ 
[email protected]:~$ unshare --user -r /bin/bash
[email protected]:~# id
uid=0(root) gid=0(root) groups=0(root)
[email protected]:~# reboot
Failed to connect to bus: Operation not permitted
Failed to open initctl fifo: Permission denied
Failed to talk to init daemon.
[email protected]:~# 

可以看到,在新创建的User Namespace内虽然是root用户,但是并没有权限执行reboot命令。这说明在隔离的User Namespace中,并不能获取到主机的root权限,也就是说User Namespace实现了用户和用户组的隔离。

Cgroups

Cgroups,其名称源自控制组群(control groups)的简写,也是Linux内核的一个功能,用来限制、控制与统计一个进程组的资源(如CPU、内存、磁盘输入输出等)。

1
Cgroups技术用来限制容器内进程使用CPU、内存的资源的使用量。

cgroups的主要作用

cgroups的主要目的是为不同用户层面的资源管理提供一个统一化的接口。从单个任务的资源控制到操作系统层面的虚拟化,cgroups四大功能:

  • 资源限制:cgroups可以对任务要求的总资源总额进行限制。诸如,设定任务运行时使用的内存上限,一旦超出内核就发OOM killer(Out-Of-Memory killer)
  • 资源统计:cgoups可以统计系统的资源使用量,比如CPU使用时长、内存用量等。当前云端产品按使用量计费的方式采用的底层实现方式。
  • 任务控制:cgroups可以对任务执行挂起、恢复等操作。
  • 优先级分配:通过分配的CPU时间片数量和磁盘IO带宽,实际上就等同于控制了任务运行的优先级。

cgroups的文件系统接口

cgroups以文件的方式提供应用接口,可以通过mount命令查看cgroups默认的挂载点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[email protected]:~# mount | grep cgroup
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
[email protected]:~# 

Subsystem(子系统)

子系统作用
hugetlb限制HugeTLB(内存页)的使用
net_cls配合流控限制网络带宽
net_prio设置进程的网络流量优先级
freezer暂停/恢复cgroup中的任务/进程
pids限制任务/进程的数量
rdma限制RDMA/IB资源
devices设备访问权限控制
cpuset分配指定的CPU和内存节点
perf_event允许Perf工具基于Cgroup分组做性能检测
cpu控制CPU使用率
cpuacct统计CPU使用情况
memory限制内存的使用上限
blkio对块设备的IO进行限制

Tip

23333

updatedupdated2021-08-232021-08-23
点击刷新