说 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 是 将要运行的容器
这样就可以达到 多个容器数据的共享