菜单
本页目录

5 Docker-资源限制

问题:
		VM中,在创建虚拟机时,就限制了资源的使用
		容器中,本质上为物理机中的进程,由于Linux系统中的进程可以消耗当前系统的所有资源
		所以:默认情况下,容器没有资源限制

一、Cgroup:

				Linux Cgroup:Linux Control group
				(process container ----》 cgroup)

核心:
    限制资源使用
    优先级控制
    一些审计或一些统计
    挂起进程,恢复执行进程

1)Cgroup子系统:

cpu 子系统,主要限制进程的 cpu 使用率
cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告
cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点
memory 子系统,可以限制进程的 memory 使用量
blkio 子系统,可以限制进程的块设备 io
devices 子系统,可以控制进程能够访问某些设备
net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制
net_prio — 这个子系统用来设计网络流量的优先级
freezer 子系统,可以挂起或者恢复 cgroups 中的进程
ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace
hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统

2)实验:Cgroup实现资源限制

1、挂载

将当前的cgroup系统挂载为文件的视图,修改文件即可实现对应的功能

$ mount -t cgroup			
		拓展:	

		cpu.cfs_period_us		#周期:调度的频率
		cpu.cfs_quota_us		#配额:默认为10万
		tasks					#其中包含了进程ip号
				Cgroup cpu	子系统
					跟系统	/	当前系统所有进程默认都在CPU子系统的跟系统中,且跟系统中没有任何资源限制

2、创建隔离组

$ cd /sys/fs/cgroup/cpu			#进入cgroup子系统中的,CPU子系统的,根系统目录
$ mkdir test					#在CPU子系统的根系统下,创建一个test子系统


            #示例代码
            int main(void)				#定义一个main函数
            { 
                int i = 0;				#定义一个变量为int类型(整型),初始值为0
                for(;;) i++;			#for死循环,每个循环i+1
                return 0;				#如果退出的返回码为0
            }


3、写一个c代码,并执行

$ vim main.c
$ cat main.c
int main(void)
{ 
    int i = 0;
    for(;;) i++;
    return 0;
}

$ yum -y install gcc gcc-c++
$ gcc main.c 
$ ls
a.out  Dockerfile  main.c  overlay
$ ./a.out


另开一个终端,通过top命令得到进程id为:2079

image-20230105195453991

4、对进程加以限制

$ cd /sys/fs/cgroup/cpu/test
$ echo "20000" > cpu.cfs_quota_us

限制成功

image-20230105195610982

3)验证docker使用了cgroup

验证docker创建容器时,使用了cgroup对资源进行限制

[root@localhost ~]# cd /sys/fs/cgroup/cpu/docker/						#进入cgroup子系统中的,cpu根系统中的,docker子系统
[root@localhost docker]# ls												#没有创建容器时,没有对应的容器,资源限制的子系统目录
cgroup.clone_children  cpuacct.usage_percpu  cpu.shares
cgroup.event_control   cpu.cfs_period_us     cpu.stat
cgroup.procs           cpu.cfs_quota_us      notify_on_release
cpuacct.stat           cpu.rt_period_us      tasks
cpuacct.usage          cpu.rt_runtime_us
[root@localhost docker]# docker run --name wordpress -d wordpress		#创建容器,给出容器id号
15f62c10aebcd46f86fdbcd7cb368964cbd47c100e93705548292d213b29633f
[root@localhost docker]# ls												#在docker子系统中出现,以容器id号命名的,限制资源的子系统目录
15f62c10aebcd46f86fdbcd7cb368964cbd47c100e93705548292d213b29633f
cgroup.clone_children
cgroup.event_control
cgroup.procs
cpuacct.stat
cpuacct.usage
cpuacct.usage_percpu
cpu.cfs_period_us
cpu.cfs_quota_us
cpu.rt_period_us
cpu.rt_runtime_us
cpu.shares
cpu.stat
notify_on_release
tasks

#其中也存在对应的限制文件
[root@localhost docker]# ls 15f62c10aebcd46f86fdbcd7cb368964cbd47c100e93705548292d213b29633f/
cgroup.clone_children  cpuacct.usage_percpu  cpu.shares
cgroup.event_control   cpu.cfs_period_us     cpu.stat
cgroup.procs           cpu.cfs_quota_us      notify_on_release
cpuacct.stat           cpu.rt_period_us      tasks
cpuacct.usage          cpu.rt_runtime_us

