【未经授权,请勿转载,感谢支持】
都知道 numactl 有个 --interleave 的参数,可以将运行的应用绑定在指定的numa节点上,而且使用的内存可以均匀的在多个numa节点上。
例如运行一个脚本 我们可以这样:
bash
// 假设本地有两个numa节点,分别为 0,1
// 如果不知道有多少个numa,可以用 lscpu 查询本地信息
numactl --interleave=0,1 bash ./test.sh
上述命令即可实现运行 test.sh 脚本时,在 0,1 两个numa上都均匀使用内存。
那么问题来了,如果我们是 docker 容器场景,怎样在不修改 docker 镜像的前提下,通过设置docker的配置,使得运行起来的所有容器,都自动可以绑定到多个numa 上,且均匀使用这些numa节点的内存呢?
有的兄弟,有的!下面,直接讲干货!
大致原理如下:
通过截获底层创建容器时 create 命令,在命令前面加上 numactl --interleave 即可。
步骤如下:
- 编写如下脚本
bash
#!/bin/bash
# 定义 NUMA 策略
# 这里设置绑定节点 0,1 并交错分配内存
NUMA_ARGS="--interleave=0,1"
# 真正的 OCI 运行时路径 (通常是 runc,可以通过 which runc 确认)
# 如果你的系统使用的是 crun,请改为 /usr/bin/crun
REAL_RUNTIME="/usr/sbin/runc"
if [ ! -x "$REAL_RUNTIME" ]; then
# 尝试备用路径
REAL_RUNTIME="/usr/bin/runc"
fi
if [ ! -x "$REAL_RUNTIME" ]; then
echo "Error: Cannot find runc. Please install runc or update the script." >&2
exit 1
fi
# 解析参数
# Docker/containerd 会传递一系列参数,我们需要找到 "create" 或 "run" 命令,
# 并在其执行前插入 numactl。
# 但更简单的方法是:如果最后一个参数是 bundle 目录,或者命令是 create,
# 我们直接用 numactl 包裹 runc 的执行。
# 检查是否包含 "create" 子命令 (containerd 通常调用 create)
if [[ "$@" == *"create"* ]]; then
# 构造新命令:numactl [numa_args] runc [original_args]
exec numactl $NUMA_ARGS "$REAL_RUNTIME" "$@"
else
# 如果是其他命令 (如 delete, kill, spec 等),直接透传给 runc,不加 numactl
exec "$REAL_RUNTIME" "$@"
fi
将上面脚本内容存入到一个脚本文件中,例如我这里存放到如下路径中:
/usr/local/bin/docker-numa-runtime
然后将这个脚本添加可执行权限:
bash
chmod +x /usr/local/bin/docker-numa-runtime
- 修改docker的daemon.json文件
vim /etc/docker/daemon.json
如果没有这个文件,可以先创建。添加如下 runtime 信息:
bash
{
"runtimes": {
// numa-balanced 这个名字,建议不要修改
"numa-balanced": {
"path": "/usr/local/bin/docker-numa-runtime", // 如果脚本的位置是自定义的路径,这里记得改一下路径,且确保脚本有可执行权限,最好是 root 权限。
"runtimeArgs": [] // 这里之所以参数留空,是因为脚本中已经处理参数了
}
},
"default-runtime": "numa-balanced" // 这里设置了默认运行时,表示所有的容器都按照上述情况执行;也可以不设置这个,只需要添加 docker run 参数即可,例如 "docker run --runtime numa-balanced ..."
}
- 重启docker
bash
sudo systemctl daemon-reload
sudo systemctl restart docker
ok,至此docker设置已完成!下面来验证成果!
先手动运行一个容器:

说明一下,容器中运行了一个redis,其中规格为16g,且运行了压测命令,让容器尽量使用完分配的内存。如果我设置在numa 0,1 上均匀使用的话,理论上 每个numa肯定会占用超过8G内存。
那么用如下命令,看一下实时的内存使用情况:
watch -n 1 "numastat -m"
bash
Node 0 Node 1 Total
--------------- --------------- ---------------
MemTotal 1547117.36 1572864.00 3119981.36
MemFree 1508840.50 1562577.61 3071418.11
MemUsed 38276.86 10286.39 48563.25
SwapCached 0.00 0.00 0.00
可以看到,numa 0,1 上都使用了内存超过8GB,验证成功!
另外说明一下,我在 k3s 集群中用工具自动化部署容器,生成的容器也是会均匀使用多个numa节点内存资源的。