第 4 章 容器 - 030 - 实现容器的底层技术

Posted gsophy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第 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 [email protected]:~# 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 [email protected]:~# 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 [email protected]:~# 
13 [email protected]:~# 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 [email protected]:~# cat /sys/fs/cgroup/cpu/docker/2200e14c5e1db5fafd029405f210fc2b208b62823cfd8cbdd7a50bdfe2cf6522/cpu.shares 
29 512
30 [email protected]:~#

 

目录中包含所有与 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 [email protected]:~# docker run -it -h myhost ubuntu
2 [email protected]:/# 
3 [email protected]:/# hostname
4 myhost
5 [email protected]:/#

 

IPC namespace

IPC namespace 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起。

 

PID namespace

我们前面提到过,容器在 host 中以进程的形式运行。例如当前 host 中运行了两个容器:

1 [email protected]:~# 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 [email protected]:~# 

通过 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 [email protected]:~# docker exec -it 2200e14c5e1d bash
 2 [email protected]:/# 
 3 [email protected]:/# 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 [email protected]:/# 

而且进程的 PID 不同于 host 中对应进程的 PID,容器中 PID=1 的进程当然也不是 host 的 init 进程。也就是说:容器拥有自己独立的一套 PID,这就是 PID namespace 提供的功能。

 

Network namespace

Network namespace 让容器拥有自己独立的网卡、IP、路由等资源。我们会在后面网络章节详细讨论。

 

User namespace

User namespace 让容器能够管理自己的用户,host 不能看到容器中创建的用户。

 1 [email protected]:~# docker exec -it 2200e14c5e1d bash
 2 [email protected]:/# 
 3 [email protected]:/# useradd aaa    
 4 [email protected]:/# 
 5 [email protected]:/# id aaa
 6 uid=1000(aaa) gid=1000(aaa) groups=1000(aaa)
 7 [email protected]:/# exit
 8 exit
 9 [email protected]:~# 
10 [email protected]:~# id aaa
11 id: ‘aaa’: no such user
12 [email protected]:~#

在容器中创建了用户 aaa,但 host 中并不会创建相应的用户。

 

 

 

小结

本章内容

  1. 容器的各种操作
  2. 容器状态之间如何转换
  3. 限制容器使用 CPU、内存和 Block IO 的方法
  4. 实现容器的底层技术: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


以上是关于第 4 章 容器 - 030 - 实现容器的底层技术的主要内容,如果未能解决你的问题,请参考以下文章

第 4 章 容器 - 025 - 容器常用操作

第 4 章 容器 - 027 - 限制容器对内存的使用

系统学习Docker 践行DevOps理念-第1章 容器技术和Docker简介

手写SpringMVC,实现SpringMVC容器,加底层@RequestMapping的映射

《C++Primer(第5版)》第九章笔记

第 09 章:虎行有雨,定义标记类型Aware接口,实现感知容器对象