课程原文: 李程远. 认识容器:容器的基本操作和实现原理
本文实践环境:
Operating System: Ubuntu 20.04.2 LTS
Kernel: Linux 5.8.0-50-generic
Architecture: x86-64Docker 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)
经过前面的操作,让我们逐渐感觉到,运行在宿主机上的容器有自己独立的网络、文件系统和进程。从字面上来理解,容器就是一个沙箱环境,各个沙箱之间相互不干扰,都有一片自己的小天地。更专业一点说,就是程序在一个资源可控的独立(隔离)环境中运行。
没错,容器和一台独立的机器没啥区别,但它却没有硬件虚拟层,没有独立的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] 张磊. 白话容器基础(一):从进程说开去