如果你曾遇到过“在我机器上跑得好好的,怎么在你那就不行了?”这种程序员终极悖论,那么 Docker 就是为你准备的救命稻草。
🐳 一、 什么是 Docker?(生动的比喻)
1. 搬家与集装箱
想象你要从上海搬家到新加坡。
- 传统方式:你把衣服塞进编织袋,电视机直接搬上车,电脑零散放着。到了新家,你发现插座型号不对,电视机磕坏了,电脑系统因为环境变化启动不了。
- Docker 方式:你租了一个集装箱(Container)。你把所有东西按原样布置在箱子里,连插线板都接好。到了新家,你只需要给集装箱通上电,一切立刻恢复原状。
Docker 就是那个集装箱。 它把你的代码、配置、库文件、甚至操作系统环境全部打包,确保“一次构建,到处运行”。
2. 核心概念三剑客
- 镜像 (Image) —— 建筑蓝图:它是只读的模板。比如“一个装好了 Python 3.9 和 Flask 的 Ubuntu 系统”。
- 容器 (Container) —— 真实的房子:镜像运行起来后的实例。你可以启动、停止、删除它。
- 仓库 (Registry) —— 蓝图展示馆:存放镜像的地方,最著名的是 Docker Hub。
🛠 二、 Docker 核心原理深度解析
Docker 为什么比虚拟机(VM)快?
| 特性 | 虚拟机 (VM) | Docker 容器 |
|---|---|---|
| 内核 | 每个 VM 都有独立的操作系统内核 | 共享宿主机的内核 |
| 启动时间 | 分钟级(需要加载整个系统) | 秒级(本质上是一个受限的进程) |
| 资源占用 | 很大(预分配 GB 级内存) | 极小(按需使用) |
底层技术简述:
- Namespaces (命名空间):给进程戴上“眼罩”,让容器以为自己拥有独立的网络、用户和进程空间。
- Control Groups (控制组):给容器套上“紧箍咒”,限制它能使用的 CPU 和内存上限。
- UnionFS (联合文件系统):分层存储。你修改了文件,它只记录“增量”,不破坏底层的原始镜像。
💻 三、 常用命令与实战场景
1. 镜像操作
| 命令 | 适用场景 |
|---|---|
docker pull nginx |
“买蓝图”:从网上下载一个 Nginx 镜像。 |
docker images |
“看库存”:查看本地电脑里存了哪些镜像。 |
docker rmi <image_id> |
“撕蓝图”:删除不再需要的本地镜像。 |
2. 容器生命周期
| 命令 | 适用场景 |
|---|---|
docker run -d -p 8080:80 --name my-web nginx |
“盖房子”:-d 后台运行,-p 把容器的 80 端口映射到电脑的 8080,并起个名字叫 my-web。 |
docker ps |
“查户口”:看看现在有哪些容器正在运行。 |
docker ps -a |
“翻旧账”:查看所有容器,包括那些已经停止运行运行的。 |
docker stop/start <name> |
“开关灯”:停止或启动现有的容器。 |
docker rm -f <name> |
“强拆”:强制删除一个运行中的容器。 |
3. 进阶调试与交互
| 命令 | 适用场景 |
|---|---|
docker exec -it my-web /bin/bash |
“进屋看看”:以交互模式进入正在运行的容器内部进行排查。 |
docker logs -f my-web |
“听听动静”:实时查看容器打印出来的日志。 |
docker inspect my-web |
“深度查验”:查看容器的 IP 地址、配置、挂载卷等底层信息。 |
📝 四、 快速上手实战:运行一个 Web 服务器
只需要一行命令,你就可以拥有一个 Nginx 服务器:
1 | # 下载并启动 Nginx,映射到本地 80 端口 |
五、 使用经验总结
- 空间管理:根目录不足时的生存法则
docker save极其吃空间,通常需要镜像体积 2-3 倍 的剩余空间作为临时缓存。- 当磁盘空间告急(如剩余不足 30GB 处理 16GB 镜像)时,
docker export是唯一的救命稻草。它采用流式处理,几乎不产生临时文件。 - 瘦身先行:在打包前执行
conda clean --all和rm -rf ~/.cache/pip,通常能瞬间释放数 GB 空间。
- 镜像打包策略:Save vs Export
docker save:适合完整备份。保留所有层(Layers)、环境变量、工作目录和入口脚本(Entrypoint)。docker export:适合环境交付。它将镜像“压扁”成一层,丢失所有历史元数据,但换来的是更小的体积和更高的导入兼容性。
- 协作交付:把“方便”留给对方
- 使用
export导出的镜像在import时必须手动补回关键变量(如PATH、LD_LIBRARY_PATH)。 - 最佳实践:不要给合作者一串长长的运行时命令(
docker run --env ...),而应在docker import时通过--change选项将配置固化进镜像,实现“开箱即用”。
六、 常用命令速查表(增补版)
1. 基础服务管理
| 命令 | 作用对象 | 功能描述 |
|---|---|---|
systemctl start docker |
Docker 服务进程 | 在宿主机上启动 Docker 守护进程(Daemon)。如果不先运行此命令,任何 docker 命令都无法执行。 |
docker start <容器ID> |
具体容器 | 启动一个已经存在但处于停止状态(Exited)的容器。它不会创建新容器,只是唤醒旧容器。 |
2. 核心概念辨析:镜像 vs 容器
- 镜像 (Image):是静态的、只读的模板(类似于电脑操作系统的
.iso安装光盘或类定义)。它存储了运行环境和代码,但不能直接运行代码。 - 容器 (Container):是动态的、可运行的实例(类似于从光盘安装好并开机运行的电脑系统)。它是从镜像启动的,包含一个可写层,你可以在里面跑训练、改代码。
3. 运行与创建命令
- 从镜像启动新容器:
1
2
3# 常用参数:-it (交互模式), --gpus all (调用显卡), -v (挂载目录)
docker run -it --gpus all --name <自定义容器名> <镜像名> /bin/bash
- 进入正在运行的容器:
1
2docker exec -it <容器ID> /bin/bash
4. 状态保存与迁移(Commit/Export/Save)
当你修改了容器内的代码或环境,想把它存下来时:
- 本地保存修改 (Commit):
将当前容器的状态保存为一个新镜像。1
2
3docker commit <容器ID> <新镜像名>:<新标签>
# 示例:docker commit e82722eaf5f8 dp_train:v3
- 三种导出方式对比:
| 命令 | 适用场景 | 特点 |
|---|---|---|
docker commit |
本地迭代 | 产生新镜像,保留所有层。 |
docker save |
完整迁移 | 包含镜像所有历史记录(Metadata),体积大,还原简单(load)。 |
docker export |
环境分发 | 只导出当前容器文件系统,体积最小,需手动补回配置(import)。 |
5. 资源清理命令
- 查看镜像:
docker images - 查看运行中的容器:
docker ps - 查看所有容器(含已停止):
docker ps -a - 删除容器:
docker rm <容器ID> - 删除镜像:
docker rmi <镜像ID> - 清理所有无用的数据:
docker system prune(慎用,会删除所有停止的容器和孤儿镜像)。
镜像打包完成后,建议先执行一次 docker commit 将改动固化,然后再进行 export 或 save 操作,以防导出过程中意外丢失未保存的容器修改。
七、 Dockerfile 核心语法拆解(以项目为例)
一个标准的、高质量的 Dockerfile 通常遵循“从基础到应用”的逻辑。
1 | # 1. FROM: 指定基础镜像。就像选择地基,这里选的是带 CUDA 的 Ubuntu 20.04 |
八、Dockerfile 编写与使用经验总结
1. 常用指令速查表
| 指令 | 作用 | 技巧 |
|---|---|---|
FROM |
声明基础镜像 | 尽量使用官方维护的镜像(如 nvidia/cuda)。 |
RUN |
构建时运行命令 | 每一条 RUN 都会增加一层镜像,用 && 合并命令。 |
COPY |
拷贝本地文件 | COPY . . 会拷贝当前目录下所有文件。 |
ENV |
设置环境变量 | 比如 ENV MUJOCO_GL=osmesa。 |
WORKDIR |
切换工作目录 | 避免频繁使用 RUN cd ...,因为 cd 只在当前 RUN 生效。 |
ARG |
构建时变量 | 只在镜像构建过程中有效,容器运行时无效。 |
EXPOSE |
声明端口 | 比如运行 Jupyter 时需要 EXPOSE 8888。 |
2. 如何使用 Dockerfile 构建镜像?
编写完 Dockerfile 后,在同级目录下执行:
1 | # -t 指定镜像名和标签,后面的点 "." 代表当前目录(寻找 Dockerfile) |
3. 编写“高手”准则
- 顺序原则:将最不常变动的命令(如安装基础系统工具)放在前面,经常改动的(如拷贝代码)放在后面。这样在修改代码重新构建时,Docker 可以直接利用前面的缓存(Cache),秒速完成。
- 清理原则:在同一条
RUN指令中完成安装和清理(如rm -rf /var/lib/apt/lists/*),否则清理操作只会增加一个“删除层”,而不会真正减小镜像体积。 - 非交互模式:使用
ENV DEBIAN_FRONTEND=noninteractive防止构建过程中弹出对话框导致失败。
九、 常用命令速查表(增补:构建与运行)
- 构建镜像:
docker build -t <镜像名>:<标签> . - 查看构建过程中的每一层:
docker history <镜像名> - 从 Dockerfile 启动并重命名:
docker run -d --name my_container <镜像名> - 强制不使用缓存构建:
docker build --no-cache -t <镜像名> .