【容器实战高手课-学习-1】容器的基本操作和实现原理

Demon.Lee 2021年05月01日 2,534次浏览

课程原文: 李程远. 认识容器:容器的基本操作和实现原理

本文实践环境:
Operating System: Ubuntu 20.04.2 LTS
Kernel: Linux 5.8.0-50-generic
Architecture: x86-64

Docker Client/Server Version: 20.10.5

要想了解容器,需要先从镜像开始。

镜像(Image)

动手制作一个httpd服务的镜像,其Dockerfile文件样例如下:

# ls
Dockerfile  file1  file2.tar.gz
# cat Dockerfile
FROM centos:8.1.1911
RUN yum install -y httpd
COPY file1 /var/www/html/
ADD file2.tar.gz /var/www/html/
CMD ["/sbin/httpd", "-D", "FOREGROUND"]
#

这里的COPY和ADD命令非常相似,但它们之间也有区别,比如ADD可以将压缩包文件先解压再添加到镜像中,所以上面的例子中,/var/www/html/目录下的两个文件分别是file1和file2,更多比较可以参考这里

通过docker build命令构建一个镜像,镜像制作好之后再通过docker images命令即可看到已经打包好的镜像了。

# docker build -t registry/httpd:v1 -f ./Dockerfile .
Sending build context to Docker daemon  3.584kB
Step 1/5 : FROM centos:8.1.1911
 ---> 470671670cac
Step 2/5 : RUN yum install -y httpd
 ---> Running in ae7270a4eee4
CentOS-8 - AppStream                            1.0 MB/s | 6.3 MB     00:06    
CentOS-8 - Base                                 1.6 MB/s | 2.3 MB     00:01    
CentOS-8 - Extras                               6.9 kB/s | 9.6 kB     00:01    
Dependencies resolved.
================================================================================
 Package           Arch   Version                               Repo       Size
================================================================================
Installing:
 httpd             x86_64 2.4.37-30.module_el8.3.0+561+97fdbbcc AppStream 1.7 M
Installing dependencies:
...
...
...
Complete!
Removing intermediate container ae7270a4eee4
 ---> c5d8f0dc03db
Step 3/5 : COPY file1 /var/www/html/
 ---> a850863775b7
Step 4/5 : ADD  file2.tar.gz /var/www/html/
 ---> cec34b566b87
Step 5/5 : CMD ["/sbin/httpd", "-D", "FOREGROUND"]
 ---> Running in 999e2d0a23ff
Removing intermediate container 999e2d0a23ff
 ---> 2791c2079f58
Successfully built 2791c2079f58
Successfully tagged registry/httpd:v1
# docker images
REPOSITORY             TAG        IMAGE ID       CREATED         SIZE
registry/httpd         v1         2791c2079f58   7 seconds ago   278MB
registry/fwd_sig       v1         66d50f60736a   9 days ago      237MB
registry/zombie-proc   v1         22e84b665da6   9 days ago      237MB
registry/sig-proc      v1         d525c28d04a4   10 days ago     239MB
prom/prometheus        v2.26.0    6d6859d1a42a   2 weeks ago     169MB
centos                 8.1.1911   470671670cac   15 months ago   237MB
#

有了镜像,通过docker run命令就可以启动运行httpd服务了,通过脚本即可验证容器内/var/www/html目录下的内容是否与我们制作镜像时保持一致:

# docker run --name my-httpd -d registry/httpd:v1 
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd
# docker exec my-httpd ls -l /var/www/html
total 4
-rw-rw-r-- 1 root root 0 Apr 10 15:18 file1
-rw-r--r-- 1 root root 6 Aug 22  2020 file2
#

上面是通过docker exec命令查看容器内部情况的,如果对这个命令不清楚,可以通过–help了解:

# docker exec --help

Usage:  docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

Run a command in a running container

Options:
  -d, --detach               Detached mode: run command in the background
      --detach-keys string   Override the key sequence for detaching a container
  -e, --env list             Set environment variables
      --env-file list        Read in a file of environment variables
  -i, --interactive          Keep STDIN open even if not attached
      --privileged           Give extended privileges to the command
  -t, --tty                  Allocate a pseudo-TTY
  -u, --user string          Username or UID (format: <name|uid>[:<group|gid>])
  -w, --workdir string       Working directory inside the container
