为什么 Docker 容器中有额外的目录(如 `/dev`、`/proc`、`/sys`)?及 `docker run` 详细执行过程

news2025/4/23 1:28:42

、当你使用 docker run 启动一个基于极简镜像(如 scratch 或手动构建的镜像)的容器时,发现容器内出现了 /dev/proc/sys 等目录,即使你的镜像中并未包含这些目录。这是因为 Docker 在启动容器时,会自动挂载一些必要的文件系统,以确保容器能够正常运行。本文将详细解答这一现象的原因,并结合问题深入讲解 docker run 的执行过程,揭示 Docker 容器启动的底层机制,提供大量干货。


1. 为什么容器中有 /dev/proc/sys 等目录?

1.1 原因:Docker 自动挂载的默认文件系统

Docker 容器运行在宿主机的 Linux 内核之上,依赖内核提供的功能(如进程管理、设备访问)。为了让容器正常工作,Docker 在启动容器时会自动挂载一些特殊文件系统到容器的根文件系统(rootfs),即使你的镜像(如基于 scratch)不包含这些目录。这些文件系统包括:

  • /proc

    • 类型:procfs(虚拟文件系统)。
    • 作用:提供进程信息、内核状态和系统配置。例如,/proc/cpuinfo 显示 CPU 信息,/proc/self 显示当前进程信息。
    • 为什么挂载:容器内的进程需要访问自身状态(如 PID、环境变量),或与内核交互。
  • /sys

    • 类型:sysfs(虚拟文件系统)。
    • 作用:暴露内核的设备、驱动和硬件信息。例如,/sys/devices 显示设备树。
    • 为什么挂载:容器可能需要访问硬件或内核参数(如网络设备状态)。
  • /dev

    • 类型:devtmpfstmpfs(部分设备节点由 udev 管理)。
    • 作用:提供设备文件(如 /dev/null/dev/zero/dev/random),用于与硬件或内核交互。
    • 为什么挂载:容器需要基本的设备访问,例如标准输入/输出(/dev/stdin)或伪随机数生成(/dev/random)。

这些目录不是来自你的镜像,而是 Docker 在容器启动时通过 挂载(mount) 动态添加的。它们是 Linux 内核提供的虚拟文件系统,存在于内存中,不占用镜像的磁盘空间。

1.2 挂载的来源

这些文件系统通常来自宿主机的内核或特定的文件系统挂载点:

  • /proc/sys:直接挂载宿主机的 /proc/sys,但通过 命名空间隔离(如 PID 和 Mount 命名空间)限制容器只能看到自己的进程和设备信息。
  • /dev:Docker 创建一个 tmpfs 文件系统,并填充必要的设备节点(如 /dev/null)。某些设备(如 GPU)可能通过 --device 选项从宿主机映射。

1.3 如何验证这些目录是挂载的?

在容器内运行以下命令,查看挂载点:

docker run -it my-hello sh
# 假设镜像包含 sh,运行:
mount

输出示例:

proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
devtmpfs on /dev type devtmpfs (rw,nosuid,relatime,size=1024000k,nr_inodes=256000,mode=755)
  • 说明:/procsys/dev 是挂载的虚拟文件系统。

若镜像(如 scratch)不包含 sh,可通过宿主机检查:

# 获取容器 ID
docker ps
# 检查容器挂载
cat /proc/$(docker inspect <container_id> --format '{{.State.Pid}}')/mounts

1.4 如何避免这些目录?

如果你希望容器内不出现这些目录,可以通过以下方式控制,但需谨慎,因为这可能导致容器无法正常运行:

  • 禁用默认挂载:使用 --mount--volume 自定义挂载,但 Docker 通常强制挂载 /proc/dev 的子集。

  • 使用 --read-only:将文件系统设为只读,限制动态修改:

    docker run --read-only my-hello
    

    /proc/sys 仍会挂载,因为它们是必需的。

  • 自定义挂载命名空间:通过 unshare 或自定义容器运行时(如 runc)手动配置,但复杂且不推荐。

注意:完全禁用这些挂载可能导致程序无法运行(如无法访问 /dev/null 或进程信息)。


2. docker run 的执行过程详解

为了深入理解为何会出现 /dev/proc/sys,我们需要剖析 docker run 的完整执行过程。docker run 是 Docker 的核心命令,负责创建和启动容器。以下是其底层步骤,结合问题进行详细讲解:

2.1 解析命令参数

当你运行:

docker run --rm -it my-hello