#对应的,task文件中记录了:容器中运行的进程id
[root@localhost docker]# cat 15f62c10aebcd46f86fdbcd7cb368964cbd47c100e93705548292d213b29633f/tasks 
2200
2232
2233
2234
2235
2236
[root@localhost docker]# ps aux|grep 2200 |grep -v grep
root       2200  0.0  1.1 226812 22284 ?        Ss   20:00   0:00 apache2 -DFOREGROUND

总结:

虽然此处,可以在对应子系统,向cpu.cfg_quota_us中写入资源限制,以实现资源限制
但是,docker有对应的命令,直接实现,所以一般不会直接在此处进行修改,从而实现资源限制
综上:docker使用 cgroup 技术,从而可以实现对资源使用的限制

二、内存资源限制

1)相关概念:

​ 容器默认使用MEM无限制

​ OOME

​ 资源分类

> 默认情况下,如果不对容器做任何限制,容器能够占用当前系统能给容器提供的所有资源
    > Docker 限制可以从 Memory、CPU、Block I/O 三个方面	(Block I/O 很少用)
    													#若仅有一个进程对磁盘I/O进行使用,而进行了限制,会导致资源浪费
    > OOME:Out Of Memory Exception
			>> 当用户使用内存过高时,则会随机杀死进程,知道内存够用位置,包括 docker daemon。
			>> 为此,docker 调整了docker daemon 的 OOM 的优先级,在其他进程被杀死完之前,不会杀死docker daemon
				
	资源分类:
			可压缩型资源:最低所需达不到时,仍然能使用(CPU、等)
			不可压缩型资源:最低所需达不到时,不能使用(仅MEM)

2)容器内存资源限制参数

-m,--memory			#内存限制,最小为4M(官方认为:一个容器能正常运行,他的内核最小使用空间为4M)
					#格式:数字加单位,单位可以是b,k,m,g
					#设置的是物理内存
					
--memory-swap		#内存+swap总大小


拓展:--memory-swap 与 --memory 组合的所有情况

--memoey-swap--memoryMEM+swapMEMswap其他
正数S正数M容器可用总空间SMS-M若S=M,则无可用swap资源
0正数MMM0
unset正数M(前提:主机启用了swap)
3M
M2M
-1(表示最大)正数M(前提:主机启用了swap)
all swap+M
M主机中所有swap

**注意:**在容器中的free命令,看到的空间并不可靠

--memory-reservation			#内存软限制,设置以后(进程使用内存会按照软限制为最大使用,必要情况下,会超出软限制的使用)

--oom-kill-disable				#是否阻止oom killer杀死容器,默认没设置,使用此选项,必须与--memory组合

--oom-score-adj					#容器被oom killer杀死的优先级,范围:[-1000,1000],默认为0
								#数字越小,优先级越低,越不容易被杀死
								#数字越大,优先级越高,越容易被杀死
								
--memory-swappiness				#设置容器的swap控制行为,[0,100),100不能用
						使用实例:需要运行的进程需要1G内存
								若设置为0,此时:内存消耗完全由物理内存提供
								若设置为10,此时:物理内存提供900M,虚拟内存提供100M
						
						拓:swap分区设置(历史遗留问题)
							一般为内存的2倍,但不超过4G,在有PHP或特殊情况下,必须有swap,也有不能使用swap的情况(k8s)
							若物理内存够用下,建议关闭swap
							

--kernel-memory					#容器中,核心内存使用限制,最小为4M

拓展:

容器内部中,内核可以设置OOME
在容器中,设置--kelnel-memory参数后,保障了内核的使用,若内存不够用,内核会随即杀死进程,即容器中的OOME

注意:

加了--kelnel-memory参数后,并不表示万事大吉,还需要对容器整体内存进行设置
			容器中进程无法正常运行的可能:
                        由于容器中内核的OOME,杀死进程
                        由于容器本身整体内存受限制,而启动进程所需内存大于整体内存,导致无法启动而自己死亡

**注意:**物理机不会对内核设置资源限制,避免资源浪费

image-20230105211648614

三、CPU资源限制

