容器比较强大的地方就是使用方便,强大的隔离性,但是生产上往往需要做到保护,比如a容器不能对于b容器造成任何影响,比如a容器资源占用太高,导致b容器无法响应,获取a容器down机影响宿主机或其他容器等等,都是不允许的,所以容器隔离技术就解决了这些问题!
1、cgroup简介
cgroup是Control Groups的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被LXC、docker等很多项目用于实现进程资源控制。cgroup将任意进程进行分组化管理的 Linux 内核功能。cgroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为cgroup子系统,有以下几大子系统实现:
- blkio:设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
- cpu:使用调度程序为cgroup任务提供cpu的访问。
- cpuacct:产生cgroup任务的cpu资源报告。
- cpuset:如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
- devices:允许或拒绝cgroup任务对设备的访问。
- freezer:暂停和恢复cgroup任务。
- memory:设置每个cgroup的内存限制以及产生内存资源报告。
- net_cls:标记每个网络包以供cgroup方便使用。
- ns:命名空间子系统。
- perf_event:增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程。
目前docker只是用了其中一部分子系统,实现对资源配额和使用的控制。
2、docker如何限制的
使用的是stress镜像压测 :https://hub.docker.com/r/jfusterm/stress
1 | docker pull jfusterm/stress |
1、内存限制
选项 | 描述 |
---|---|
-m ,--memory | 内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M |
--memory-swap | 内存+交换分区大小总限制。格式同上。必须必-m 设置的大 |
--memory-reservation | 内存的软性限制。格式同上 |
--oom-kill-disable | 是否阻止 OOM killer 杀死容器,默认没设置 |
--oom-score-adj | 容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0 |
--memory-swappiness | 用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数 |
--kernel-memory | 核心内存限制。格式同上,最小为 4M |
为了压测我们选择一个容器
1、只设置 -m
参数,可以发现当限制内存在 128m
时,我们还是可以分配128m
的,所以-m并不是全部的限制
1 | ➜ ~ docker run -it --rm -m 128m jfusterm/stress --vm 1 --vm-bytes 128m -t 5s |
当我们继续增加到原来的两倍,发现运行失败
1 | ➜ ~ docker run -it --rm -m 128m jfusterm/stress --vm 1 --vm-bytes 256m -t 5s |
当我们设置为 250m
,发现运行成功
1 | ➜ ~ docker run -it --rm -m 128m jfusterm/stress --vm 1 --vm-bytes 250m -t 5s |
2、设置--memory
和--memory-swap
参数
设置 128 & 128 ,分配 127时OK的,
1 | ➜ ~ docker run -it --rm --memory 128m --memory-swap 128m jfusterm/stress --vm 1 --vm-bytes 127m -t 5s |
设置128 & 128 ,分配128 失败
1 | ➜ ~ docker run -it --rm --memory 128m --memory-swap 128m jfusterm/stress --vm 1 --vm-bytes 128m -t 5s |
2、如何查看容器真实内存
1 | [root@centos-linux ~]# docker run --rm -it -m 128m alpine /bin/sh |
经常遇到这种问题,容器内的内存和真实内存其实不一致的,原因是啥?
这是由于docker产生的容器的隔离性不足造成的。docker创建容器时,会为容器提供一些必要的目录和文件,比如/proc下的若干文件。其中/proc/meminfo文件docker并没有直接提供其生成,而是将宿主机的/proc/meminfo挂载给了容器。因此容器看到的/proc/meminfo与宿主机的/proc/meminfo的内容是一样的。而free命令也不过是查看该文件的信息而已。如果想增强其隔离性,可以使用lxcfs的方式。
那如何解决了?
docker其实本身使用 cgroup
进行隔离,其实它监控也只能监控cgroup,本机是 CentOS Linux release 7.9.2009 (Core)
1 | [root@centos-linux ~]# ls /sys/fs/cgroup/memory/docker/9fc278a2cab2bde9f2aa10fb8eb58732f4d1f3ce0f988ed64cac4b4616a585f1/ |
文件名称 | 含义 |
---|---|
memory.usage_in_bytes | 已使用的内存量(包含cache和buffer)(字节),相当于linux的used_meme |
memory.limit_in_bytes | 限制的内存总量(字节),相当于linux的total_mem |
memory.failcnt | 申请内存失败次数计数 |
memory.memsw.usage_in_bytes | 已使用的内存和swap(字节) |
memory.memsw.limit_in_bytes | 限制的内存和swap容量(字节) |
memory.memsw.failcnt | 申请内存和swap失败次数计数 |
memory.stat | 内存相关状态 |
查看
1 | [root@centos-linux ~]# docker stats 9fc278a2cab2bde9f2aa10fb8eb58732f4d1f3ce0f988ed64cac4b4616a585f1 --no-stream |
所以可以看到俩数据基本一致,具体逻辑:https://github.com/opencontainers/runc/blob/v0.1.1/libcontainer/cgroups/fs/memory.go#L148
参考:Docker容器内存监控