#

因为有了镜像,部署服务就简单多了,实现了类似于java的曾经喊出的口号:“Write once, Run anywhere”,那镜像到底是啥呢?

镜像就是一个特殊的文件系统,它提供了容器中程序执行需要的所有文件,包括三类:相关的程序可执行文件、库文件和配置文件,这些文件都被一次性打包好了。

Build once, Run anywhere…(一次构建,到处运行)

接着,通过docker exec container ip addr 拿到容器的ip,就可以调用api了:

# docker exec my-httpd ip addr
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
4: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
# curl http://172.17.0.2/file2
file2
#

那么,当前宿主机的ip是多少呢?

# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:b8ff:fe2e:ca66  prefixlen 64  scopeid 0x20<link>
        ether 02:42:b8:2e:ca:66  txqueuelen 0  (Ethernet)
        RX packets 9  bytes 907 (907.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 46  bytes 5656 (5.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.50.244  netmask 255.255.255.0  broadcast 192.168.50.255
        inet6 fe80::4fbe:704f:61b:4722  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:06:96:00  txqueuelen 1000  (Ethernet)
        RX packets 17198  bytes 18452007 (18.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8578  bytes 774276 (774.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 411  bytes 38950 (38.9 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 411  bytes 38950 (38.9 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth81e7948: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::9c4d:b6ff:fea9:bbdd  prefixlen 64  scopeid 0x20<link>
        ether 9e:4d:b6:a9:bb:dd  txqueuelen 0  (Ethernet)
        RX packets 9  bytes 1033 (1.0 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 81  bytes 9576 (9.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
#

到这里,通过容器的运行,我们可以思考一个问题:如何描述容器与镜像之间的关系?

容器可以理解为镜像运行时的一个实例,同一个镜像可以重复产生具备一致性的运行实例,这也说明容器在运行过程中产生的数据变化,不会影响到镜像本身。

容器(Container)

经过前面的操作,让我们逐渐感觉到,运行在宿主机上的容器有自己独立的网络、文件系统和进程。从字面上来理解,容器就是一个沙箱环境,各个沙箱之间相互不干扰,都有一片自己的小天地。更专业一点说,就是程序在一个资源可控的独立(隔离)环境中运行。

认识容器-1

没错,容器和一台独立的机器没啥区别,但它却没有硬件虚拟层,没有独立的Linux内核,这又是如何做到的呢?

Namespace

需要满足容器的第一个条件,就是隔离功能,而这正是通过Namespace来实现的。如果你写过C++,一定对namespace关键字很熟悉,名称空间的概念在很多现代的高级程序语言中都存在,其主要的作用就是避免不同的开发者提供的API相互冲突。

继续以上面的容器为例,我们比较一下进程id、文件系统:

# ps -ef|grep -v grep|grep httpd
root       11936   11916  0 22:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
48         11970   11936  0 22:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
48         11971   11936  0 22:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
48         11972   11936  0 22:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
48         11973   11936  0 22:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
# docker exec my-httpd ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 14:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
apache         7       1  0 14:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
apache         8       1  0 14:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
apache         9       1  0 14:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
apache        10       1  0 14:53 ?        00:00:00 /sbin/httpd -D FOREGROUND
# docker exec my-httpd ls -l /
total 56
lrwxrwxrwx   1 root root    7 May 11  2019 bin -> usr/bin
drwxr-xr-x   5 root root  340 Apr 25 13:51 dev
drwxr-xr-x   1 root root 4096 Apr 20 15:19 etc
drwxr-xr-x   2 root root 4096 May 11  2019 home
lrwxrwxrwx   1 root root    7 May 11  2019 lib -> usr/lib
lrwxrwxrwx   1 root root    9 May 11  2019 lib64 -> usr/lib64
drwx------   2 root root 4096 Jan 13  2020 lost+found
drwxr-xr-x   2 root root 4096 May 11  2019 media
drwxr-xr-x   2 root root 4096 May 11  2019 mnt
drwxr-xr-x   2 root root 4096 May 11  2019 opt
dr-xr-xr-x 287 root root    0 Apr 25 13:51 proc
dr-xr-x---   2 root root 4096 Jan 13  2020 root
drwxr-xr-x   1 root root 4096 Apr 20 15:07 run
lrwxrwxrwx   1 root root    8 May 11  2019 sbin -> usr/sbin
drwxr-xr-x   2 root root 4096 May 11  2019 srv
dr-xr-xr-x  13 root root    0 Apr 25 13:51 sys
drwxrwxrwt   1 root root 4096 Apr 25 13:51 tmp
drwxr-xr-x   1 root root 4096 Jan 13  2020 usr
drwxr-xr-x   1 root root 4096 Apr 20 15:07 var
# ls -l /
total 2097232
lrwxrwxrwx   1 root root          7 4月  10 14:00 bin -> usr/bin
drwxr-xr-x   4 root root       4096 4月  17 16:30 boot
drwxrwxr-x   2 root root       4096 4月  10 14:02 cdrom
drwxr-xr-x  19 root root       4140 4月  21 22:22 dev
drwxr-xr-x 133 root root      12288 4月  21 22:54 etc
drwxr-xr-x   3 root root       4096 4月  10 14:02 home
lrwxrwxrwx   1 root root          7 4月  10 14:00 lib -> usr/lib
lrwxrwxrwx   1 root root          9 4月  10 14:00 lib32 -> usr/lib32
lrwxrwxrwx   1 root root          9 4月  10 14:00 lib64 -> usr/lib64
lrwxrwxrwx   1 root root         10 4月  10 14:00 libx32 -> usr/libx32
drwx------   2 root root      16384 4月  10 14:00 lost+found
drwxr-xr-x   3 root root       4096 4月  10 14:25 media
drwxr-xr-x   2 root root       4096 2月  10 02:47 mnt
drwxr-xr-x   4 root root       4096 4月  10 14:38 opt
dr-xr-xr-x 286 root root          0 4月  21 22:22 proc
drwx------   4 root root       4096 4月  10 23:32 root
drwxr-xr-x  36 root root       1040 4月  24 15:16 run
lrwxrwxrwx   1 root root          8 4月  10 14:00 sbin -> usr/sbin
drwxr-xr-x  10 root root       4096 4月  11 17:06 snap
drwxr-xr-x   2 root root       4096 2月  10 02:47 srv
-rw-------   1 root root 2147483648 4月  10 14:00 swapfile
dr-xr-xr-x  13 root root          0 4月  21 22:22 sys
drwxrwxrwt  20 root root       4096 4月  25 23:20 tmp
drwxr-xr-x  14 root root       4096 2月  10 02:48 usr
drwxr-xr-x  14 root root       4096 2月  10 02:56 var
#

比较容器与宿主机上的这几个httpd进程,可以发现:

1)在宿主机上的pid是(11936,11970~11973),而它们在容器内则从1开始编号;

2)通过 “ls -l /” 命令看到的文件系统也不同;

3)网络,前面我们已经得知,容器有自己的独立ip;

种种这些,都是通过不同的Namespace来实现的。

namespace Flag Publish Date Isolates First Supported Kernel Version
Mount CLONE_NEWNS 03-Aug-2002 隔离文件系统,功能上可以类比chroot 2.4.19
UTS CLONE_NEWUTS 29-Nov-2006 隔离主机的Hostname、Domain names 2.6.19
IPC CLONE_NEWIPC 29-Nov-2006 隔离进程间的通信的渠道 2.6.19
PID CLONE_NEWPID 24-Jan-2008 隔离进程编号,无法看到其他名称空间中的PID,意味着无法对其他进程产生影响 2.6.24
Network CLONE_NEWNET 23-Mar-2009 隔离网络资源,如网卡、网络栈、IP地址、端口,等等 2.6.29
User CLONE_NEWUSER 19-Feb-2013 隔离用户和用户组 3.8
Cgroup CLONE_NEWCGROUP 15-May-2016 隔离cgroups信息,进程有自己的cgroups的根目录视图(在/proc/self/cgroup不会看到整个系统的信息) 4.6
Time CLONE_NEWTIME 30-Mar-2020 隔离系统时间,2020年3月最新的5.6版本内核开始支持进程独立设置系统时间 5.6

Namespace的使用方式可能没我们想的那么复杂。linux系统下,普通的进(线)程创建,我们会通过调用clone()函数来实现,它会返回进程号pid,比如:

int pid = clone(main_function, stack_size, SIGCHLD, NULL); 

如果我们在调用clone()函数的参数中指定CLONE_NEWPID,则会创建一个PID Namespace(PID就是进程的编号)。PID Namespace就是指每建立一个Namespace,都会从1开始重新设置进程编号,它看不到宿主机上Host PID Namespace(它是其他Namespace的父Namespace,Host PID Namespace可以看到整个宿主机上所有的进程)中的进程空间,也看不到其他PID Namespace中的进程,仿佛自己就是这片天地的主人。

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL); 

理解了PID Namespace,其他Namespace的机制也就好理解了。只要将其他的Namespace对应的Flag值传入该函数,如CLONE_NEWNS、CLONE_NEWNET等,则被隔离的进程就只能看到当前Namespace中的挂载点信息、网络设备和配置等。

Cgroups

Namespace虽然解决了各个独立进程之间的访问操作,相互隔离不影响,降低了安全风险,但这还不足以让各个进程看起来像一台独立运算的计算机。

可以设想下,如果一个容器内的进程发生了内存溢出或将所有CPU占满了的话,其他容器的进程就会莫名其妙的被牵连,这就算不上完美的隔离了。

容器不仅要能隔离各自进程之间的访问操作,还必须独立控制分配给各个进程的资源使用配额,这些资源包括CPU,memory,磁盘IO,网络流量等。

而这个资源限制就是通过Cgroups(Control Groups)来实现的:

Cgroups通过不同的子系统限制了不同的资源,每个子系统限制一种资源。每个子系统限制资源的方式都是类似的,就是把相关的一组进程分配到一个控制组里,然后通过树结构进行管理,每个控制组都设有自己的资源控制参数。

以memory Cgroup为例,我们看看如何控制memory的使用量。Cgroups的基本目录是:/sys/fs/cgroup,memory Cgroup路径:/sys/fs/cgroup/memory,相关操作如下:

~$ docker ps
CONTAINER ID   IMAGE                     COMMAND                  CREATED       STATUS          PORTS                    NAMES
ec9a0966c400   registry/httpd:v1         "/sbin/httpd -D FORE…"   11 days ago   Up 18 minutes                            my-httpd
711846c8934c   prom/prometheus:v2.26.0   "/bin/prometheus --c…"   3 weeks ago   Up 3 minutes    0.0.0.0:9090->9090/tcp   prometheus
~$ cd /sys/fs/cgroup/memory/
memory$ find ./ -name "*ec9a0966c400*"
./docker/ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd
memory$ cd docker/ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd/
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ pwd
/sys/fs/cgroup/memory/docker/ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ ll
total 0
drwxr-xr-x 2 root root 0 5月   1 22:32 ./
drwxr-xr-x 4 root root 0 5月   1 22:32 ../
-rw-r--r-- 1 root root 0 5月   1 22:34 cgroup.clone_children
--w--w--w- 1 root root 0 5月   1 22:32 cgroup.event_control
-rw-r--r-- 1 root root 0 5月   1 22:32 cgroup.procs
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.failcnt
--w------- 1 root root 0 5月   1 22:34 memory.force_empty
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 5月   1 22:34 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 5月   1 22:34 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 5月   1 22:34 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 5月   1 22:34 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 5月   1 22:34 memory.numa_stat
-rw-r--r-- 1 root root 0 5月   1 22:32 memory.oom_control
---------- 1 root root 0 5月   1 22:34 memory.pressure_level
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 5月   1 22:34 memory.stat
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.swappiness
-r--r--r-- 1 root root 0 5月   1 22:34 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 5月   1 22:34 memory.use_hierarchy
-rw-r--r-- 1 root root 0 5月   1 22:34 notify_on_release
-rw-r--r-- 1 root root 0 5月   1 22:34 tasks
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ cat memory.limit_in_bytes 
9223372036854771712
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ echo 1073741824 > memory.limit_in_bytes
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ cat memory.limit_in_bytes 
1073741824
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ cat cgroup.procs
3372
3409
3410
3411
3412
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ ps aux|grep -v grep|grep httpd
root        3372  0.0  0.1 257308 10240 ?        Ss   22:32   0:00 /sbin/httpd -D FOREGROUND
48          3409  0.0  0.1 259360  7812 ?        S    22:32   0:00 /sbin/httpd -D FOREGROUND
48          3410  0.0  0.1 2758992 9088 ?        Sl   22:32   0:00 /sbin/httpd -D FOREGROUND
48          3411  0.0  0.1 2496784 9100 ?        Sl   22:32   0:00 /sbin/httpd -D FOREGROUND
48          3412  0.0  0.1 2496784 9048 ?        Sl   22:32   0:00 /sbin/httpd -D FOREGROUND
ec9a0966c400a337f6439208f72c8699d271698e5446c6a967341f13a1c29ecd$ 

针对上面的操作,简单说明如下:

1、通过docker ps找到对应的container id,然后到Cgroups子目录下搜索该容器;

2、memory Cgroup控制组中的memory.limit_in_bytes表示内存使用的上限,默认值很大,可以理解为不限制,我们改成了1GB,表示该容器内所有进程memory使用量之和,最大不会超过1GB;

3、查看cgroup.procs文件,我们发现这里面存放的就是容器内进程在宿主机上的真实pid。

更多Cgroups子系统的汇总如下表所示,详细请参阅官方说明,cgroups(7) — Linux manual page

Cgroups子系统 功能 First Supported Kernel Version
blkio 为块设备(如磁盘,固态硬盘,USB等等)设定I/O限额 2.6.33
cpu 控制Cgroups中进程的处理器占用比率 2.6.24
cpuacct 自动生成Cgroups中进程所使用的处理器时间的报告 2.6.24
cpuset 为Cgroups中的进程分配独立的处理器(包括多路系统的处理器,多核系统的处理器核心),即一个控制组里的进程可以在哪几个物理CPU上运行 2.6.24
devices 设置Cgroups中的进程访问某个设备的权限(读、写、创建三种权限) 2.6.26
freezer 挂起或恢复Cgroups中的进程 2.6.28
memory 设定Cgroups中进程使用内存的限制,并自动生成内存资源使用报告 2.6.25
net_cls 使用等级识别符标记网络数据包,可允许Linux流量控制程序识别从具体Cgroups中生成的数据包 2.6.29
net_prio 用来设置网络流量的优先级 3.3
perf_event 允许Perf工具基于Cgroups分组做性能监测 2.6.39
hugetlb 主要针对HugeTLB系统进行限制 3.5
pids 限制一个控制组内最多可以运行多少个进程 4.3
rdma 限制一个控制组内RDMA/IB-specific资源的使用 4.11

Cgroups有v1/v2两个版本,其比较请查看下面的表格:

item\version v1 v2
时间 2006~2008 2015~2016
开发工程师 Google工程师Paul Menage、Rohit Seth等 Facebook工程师Tejun Heo等
内核版本 2.6.24 4.5
迭代 各种子系统独立,每个进程在各个Cgroups子系统中独立配置,灵活但对同一进程的资源协调比较困难(比如memory Cgroup与blkio Cgroup之间就不能协作) 支持Unified Hierarchy,清晰精确地控制资源的层级关系,各个子系统可以协调统一管理资源
现状 目前主流Linux发行版本及容器云平台主要使用v1版本,因为v2需要较新版本的Linux内核

总结来说:

1、Namespace帮助容器实现各种计算资源的隔离,Cgroups主要对容器使用某种资源量的多少做一个限制。

2、容器其实就是 Namespace + Cgroups。

参考资料

[1] 周志明. 容器的崛起(上):文件、访问、资源的隔离

[2] 张磊. 白话容器基础(一):从进程说开去