第 4 章 容器 - 030 - 实现容器的底层技术
实现容器的底层技术
cgroup 和 namespace 是最重要的两种技术。
- cgroup 实现资源限额
- namespace 实现资源隔离
cgroup
- cgroup 全称 Control Group。
Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额。
- 前面我们看到的
--cpu-shares
、-m
、--device-write-bps
实际上就是在配置 cgroup。 - cgroup 存在 /sys/fs/cgroup 文件中。
举个例子:启动一个容器,设置 --cpu-shares=512
在 /sys/fs/cgroup/cpu/docker 目录中,Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名:
1 root@ubuntu:~# docker run --name container_C -it -c 512 progrium/stress --cpu 2 2 stress: info: [1] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd 3 stress: dbug: [1] using backoff sleep of 6000us 4 stress: dbug: [1] --> hogcpu worker 2 [6] forked 5 stress: dbug: [1] using backoff sleep of 3000us 6 stress: dbug: [1] --> hogcpu worker 1 [7] forked 7 8 root@ubuntu:~# docker ps 9 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 10 2200e14c5e1d progrium/stress "/usr/bin/stress --v…" 10 seconds ago Up 9 seconds container_C 11 f27eb6348026 registry:2 "/entrypoint.sh /etc…" 2 weeks ago Up 2 weeks 0.0.0.0:5000->5000/tcp vigilant_swartz 12 root@ubuntu:~# 13 root@ubuntu:~# ll /sys/fs/cgroup/cpu/docker/2200e14c5e1db5fafd029405f210fc2b208b62823cfd8cbdd7a50bdfe2cf6522/ 14 total 0 15 drwxr-xr-x 2 root root 0 Jan 29 07:32 ./ 16 drwxr-xr-x 4 root root 0 Jan 3 11:00 ../ 17 -rw-r--r-- 1 root root 0 Jan 29 07:32 cgroup.clone_children 18 -rw-r--r-- 1 root root 0 Jan 29 07:31 cgroup.procs 19 -r--r--r-- 1 root root 0 Jan 29 07:32 cpuacct.stat 20 -rw-r--r-- 1 root root 0 Jan 29 07:32 cpuacct.usage 21 -r--r--r-- 1 root root 0 Jan 29 07:32 cpuacct.usage_percpu 22 -rw-r--r-- 1 root root 0 Jan 29 07:32 cpu.cfs_period_us 23 -rw-r--r-- 1 root root 0 Jan 29 07:32 cpu.cfs_quota_us 24 -rw-r--r-- 1 root root 0 Jan 29 07:31 cpu.shares 25 -r--r--r-- 1 root root 0 Jan 29 07:32 cpu.stat 26 -rw-r--r-- 1 root root 0 Jan 29 07:32 notify_on_release 27 -rw-r--r-- 1 root root 0 Jan 29 07:32 tasks 28 root@ubuntu:~# cat /sys/fs/cgroup/cpu/docker/2200e14c5e1db5fafd029405f210fc2b208b62823cfd8cbdd7a50bdfe2cf6522/cpu.shares 29 512 30 root@ubuntu:~#
目录中包含所有与 cpu 相关的 cgroup 配置,文件 cpu.shares 保存的就是 --cpu-shares
的配置,值为 512。
同样的,/sys/fs/cgroup/memory/docker 和 /sys/fs/cgroup/blkio/docker 中保存的是内存以及 Block IO 的 cgroup 配置。
namespace
在每个容器中,我们都可以看到文件系统,网卡等资源,这些资源看上去是容器自己的。拿网卡来说,每个容器都会认为自己有一块独立的网卡,即使 host 上只有一块物理网卡。这种方式非常好,它使得容器更像一个独立的计算机。
Linux 实现这种方式的技术是 namespace。namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace 实现了容器间资源的隔离。
Linux 使用了六种 namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User,下面我们分别讨论。
Mount namespace
Mount namespace 让容器看上去拥有整个文件系统。
容器有自己的 /
目录,可以执行 mount
和 umount
命令。当然我们知道这些操作只在当前容器中生效,不会影响到 host 和其他容器。
UTS namespace
简单的说,UTS namespace 让容器有自己的 hostname。 默认情况下,容器的 hostname 是它的短ID,可以通过 -h
或 --hostname
参数设置。
1 root@ubuntu:~# docker run -it -h myhost ubuntu 2 root@myhost:/# 3 root@myhost:/# hostname 4 myhost 5 root@myhost:/#
IPC namespace
IPC namespace 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起。
PID namespace
我们前面提到过,容器在 host 中以进程的形式运行。例如当前 host 中运行了两个容器:
1 root@ubuntu:~# docker ps 2 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3 2200e14c5e1d progrium/stress "/usr/bin/stress --v…" 38 minutes ago Up 38 minutes container_C 4 f27eb6348026 registry:2 "/entrypoint.sh /etc…" 2 weeks ago Up 2 weeks 0.0.0.0:5000->5000/tcp vigilant_swartz 5 root@ubuntu:~#
通过 ps axf
可以查看容器进程:
1 1015 ? Ssl 11:01 /usr/bin/dockerd -H unix:// 2 15084 ? Sl 0:01 \_ /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 5000 -container-ip 172.17.0.2 -container-port 5000 3 1023 ? Ssl 7:15 /usr/bin/containerd 4 15090 ? Sl 0:44 \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/f27eb6348026862f5744a916f67c4079d36c54fd4609 5 15108 ? Ssl 1:28 | \_ registry serve /etc/docker/registry/config.yml 6 24252 ? Sl 0:00 \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/2200e14c5e1db5fafd029405f210fc2b208b62823cfd 7 24271 pts/0 Ss+ 0:00 \_ /usr/bin/stress --verbose --cpu 2 8 24305 pts/0 R+ 38:57 \_ /usr/bin/stress --verbose --cpu 2 9 24306 pts/0 R+ 38:57 \_ /usr/bin/stress --verbose --cpu 2
所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程。 如果我们进入到某个容器,ps
就只能看到自己的进程了:
1 root@ubuntu:~# docker exec -it 2200e14c5e1d bash 2 root@2200e14c5e1d:/# 3 root@2200e14c5e1d:/# ps axf 4 PID TTY STAT TIME COMMAND 5 8 pts/1 Ss 0:00 bash 6 15 pts/1 R+ 0:00 \_ ps axf 7 1 pts/0 Ss+ 0:00 /usr/bin/stress --verbose --cpu 2 8 6 pts/0 R+ 41:49 /usr/bin/stress --verbose --cpu 2 9 7 pts/0 R+ 41:48 /usr/bin/stress --verbose --cpu 2 10 root@2200e14c5e1d:/#
而且进程的 PID 不同于 host 中对应进程的 PID,容器中 PID=1 的进程当然也不是 host 的 init 进程。也就是说:容器拥有自己独立的一套 PID,这就是 PID namespace 提供的功能。
Network namespace
Network namespace 让容器拥有自己独立的网卡、IP、路由等资源。我们会在后面网络章节详细讨论。
User namespace
User namespace 让容器能够管理自己的用户,host 不能看到容器中创建的用户。
1 root@ubuntu:~# docker exec -it 2200e14c5e1d bash 2 root@2200e14c5e1d:/# 3 root@2200e14c5e1d:/# useradd aaa 4 root@2200e14c5e1d:/# 5 root@2200e14c5e1d:/# id aaa 6 uid=1000(aaa) gid=1000(aaa) groups=1000(aaa) 7 root@2200e14c5e1d:/# exit 8 exit 9 root@ubuntu:~# 10 root@ubuntu:~# id aaa 11 id: ‘aaa’: no such user 12 root@ubuntu:~#
在容器中创建了用户 aaa,但 host 中并不会创建相应的用户。
小结
本章内容
- 容器的各种操作
- 容器状态之间如何转换
- 限制容器使用 CPU、内存和 Block IO 的方法
- 实现容器的底层技术:cgroup 和 namespace
下面是容器的常用操作命令:
- create 创建容器
- run 运行容器
- pause 暂停容器
- unpause 取消暂停继续运行容器
- stop 发送 SIGTERM 停止容器
- kill 发送 SIGKILL 快速停止容器
- start 启动容器
- restart 重启容器
- attach attach 到容器启动进程的终端
- exec 在容器中启动新进程,通常使用 "-it" 参数
- logs 显示容器启动进程的控制台输出,用 "-f" 持续打印
- rm 从磁盘中删除容器
----------------引用来自------------------
https://mp.weixin.qq.com/s?__biz=MzIwMTM5MjUwMg==&mid=2653587673&idx=1&sn=476207c0186182ecdfa70256fbc9853a&chksm=8d3080c0ba4709d62f1b01d631c3456e2536d44207d1ed40947898f9efd2592cd79460a68e00&scene=21#wechat_redirect