一、容器

使用docker运行一个httpd容器。因为接下来要在容器内执行ps -ef命令,有些容器没有这个命令,例如nginx的官方镜像运行的容器就没有这个命令。

[root@dev1 ~]# docker run -d --name httpd centos/httpd-24-centos7
latest: Pulling from centos/httpd-24-centos7
73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7
[root@dev1 ~]# docker ps
CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS          PORTS                NAMES
73808be511e5   centos/httpd-24-centos7   "container-entrypoin…"   35 seconds ago   Up 33 seconds   8080/tcp, 8443/tcp   httpd

在这个启动好的nginx容器中运行一个命令,如下是在容器内的进程:

[root@dev1 ~]# docker exec httpd ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
default       1      0  0 23:35 ?        00:00:00 httpd -D FOREGROUND
default      41      1  0 23:35 ?        00:00:00 /usr/bin/cat
default      42      1  0 23:35 ?        00:00:00 /usr/bin/cat
default      43      1  0 23:35 ?        00:00:00 /usr/bin/cat
default      44      1  0 23:35 ?        00:00:00 /usr/bin/cat
default      45      1  0 23:35 ?        00:00:00 httpd -D FOREGROUND
default      50      1  0 23:35 ?        00:00:00 httpd -D FOREGROUND
default      67      1  0 23:35 ?        00:00:00 httpd -D FOREGROUND
default      69      1  0 23:35 ?        00:00:00 httpd -D FOREGROUND
default      70      1  0 23:35 ?        00:00:00 httpd -D FOREGROUND
default      91      0  0 23:36 ?        00:00:00 ps -ef

在启动好的容器,没有ip addr命令查看不了容器ip,不过可以通过docker inspect命令查看分配给httpd容器的ip是多少。运行后可以看到分配到的ip是172.7.4.2

[root@dev1 ~]# docker inspect httpd | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.7.4.2",
                    "IPAddress": "172.7.4.2",

学习过或者使用过容器都知道,容器之间是相互隔离的,每个容器都有自己的文件系统,运行的进程和网络也是独立的。并且是和主机上的文件系统、进程以及网络上都是隔离的。如下所示:

容器都是怎么做到文件系统、进程、网络等环境相互隔离的,核心就是Namespace和Cgroups可以让一程序在一个资源可控的独立环境中运行,从而做到隔离,这就是容器。

二、命令空间:Namespace

在上面在容器里面执行过命令,查看了容器内的进程,现在在主机上执行:ps -ef | grep httpd。如下主机上的有关httd的进程,可以看到进程号是不一样的。这时候就要说到Namespace了。

[root@dev1 ~]# ps -ef | grep httpd
1001       1895   1875  2 22:48 ?        00:00:00 httpd -D FOREGROUND
1001       1965   1895  0 22:48 ?        00:00:00 httpd -D FOREGROUND
1001       1966   1895  0 22:48 ?        00:00:00 httpd -D FOREGROUND
1001       1972   1895  0 22:48 ?        00:00:00 httpd -D FOREGROUND
1001       1982   1895  0 22:48 ?        00:00:00 httpd -D FOREGROUND
1001       1990   1895  0 22:48 ?        00:00:00 httpd -D FOREGROUND
root       2054   1675  0 22:48 pts/0    00:00:00 grep --color=auto httpd

在主机上执行:可以看到

[root@dev1 ~]# docker ps
CONTAINER ID   IMAGE                     COMMAND                  CREATED        STATUS          PORTS                NAMES
73808be511e5   centos/httpd-24-centos7   "container-entrypoin…"   16 hours ago   Up 30 minutes   8080/tcp, 8443/tcp   httpd
[root@dev1 ~]# ps -ef | grep containerd
root        901      1  0 22:47 ?        00:00:01 /usr/bin/containerd
root       1061      1  0 22:47 ?        00:00:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root       1875      1  0 22:48 ?        00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7 -address /run/containerd/containerd.sock
root      12941   1675  0 23:18 pts/0    00:00:00 grep --color=auto containerd

一张图来诠释主机进程以及容器进程的关系:

Linux在创建容器的时候会创建出一个PID Namespace,这PID就是容器里面的1号进程,然后不同容器的1号进程会创建其子进程。所以containerd-shim在创建容器进程时,会建立一个该进程的Namespace,Namespace下的进程号会从1开始编号,被containerd-shim出来的进程就是1号进程,也就init进程(容器内),被创建出来的Namespace下的进程是看不到其他Namespace里面的进程。

因此通过PID Namespace便实现了进程隔离。

但是在宿主机下是可以看到所有的在此主机上创建出来的Namespace的进程,Host PID Namespace(宿主机命名空间),可以看到它的所有子PID Namespace,这些子命名空间内的进程会在父命名空间里重新被编号。

Namespace就是一种隔离机制,可以隔离运行在同一个宿主机上的进程,让这些进程之间不能访问彼此的资源。

2.1 其他的Namespace

玩过容器的都知道,根据镜像不一样,运行出来的容器里面的文件系统也不一样。可以得出文件系统也是我们做的镜像,例如想容器的网络、文件挂载等,其实都是有对应的Namespace。除了常见的PID Namespace还有实现网络隔离的Network Namespace、实现独立于宿主机文件系统隔离的Mount Namespace。在Linux Program's Manual中可以看到所有Linux支持的Namespace:cgroup/ipc/network/pid/mount/time/user/uts。