Docker CLI 解析参数:

  • --rm:容器退出后自动删除。
  • -it:分配交互式终端(-i 保持标准输入,-t 分配伪终端)。
  • my-hello:指定镜像名称。

Docker CLI 将解析后的参数发送到 Docker Daemon(通过 /var/run/docker.sock)。

2.2 拉取镜像(若必要)

若本地不存在 my-hello 镜像,Docker 会从配置的镜像仓库(如 Docker Hub)拉取:

docker pull my-hello

对于本地构建的镜像(如上文基于 scratch 或手动导入),此步骤跳过。

2.3 创建容器

Docker Daemon 调用容器运行时(通常为 containerdrunc)创建容器,涉及以下子步骤:

2.3.1 初始化容器配置
  • 从镜像的元数据(config.json)读取配置,如:
    • CMD:默认执行命令(如 /bin/hello)。
    • ENV:环境变量。
    • EXPOSE:暴露的端口。
  • 合并 docker run 的参数(如 --rm-it)覆盖默认配置。
2.3.2 设置命名空间

Docker 使用 Linux 命名空间隔离容器环境:

  • PID 命名空间:容器有独立的进程树,/proc 只显示容器内进程。
  • Network 命名空间:隔离网络接口、IP 地址。
  • Mount 命名空间:隔离文件系统挂载点。
  • UTS 命名空间:隔离主机名和域名。
  • IPC 命名空间:隔离进程间通信。
  • User 命名空间(可选):映射用户和组 ID。

这些命名空间确保容器内的 /proc/sys 只反映容器自己的状态,而非宿主机。

2.3.3 创建文件系统

Docker 准备容器的根文件系统(rootfs):

  1. 解压镜像层:镜像由多层 tar 归档组成,Docker 将其解压到容器的工作目录(如 /var/lib/docker/overlay2/<container_id>)。

  2. 挂载 rootfs:使用 overlayfs(或其他存储驱动)将镜像层和容器可写层合并,形成统一的根文件系统。

  3. 挂载特殊文件系统

    • 挂载 /procprocfs)以提供进程信息。
    • 挂载 /syssysfs)以提供设备和内核信息。
    • 挂载 /devdevtmpfs),并填充基本设备节点(如 /dev/null/dev/zero)。
    • 若使用 -it,挂载 /dev/tty 或伪终端设备。

    这就是为何 /dev/proc/sys 出现在容器中的原因

  4. 应用挂载选项

    • 如果指定了 --volume--mount,挂载额外的卷。
    • 如果使用了 --read-only,将 rootfs 设为只读(但 /proc 等仍为读写)。
2.3.4 配置 cgroups

Docker 使用 cgroups(控制组)限制容器资源:

  • CPU:限制 CPU 使用(如 --cpus)。
  • 内存:限制内存(如 --memory)。
  • IO:限制磁盘 IO(如 --blkio-weight)。
  • 进程数:限制最大进程数(如 --pids-limit)。

cgroups 信息可在宿主机的 /sys/fs/cgroup 中查看。

2.3.5 配置安全选项
  • Capabilities:Docker 默认丢弃大部分 Linux 权限(如 CAP_SYS_ADMIN),仅保留必要权限(如 CAP_NET_BIND_SERVICE 用于绑定低端口)。
  • Seccomp:应用默认的 seccomp 配置文件,限制系统调用。
  • AppArmor/SELinux(若启用):应用额外的安全策略。

2.4 启动容器

容器创建完成后,Docker 调用运行时(如 runc)启动容器:

  1. 执行入口点
    • 根据镜像的 CMDdocker run 指定的命令,运行主进程(如 /bin/hello)。
    • 如果指定了 -it,为主进程分配伪终端。
  2. 设置网络
    • 默认使用桥接网络(bridge),为容器分配 IP。
    • 如果指定了 --network,应用自定义网络(如 hostnone)。
  3. 处理信号
    • Docker 捕获容器主进程的退出状态,决定是否停止容器。
    • 如果指定了 --rm,容器退出后自动删除。

2.5 运行后处理

  • 日志收集:Docker 将容器的标准输出和错误输出记录到日志(查看:docker logs <container_id>)。
  • 状态更新:更新容器状态(如 runningexited),可通过 docker ps 查看。
  • 端口映射:如果指定了 -p,Docker 配置 iptables 或其他网络规则,将宿主机端口映射到容器端口。

3. 深入剖析 /dev/proc/sys 的挂载

