What I cannot create, I do not understand. —— Richard Feynman
操作步骤
0x00 准备环境
强烈建议使用虚拟机
0x01 构建容器文件系统
初始化文件系统目录
1
| mkdir -p /tmp/container-demo/{lower,upper,work,merged}
|
此处的目录结构模拟 Docker 使用的 overlayfs 文件系统。 Overlayfs 允许我们将 2 个文件文件树(upper 和 lower)组合成一个 merged 的组合视图。
下载一个 alpine 的 minirootfs 并解压到 lower 目录中
1 2
| wget https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/alpine-minirootfs-3.21.0-x86_64.tar.gz tar -xvf alpine-minirootfs-3.21.0-x86_64.tar.gz -C lower
|
挂载 overlayFS 文件系统到 merged
1
| mount -t overlay overlay -o lowerdir=lower,upperdir=upper,workdir=work merged
|
现在可以看到 merged 下存在一个 Alpine 文件系统:
1 2
| [root@hyxcontainer-demo] bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
|
现在测试可以直接通过 chroot 可以直接在新的文件系统下启动一个进程
1 2 3 4 5 6 7 8
| [root@hyxcontainer-demo] / NAME="Alpine Linux" ID=alpine VERSION_ID=3.21.0 PRETTY_NAME="Alpine Linux v3.21" HOME_URL="https://alpinelinux.org/" BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
|
确认后退出 chroot
0x02 配置 cgroups
以下步骤基于 cgroup v1,cgroup v2 可以参考引用文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| mkdir -p /sys/fs/cgroup/cpu/docker-demo mkdir -p /sys/fs/cgroup/memory/docker-demo
cd /sys/fs/cgroup/cpu/docker-demo echo 10000 > cpu.cfs_quota_us echo 100000 > cpu.cfs_period_us
cd /sys/fs/cgroup/memory/docker-demo echo 524288000 > memory.limit_in_bytes
echo 0 > memory.swappiness
echo $$ > /sys/fs/cgroup/cpu/docker-demo/tasks echo $$ > /sys/fs/cgroup/memory/docker-demo/tasks
|
这里我们将 cpu.cfs_quota_us 设置为 10000 微秒,cpu.cfs_period_us 设置为 100000 微秒。这意味着在每 100000 微秒(0.1 秒)的时间内,docker-demo 这个 cgroup 内的所有进程总共最多可以使用 10000 微秒的 CPU 时间,相当于限制了 CPU 使用率为 10% (10000 / 100000 = 0.1)。
0x03 命名空间
cgroup 的目的在于限制资源,而命名空间的作用是隔离资源。
下面通过 unshare
新建一个命名空间
1 2 3 4 5 6 7 8 9
| unshare \ --uts \ --pid \ --mount \ --mount-proc \ --net \ --ipc \ --fork \ /bin/bash
|
v1 版本的 cgroup 不支持携带 –cgroup 参数
测试 UTS 隔离
1 2 3 4 5 6 7 8 9 10
| [root@hyxdocker-demo] hyx [root@hyxdocker-demo] [root@hyxdocker-demo] mycontainer
[root@hyxdocker-demo.slice] hyx
|
测试 PID 隔离
1 2 3 4 5 6 7 8 9
| [root@hyxdocker-demo] PID TTY TIME CMD 1 pts/6 00:00:00 bash 48 pts/6 00:00:00 ps
root 26211 5755 0 17:01 pts/6 00:00:00 unshare --uts --pid --mount --mount-proc --net --ipc --fork /bin/bash root 26213 26211 0 17:01 pts/6 00:00:00 /bin/bash
|
0x04 容器端设置
首先通过 pivot_root
重新挂载根目录,实现文件系统级别的隔离
1 2 3 4 5 6 7 8 9 10 11 12
| cd /tmp/container-demo/merged/
mount --make-rprivate /
mkdir old_root
pivot_root . old_root
/bin/umount -l /old_root
/bin/rm -rf /old_root
|
创建设备文件
1 2 3 4 5 6 7 8
| /bin/mknod -m 666 dev/null c 1 3
/bin/mknod -m 666 dev/zero c 1 5
/bin/mknod -m 666 dev/tty c 5 0
/bin/mkdir -p dev/{pts,shm}
|
挂载虚拟文件系统
1 2 3 4 5 6 7 8 9 10
| /bin/mount -t devpts devpts dev/pts
/bin/mount -t tmpfs tmpfs dev/shm
/bin/mount -t sysfs sysfs sys/
/bin/mount -t tmpfs tmpfs run/
/bin/mount -t proc proc proc/
|
启动 sh
0x05 使用容器
在新启动的容器中执行以下命令:
在宿主机中确认 CPU 限制在 10%
1 2 3 4 5 6 7 8 9
| [root@hyx~] top - 09:17:01 up 2 days, 18:40, 4 users, load average: 0.06, 0.10, 0.10 Tasks: 334 total, 2 running, 332 sleeping, 0 stopped, 0 zombie %Cpu(s): 2.9 us, 0.4 sy, 0.0 ni, 96.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 8009200 total, 2174408 free, 3032924 used, 2801868 buff/cache KiB Swap: 15626236 total, 15626236 free, 0 used. 4062548 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 13079 root 20 0 1692 656 460 R 10.0 0.0 0:03.54 busybox
|
在容器中测试使用内存超过 500 MiB (容器内进程)即被终止
完成后取消挂载
1
| umount /tmp/container-demo/merged
|
参考资料
https://michalpitr.substack.com/p/linux-container-from-scratch