您现在的位置是:首页 >技术教程 >容器底层实现技术网站首页技术教程
容器底层实现技术
一、Namespace 和 Cgroup
1、容器技术发展历史
2、Docker 容器实现原理
1. Docker 容器在实现上通过 namespace 技术实现进程隔离,
通过Cgroup 技术实现容器进程可用资源的限制
3、Namespace
Namespace :命名空间
1. 作用:资源隔离
2. 原理:namespace 将内核的全局资源进行封装,使得每个 namespace 都有一份独立的资源。因此不同进程在各自namespace 内对同一种资源的使用不会互相干扰
4、PID namespace 隔离示例
1. 已交互模式启动一个centos 容器,并在其中运行 /bin/bash 程序。执行 ps 命令查看到
“ /bin/bash ” 是PID=1 的进程,即Docker 将其隔离于宿主机中的其他进程
# docker run -itd centos /bin/bash 9ed23662b68c43560bfd81951f5638dbaf3c9fa856620a43f68c462c4fae4da3 # docker ps #确认容器是否运行 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9ed23662b68c centos "/bin/bash" 9 seconds ago Up 8 seconds silly_murdock # docker exec -it 9ed23662b68c ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 12052 3008 pts/0 Ss+ 06:15 0:00 /bin/bash root 14 0.0 0.0 44668 3284 pts/1 Rs+ 06:40 0:00 ps aux
2. 使用 docker inspect 查看容器进程在宿主机上的真实 PID。实际上,该容器上运行的
“ /bin/bash ” 在宿主机上时 PID=1282885 的进程
# docker inspect 9ed23662b68c | grep -w Pid "Pid": 1282885, # ps aux | grep 1282885 root 1282885 0.0 0.0 12052 3008 pts/0 Ss+ 06:15 0:00 /bin/bash
3. 分别在宿主机和容器中查看该容器进程相关的 namespace 信息,发现两者是一致的
注:每个进程在 /proc 下都有一个目录,存放 namespace 相关信息
# docker exec -it 9ed23662b68c ls -l /proc/1/ns/ total 0 lrwxrwxrwx 1 root root 0 Oct 27 07:08 cgroup -> 'cgroup:[4026533771]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 ipc -> 'ipc:[4026533675]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 mnt -> 'mnt:[4026533673]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 net -> 'net:[4026533677]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 pid -> 'pid:[4026533676]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 pid_for_children -> 'pid:[4026533676]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 time -> 'time:[4026531834]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 time_for_children -> 'time:[4026531834]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 user -> 'user:[4026531837]' lrwxrwxrwx 1 root root 0 Oct 27 07:08 uts -> 'uts:[4026533674]' # ls -l /proc/1282885/ns/ total 0 lrwxrwxrwx 1 root root 0 Oct 27 06:40 cgroup -> 'cgroup:[4026533771]' lrwxrwxrwx 1 root root 0 Oct 27 06:40 ipc -> 'ipc:[4026533675]' lrwxrwxrwx 1 root root 0 Oct 27 06:40 mnt -> 'mnt:[4026533673]' lrwxrwxrwx 1 root root 0 Oct 27 06:15 net -> 'net:[4026533677]' lrwxrwxrwx 1 root root 0 Oct 27 06:40 pid -> 'pid:[4026533676]' lrwxrwxrwx 1 root root 0 Oct 27 07:10 pid_for_children -> 'pid:[4026533676]' lrwxrwxrwx 1 root root 0 Oct 27 07:10 time -> 'time:[4026531834]' lrwxrwxrwx 1 root root 0 Oct 27 07:10 time_for_children -> 'time:[4026531834]' lrwxrwxrwx 1 root root 0 Oct 27 07:10 user -> 'user:[4026531837]' lrwxrwxrwx 1 root root 0 Oct 27 06:40 uts -> 'uts:[4026533674]'
5、Cgroups
1.概念
control groups:控制组,被整合在了linux内核当中,把进程(tasks)放到组里面,对组设置权限,对进程进行控制。可以理解为用户和组的概念,用户会继承它所在组的权限。
cgroups是linux内核中的机制,这种机制可以根据特定的行为把一系列的任务,子任务整合或者分离,按照资源划分的等级的不同,从而实现资源统一控制的框架,cgroup可以控制、限制、隔离进程所需要的物理资源,包括cpu、内存、IO,为容器虚拟化提供了最基本的保证,是构建docker一系列虚拟化的管理工具
2. 对于开发Cgroup的特点
API:实现管理
cgroup管理可以管理到线程界别
所有线程功能都是subsystem(子系统)统一的管理方式
子进程和父进程在一个cgroup里面,只需要控制父进程就可以
3. cgroup提供了四个功能
资源控制:cgroup通过进程组对资源总额进行限制。如:程序使用内存时,要为程序设定可以使用主机的多少内存,也叫作限额
优先级分配:使用硬件的权重值。当两个程序都需要进程读取cpu,哪个先哪个后,通过优先级来进行控制
资源统计:可以统计硬件资源的用量,如:cpu、内存…使用了多长时间
进程控制:可以对进程组实现挂起/恢复的操作
4. 术语表
- task:表示系统中的某一个进程—PID
- cgoupr:资源控制,以控制组(cgroup)为单位实现,cgroup中有都是task,可以有多个cgroup组,可以限制不同的内容,组名不能相同。
- subsystem:子系统。资源调度控制器。具体控制内容。如:cpu的子系统控制cpu的时间分配,内存的子系统可以控制某个cgroup内的内存使用量,硬盘的子系统,可以控制硬盘的读写等等。
- hierarchy:层级树,一堆cgroup构成,包含多个cgroup的叫层级树,,每个hierarchy通过绑定的子系统对资源进行调度,可以包含0个或多个子节点,子节点继承父节点的属性,整个系统可以有多个hierarchy,是一个逻辑概念
关系:一个cgroup里可以有多个task,subsystem相当于控制cgroup限制的类型, hierarchy里可以有多个cgroup,一个系统可以有多个hierarchy
5. 层级树的四大规则
传统的进程启动,是以init为根节点,也叫父进程,由它来创建子进程,作为子节点,而每个子节点还可以创建新的子节点,这样构成了树状结构。而cgroup的结构跟他类似的。子节点继承父节点的属性。他们最大的不同在于,系统的cgroup构成的层级树允许有多个存在,如果进程模型是init为根节点形成一个树,那cgroup的模型由多个层级树来构成。
如果只有一个层级树,所有的task都会受到一个subsystem的相同的限制,会给不需要这种限制的task造成麻烦
① 同一个层级树(hierarchy)可以附加一个或多个子系统(subsystem)
在一个层级树中,有一个cpu_mem_cg的cgroup组下还有两个子节点cg1和cg2,如图所示,也就意味着在cpu_mem_cg的组中,附加了cpu和mem内存两个子系统,同时来控制cg1和cg2的cpu和内存的硬件资源使用
② 一个子系统(subsystem)可以附加到多个层级树(hierarchy)中,但是仅仅是可以附加到多个没有任何子系统的层级树中
如图所示,cpu子系统先附加到层级树A上,同时就不能附加到层级树B上,因为B上已经有了一个mem子系统,如果B和A同时都是没有任何子系统时,这时,cpu子系统可以同时附加到A和B两个层级树中
言外之意就是,如果多个层级树中都没有子系统,这个时候一个cpu子系统依次可以附加到这些层级树中
③ 一个进程(task)不能属于同一个层级树(hierarchy)的不同cgroup
系统每次新建一个层级树(hierarchy)时,默认的构成了新的层级树的初始化的cgroup,这个cgroup被称为root cgroup,对于你自己成功的层级树(hierarchy)来说,task只能存在这个层级树的一个cgroup当中,意思就是一个层级树中不能出现两个相同的task,但是它可以存在不同的层级树中的其他cgroup。
如果要将一个层级树cgroup中的task添加到这个层级树的其他cgroup时,会被从之前task所在的cgroup移除
如以上图中示例:
httpd已经加入到层级树(hierarchy)A中的cg1中,且pid为58950,此时就不能将这个httpd进程放入到cg2中,不然cg1中的httpd进程就会被删除,但是可以放到层级树(hierarchy)B的cg3控制组中
其实是为了防止出现进程矛盾,如:在层级树A中的cg1中存在httpd进程,这时cpu对cg1的限制使用率为30%,cg2的限制使用率为50%,如果再将httpd进程添加到cg2中,这时httpd的cpu使用率限制就有了矛盾
④ 刚fork出的子进程在初始状态与父进程处于同一个cgroup
进程task新开的一个子进程(child_task)默认是和原来的task在同一个cgroup中,但是child_task允许被移除到该层级树的其他不同的cgroup中。
当fork刚完成之后,父进程和子进程是完全独立的
如图中所示中,httpd58950进程,当有人访问时,会fork出另外一个子进程httpd58951,这个时候默认httpd58951和httpd58950都在cg1中,他们的关系也是父子进程,httpd58951是可以移动到cg2中,这时候就改变了他们的关系,都变为了独立的进程。
Cgroups: Linux Control Group
1.作用:限制一个进程组对系统资源的使用上限,包括CPU、内存、Block/IO等
- Cgroups 还能设置进程优先级,对进程进行挂起和恢复等操作
2.原理:将一组进程放在一个Cgroup中,通过给这个 Cgroup 分配指定的可用资源,
达到控制这一组进程可用资源的目的
3.实现:在Linux 中,Cgroups 以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。 该路径中所有的资源种类均可被 cgroup 限制
术语表:
二、容器资源限制
1、CPU资源限制
可通过如下参数,对容器的可用CPU 资源进行限制:
1. 权重值,表示该进程能使用的CPU 资源的权重值
· --cpu-shares
· -c
# man docker run #可以使用man来查看相关配置含义 定位到这里--cpu-shares=0,看以下参数
2、CPU资源限制示例
1. 启动一个容器,进行压力测试,设置 cpu 权重值为 1024
# docker run -it --name yl1 -c 1024 progrium/stress --cpu 1
stress: info: [1] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [7] forked
可以通过 hub.docker.com 去查询相关镜像想用法
2. 使用 top 命令查看系统宿主机 cpu 使用率
3. 启动第二个容器,cpu权重值依旧设置为 1024
# docker run -it --name yl2 -c 1024 polinux/stress-ng --cpu 1
4. 再次使用 top 命令查看宿主机系统 cpu 使用率
因为我的cpu是多核,所以权重值一样的话各占1个cpu的百分之百
若为单核,CPU占用率应为各占百分之50
5. 查看刚才创建的两个名为 yl1 和 yl2 容器的 CONTAINER ID
# docker ps -a -f name="yl[1|2]"| awk '{print $1}'
CONTAINER
ea105d396f6f
61949c6d3d93
6. 已经自动为这两个容器在 /sys/fs/cgroup/cpu/docker 目录下创建了相应的文件夹
7. 查看容器的 cpu.share 参数值和 tasks 值。其中 tasks 值即为该容器进程在宿主机上的PID
3、内存资源限制
1. 默认情况下,宿主机不限制容器对内存资源的使用。可使用如下参数来控制容器对内存资源的使用:
· --memory: 设置内存资源的使用限额
· --memory-swap:设置内存和 SWAP 资源的使用限额
2. 对进程内存使用限制的纤细配置参数在 /sys/fs/cgroup/memory 目录:
4、内存资源限制示例
1. 启动一个容器,使其最多使用 400M 内存和 100M swap。并进行压力测试
5、Block IO 限制
BLOCK IO 指的是磁盘的读写,可通过如下 3 种方式限制容器读写磁盘的带宽
1. 设置相对权重
· --blkio-weight Block IO(relative weight)
2. 设置 bps:每秒读写的数据量
· --device-read-bps Limit read rate(bytes per second)from a device
· --device-write-bps Limit write rate(bytes per second)to a device
3. 设置 iops:每秒 IO 次数
· --device-read-iops Limit read rate(IO per second)from a device
· --device-write-iops Limit write rate(IO per second)to a device