3.1 /proc 的挂载

  • 挂载命令(内部执行):
    mount -t proc proc /proc
    
  • 隔离机制:通过 PID 命名空间,容器只能看到自己的进程(如 /proc/1 是容器主进程)。
  • 禁用方法(不推荐):
    使用 --mount type=tmpfs,destination=/proc 覆盖,但会导致进程无法访问自身信息,可能崩溃。

3.2 /sys 的挂载

  • 挂载命令
    mount -t sysfs sysfs /sys
    
  • 隔离机制:Mount 命名空间限制容器只能访问相关设备信息。
  • 禁用方法:类似 /proc,但禁用可能导致网络或硬件功能失效。

3.3 /dev 的挂载

  • 挂载命令
    mount -t devtmpfs devtmpfs /dev
    
  • 设备节点:Docker 默认创建以下节点:
    • /dev/null/dev/zero/dev/random/dev/urandom
    • /dev/stdin/dev/stdout/dev/stderr(符号链接)。
    • 如果使用 -it,创建 /dev/tty 或伪终端。
  • 管理方式:Docker 使用 udev 或静态设备节点填充 /dev
  • 自定义设备:通过 --device 添加宿主机设备:
    docker run --device=/dev/sda my-hello
    

3.4 为什么不能完全避免挂载?

这些文件系统是 Linux 内核与用户空间交互的桥梁,许多程序(即使是静态二进制)都会直接或间接依赖它们。例如:

  • Go 程序可能读取 /proc/self/stat 获取进程状态。
  • HTTPS 请求需要 /dev/urandom 生成随机数。
  • 标准输出依赖 /dev/stdout

完全禁用这些挂载需要自定义容器运行时(如 runc)并修改内核配置,但复杂且不实用。


4. 高级技巧与干货

4.1 控制挂载行为

  • 最小化 /dev:使用 --device-cgroup-rule 限制设备访问:

    docker run --device-cgroup-rule='c 1:3 rwm' my-hello
    
    • 仅允许访问 /dev/null(字符设备,主设备号 1,次设备号 3)。
  • 覆盖挂载(实验性):

    docker run --mount type=tmpfs,destination=/proc my-hello
    

    但可能导致程序崩溃。

4.2 调试挂载问题

  • 查看容器挂载

    docker inspect <container_id> | grep -i mount
    

    或在宿主机:

    cat /proc/$(docker inspect <container_id> --format '{{.State.Pid}}')/mounts
    
  • 进入容器检查
    如果镜像包含 sh

    docker run -it my-hello sh
    ls /proc /sys /dev
    
  • 导出镜像分析

    docker save my-hello -o my-hello.tar
    tar -tf my-hello.tar
    

    确认镜像本身不包含 /dev/proc/sys

4.3 优化容器启动

  • 禁用不必要的命名空间(实验性):

    docker run --pid=host --network=host my-hello
    
    • 使用宿主机的 PID 和网络命名空间,减少隔离,但安全性降低。
  • 最小化 cgroups

    docker run --memory=50m --cpus=0.5 my-hello
    

4.4 安全性增强

  • 丢弃权限
    docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE my-hello
    
  • 只读文件系统
    docker run --read-only my-hello
    
  • 自定义 seccomp
    创建自定义 seccomp 配置文件,限制系统调用:
    docker run --security-opt seccomp=custom.json my-hello
    

4.5 常见问题与解决

  • 容器无法启动
    • 检查二进制是否静态编译:ldd /bin/hello
    • 确保入口点正确:docker run my-hello /bin/hello
  • 意外挂载过多设备
    • 检查 docker run 参数是否包含 --privileged(会导致挂载宿主机的 /dev)。
    • 使用 --device 精确控制设备。
  • 性能问题
    • 减少挂载:避免不必要的 --volume
    • 优化 cgroups:限制资源使用。

5. 实战案例

案例 1:验证挂载来源

  1. 运行容器:
    docker run -it my-hello sh
    
  2. 检查挂载:
    mount | grep -E 'proc|sys|dev'
    
  3. 宿主机验证:
    docker inspect <container_id> --format '{{.State.Pid}}'
    cat /proc/<pid>/mounts
    

案例 2:最小化 /dev 设备

运行容器,仅允许 /dev/null

docker run --device-cgroup-rule='c 1:3 rwm' my-hello

检查:

docker run -it my-hello ls /dev

仅显示 /dev/null

案例 3:禁用默认挂载(实验性)

尝试覆盖 /proc

docker run --mount type=tmpfs,destination=/proc my-hello

注意:可能导致程序崩溃,仅用于实验。


