公司动态

【Docker】4.NameSpace空间隔离实战

📅 2026/8/1 22:20:23
【Docker】4.NameSpace空间隔离实战
文章目录1. 基础准备1.1 dd 命令详解与镜像创建1.2 mkfs 命令与文件系统格式化1.2.1 为什么要格式化1.3 df命令详解1.4 mount 命令与磁盘挂载原理1.4.1 U盘类比1.5 unshare 命令概览2. PID 隔离实战2.1 PID 隔离实验操作3. Mount 隔离与文件系统视图4. 思维导图1. 基础准备1.1 dd 命令详解与镜像创建dd(Data Duplicator): 这是一个底层的命令行工具用于在Linux系统上读取、转换并输出原始数据。它可以直接操作字节流因此常用于备份分区、销毁数据或创建指定大小的空文件镜像。dd可从标准输入或文件中读取数据根据指定的格式来转换数据再输出到文件、设备或标准输出。在Linux中最常用于创建、转换和拷贝原始数据的工具就是dd命令。语法ddOPTION参数 if文件名输入文件名默认为标准输入。即指定源文件。of文件名输出文件名默认为标准输出。即指定目的文件。ibsbytes一次读入 bytes 个字节即指定一个块大小为 bytes 个字节。obsbytes一次输出 bytes 个字节即指定一个块大小为 bytes 个字节。bsbytes同时设置读入/输出的块大小为 bytes 个字节。cbsbytes一次转换 bytes 个字节即指定转换缓冲区大小。skipblocks从输入文件开头跳过 blocks 个块后再开始复制。seekblocks从输出文件开头跳过 blocks 个块后再开始复制。countblocks仅拷贝 blocks 个块块大小等于 ibs 指定的字节数。conv关键字关键字可以有以下 11 种conversion用指定的参数转换文件。ascii转换 ebcdic 为 asciiebcdic转换 ascii 为 ebcdicibm转换 ascii 为 alternate ebcdicblock把每一行转换为长度为 cbs不足部分用空格填充unblock使每一行的长度都为 cbs不足部分用空格填充lcase把大写字符转换为小写字符ucase把小写字符转换为大写字符swap交换输入的每对字节noerror出错时不停止notrunc不截短输出文件sync将每个输入块填充到 ibs 个字节不足部分用空NUL字符补齐。--help显示帮助信息--version显示版本信息数据量计算最终生成文件的大小 bs×count。/dev/zero这是一个特殊的设备文件它会提供无限的空字符NULL常作为if来创建空镜像。示例创建一个880MB的磁盘镜像# 创建一个名为 testns 的新目录rootyudukai:~# mkdir testns# 进入 testns 目录rootyudukai:~# cd testns# 查看当前目录的详细信息# total 0 表示目录为空rootyudukai:~/testns# ls -ltotal0# 使用 dd 命令创建一个8MB的测试文件# if/dev/zero输入文件为 /dev/zero生成全0字节# oftest.img输出文件名为 test.img# bs8k块大小为8KB# count1024复制1024个块# 总大小 8KB × 1024 8MBrootyudukai:~/testns# dd if/dev/zero oftest.img bs8k count102410240 recordsin# 读入1024个记录10240 records out# 写出1024个记录8388608bytes(8.4MB,8.0MiB)copied,0.00944598s,888MB/s# 文件大小8MB耗时约0.009秒速度888 MB/s# 以人类可读格式查看文件信息rootyudukai:~/testns# ll -htotal8.1M drwxr-xr-x2root root4.0K Jul2918:14 ./ drwx------15root root4.0K Jul2918:13../ -rw-r--r--1root root8.0M Jul2918:14 test.img# 可以看到 test.img 文件大小为 8.0M# 使用 dd 命令创建一个80MB的测试文件# 总大小 8KB × 10240 80MBrootyudukai:~/testns# dd if/dev/zero oftest2.img bs8k count10240102400 recordsin102400 records out83886080bytes(84MB,80MiB)copied,0.0827256s,1.0GB/s# 文件大小80MB耗时约0.083秒速度1.0 GB/s# 查看目录中的所有文件rootyudukai:~/testns# ll -htotal 89M drwxr-xr-x2root root4.0K Jul2918:14 ./ drwx------15root root4.0K Jul2918:13../ -rw-r--r--1root root 80M Jul2918:14 test2.img# 80MB文件-rw-r--r--1root root8.0M Jul2918:14 test.img# 8MB文件# 使用 vi 编辑器创建一个名为 in.txt 的文件rootyudukai:~/testns# vi in.txt# 在 vi 中输入了内容 Hello World 并保存退出# 使用 dd 命令将 in.txt 转换为大写并输出到 out.txt# ifin.txt输入文件为 in.txt# ofout.txt输出文件为 out.txt# convucase转换选项将小写字母转换为大写字母uppercaserootyudukai:~/testns# dd ifin.txt ofout.txt convucase01 recordsin# 读入01个记录不满一个块01 records out# 写出01个记录12bytes copied,0.000178502s,67.2kB/s# 共复制了12字节Hello World\n含换行符# 查看 in.txt 文件内容rootyudukai:~/testns# cat in.txtHello World# 查看 out.txt 文件内容已全部转为大写rootyudukai:~/testns# cat out.txtHELLO WORLD1.2 mkfs 命令与文件系统格式化mkfs(Make Filesystem):这是一个用于在设备或镜像文件上建立Linux文件系统的命令。它会在磁盘空间内构建必要的数据结构如Superblock超级块、Inode列表和数据块区。在Linux中刚创建的镜像文件就像一张白纸虽然有了空间但没有“格子”和“页码”。为了让操作系统能够管理文件、目录和权限我们需要对其进行格式化。语法mkfs[-V][-t fstype][fs-options]filesys[blocks]参数-t fstype指定要建立何种文件系统如ext3ext4filesys指定要创建的文件系统对应的设备文件名blocks指定文件系统的磁盘块数。-V: 详细显示模式fs-options传递给具体的文件系统的参数1.2.1 为什么要格式化dd创建的镜像像是一本刚从造纸厂出来的、没有任何线条的空白本子。虽然它有厚度空间但你很难整齐地书写信息事后也很难查找某个特定的段落。mkfs格式化后相当于给这本本子画上了行线、印上了页码并预留了目录页。有了这些结构操作系统读者就能根据目录Inode快速找到对应的文字内容Data Block。在Namespace隔离实战中只有经过格式化的镜像才能被mount命令识别并挂载到文件系统中作为一个独立的“小世界”存在。mkfs实际上是一系列命令的通用前端。当你执行格式化时它会根据你指定的类型调用对应的程序如mkfs.ext4或mkfs.xfs在Linux容器技术中ext4是最常用的日志文件系统之一它稳定且支持各种高级特性。示例# 在 test.img 文件上创建 ext4 类型的文件系统# -t ext4指定文件系统类型为 ext4# ./test.img目标文件之前创建的8MB空文件rootyudukai:~/testns# mkfs -t ext4 ./test.imgmke2fs1.46.5(30-Dec-2021)# mke2fs 工具版本信息Discarding device blocks:done# 丢弃设备块针对虚拟设备的优化Creating filesystem with20484k blocks and2048inodes# 创建文件系统信息# - 2048 个 4KB 块 2048 × 4KB 8MB 总大小# - 2048 个 inode索引节点用于存储文件元数据Allocating group tables:done# 分配组表文件系统结构Writing inode tables:done# 写入 inode 表Creating journal(1024blocks):done# 创建日志1024个块用于数据安全恢复Writing superblocks and filesystem accounting information:done# 写入超级块和文件系统统计信息rootyudukai:~/testns#1.3 df命令详解Linux df英文全拼disk free 命令用于显示目前在Linux系统上的文件系统磁盘使用情况统计。语法df[OPTION]...[FILE]...常见参数-a,--all包含所有的具有0Blocks的文件系统-h,--human-readable使用人类可读的格式(预设值是不加这个选项的…)-H,--si很像-h, 但是用1000为单位而不是用1024-t,--typeTYPE限制列出文件系统的TYPE-T,--print-type显示文件系统的形式案例rootyudukai:~/testns# dfFilesystem 1K-blocks Used Available Use% Mounted on tmpfs8140287978473424410% /run /dev/vda11854580522553485215195032815% / tmpfs4070132040701320% /dev/shm tmpfs5120051200% /run/lock tmpfs81402408140240% /run/user/0 rootyudukai:~/testns# df -aFilesystem 1K-blocks Used Available Use% Mounted on sysfs000- /sys proc000- /proc udev4012160040121600% /dev devpts000- /dev/pts tmpfs8140287978473424410% /run /dev/vda11854580522553485215195032815% / securityfs000- /sys/kernel/security tmpfs4070132040701320% /dev/shm tmpfs5120051200% /run/lock cgroup2000- /sys/fs/cgroup pstore000- /sys/fs/pstore bpf000- /sys/fs/bpf systemd-1 - - - - /proc/sys/fs/binfmt_misc mqueue000- /dev/mqueue hugetlbfs000- /dev/hugepages debugfs000- /sys/kernel/debug tracefs000- /sys/kernel/tracing fusectl000- /sys/fs/fuse/connections configfs000- /sys/kernel/config none000- /run/credentials/systemd-sysusers.service binfmt_misc000- /proc/sys/fs/binfmt_misc tracefs000- /sys/kernel/debug/tracing tmpfs81402408140240% /run/user/0 rootyudukai:~/testns# df -hFilesystem Size Used Avail Use% Mounted on tmpfs 795M 78M 718M10% /run /dev/vda1 177G 25G 145G15% / tmpfs3.9G03.9G0% /dev/shm tmpfs5.0M05.0M0% /run/lock tmpfs 795M0795M0% /run/user/0 rootyudukai:~/testns# df -HFilesystem Size Used Avail Use% Mounted on tmpfs 834M 82M 752M10% /run /dev/vda1 190G 27G 156G15% / tmpfs4.2G04.2G0% /dev/shm tmpfs5.3M05.3M0% /run/lock tmpfs 834M0834M0% /run/user/0 rootyudukai:~/testns# df -ThFilesystem Type Size Used Avail Use% Mounted on tmpfs tmpfs 795M 78M 718M10% /run /dev/vda1 ext4 177G 25G 145G15% / tmpfs tmpfs3.9G03.9G0% /dev/shm tmpfs tmpfs5.0M05.0M0% /run/lock tmpfs tmpfs 795M0795M0% /run/user/0 rootyudukai:~/testns#1.4 mount 命令与磁盘挂载原理在Linux的世界里有一个非常迷人的哲学一切皆文件。即使我们刚刚创建并格式化了一个80MB的虚拟磁盘fdimage.img如果你直接用文本编辑器打开它你看到的只会是一堆乱码。要真正像访问普通文件夹那样去读写它我们需要执行“挂载Mount”操作。挂载 (Mounting):是指将一个存储设备如硬盘分区、镜像文件的内容连接到Linux系统目录树中的某个特定目录称为挂载点的过程。一旦挂载成功该目录就成了进入该存储设备的“入口”。1.4.1 U盘类比我们可以把挂载想象成在电脑上插 U 盘镜像文件 (fdimage.img)就像是一个物理 U 盘。虽然你手里拿着它但如果不把它插进接口你无法查看里面的数据。挂载点 (Mount Point)就像是电脑上的USB 接口或路径。在 Linux 中这通常是一个空的文件夹如/data/tmpmount。执行mount就像是把 U 盘插入接口。动作完成后你访问那个文件夹实际上就是在读写这个虚拟镜像里的内容。mount的作用通过mount我们可以把这个镜像挂载到特定的Namespace中让在该空间运行的进程只能看到这个镜像里的文件从而实现文件系统的完全隔离。语法mount[-l]mount[-t vfstype][-o options]devicedir常见参数-l显示已加载的文件系统列表-t加载文件系统类型支持常见系统类型的ext3,ext4,iso9660,tmpfs,xfs等,大部分情况可以不指定mount可以自己识别-o options主要用来描述设备或档案的挂接方式。loop用来把一个文件当成硬盘分区挂接上系统ro采用只读方式挂接设备rw采用读写方式挂接设备device要挂接(mount)的设备。dir挂载点的目录案例rootyudukai:~/testns# lltotal86144drwxr-xr-x2root root4096Jul2918:16 ./ drwx------15root root4096Jul2918:15../ -rw-r--r--1root root12Jul2918:15 in.txt -rw-r--r--1root root12Jul2918:16 out.txt -rw-r--r--1root root83886080Jul2918:14 test2.img -rw-r--r--1root root8388608Jul2918:18 test.img# 再次对 test.img 进行 ext4 文件系统格式化# 注意系统检测到该文件已包含一个 ext4 文件系统并询问是否继续rootyudukai:~/testns# mkfs -t ext4 ./test.imgmke2fs1.46.5(30-Dec-2021)./test.img contains a ext4filesystem created on Wed Jul2918:18:392026Proceed anyway?(y,N)y Discarding device blocks:doneCreating filesystem with20484k blocks and2048inodes Allocating group tables:doneWriting inode tables:doneCreating journal(1024blocks):doneWriting superblocks and filesystem accounting information:done# 创建挂载目录# -p 参数确保父目录 /data 不存在时会自动创建rootyudukai:~/testns# mkdir -p /data/testmymountrootyudukai:~/testns# ll /data/testmymount/total8drwxr-xr-x2root root4096Jul2918:42 ./ drwxr-xr-x3root root4096Jul2918:42../# 将 test.img 挂载到 /data/testmymount# 此时系统会自动使用 loop 设备/dev/loop0来挂载这个镜像文件rootyudukai:~/testns# mount ./test.img /data/testmymount/# 查看磁盘空间确认挂载成功rootyudukai:~/testns# df -hFilesystem Size Used Avail Use% Mounted on tmpfs 795M 78M 718M10% /run /dev/vda1 177G 25G 145G15% / tmpfs3.9G03.9G0% /dev/shm tmpfs5.0M05.0M0% /run/lock tmpfs 795M0795M0% /run/user/0 /dev/loop03.5M 24K3.0M1% /data/testmymount# 可以看到 loop0 设备大小为 3.5M8MB镜像扣除元数据后的实际可用空间较小# 这里显示 3.5M 是因为 8MB 空间很大一部分被日志4MB和元数据占用了# 进入挂载目录rootyudukai:~/testns# cd /data/testmymount/# 写入字符串 123 到 test.txt 文件rootyudukai:/data/testmymount# echo 123 test.txtrootyudukai:/data/testmymount# ll -htotal 28K drwxr-xr-x3root root4.0K Jul2918:43 ./ drwxr-xr-x3root root4.0K Jul2918:42../ drwx------2root root 16K Jul2918:42 lostfound/# ext4 自动创建的恢复目录-rw-r--r--1root root4Jul2918:43 test.txt# 新创建的文件rootyudukai:/data/testmymount# cat test.txt123rootyudukai:/data/testmymount#1.5 unshare 命令概览unshare:顾名思义它的作用是“不共享”。它允许一个进程与其父进程脱离关系不再共享某些指定的命名空间Namespace从而拥有自己独立的运行环境。默认情况下你和你的父进程比如当前的Shell生活在同一个“空间”里。你改了主机名它也能看到你挂载了磁盘它也跟着变。执行unshare后你瞬间开启了一个平行空间。在这个空间里你可以随意修改系统设置如网络、进程ID、挂载点而原本那个空间父进程所在的空间对此一无所知完全不受影响。语法unshare[options]program[arguments]参数-i, --ipc不共享IPC空间-m, --mount不共享Mount空间-n, --net不共享Net空间-p, --pid不共享PID空间-u, --uts不共享UTS空间-U, --user不共享用户-V, --version版本查看--fork执行unshare的进程fork一个新的子进程在子进程里执行unshare传入的参数。--mount-proc执行子进程前将proc优先挂载过去案例# 创建新的 UTS 命名空间并在其中运行 /bin/bash# -u 参数表示创建新的 UTS 命名空间rootyudukai:/data/testmymount# unshare -u /bin/bash# 此时进入了一个隔离的 shell 环境拥有独立的 hostname# 在新命名空间中修改主机名rootyudukai:/data/testmymount# hostname test1# 这里的修改只影响当前命名空间不影响宿主机# 在新命名空间中查看主机名rootyudukai:/data/testmymount# hostnametest1# 显示的是新设置的主机名仅在当前命名空间内有效# 退出命名空间rootyudukai:/data/testmymount# exitexit# 退出后回到原来的命名空间宿主机环境# 在原命名空间中查看主机名rootyudukai:/data/testmymount# hostnameyudukai# 显示原来的主机名证明刚才的修改没有影响宿主机在执行exit之前你在隔离Shell里看到的是新名字。执行exit后你会发现宿主机的主机名完全没有变化。通过这个实验我们成功验证了UTS Namespace的隔离能力。虽然进程依然运行在同一台物理机器和同一个内核上但通过Namespace我们为进程营造了一个“我拥有独立主机名”的假象。这是容器能够拥有独立身份标识的基础。2. PID 隔离实战2.1 PID 隔离实验操作PID(Process ID)Namespace的作用是隔离进程ID空间。在不同的PID命名空间里可以有相同的PID。这也是为什么每个容器都可以有一个PID为1的init进程而互不干扰。--fork参数告诉unshare命令创建一个新的子进程Fork并让这个子进程进入新的PID主机Arootyudukai:/data/testmymount# unshare -p /bin/bashbash: fork: Cannot allocate memory# 直接运行 unshare --pid /bin/bash内核会尝试让当前的 unshare 进程进入新空间这违反了内核规则通常会导致报错Cannot allocate memory无法分配内存rootyudukai:/data/testmymount# exitexitrootyudukai:/data/testmymount# unshare -p --fork /bin/bashrootyudukai:/data/testmymount#主机Bps-ef主机Aps-ef然后主机Arootyudukai:/data/testmymount# exitexitrootyudukai:/data/testmymount# unshare -p --fork --mount-proc /bin/bash如果你只是开启了PID Namespace而不处理/proc新创建的子进程依然会沿用宿主机的/proc挂载点。这意味着虽然你的进程在内核里确实被隔离了但ps工具由于读取的是旧的“窗口”依然会把宿主机的进程全部显示出来。--mount-proc这个参数会在进入新Namespace的同时自动为我们重新挂载一个只属于当前Namespace的/proc文件系统。这样ps就只能看到当前空间内的进程了。使用--mount-proc时内核并不是创建了一个新的文件夹而是为当前PID Namespace提供了一个定制化的接口。这个接口会过滤掉不属于该Namespace的所有进程信息。执行后主机Arootyudukai:/data/testmymount# ps -efUIDPIDPPIDC STIME TTY TIME CMD root10019:17 pts/0 00:00:00 /bin/bash root91019:17 pts/0 00:00:00ps-efrootyudukai:/data/testmymount#主机B一堆进程3. Mount 隔离与文件系统视图设备Arootyudukai:/data/testmymount# df -hFilesystem Size Used Avail Use% Mounted on /dev/vda1 177G 25G 145G15% / tmpfs3.9G03.9G0% /dev/shm tmpfs 795M 78M 718M10% /run tmpfs5.0M05.0M0% /run/lock tmpfs 795M0795M0% /run/user/0 /dev/loop03.5M 28K3.0M1% /data/testmymount rootyudukai:/data/testmymount# exitexitrootyudukai:/data/testmymount#rootyudukai:/data/testmymount# unshare --fork --mount /bin/bashrootyudukai:/data/testmymount# ll -htotal 28K drwxr-xr-x3root root4.0K Jul2918:43 ./ drwxr-xr-x3root root4.0K Jul2918:42../ drwx------2root root 16K Jul2918:42 lostfound/ -rw-r--r--1root root4Jul2918:44 test.txt rootyudukai:/data/testmymount# dd if/dev/zero ofdata2.img bs8k count10240dd: error writingdata2.img:No space left on device4230 recordsin4220 records out3457024bytes(3.5MB,3.3MiB)copied,0.00879398s,393MB/s rootyudukai:/data/testmymount# ls -ltotal3400-rw-r--r--1root root3457024Jul2919:22 data2.img drwx------2root root16384Jul2918:42 lostfound -rw-r--r--1root root4Jul2918:44 test.txt rootyudukai:/data/testmymount# mkfs -t ext4 ./data2.imgmke2fs1.46.5(30-Dec-2021)Filesystem too smallfora journal Discarding device blocks:doneCreating filesystem with8444k blocks and848inodes Allocating group tables:doneWriting inode tables:doneWriting superblocks and filesystem accounting information:donerootyudukai:/data/testmymount# df -hFilesystem Size Used Avail Use% Mounted on /dev/vda1 177G 25G 145G15% / tmpfs3.9G03.9G0% /dev/shm tmpfs 795M 78M 718M10% /run tmpfs5.0M05.0M0% /run/lock tmpfs 795M0795M0% /run/user/0 /dev/loop03.5M 188K2.8M7% /data/testmymount rootyudukai:/data/testmymount# mkdir -p /data/maxhou/data2mountrootyudukai:/data/testmymount# lltotal188drwxr-xr-x3root root4096Jul2919:22 ./ drwxr-xr-x4root root4096Jul2919:24../ -rw-r--r--1root root3457024Jul2919:23 data2.img drwx------2root root16384Jul2918:42 lostfound/ -rw-r--r--1root root4Jul2918:44 test.txt rootyudukai:/data/testmymount# ll /data/maxhou/data2mounttotal8drwxr-xr-x2root root4096Jul2919:24 ./ drwxr-xr-x3root root4096Jul2919:24../ rootyudukai:/data/testmymount# mount -t ext4 ./data2.img /data/maxhou/data2mountrootyudukai:/data/testmymount# df -hFilesystem Size Used Avail Use% Mounted on /dev/vda1 177G 25G 145G15% / tmpfs3.9G03.9G0% /dev/shm tmpfs 795M 78M 718M10% /run tmpfs5.0M05.0M0% /run/lock tmpfs 795M0795M0% /run/user/0 /dev/loop03.5M 188K2.8M7% /data/testmymount /dev/loop13.1M 24K2.9M1% /data/maxhou/data2mount rootyudukai:/data/testmymount# cd /data/maxhou/data2mount/rootyudukai:/data/maxhou/data2mount# echo hello test test.txtrootyudukai:/data/maxhou/data2mount# lltotal28drwxr-xr-x3root root4096Jul2919:27 ./ drwxr-xr-x3root root4096Jul2919:24../ drwx------2root root16384Jul2919:23 lostfound/ -rw-r--r--1root root11Jul2919:27 test.txt rootyudukai:/data/maxhou/data2mount# cat test.txthellotestrootyudukai:/data/maxhou/data2mount#设备Brootyudukai:/data/testmymount# df -hFilesystem Size Used Avail Use% Mounted on tmpfs 795M 78M 718M10% /run /dev/vda1 177G 25G 145G15% / tmpfs3.9G03.9G0% /dev/shm tmpfs5.0M05.0M0% /run/lock tmpfs 795M0795M0% /run/user/0 /dev/loop03.5M 188K2.8M7% /data/testmymount rootyudukai:/data/testmymount# ll /data/maxhou/data2mount/test.txtls: cannot access/data/maxhou/data2mount/test.txt:No suchfileor directory rootyudukai:/data/testmymount#执行unshare --fork --mount /bin/bash时内核为设备A复制了一份当前的挂载点信息但此后设备A的任何挂载操作都是私有的。设备A的视角你执行了mount -t ext4 ./data2.img /data/maxhou/data2mount。内核将这个新的挂载信息记录在了设备A的私有挂载表中。因此执行df -h时你能看到/dev/loop1成功挂载。设备B的视角宿主机的挂载表没有任何变化。在它的认知里/data/maxhou/data2mount仅仅是一个普通目录。在设备A中执行echo hello test test.txt。这个文件实际上被写进了data2.img这个文件系统镜像里。在设备B中执行ll /data/maxhou/data2mount/test.txt。系统返回No such file or directory。结论虽然大家都在访问/data/maxhou/data2mount/但设备A访问的是镜像挂载后的内容而设备B访问的是底层文件系统的空目录。为什么容器需要Mount隔离通过MountNamespace容器可以拥有自己独立的/目录根文件系统。容器启动时开启Mount Namespace。使用pivot_root或chroot将私有挂载点切换为根目录。这样容器里的进程就只能看到镜像里的/bin,/etc而完全感知不到宿主机的存在。4. 思维导图