在云原生场景中,Namespace 和 Cgroup 为资源隔离提供了基础支持,但容器的整体隔离能力仍然不完整。特别是,/proc 和 /sys 文件系统中的一些资源统计信息未被容器化,导致在容器内运行的一些常用命令(如 free、top)无法准确展示容器资源的使用情况。
针对这一问题,TencentOS 内核推出了 cgroupfs 解决方案,旨在改善容器资源展示。cgroupfs 实现了一个虚拟文件系统,包括业务所需的、从容器视角出发的 /proc 和 /sys 等文件,其目录结构与全局的 procfs 和 sysfs 保持一致,以确保与用户工具的兼容性。当实际读取这些文件时,cgroupfs 通过读者进程的上下文动态生成相应的容器信息视图。
挂载 cgroupfs 文件系统
1. 执行以下命令以挂载 cgroupfs 文件系统。
mount -t cgroupfs cgroupfs /cgroupfs/
截图如下:
2. 使用 -v
选项按需挂载 cgroupfs
下的文件到指定容器中,针对 docker 的启动命令如下:
docker run -itd --cpus 2 --cpuset-cpus 0,1,2,4 -v
/cgroupfs/sys/devices/system/cpu/:/sys/devices/system/cpu -v
/cgroupfs/proc/cpuinfo:/proc/cpuinfo -v
/cgroupfs/proc/stat:/proc/stat -v
/cgroupfs/proc/meminfo:/proc/meminfo <image-id> /bin/bash
3. 随后,在容器中查看到了仅属于容器视角的 cpuinfo、stat 以及 meminfo 信息。
pagecache 隔离
当进程文件页的使用过多时,会占用大量内存,导致其他业务的可用内存量减少。这种情况会使得机器的内存分配频繁地陷入低效路径,很容易触发内存不足(OOM)错误。在云原生场景中,我们对内核的页面缓存(page cache)进行了拓展,实现了对容器页面缓存使用量的限制(page cache limit),从而可以单独限制某个容器的页面缓存使用量。
整机 pagecache 限制:
sysctl -w vm.memory_qos=1
sysctl -w vm.pagecache_limit_global=1
echo x > /proc/sys/vm/pagecache_limit_ratio
脏页回收的过程相对耗时。pagecache_limit_ignore_dirty
用于判断在计算 page cache
占用内存时是否忽略脏页。其位置如下:
/proc/sys/vm/pagecache_limit_ignore_dirty。
默认值是 1,表示忽略脏页。
设置 page cache 的回收方式:
/proc/sys/vm/pagecache_limit_async
1 表示异步回收 page cache,TencentOS 会创建一个 [kpclimitd] 内核线程,由这个线程负责 page cache 的回收。
0 表示同步回收,不会创建任何专用回收线程,直接在 page cache limit 触发的进程上下文回收,默认值为 0。
容器 pagecache 限制:
除了支持全局系统级 pagecache 限制外,TencentOS 还支持容器级别的 pagecache 限制,使用方式如下:
1. 开启内存 QOS:
sysctl -w vm.memory_qos=1
2. 关闭全局 pagecache 限制:
sysctl -w vm.pagecache_limit_global=0
3. 进入容器所在 memcg,并设置 memory 限制:
echo value > memory.limit_in_bytes
4. 设置 pagecache 最大用量为当前内存限额的百分比,例如设置10%:
echo 10 > memory.pagecache.max_ratio
5. 设置 pagecache 超额后的回收比例:
echo 5 > memory.pagecache.reclaim_ratio
读写统一限速
Linux 内核原有的 IO 限速方案按照读写分开的方式进行,这要求管理员根据业务模型将 IO 带宽按照读写分开划分并分别实施限速,从而存在带宽浪费的问题。例如,如果配置为读50MB/s,写50MB/s,而实际 IO 带宽为读20MB/s,写50MB/s,则会浪费30MB/s的读带宽。为了解决这一问题,TencentOS 推出了读写统一限速的方案。该方案在用户态为客户提供读写统一限速的配置接口,而在内核态则根据业务流量动态划分读写比例。使用方式如下:
进入业务对应的 blkio 所在的 cgroup,并使用以下配置:echo MAJ:MIN VAL > FILE。
其中 MAJ:MIN 代表设备号,FILE 及 VAL 如下表所示:
|
blkio.throttle.readwrite_bps_device | 读写 Bps 总限制值 |
blkio.throttle.readwrite_iops_device | 读写 iops 总限制 |
blkio.throttle.readwrite_dynamic_ratio | 动态预测读写比例: 0:关闭。使用固定(读:写 - 3:1)比例 1~5:开启动态预测方案。 |
buffer io 限速支持
在 Linux 原生内核中,cgroup v1在限制 buffer IO 速度方面存在一定缺陷。Buffer IO 回写通常是一个异步过程,内核在进行异步刷脏操作时,无法确定该 IO 应该提交给哪个 blkio cgroup,因此无法应用相应的 blkio 限速策略。
基于此,TencentOS 对 cgroup v1 下的 buffer IO 限速功能进行了进一步完善,使其与基于 cgroup v2 的 buffer IO 限速功能保持一致。对于 cgroup v1,我们提供了一个用户态接口,允许将 page cache 所属的 mem_cgroup 与相应的 blkio cgroup 绑定,从而使内核能够根据绑定信息对 buffer IO 进行限速。
1. 要启用 buffer io 的限速功能,需要开启 kernel.io_qos 和 kernel.io_cgv1_buff_wb 两个特性。
sysctl -w kernel.io_qos=1
sysctl -w kernel.io_cgv1_buff_wb=1
2. 为了实现容器的 buffer I/O 限速,需要显式地将容器对应的 memcg 与 blkcg 的 cgroup 进行绑定,操作如下:
echo /sys/fs/cgroup/blkio/A > /sys/fs/cgroup/memory/A/memory.bind_blkio
绑定之后,由 blkio cgroup 提供的对于容器的 buffer i/o 资源的限制机制将能够通过以下接口进行查看:
blkio.throttle.read_bps_device
blkio.throttle.write_bps_device
blkio.throttle.write_iops_device
blkio.throttle.read_iops_device
blkio.throttle.readwrite_bps_device
blkio.throttle.readwrite_iops_device
异步 fork
当大内存业务执行 fork 系统调用以创建子进程时,fork 调用过程的耗时会相对较长,导致业务可能长时间处于内核态而无法处理业务请求。因此,针对该场景优化内核的 fork 时间显得尤为必要。
在 Linux 下,内核处理 fork 的默认流程中,父进程需要将大量进程元数据复制到子进程,其中页表的复制是最耗时的部分,通常占据 fork 调用耗时的97%以上。异步 fork 的设计思想是将复制页表的工作从父进程转移到子进程,这样可以缩短父进程调用 fork 系统调用并陷入内核态的时间,使应用能够尽快返回用户态来处理业务请求,从而解决因 fork 导致的性能抖动问题。
本功能通过 cgroup
来进行开关控制,基本用法:
echo 1 > <cgroup目录>/memory.async_fork
echo 0 > <cgroup目录>/memory.async_fork
该接口默认值为0,即默认关闭。
本页内容是否解决了您的问题?