6. 扩展学习

  • OCI 规范:研究 OCI 运行时规范 理解容器启动。
  • containerd 和 runc:深入学习 Docker 的底层运行时。
  • Linux 内核:学习 procfssysfsdevtmpfs 的实现。
  • 安全强化:探索 AppArmorSELinuxseccomp 的配置。

7. 总结

容器中出现的 /dev/proc/sys 是 Docker 在启动时自动挂载的虚拟文件系统,用于支持进程管理、设备访问和内核交互。这些挂载由 Linux 内核提供,通过命名空间隔离,确保容器只访问自身相关的信息。docker run 的执行过程包括解析参数、拉取镜像、创建容器(设置命名空间、文件系统、cgroups、安全选项)以及启动主进程。通过理解这一过程,你可以优化容器配置、调试问题并增强安全性。建议在测试环境多实践,结合 docker inspectmount 命令深入探索挂载行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2340439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Tailwind 武林奇谈:bg-blue-400 失效,如何重拾蓝衣神功?

前言 江湖有云,Tailwind CSS,乃前端武林中的轻功秘籍。习得此技,排版如行云流水,配色似御风随形,收放自如,随心所欲。 某日,小侠你奋笔敲码,正欲施展“蓝衣神功”(bg-blue-400),让按钮怒气冲冠、蓝光满面,怎料一招使出,画面竟一片白茫茫大地真干净,毫无半点杀气…

开始放飞之先搞个VSCode

文章目录 开始放飞之先搞个VSCode重要提醒安装VSCode下载MinGW-w64回到VSCode中去VSCode原生调试键盘问题遗留问题参考文献 开始放飞之先搞个VSCode 突然发现自己的新台式机上面连个像样的编程环境都没有&#xff0c;全是游戏了&#xff01;&#xff01;&#xff01;&#xff…

基于SA模拟退火算法的车间调度优化matlab仿真,输出甘特图和优化收敛曲线

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于SA模拟退火算法的车间调度优化matlab仿真,输出甘特图和优化收敛曲线。输出指标包括最小平均流动时间&#xff0c;最大完工时间&#xff0c;最小间隙时间。 2…

【仿Mudou库one thread per loop式并发服务器实现】SERVER服务器模块实现

SERVER服务器模块实现 1. Buffer模块2. Socket模块3. Channel模块4. Poller模块5. EventLoop模块5.1 TimerQueue模块5.2 TimeWheel整合到EventLoop5.1 EventLoop与线程结合5.2 EventLoop线程池 6. Connection模块7. Acceptor模块8. TcpServer模块 1. Buffer模块 Buffer模块&…

uniapp h5接入地图选点组件

uniapp h5接入地图选点组件 1、申请腾讯地图key2、代码接入2.1入口页面 &#xff08;pages/map/map&#xff09;templatescript 2.2选点页面&#xff08;pages/map/mapselect/mapselect&#xff09;templatescript 该内容只针对uniapp 打包h5接入地图选点组件做详细说明&#x…

【随缘更新,免积分下载】Selenium chromedriver驱动下载(最新版135.0.7049.42)

目录 一、chromedriver概述 二、chromedriver使用方式 三、chromedriver新版本下载&#x1f525;&#x1f525;&#x1f525; 四、Selenium与Chrome参数设置&#x1f525;&#x1f525; 五、Selenium直接操控已打开的Chrome浏览器&#x1f525;&#x1f525;&#x1f525;…

jenkins批量复制Job项目的shell脚本实现

背景 现在需要将“测试” 目录中的所有job全部复制到 一个新目录中 test2。可以结合jenkins提供的apilinux shell 进行实现。 测试目录的实际文件夹名称是 test。 脚本运行效果如下&#xff1a; [qdevsom5f-dev-hhyl shekk]$ ./copy_jenkins_job.sh 创建文件夹 test2 获取源…

iOS Google登录

iOS Google登录 SDK下载地址在 Firebase 有下载&#xff0c;要下载整个SDK文件&#xff0c;然后拿其中的Google 登录SDK来使用 Firebase 官方文档 github 下载链接

嵌入式工程师( C / C++ )笔试面试题汇总

注&#xff1a;本文为 “嵌入式工程师笔试面试题” 相关文章合辑。 未整理去重。 如有内容异常&#xff0c;请看原文。 嵌入式必会 C 语言笔试题汇总 Z 沉浮 嵌入式之旅 2021 年 01 月 19 日 00:00 用预处理指令 #define 声明一个常数&#xff0c;用以表明 1 年中有多少秒&a…

重构便携钢琴专业边界丨特伦斯便携钢琴V30Pro定义新一代便携电钢琴