1)相关概念

 > 默认情况下,如果不对容器做任何限制,容器能够占用当前系统中的所有 CPU 资源
    > 大多数进程是采用 CFS 调度算法		(公平调度算法,平均分配算法)
    > 1.13 Docker 版本后支持实时调度算法	(动态调度算法)

2)容器CPU限制参数

1、--cpuset-cpus=""

--cpuset-cpus=""			#允许使用的cpu集,值可以为 0-3,0,1(能实现cpu亲和绑定)

详解:

image-20230105224149295

比如CPU有4个计算单元:
      	 0-3		#4个计算单元都允许使用,能使用400%的资源
       	 0			#仅允许使用其中的1个计算单元,能使用100%的资源
       	 0,1		#允许使用其中的2个计算单元,能使用200%的资源

2、--c,--cpu-shares=0

--c,--cpu-shares=0			#cpu共享权值(相对权值),默认为4096

image-20230105230310328

3、--cpu-period=0

--cpu-period=0				#限制cpu CFS的周期

4、--cpu-quota=0

--cpu-quota=0				#限制cpu CFS的配额
						注意:周期/配额,为资源利用率

5、--cpu-mems=""

--cpu-mems=""				#允许在上执行的内存节点(MEMS),只对NUMA系统有效
			拓展:服务器性能升级方式:
						NUMA:cpu之间独立处理
						非NUMA:cpu之间整合处理
						
			实例:
				--cpu-mems="0"		#将cpu产生的数据,仅记录到当前CPU所管理的0号内存条上
				--cpu-mems="0,1"	#将cpu产生的数据,仅记录到当前CPU所管理的0号、1号内存条上
									#根据cpu产生的量级数据确定:
												若量级少,优先一个内存条存储
												若量级大,有限多个内存条存储

6、--cpus

--cpus						#设置能够使用的cpu核心数目
							#可以使用小数
									2		#cpu200%的调用
									0.1		#cpu10%的调用
									0.01	#cpu1%的调用

四、实验演示

1)核心命令

$ docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress -vm 2 

$ docker run --name stress -it --rm --cpus 2 lorel/docker-stress-ng:latest stress --cpu 8  

$ docker run --name stress -it --rm --cpuset-cpus 0 lorel/docker-stress-ng:latest stress --cpu 8 

image-20230105235117493

2)详细实验

建议:将内存调为1G,核心跳到最大

1、不加参数压测-mem

不加参数,对内存进行压测

[root@localhost ~]# docker run --name stress -it --rm lorel/docker-stress-ng:latest stress -vm 8

系统出现OOME杀死进程:

image-20230105235825369

2、加参数压测-mem

加参数限制内存使用,进行压测

#限制容器内存为256m
[root@localhost ~]# docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress -vm 8

容器中出现内核OOME:

image-20230106000557783image-20230106000658614

3、加参数压测-cpu

加参数限制cpu使用,进行压测

#限制cpu使用量200%,所有cpu核心拼凑达到
[root@localhost ~]# docker run --name stress -it --rm --cpus 2 lorel/docker-stress-ng:latest stress --cpu 8

image-20230106001130949

#限制cpu使用量200%,完整使用第0个cpu核心和第1和cpu核心达到
[root@localhost ~]# docker run --name stress -it --rm --cpuset-cpus 0-1 lorel/docker-stress-ng:latest stress --cpu 8

image-20230106001515364

注意:--cpus 与 --cpuset-cpus参数区别

--cpuset-cpus="0-2"		表示使用第0个cpu核心到第2个cpu核心完全调用,最后达到300%使用量
--cpus 3				表示所有cpu核心都能使用,最后拼凑为300%使用量

两者都表示:能使用cpu300%使用量

--cpuset-cpus 0-2 --cpus 1		#表示使用第0个cpu核心到第2个cpu核心,最后拼凑到一起,使用100%的使用量

五:docker是什么?

Docker 是一个使用LXC(linux container)技术
		基于golang语言开发的高级容器引擎
		代码基于h2协议托管在github上
		当前容器技术,也需要依托于内核的各种机制
            比如:
                CGROUP 进行资源限制
                namespace进行隔离,NETWORK、MOUNT、PID等
                veth、bridge、netfilter进行网络联通
                chroot实现内部伪根
                UFS的overlay实现容器可写层、镜像的分层

(限制     隔离       联通   伪根    存储)
(cgroup  namespace  veth  chroot  overlay)