docker基本原理与docker 网络 内存 cpu资源控制 与 目录挂载

说 docker之前,有必要知道 LUX的基本概念

说LUX之前,首先要知道两个概念,一个是cgroups,一个是namespace。

         cgroups 是用于资源的限制与资源的使用监控,cgroups能够为进程分配资源,使linux的资源不再是全局性的。被限制住的进程使用的资源,不能超过所分配阀值。例如该进程的内存被限制住了200个单位,当进程所使用的内存超过200就会报内存溢出错误

         namespace 用于进程的隔离,假如进程A的 namespace 为namespaceA,进程B的namespace为namespaceB,AB进程是无法互相感知的,但A 和 B为同一个namespace ,它们就能相互感知,就好比默认情况下,用户B能用命令查看用户B所启动的进程. namespace不是最近刚出来的新技术,早在Linux内核2.4.19中,就包含了最早的Mount Namespaces.但因为只有一个Mount Namespace,无法满足实际复杂的场景需要,所有在随后的内核版本里面,陆续添加了IPC,Network,PID,User和UTS。docker就是借助这六个Namespace完成资源隔离。

          LXC 就是基于linux内核的 cgroups 与 namespace机制的容器虚拟化技术

AUFS文件系统

         AUFS的功能简单说就是,可以将分布在不同地方的目录挂载到同一个虚拟文件系统当中

         docker 利用LXC虚拟化出来一个容器之后。docker参考linux的启动过程,将一个readonly权限的bootfs挂载到文件系统中,然后通过AUFS,再将readonly权限的rootfs添加到bootfs之上,当rootfs检查完毕之后,再将用户所要使用的文件内容挂载到rootfs之上,同样是readonly权限。每次挂载一个FS文件层,并且每层之间只会挂载增量

          而一个镜像(image)就可以理解为:特定FS层的集合。假如,我们使用一个 tomcat 镜像制造一个自己的镜像,我们会复用原来的tomcat镜像,我们所加的项目文件,就相当于在最上层的可读写层级目录上写入数据,并不会改变原本tomcat镜像中的任何东西。这就是docker的写时复制。因为复用了镜像文件,这样就可以减少硬盘的存储空间,而且不会干扰到容器的运行

AUFS详细请参考 https://blog.csdn.net/leo_is_ant/article/details/52892379

docker daemon

在docker当中,负责管理容器生命周期的是docker daemon,但daemon的作用并不限于管理容器,它可谓是docker环境的大管家,而docker cli就是一堆命令

daemon主要完成的工作是:

(1) daemon负责任务调度,它需要将客户端传来的命令请求转化为特定的任务

(2) daemon 负责维护镜像数据。一个完整的镜像由许多子文件层组成,而这个镜像的依赖关系就是由daemon 来维护的

(3) daemon 负责资源的分配和资源的隔离

(4) daemon负责容器生命周期。daemon需要根据用户指令和容器自身状态来维护容器的生命周期

docker的 资源管理

docker 内存约束

与操作系统类似。容器可使用的内存包括物理内存和swap. docker通过下面两组参数来控制容器内存的使用量

针对容器可使用的内存总量,docker有4种设置方式

a memory=-1,memory-swap=-1(默认设置)

默认情况下,对容器没有内存使用限制,容器可以使用尽可能多的内存资源

b memory=L,memory-swap=-1

仅允许容器使用不超过L字节的内存,但允许容器使用尽可能多的交换分区

例如 docker run -ti -m 300M --memory-swap -1 ubuntu:14.04 /bin/bash

c memory=L

在docker中,如果对--memory-swap没有限制,那么默认是 memory+swap=memory*2

例如 docker run -ti -m 300M ubuntu:14.04 /bin/bash 那么 此时的交换分区为 300M

e memory=L ,memory-swap=M

我们还可以通过--memory-swap来设定允许使用的交换分区上限值

例如 docker run -ti -m 300M --memory-swap 1G ubuntu:14.04 /bin/bash

此时并没有对--memory-swap作出限制,那么默认就是 memory+swap=2memory

--oom-kill-disable

当docker 容器进程超过该资源限制的阀值,就会被操作系统发出OOM事件杀掉,当不想被操作系统杀掉,可以使用--oom-kill-disable 来禁用OOM-Killer,使用此参数时,如果docker 容器 使用 -m 参数,那么当容器使用资源到达上限时,系统既不会停止掉,也不会分配资源给该容器,那么该容器就会处于hung状态, 如果 用户使用了 --oom-kill-disable 参数,而又没有使用-m参数,那么该docker 容器就会尽可能多地使用主机内存资源