在便携电钢琴领域&#xff0c;特伦斯推出的V30Pro折叠钢琴以"技术革新场景适配"的双重升级引发关注。这款产品不仅延续了品牌标志性的折叠结构&#xff0c;更通过声学系统重构与智能交互优化&#xff0c;重新定义了便携乐器的专业边界。 ▶ 核心特点&#xff1a;技术…

DiffuRec: A Diffusion Model for Sequential Recommendation

DiffuRec: A Diffusion Model for Sequential Recommendation Background 序列推荐&#xff08;Sequential Recommendation, SR&#xff09;领域&#xff0c;主流方法是将用户与物品表示为fixed embedding。然而&#xff0c;这种静态向量表达方式难以全面刻画用户多样化的兴趣…

多模态大语言模型arxiv论文略读(三十三)

Jailbreaking Attack against Multimodal Large Language Model ➡️ 论文标题&#xff1a;Jailbreaking Attack against Multimodal Large Language Model ➡️ 论文作者&#xff1a;Zhenxing Niu, Haodong Ren, Xinbo Gao, Gang Hua, Rong Jin ➡️ 研究机构: Xidian Univer…

IntelliJ IDEA download JDK

IntelliJ IDEA download JDK 自动下载各个版本JDK&#xff0c;步骤 File - Project Structure &#xff08;快捷键 Ctrl Shift Alt S&#xff09; 如果下载失败&#xff0c;换个下载站点吧。一般选择Oracle版本&#xff0c;因为java被Oracle收购了 好了。 花里胡哨&#…

计算机网络——常见的网络攻击手段

什么是XSS攻击&#xff0c;如何避免? XSS 攻击&#xff0c;全称跨站脚本攻击&#xff08;Cross-Site Scripting&#xff09;&#xff0c;这会与层叠样式表(Cascading Style Sheets, CSS)的缩写混淆&#xff0c;因此有人将跨站脚本攻击缩写为XSS。它指的是恶意攻击者往Web页面…

面向对象设计中的类的分类:实体类、控制类和边界类

目录 前言1. 实体类&#xff08;Entity Class&#xff09;1.1 定义和作用1.2 实体类的特点1.3 实体类的示例 2. 控制类&#xff08;Control Class&#xff09;2.1 定义和作用2.2 控制类的特点2.3 控制类的示例 3. 边界类&#xff08;Boundary Class&#xff09;3.1 定义和作用3…

鸿蒙ArkUI实战之TextArea组件、RichEditor组件、RichText组件、Search组件的使用

本文接上篇继续更新ArkUI中组件的使用&#xff0c;本文介绍的组件有TextArea组件、RichEditor组件、RichText组件、Search组件&#xff0c;这几个组件的使用对应特定场景&#xff0c;使用时更加需要注意根据需求去使用 TextArea组件 官方文档&#xff1a; TextArea-文本与输…

初创企业机器学习训练:云服务器配置对效率、成本与可扩展性的影响

在当今的初创企业中&#xff0c;机器学习模型训练已成为驱动创新和智能产品的核心环节。然而&#xff0c;深度学习模型的训练通常需要大量的计算资源&#xff0c;如何高效利用云服务器的基础配置成为初创团队关注的重点。云服务器的基础配置通常包括 vCPU&#xff08;虚拟CPU&a…

【“星瑞” O6 评测】—NPU 部署 face parser 模型

前言 瑞莎星睿 O6 (Radxa Orion O6) 拥有高达 28.8TOPs NPU (Neural Processing Unit) 算力&#xff0c;支持 INT4 / INT8 / INT16 / FP16 / BF16 和 TF32 类型的加速。这里通过通过官方的工具链进行FaceParsingBiSeNet的部署 1. FaceParsingBiSeNet onnx 推理 首先从百度网盘…

56、如何快速让⼀个盒⼦⽔平垂直居中

在网页开发中&#xff0c;有多种方式能让一个盒子实现水平垂直居中。下面为你介绍几种常见且快速的方法。 1. 使用 Flexbox 布局 Flexbox 是一种非常便捷的布局模型&#xff0c;能够轻松实现元素的水平和垂直居中。 html <!DOCTYPE html> <html lang"en"&…

BDO分厂积极开展“五个一”安全活动

BDO分厂为规范化学习“五个一”活动主题&#xff0c;按照“上下联动、分头准备 、差异管理、资源共享”的原则&#xff0c;全面激活班组安全活动管理新模式&#xff0c;正在积极开展班组安全活动&#xff0c;以单元班组形式对每个班组每周组织一次“五个一”安全活动。 丁二醇单…