三、源自控制组群:Cgroups

全称: Control Groups,是Linux内核的功能,用来限制、控制并分离一个进程的资源(CPU、内存、磁盘等)。在我们通过Namespace隔离出一个容器后,如何让限制这个容器能够访问的资源,例如限制CPU的使用率,内存使用量以及I/O等资源。是支撑容器化的第二个技术Cgroups。

  • CPU子系统:用来限制一组进程能够使用的最大CPU

  • Memory子系统:用来限制一组进程最大的内存使用量

  • pids子系统:用来限制一组进程内最多可以运行多少个进程

  • cpuset子系统:用来控制一组进程可以在哪几个物理CPU上运行

在容器启动后会在Cgroups子系统下创建一个目录,这个目录也被称作控制组。例如在宿主机下:/sys/fs/cgroup/memory/system.slice

[root@dev1 cgroup]# pwd
/sys/fs/cgroup
[root@dev1 cgroup]# ll
total 0
drwxr-xr-x. 4 root root  0 Jul 16 22:47 blkio
lrwxrwxrwx. 1 root root 11 Jul 16 22:47 cpu -> cpu,cpuacct
lrwxrwxrwx. 1 root root 11 Jul 16 22:47 cpuacct -> cpu,cpuacct
drwxr-xr-x. 4 root root  0 Jul 16 22:47 cpu,cpuacct
drwxr-xr-x. 3 root root  0 Jul 16 22:47 cpuset
drwxr-xr-x. 4 root root  0 Jul 16 22:47 devices
drwxr-xr-x. 3 root root  0 Jul 16 22:47 freezer
drwxr-xr-x. 3 root root  0 Jul 16 22:47 hugetlb
drwxr-xr-x. 4 root root  0 Jul 16 22:47 memory
lrwxrwxrwx. 1 root root 16 Jul 16 22:47 net_cls -> net_cls,net_prio
drwxr-xr-x. 3 root root  0 Jul 16 22:47 net_cls,net_prio
lrwxrwxrwx. 1 root root 16 Jul 16 22:47 net_prio -> net_cls,net_prio
drwxr-xr-x. 3 root root  0 Jul 16 22:47 perf_event
drwxr-xr-x. 4 root root  0 Jul 16 22:47 pids
drwxr-xr-x. 4 root root  0 Jul 16 22:47 systemd
[root@dev1 cgroup]# cd memory/
[root@dev1 memory]# cd system.slice/
[root@dev1 system.slice]# ll | grep docker
drwxr-xr-x. 2 root root 0 Jul 16 22:48 data-docker-overlay2-27ff338930f9111ae251b3b4e08078f785e41759e70fc881e1c8d06f6378eb36-merged.mount
drwxr-xr-x. 2 root root 0 Jul 16 22:48 docker-73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7.scope
drwxr-xr-x. 2 root root 0 Jul 16 22:47 docker.service
drwxr-xr-x. 2 root root 0 Jul 16 23:03 run-docker-netns-08a9607d2553.mount

docker-73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7.scope这个目录下的cgroup.procs存储着httpd容器的控制组信息,因为httpd容器的ID就是73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7

如下所示可以看到是httpd容器内的所有进程号(在宿主机上的进程编号),限制了这些进程能够访问的资源。

[root@dev1 docker-73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7.scope]# cat cgroup.procs
1895
1961
1962
1963
1964
1965
1966
1972
1982
1990

在这个容器的目录下,例如memory.limit_in_bytes文件可以限制容器73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7内进程能够访问到的内存总量,写入1073741824到memory.limit_in_bytes,即可限制httpd容器最大能访问宿主机1G的内存。

[root@dev1 docker-73808be511e558e3098c0f513e5bfd274a701751b004292116ae971b8beebcb7.scope]# ll
total 0
-rw-r--r--. 1 root root 0 Jul 16 22:48 cgroup.clone_children
--w--w--w-. 1 root root 0 Jul 16 22:48 cgroup.event_control
-rw-r--r--. 1 root root 0 Jul 16 22:48 cgroup.procs
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.failcnt
--w-------. 1 root root 0 Jul 16 22:48 memory.force_empty
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.failcnt
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.limit_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.max_usage_in_bytes
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.slabinfo
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.tcp.failcnt
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.tcp.limit_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.tcp.max_usage_in_bytes
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.tcp.usage_in_bytes
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.kmem.usage_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.limit_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.max_usage_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.memsw.failcnt
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.memsw.limit_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.memsw.max_usage_in_bytes
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.memsw.usage_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.move_charge_at_immigrate
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.numa_stat
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.oom_control
----------. 1 root root 0 Jul 16 22:48 memory.pressure_level
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.soft_limit_in_bytes
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.stat
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.swappiness
-r--r--r--. 1 root root 0 Jul 16 22:48 memory.usage_in_bytes
-rw-r--r--. 1 root root 0 Jul 16 22:48 memory.use_hierarchy
-rw-r--r--. 1 root root 0 Jul 16 22:48 notify_on_release
-rw-r--r--. 1 root root 0 Jul 16 22:48 tasks

四、总结

Linux的两大技术NamespaceCgroups是用于试下容器的特性,Namespace帮助容器实现各类计算资源的隔离,Cgroups主要对容器的能访问宿主机资源的多少。所以可以说,容器就是Namespace+Cgroups