例如 限制容器内存使用总量,同时关闭OOM功能

docker run -ti -m 300M --oom-kill-disable ubuntu:14.04 /bin/bash

例如 没有限制内存使用总量,同时还关闭了OOM功能

docker run -ti --oom-kill-disable ubuntu:14.04 /bin/bash

CPU限制

c或者-shares

默认情况下,所有容器都具有平等的CPU使用权重。但用户可以通过Run命令来为每个容器设定CPU使用权重。CPU权重取值从0到1024,可以通过-c或者-shares(CPU共享使用权重)来修改默认值,

例如(容器C0的CPU使用权重为1024 那么 -c=1024)

如果,当前主机系统中运行3个容器:C1,C2,C3。C1的cpu使用权重为1024,另外两个为512,那么C1就会得到50%的CPU时间片,而另外两个就会得到25%的CPU时间片,例外

在一个多核CPU体系中,CPU时间片是所有CPU核总共时间片的总和。

–cpu-period、–cpu-quota

docker提供了–cpu-period、–cpu-quota两个参数控制容器可以分配到的CPU时钟周期。–cpu-period是用来指定容器对CPU的使用要在多长时间内做一次重新分配

cpu-period和cpu-quota的单位为微秒(μs)。cpu-period的最小值为1000微秒,最大值为1秒(10^6 μs),默认值为0.1秒(100000 μs)。cpu-quota的值默认为-1,表示不做控制

详细请参考 https://blog.csdn.net/asd05txffh/article/details/52267818

例如 docker run -ti --cpu-period=50000 --cpu-quota=25000 ubuntu:14.04 /bin/bash

容器进程需要每0.05秒使用单个CPU 0.025秒时间

--cpuset-cpus

除了可以设定CPU使用权重外,我们还可以设定容器可以使用CPU核数

docker run -ti --cpuset-cpus="1,3" ubuntu:14.04 /bin/bash

设定容器仅能使用1号CPU和3号CPU

cgroups 详细介绍

上面提到docker 的 docker 分配系统资源的基本用法,但是docker是怎么做到的呢

docker底层是通过cgroups 来管理资源的,

cgroups 可以限制,记录,隔离进程组所使用的物理资源(包括CPU,memory,I/O等)

cgroups 机制中有四个需要理解的的概念

(1) 任务(task).一个任务对应宿主机环境当中的一个进程

(2) 子系统(subsystem) 每一个子系统是对某一项具体物理资源的控制器。例如cpu 子系统就是对CPU资源的控制,内存子系统就是对内存资源的控制,

例如子系统cpuset可以在多个CPU系统中,为cgroup中的任务分配独立的CPU和内存节点

子系统 memory,可以使在cgroup中任务使用的内存作出限制,同时生产任务的内存资源使用报告

(3) 控制组(control group) cgroups当中最基本的控制单元。假设这里有个进程A,我们要限制其使用的系统内存总量的10%,我们就可以创建一个memory占用10%的cgroup。然后,将进程A添加到cgroup,那么进程A最多占用内存总量的10%

(4)层级树(hierarchy)。cgroups的调度单位,由一个或多个group组成的树状结构.每个层级树通过绑定对应的子系统进行资源调度,同时子节点继承父节点的属性。整个系统可以有多个

层级树

本质上,几十docker daemon将此容器所有的进程都添加到一个group中,所以才能对docker作出资源的限制

docker 网络:

Veth 设备对

引入Veth 设备对是为了在不同的网络命名空间之间进行通信,利用它可以直接将两个网络命名空间连接起来。由于要连接两个网络命名空间,所以Veth设备对都是成对出现的,

很像一对以太网卡,并且中间有一根直连的网线。既然是一对网卡,那么我们将其中一端称为另一端的peer。在veth设备的一端发送数据时,它会将数据直接发送到另一端,

并触发另一端的接收数据

创建设备对

ip link add veth0 type veth peer name veth1

查看网络设备 ip link show

9: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000

link/ether 6a:49:b0:44:c4:90 brd ff:ff:ff:ff:ff:ff

10: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000

