0%

Docker 新手入坑指南:从“集装箱”到“命令大师”

如果你曾遇到过“在我机器上跑得好好的,怎么在你那就不行了?”这种程序员终极悖论,那么 Docker 就是为你准备的救命稻草。

🐳 一、 什么是 Docker?(生动的比喻)

1. 搬家与集装箱

想象你要从上海搬家到新加坡。

  • 传统方式:你把衣服塞进编织袋,电视机直接搬上车,电脑零散放着。到了新家,你发现插座型号不对,电视机磕坏了,电脑系统因为环境变化启动不了。
  • Docker 方式:你租了一个集装箱(Container)。你把所有东西按原样布置在箱子里,连插线板都接好。到了新家,你只需要给集装箱通上电,一切立刻恢复原状。

Docker 就是那个集装箱。 它把你的代码、配置、库文件、甚至操作系统环境全部打包,确保“一次构建,到处运行”。

2. 核心概念三剑客

  • 镜像 (Image) —— 建筑蓝图:它是只读的模板。比如“一个装好了 Python 3.9 和 Flask 的 Ubuntu 系统”。
  • 容器 (Container) —— 真实的房子:镜像运行起来后的实例。你可以启动、停止、删除它。
  • 仓库 (Registry) —— 蓝图展示馆:存放镜像的地方,最著名的是 Docker Hub。

🛠 二、 Docker 核心原理深度解析

Docker 为什么比虚拟机(VM)快?

特性 虚拟机 (VM) Docker 容器
内核 每个 VM 都有独立的操作系统内核 共享宿主机的内核
启动时间 分钟级(需要加载整个系统) 秒级(本质上是一个受限的进程)
资源占用 很大(预分配 GB 级内存) 极小(按需使用)

底层技术简述:

  1. Namespaces (命名空间):给进程戴上“眼罩”,让容器以为自己拥有独立的网络、用户和进程空间。
  2. Control Groups (控制组):给容器套上“紧箍咒”,限制它能使用的 CPU 和内存上限。
  3. 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
2
3
4
# 下载并启动 Nginx,映射到本地 80 端口
docker run -d -p 80:80 --name my-nginx nginx

根据我们之前的实操过程,以下是针对深度学习环境迁移、镜像瘦身及大文件处理的 Docker 经验总结。

五、 使用经验总结

  • 空间管理:根目录不足时的生存法则
  • docker save 极其吃空间,通常需要镜像体积 2-3 倍 的剩余空间作为临时缓存。
  • 当磁盘空间告急(如剩余不足 30GB 处理 16GB 镜像)时,docker export 是唯一的救命稻草。它采用流式处理,几乎不产生临时文件。
  • 瘦身先行:在打包前执行 conda clean --allrm -rf ~/.cache/pip,通常能瞬间释放数 GB 空间。
  • 镜像打包策略:Save vs Export
  • docker save:适合完整备份。保留所有层(Layers)、环境变量、工作目录和入口脚本(Entrypoint)。
  • docker export:适合环境交付。它将镜像“压扁”成一层,丢失所有历史元数据,但换来的是更小的体积和更高的导入兼容性。
  • 协作交付:把“方便”留给对方
  • 使用 export 导出的镜像在 import 时必须手动补回关键变量(如 PATHLD_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
    2
    docker exec -it <容器ID> /bin/bash

4. 状态保存与迁移(Commit/Export/Save)

当你修改了容器内的代码或环境,想把它存下来时:

  • 本地保存修改 (Commit)
    将当前容器的状态保存为一个新镜像
    1
    2
    3
    docker 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 将改动固化,然后再进行 exportsave 操作,以防导出过程中意外丢失未保存的容器修改。


七、 Dockerfile 核心语法拆解(以项目为例)

一个标准的、高质量的 Dockerfile 通常遵循“从基础到应用”的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 1. FROM: 指定基础镜像。就像选择地基,这里选的是带 CUDA 的 Ubuntu 20.04
FROM nvidia/cuda:12.1.0-devel-ubuntu20.04

# 2. ENV: 设置永久环境变量。容器运行时也会生效
ENV DEBIAN_FRONTEND=noninteractive
ENV PATH="/root/miniconda3/bin:${PATH}"

# 3. RUN: 执行 Shell 命令。这是构建镜像的核心
# 提示:将多个 apt 命令合并,并最后删除缓存,可以显著减小镜像体积
RUN apt-get update && apt-get install -y \
libosmesa6-dev \
libgl1-mesa-glx \
git \
wget \
&& rm -rf /var/lib/apt/lists/*

# 4. WORKDIR: 设置工作目录。相当于在容器里自动执行 "cd /app"
# 之后的 RUN, CMD, ENTRYPOINT 都会在这个目录下执行
WORKDIR /app

# 5. COPY: 从宿主机拷贝文件到镜像内
# 建议先拷贝配置文件安装环境,再拷贝代码,这样利用“镜像缓存”可以加快构建速度
COPY conda_environment.yaml /tmp/conda_environment.yaml

# 6. RUN (进阶): 这里利用管道符配置 Conda 和 Pip 源
RUN echo "channels: ..." > /root/.condarc && \
pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple

# 7. CMD: 指定容器启动时默认运行的命令
# 只有最后一行 CMD 生效,且可以被 docker run 后的参数覆盖
CMD ["/bin/bash"]


八、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
2
3
# -t 指定镜像名和标签,后面的点 "." 代表当前目录(寻找 Dockerfile)
docker build -t dp_train:v3 .

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 <镜像名> .