1. docker 含义
- Docker(Docker - 装卸工, 管理容器的工具) 是把应用和它的所有依赖打包成一个轻量、可移植、标准容器的开源工具/平台,做到一次构建,到处运行。
1.1 通俗理解(集装箱比喻)
- 没 Docker:代码、依赖、配置散放,换环境就报错("在我电脑能跑,你那不行")。
- 有 Docker :把应用 + 依赖 + 配置 + 运行时,全部装进一个标准化集装箱(容器) 。
- 箱内环境独立,和外界隔离
- 任意支持 Docker 的机器(Windows/Mac/Linux)都能直接运行
- 一台机器可同时跑多个容器,互不干扰
1.2 核心概念(3个关键词)
- 镜像(Image)
- 只读模板:含代码、依赖、配置、环境变量
- 类比:食谱/安装包,用来生成容器
- 容器(Container)
- 镜像的运行实例:独立可执行单元
- 类比:做好的菜/运行中的程序,可启停、隔离、资源受限
- 仓库(Registry)
- 存放镜像的地方,如官方 Docker Hub(有大量现成镜像:MySQL、Nginx 等)
2 docker中容器面有内核态驱动吗?
- Docker 容器里没有独立的内核,也不能自带 / 运行 "内核态驱动";所有内核态驱动都在宿主机内核里,容器只是共享它
2.1 硬件驱动怎么在容器里用?
两种情况:
- 通用硬件(网卡、磁盘、显卡)
- 驱动在宿主机内核
- 容器通过 /dev 设备文件(由宿主机挂载进来)访问硬件
- 容器里是用户态驱动 / 库(如 libnvidia-container),内核驱动仍在宿主机
- PCIe 直通 / 硬件虚拟化(GPU、FPGA、网卡)
- 宿主机内核加载硬件驱动
- 把设备直通给容器(--device 或 vfio)
- 容器里只有用户态驱动库,内核驱动在宿主机
3 docker中容器里的应用是怎么调用到宿主机上的内核态驱动呢?
- Docker 容器没有自己内核,容器里的所有程序本质就是宿主机上的普通进程,只是被 Docker 用 Linux 内核的 Namespace + Cgroups 做了隔离。
- 进程一发起系统调用,直接进入宿主机内核,内核再调用对应的内核态驱动。
3.1 举一个例子:
- 容器用 /dev/sda 磁盘:
- 容器里程序读写 /dev/sda
- 调用 read/write 系统调用
- 陷入宿主机内核
- 内核 VFS → 块设备子系统
- 调用 宿主机内核磁盘驱动
- 驱动操作硬件磁盘
- 全程驱动都在宿主机内核,容器根本看不到驱动本身。
3.2 Docker 靠什么隔离,但又能共用内核驱动?
- 靠 Linux 四大核心机制:
- Namespace 隔离:容器看到自己的 PID、网络、挂载、用户,但内核、驱动、硬件完全共享。
- Cgroups: 限制 CPU / 内存 / IO,不让容器乱抢资源。
- Capability 权限控制: 默认不让容器随便 insmod、改内核、访问所有设备。
- 设备文件透传: Docker 可以把宿主机 /dev/xxx 挂载进容器,容器里操作 /dev/xxx 等于直接操作宿主机设备,走宿主机驱动。
3.3 关键总结
- Docker 容器进程 = 宿主机普通进程,只是被隔离了视图
- 容器任何系统调用都会直接进入宿主机内核,没有中间层
- 内核态驱动全部在宿主机内核里,容器只是借道调用,看不到、带不走、装不了自己的驱动
4 关于设备透传
4.1 Docker 设备文件透传(--device)是怎么实现的
- Docker 的 --device /dev/xxx 不是 "模拟设备",而是把宿主机的设备文件,直接映射给容器用,设备还是那个设备,驱动还是那个驱动,只是让容器能访问它。
Docker 设备透传到底做了哪 3 件事?
当运行:
c
docker run --device /dev/nvidia0 ...
- Docker 只干 3 件非常简单的事:
- 把宿主机的 /dev/nvidia0 复制到容器里
- 容器内也出现 /dev/nvidia0。文件一模一样,主设备号 / 次设备号完全相同。这意味着:内核知道它指向同一个硬件。
- 在 cgroup 设备白名单里加一条规则
- 告诉内核允许这个容器访问(主设备号:次设备号)默认容器不能访问任何设备,加了才允许。
- 不做任何虚拟化、不加载任何驱动
- 驱动依然在宿主机内核里。容器里的程序打开 /dev/nvidia0,直接通过系统调用进入宿主机内核,内核找到对应的驱动,完成操作。
- 把宿主机的 /dev/nvidia0 复制到容器里