您现在的位置是:首页 >技术教程 >容器底层实现技术网站首页技术教程

容器底层实现技术

*_花非人陌_* 2024-07-23 00:01:02
简介容器底层实现技术

一、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

stress镜像介绍

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。