link/ether fa:1f:57:16:78:41 brd ff:ff:ff:ff:ff:ff

我们可以看到,有两个设备生成了

现在这两个设备都在自己的命名空间内

其实在docker 内部,veth设备对也是联系容器到外面的重要设备,离开它是不行的

容器网络设置参数

容器的网络设置参数共包括: --dns,--net,--add-host 和 mac-address四个参数

默认情况下,容器会复用主机的DNS设置,但是用户可以通过--dns来覆盖容器内的dns设置. docker每次创建容器时,都会生成一个虚拟MAC地址,用户可以通过

--mac-address来重新设置容器的MAC地址

容器的四种网络链接方式

(1)bridge模式

在bridge模式下,docker daemon第一次启动时会创建一个虚拟的网桥,缺省的名字是docker0,在私有的网络空间中给这个网桥分配一个子网。针对由docker创建出来

的每一个容器,都会创建一个虚拟的以太网设备(veth设备对),其中一端关联到网桥上,另一端使用linux的网络命名空间技术,映射到容器内的eth0设备,然后从网桥的地址

段内给eth0接口分配一个IP地址

(2) none模式 使用--net=none指定

docker 允许通过docker run --net none来关闭网络接口,此时将关闭所有网络数据的输入输出,用户只能通过STDIN,STDOUT或者files来完成I/O操作

(3) host模式: 使用--net=host指定

当网络模式设置为host时,这个容器完全共享host的网络堆栈。host所有的网络接口将完全对容器开放。容器的主机名也会存在于host的hostname中。这时,容器所有对

外暴露的port和对其他容器的link,将完全失效

(4) 复用容器模式

当网络模式设置为container时,这个容器将完全复用另一个容器的网络堆栈。格式为 --net container:<name|id>

如 docker run --rm -ti --net container:redis example/redis-cli -h 127.0.0.1

docker有两种方式来管理容器的存储

docker 目录挂载

数据卷

数据卷是一个特殊的容器,所有的docker 容器都可以连到这个容器里面,

类似于一个nfs

docker run  -it --name volume-test1 -v /data d23bdf5b1b1b 它会把物理机的一个目录挂载到 容器 里的 /data目录

docker inspect id

由以下打印出来的 信息可以得知 物理机的 /var/lib/docker/volumes/c5a1bf135cfa9570a4897cf89917c261ea9dc60920216f7dea6deb44e52bbe6f/_data

挂载到了 容器里的 /data目录

[root@linux-node1 ~]# docker inspect id

[]

Error: No such object: id

[root@linux-node1 ~]# docker inspect d806e787c5f9

"Mounts": [

{

"Type": "volume",

"Name": "c5a1bf135cfa9570a4897cf89917c261ea9dc60920216f7dea6deb44e52bbe6f",

"Source": "/var/lib/docker/volumes/c5a1bf135cfa9570a4897cf89917c261ea9dc60920216f7dea6deb44e52bbe6f/_data",

"Destination": "/data",

"Driver": "local",

"Mode": "",

"RW": true,

"Propagation": ""

},

{

"Type": "volume",

"Name": "1759138c3114805629f6e38387b9ee38fc20a81a410a8e2d3342028750f54969",

"Source": "/var/lib/docker/volumes/1759138c3114805629f6e38387b9ee38fc20a81a410a8e2d3342028750f54969/_data",

"Destination": "/var/lib/registry",

"Driver": "local",

"Mode": "",

"RW": true,

"Propagation": ""

}

],

]

这时我们进入/var/lib/docker/volumes/1759138c3114805629f6e38387b9ee38fc20a81a410a8e2d3342028750f54969/_data 目录

touch heh

然后进入 docker 容器内

root@3ef87a66744a:/data# ls

heh

docker run -it -v /opt:/opt centos 将 物理机的/opt 挂载到容器的/opt下,

这种方式对开发非常有用,例如我们开发时,不需要把项目文件复制到项目文件内,只需要把目录机存放 项目的 目录,挂载到容器内 的指定目录即可

数据卷容器

--volumes-from

一个容器访问另外一个容器的卷

docker run -it --name test1 --volumes-from nfs centos

其中 nfs 是被挂载的容器

centos 是 将要运行的容器

这样就可以达到 多个容器数据的共享

猜你喜欢

转载自blog.csdn.net/weixin_39639119/article/details/83046676