
cgroups
cgroups是Linux内核的一个特性,主要用来对共享资源进行隔离、限制、审计等。只有能控制分配到容器的资源,才能避免多个容器同时运行时对宿主机的资源竞争。
cgroups提供:
- 资源限制(Resource limiting):可以将组设置为不超过设定的内存限制;
- 优先级(Prioritization):通过优先级让一些组优先得到更多的CPU资源;
- 资源审计(Accounting):用来统计系统实际上把多少资源用到适合的目的上;
- 隔离(Isolation):为组隔离命名空间,这样一个组不会看到另一个组的进程、网络连接和文件系统;
- 控制(Control):挂起、恢复和重启等操作。
安装 Docker 后,用户可以在 /sys/fs/cgroup/memory/docker 目录下看到对 Docker 组应用的各种限制项,包括:
$ ll /sys/fs/cgroup/memory/docker
总用量 0
-rw-r--r-- 1 root root 0 3月 11 18:18 cgroup.clone_children
--w--w--w- 1 root root 0 3月 11 18:18 cgroup.event_control
-rw-r--r-- 1 root root 0 3月 11 18:18 cgroup.procs
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.failcnt
--w------- 1 root root 0 3月 11 18:18 memory.force_empty
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 3月 11 18:18 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 3月 11 18:18 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 3月 11 18:18 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 3月 11 18:18 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 3月 11 18:18 memory.numa_stat
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.oom_control
---------- 1 root root 0 3月 11 18:18 memory.pressure_level
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 3月 11 18:18 memory.stat
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.swappiness
-r--r--r-- 1 root root 0 3月 11 18:18 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 3月 11 18:18 memory.use_hierarchy
-rw-r--r-- 1 root root 0 3月 11 18:18 notify_on_release
-rw-r--r-- 1 root root 0 3月 11 18:18 tasks
可以在创建或启动容器时为每个容器指定资源的限制,例如使用-c|--cpu-shares[=0] 参数来调整容器使用 CPU 的权重;使用 -m|--memory[=MEMORY] 参数来调整容器使用内存的大小。
UnionFS
UnionFS是一种轻量级的高性能分层文件系统,它支持将文件系统的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果。
Docker镜像自身就是由多个文件层组成,每一层有唯一的编号(层ID)。
可以通过docker history
查看一个镜像由哪些层组成:
$ sudo docker history 04060a9dfc39
IMAGE CREATED CREATED BY SIZE COMMENT
04060a9dfc39 4 months ago /bin/sh -c set -x && apk add --no-cache o… 98.2MB
<missing> 4 months ago /bin/sh -c #(nop) ENV JAVA_ALPINE_VERSION=8… 0B
<missing> 4 months ago /bin/sh -c #(nop) ENV JAVA_VERSION=8u181 0B
<missing> 4 months ago /bin/sh -c #(nop) ENV PATH=/usr/local/sbin:… 0B
<missing> 4 months ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jv… 0B
<missing> 4 months ago /bin/sh -c { echo '#!/bin/sh'; echo 'set… 87B
<missing> 4 months ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
<missing> 4 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 4 months ago /bin/sh -c #(nop) ADD file:2ff00caea4e83dfad… 4.41MB
对于 Docker 镜像来说,这些层的内容都是不可修改、只读的。
当 Docker 利用镜像启动一个容器时,将在镜像文件系统的最顶端再挂载一个新的可读写的层给容器。容器中的内容更新将会发生在可读写层。
当所操作的对象位于较深的某层时,需要先复制到最上层的可读写层。当书记对象较大时,往往意味着IO性能较差,因此,一般推荐将容器修改的数据通过 volume 方式挂载,而不是直接修改镜像内的数据。
Docker 所有的存在都在 Docker 目录下,以 Ubuntu 为例,默认路径是 /var/lib/docker。
Linux 网络虚拟化
Docker 的本地网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 vetch pair)。
-
基本原理
Linux虚拟网络技术
-
网络创建过程
一般情况下,Docker创建一个容器的时候,会具体执行如下操作:
<1> 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;
<2> 本地主机一端的虚拟接口连接到默认的 docker0 网桥或指定网桥上,并具有一个以 veth 开头的唯一名字;
<3> 容器一端的虚拟接口将放到新创建的容器中,并修改名字为 eth0,这个接口只有容器的命名空间可见;
<4> 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0 (例如172.17.0.2/16),并配置默认网关为 docker0 网卡的内部接口 docker0 的IP地址。
下面是一个 docker0 和 vetch 的示例:
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
ether 02:42:34:34:1e:51 txqueuelen 0 (Ethernet)
RX packets 1318068311 bytes 409028946152 (380.9 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1499669933 bytes 4630779421080 (4.2 TiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth117260f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 76:39:98:ab:32:33 txqueuelen 0 (Ethernet)
RX packets 35847863 bytes 182779813303 (170.2 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 43562399 bytes 17982858256 (16.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
在使用docker run命令启动容器的时候,可以通过--net
参数来指定容器的网络配置。
有5个可选值bridge、none、container、host 和 用户定义的网络:
- --net=
bridge
:默认值,在 Docker 网桥 docker0 上为容器创建新的网络栈; - --net=
none
:让Docker 将新容器放到隔离的网络栈中,但是不进行网络配置。之后,用户可以自行进行配置。 - --net=
container:NAME_or_ID
:让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享IP地址和端口等网络资源,两者进程可以直接通过 lo 环回接口通信; - --net=
host
:告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。如果进一步使用--privileged=true
参数,容器甚至被允许直接配置主机的网络栈; - --net=
user_defined_netword
:用户自行用 network 相关命令创建一个网络,通过这种方式将容器连接到指定的已创建网络上去。
网友评论