如何用Bash脚本正确创建cgroup?I/O error报错怎么解决?
Cgroup初始化脚本错误排查与修复
问题描述
我尝试编写init_cgroup.sh脚本,用于创建带有参数指定资源限制的容器。逻辑是先判断cgroup版本、完成挂载,再配置对应资源参数。目标是让组内任务仅在单个核心执行,临时分配给随机CPU。
原实现代码
#!/bin/sh # args: # $1 - mem_limit (not used at the moment) # $2 - core usage percentage (e.g. 0.1) # $3 - container name GROUP_ID=$3 GROUP_PATH="/sys/fs/cgroup" mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=0k cgroup_root $GROUP_PATH mount -t cgroup2 none $GROUP_PATH if [ $? != "0" ]; then mkdir "$GROUP_PATH/$GROUP_ID" mount -t cgroup -o cpu,cpuset,cpuacct,memory cgroup_cpu "$GROUP_PATH/$GROUP_ID" echo 100000 > "$GROUP_PATH/$GROUP_ID/cpu.cfs_quota_us" echo "100000*$2" > "$GROUP_PATH/$GROUP_ID/cpu.cpu.cfs_period_us" cpus_count=$(grep -c processor /proc/cpuinfo) rand_cpu=$RANDOM%cpus_count echo $rand_cpu > "$GROUP_PATH/$GROUP_ID/cpuset.cpus" else { echo "+cpu" echo "+cpuset" echo "+memory" } >> "$GROUP_PATH/cgroup.subtree_control" mkdir "$GROUP_PATH/$GROUP_ID" cpus_count=$(grep -c processor /proc/cpuinfo) rand_cpu=$RANDOM%$cpus_count echo "$rand_cpu" > "$GROUP_PATH/$GROUP_ID/cpuset.cpus" fi
运行错误
使用sudo运行脚本时,能在/sys/fs/cgroup下创建文件夹,但输出错误:
> sudo ./init_cgroup.sh 0 0.1 testt > ./init_cgroup.sh: 29: echo: echo: I/O error
我推测I/O error是因为即使sudo也无足够权限写入系统文件夹,请问该如何解决?我的实现是否正确?
错误原因与修复方案
1. I/O错误的核心原因
不是权限问题,而是脚本存在多个逻辑和操作错误:
- cgroup v1挂载逻辑错误:直接在自定义分组目录挂载cgroup子系统不符合标准流程,应该先将子系统挂载到根目录下的对应子文件夹,再在子系统目录内创建分组。
- 文件路径笔误:
cpu.cpu.cfs_period_us多写了一个cpu.,正确路径是cpu.cfs_period_us。 - 算术表达式未计算:
echo "100000*$2"会把字符串直接写入文件,而非计算后的数值,需要用工具完成数值计算。 - 随机CPU计算语法错误:
rand_cpu=$RANDOM%cpus_count未正确展开变量,无法得到有效CPU编号。 - cgroup v2依赖配置缺失:设置
cpuset.cpus前必须先配置cpuset.mems(绑定内存节点),否则会触发I/O错误。
2. 关键修正要点
- 优化cgroup版本检测:通过
/proc/filesystems和挂载信息判断系统使用的cgroup版本,而非直接尝试挂载cgroup2。 - 修正cgroup v1挂载流程:遵循标准挂载方式,先挂载子系统到根目录下的对应目录,再创建分组。
- 修复数值计算与文件路径:修正笔误,用
bc或$(( ))完成算术计算后写入文件。 - 补充cgroup v2 cpuset依赖:设置
cpuset.cpus前先配置cpuset.mems=0(默认内存节点)。
3. 完整修正脚本
#!/bin/sh # args: # $1 - mem_limit (not used at the moment) # $2 - core usage percentage (e.g. 0.1) # $3 - container name GROUP_ID=$3 CGROUP_ROOT="/sys/fs/cgroup" CPU_PERIOD_US=100000 # 计算CPU配额:quota = period * 百分比,兼容整数和小数 if echo "$2" | grep -q "\."; then CALCULATED_QUOTA=$(echo "$CPU_PERIOD_US * $2" | bc | awk '{print int($1)}') else CALCULATED_QUOTA=$((CPU_PERIOD_US * $2)) fi # 获取CPU核心数并生成随机核心编号 CPUS_COUNT=$(grep -c processor /proc/cpuinfo) RAND_CPU=$((RANDOM % CPUS_COUNT)) # 判断系统使用的cgroup版本 if grep -q cgroup2 /proc/filesystems && mount | grep -q "type cgroup2"; then # cgroup v2 逻辑 # 确保所需子系统已加入控制列表 for ctrl in cpu cpuset memory; do if ! grep -q "+$ctrl" "$CGROUP_ROOT/cgroup.subtree_control"; then echo "+$ctrl" >> "$CGROUP_ROOT/cgroup.subtree_control" fi done # 创建分组目录 mkdir -p "$CGROUP_ROOT/$GROUP_ID" # 先设置内存节点,再绑定CPU核心 echo "0" > "$CGROUP_ROOT/$GROUP_ID/cpuset.mems" echo "$RAND_CPU" > "$CGROUP_ROOT/$GROUP_ID/cpuset.cpus" # 设置CPU使用率限制(cgroup v2用cpu.max,格式为quota period) echo "$CALCULATED_QUOTA $CPU_PERIOD_US" > "$CGROUP_ROOT/$GROUP_ID/cpu.max" else # cgroup v1 逻辑 # 确保cpu/cpuset子系统已挂载 CPU_CGROUP_DIR="$CGROUP_ROOT/cpu" if [ ! -d "$CPU_CGROUP_DIR" ]; then mkdir -p "$CPU_CGROUP_DIR" mount -t cgroup -o cpu,cpuset,cpuacct cgroup_cpu "$CPU_CGROUP_DIR" fi # 创建分组目录 GROUP_DIR="$CPU_CGROUP_DIR/$GROUP_ID" mkdir -p "$GROUP_DIR" # 设置CPU使用率限制 echo "$CALCULATED_QUOTA" > "$GROUP_DIR/cpu.cfs_quota_us" echo "$CPU_PERIOD_US" > "$GROUP_DIR/cpu.cfs_period_us" # 绑定CPU核心和内存节点 echo "$RAND_CPU" > "$GROUP_DIR/cpuset.cpus" echo "0" > "$GROUP_DIR/cpuset.mems" fi echo "Cgroup $GROUP_ID 创建成功,绑定至CPU核心 $RAND_CPU"
4. 使用说明
- 必须以sudo权限运行:
sudo ./init_cgroup.sh 0 0.1 testt - 脚本会自动适配cgroup v1或v2系统
- 已修复所有导致I/O错误的问题,确保资源限制配置生效
内容的提问来源于stack exchange,提问作者ippishio
相关产品推荐
相关产品推荐

