5分钟Docker容器原理 | cgroup观察和实践篇

《5分钟Docker容器原理 | 开篇》 中,我们提到,为了限制 Docker容器 运行时能够使用的最大资源,我们用到了 Linux cgroup 技术。这篇我们继续来了解下这个技术。

Linux cgroup 介绍

在一个容器中,假如不对其进行资源限制,允许其使用无限大的内存和CPU资源,有时候因为一些内存泄漏,代码逻辑问题,把整个宿主机的内存资源和CPU资源全部占满,影响宿主机上其他进程的允许。为了避免这样的情况发生,我们就必须对容器进行资源限制。
Linux cgroup , 全称 Linux crontrol group , 最初是由Google的工程师提出,后被整合进Linux内核中。Linux cgroup 是一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:内存、CPU、IO等)的机制。
Linux cgroup 的功能如下:

功能 描述
资源限制(Resource limitation) cgroup 可以对进程组使用的资源总额进行限制。比如我们可以设置应用运行时内存上限时,一旦超过整个配额(quota)就会发出OOM(Out of Memory)
优先级分配(Prioritization) cgroup 能够控制进程运行的优先级,实际上这个控制是通过限制分配的CPU时间片数量以及硬盘IO、带宽大小来实现的
资源统计(Accounting) cgroup 可以统计系统的资源总使用量,比如内存用量、CPU使用时长的。
进程控制(Process Control) cgroup 可以对进程组进行挂起、恢复等操作

Linux cgroup 的API是以一个伪文件系统的方式实现,所有资源管理的功能都以子系统(subsystem)的方式管理。比如 CPU 的 cgroup 就一般会放在 /sys/fs/cgroup/cpu里面,内存的 cgroup 就一般会放在 /sys/fs/cgroup/memory里面。

观察一个Docker容器

我们首先通过busybox镜像run一个容器,指定最大CPU配额为0.2,也就是容器能够使用的最大CPU个数为0.2个。

css 复制代码
$ docker run -it --rm --cpus=0.2 --name busybox busybox sh
/ # 
/ # while true; do a=1; done;

我们在里面运行了一个死循环,我们另开一个terminal,用docker stats指令观察容器的CPU利用率

css 复制代码
$ docker stats busybox
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O   BLOCK I/O   PIDS
565d245db66c   busybox   19.97%    148KiB / 62.65GiB   0.00%     0B / 0B   0B / 0B     1

能够看到CPU利用率一直在 20% 附近波动,证明参数是生效的。接下来我们到 /sys/fs/cgroup/cpu下面,找到docker分组下面对应容器ID的目录

shell 复制代码
$ cd /sys/fs/cgroup/cpu
$ ls
cgroup.clone_children  cpuacct.stat          cpu.cfs_quota_us   cpu.stat       notify_on_release  tasks
cgroup.event_control   cpuacct.usage         cpu.rt_period_us   docker         release_agent  
cgroup.procs           cpuacct.usage_percpu  cpu.rt_runtime_us  kubepods       scs
cgroup.sane_behavior   cpu.cfs_period_us     cpu.shares         machine.slice  system.slice
$ cd docker/
$ ls
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 cgroup.clone_children  
565d245db66c2e3fba3d6dec90a49ee03b2c90b9e8ca5ab40fd2d20bc2059562
cgroup.event_control  cgroup.procs  cpuacct.stat notify_on_release
tasks
$ cd 565d245db66c2e3fba3d6dec90a49ee03b2c90b9e8ca5ab40fd2d20bc2059562/
$ ls
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks

能够看到这里就是cgroup 对我们刚刚那个容器进程的CPU限制目录。我们关注2个文件,一个是tasks,里面配置了资源受限的进程ID,另一个是cpu.cfs_quota_us,里面配置了最大CPU额度(quota)。

shell 复制代码
$ cat cpu.cfs_quota_us
20000
$ cat tasks
146058

在CPU的cgroup中,一个CPU核心被切割成了10万份,这里配置了2万,也就是0.2个CPU

CPU cgroup的实践

接下来我们模拟下Docker容器,创建一个CPU受限的进程。

首先在/sys/fs/cgroup/cpu目录下面创建一个目录ob01

diff 复制代码
# sudo su
# cd /sys/fs/cgroup/cpu
# mkdir ob01
# cd ob01/
# ls -l
total 0
-rw-r--r--. 1 root root 0 Dec 29 08:42 cgroup.clone_children
--w--w--w-. 1 root root 0 Dec 29 08:42 cgroup.event_control
-rw-r--r--. 1 root root 0 Dec 29 08:42 cgroup.procs
-r--r--r--. 1 root root 0 Dec 29 08:42 cpuacct.stat
-rw-r--r--. 1 root root 0 Dec 29 08:42 cpuacct.usage
-r--r--r--. 1 root root 0 Dec 29 08:42 cpuacct.usage_percpu
-rw-r--r--. 1 root root 0 Dec 29 08:42 cpu.cfs_period_us
-rw-r--r--. 1 root root 0 Dec 29 08:42 cpu.cfs_quota_us
-rw-r--r--. 1 root root 0 Dec 29 08:42 cpu.rt_period_us
-rw-r--r--. 1 root root 0 Dec 29 08:42 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 Dec 29 08:42 cpu.shares
-r--r--r--. 1 root root 0 Dec 29 08:42 cpu.stat
-rw-r--r--. 1 root root 0 Dec 29 08:42 notify_on_release
-rw-r--r--. 1 root root 0 Dec 29 08:42 tasks

可以看到系统已经帮我们创建了一系列的配置文件了,接下来我们将把CPU配额cpu.cfs_quota_us修改成5万,然后把当前进程ID加入到tasks文件中

shell 复制代码
# echo "50000" > cpu.cfs_quota_us
# echo $$
152314
# echo $$ > tasks 
# while true; do a=1; done;

然后打开另外一个terminal,用top -p 指令查看CPU利用率

yaml 复制代码
$ top -p 152314
top - 21:50:40 up 86 days,  1:36,  2 users,  load average: 0.90, 0.61, 0.60
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.8 us,  0.2 sy,  0.0 ni, 97.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem : 65689704 total,  4661644 free, 20283872 used, 40744188 buff/cache
KiB Swap:        0 total,        0 free,        0 used. 41196384 avail Mem 

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                        
152314 root      20   0  116796   3624   1800 R  49.8  0.0   0:12.89 bash                                                           
......

可以看到CPU利用率也是稳定在 50%, 和我们的限额是保持一致的。

The End

目前我们已经观察和试验过了Docker容器 底层的2个重要技术,Linux namespace 的隔离,还有Linux cgroup 的限额(如果Linux namespace 隔离部分还没看的话可以跳转《5分钟Docker容器原理 | namespace 观察和实践篇》)虽然Docker容器听起来是一个很玄的概念,但是经过我们分析,其实也是一个特殊的进程,并且在我们了解了隔离和限额2个技术以后,它神秘的面纱也被慢慢解开,原理一了解,回过头来看,其实也就这么回事。

接下来我还会继续更新这个系列的其他文章,希望对大家理解Docker原理 带来帮助。

我是老骨头,下次再见。

相关推荐
阿尔帕兹1 小时前
构建 HTTP 服务端与 Docker 镜像:从开发到测试
网络协议·http·docker
ZHOU西口3 小时前
微服务实战系列之玩转Docker(十八)
分布式·docker·云原生·架构·数据安全·etcd·rbac
景天科技苑5 小时前
【云原生开发】K8S多集群资源管理平台架构设计
云原生·容器·kubernetes·k8s·云原生开发·k8s管理系统
wclass-zhengge6 小时前
K8S篇(基本介绍)
云原生·容器·kubernetes
颜淡慕潇6 小时前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
川石课堂软件测试8 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
昌sit!14 小时前
K8S node节点没有相应的pod镜像运行故障处理办法
云原生·容器·kubernetes
追风林15 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
A ?Charis17 小时前
Gitlab-runner running on Kubernetes - hostAliases
容器·kubernetes·gitlab
城南vision17 小时前
Docker学习—Docker核心概念总结
java·学习·docker