2. 镜像管理:从拉取到构建的完整闭环
镜像就是Docker的“集装箱规范”。你拉取的镜像、自己构建的镜像,都保存在本地仓库里。我每天跟质谱数据打交道,最离不开的就是这几个命令。这次我把它们串起来讲——拉取、查看、删除、构建,再加上Dockerfile的核心写法。
核心知识点:
docker pull/docker push—— 远程与本地之间搬运镜像docker images—— 查看本地镜像仓库docker rmi/docker image prune—— 清理不再需要的镜像docker build+Dockerfile—— 将你的分析环境固化为可复用的镜像
2.1 拉取镜像:让你的环境“空降”到本地
拉取镜像是使用 Docker 的第一道门槛。说白了,就是把远程仓库里的环境包下载到你电脑上。
最基本的命令长这样:
docker pull ubuntu:22.04
你想想看,ubuntu 是镜像名,22.04 是标签(tag)。如果不写 tag,Docker 默认给你拉 latest 标签。我个人习惯在拉取之前先查一下官方镜像的 tag 列表,免得拉到不兼容的版本。
| 命令 | 说明 | 我踩过的坑 |
|---|---|---|
docker pull ubuntu:22.04 |
拉取指定 tag 的镜像 | —— |
docker pull ubuntu |
等价于 docker pull ubuntu:latest |
❌ 坑:latest 可能不是你想要的版本 |
docker pull registry.cn-hangzhou.aliyuncs.com/xxx/yyy |
从国内加速器或私有仓库拉取 | ✅ 推荐:配置镜像加速器,避免超时 |
拉取时 Docker 会按层下载。每一层都有 hash 校验,你不用担心数据损坏。我记得有一次在实验室里,网络特别不稳定,一个 bioconductor 镜像拉了三遍才成功。后来我配了国内镜像加速器,问题就解决了。
加速配置小贴士: 修改 /etc/docker/daemon.json,添加 registry-mirrors,然后重启 Docker 服务。拉取质谱分析常用的 bioconductor 或 python:3.10-slim 镜像时,速度提升非常明显。
2.2 查看镜像:你的本地仓库里有什么
拉完之后,怎么确认它到了本地?用 docker images 查看。
docker images
# 或
docker image ls
输出会告诉你仓库名、标签、镜像 ID、创建时间和大小。嗯,这里要留意大小——一个基础镜像可能就 200MB,但装了分析软件后能膨胀到 2GB 甚至更大。
我个人经常搭配 --filter 参数来快速定位镜像:
docker images --filter "reference=ubuntu*" # 只看 ubuntu 系列
docker images --filter "dangling=true" # 查看悬空镜像(无标签的层)
为什么会有悬空镜像?当你用同一个 tag 反复构建新镜像时,旧镜像的 tag 会被移除,但层数据还留在磁盘上。这些镜像在 docker images 里显示为 <none>:<none>,很碍眼,而且也占空间。
2.3 删除镜像:做好磁盘的“断舍离”
长期使用 Docker,磁盘空间肯定是个大问题。删除镜像用 docker rmi 命令:
docker rmi ubuntu:22.04 # 通过仓库名+tag 删除
docker rmi a1b2c3d4 # 通过镜像 ID 删除
docker rmi ubuntu:22.04 python:3.10 # 一次删除多个
如果镜像正在被某个容器使用,Docker 会拒绝删除,并提示你容器还在运行。这时候要么先删容器,要么加 -f 强制删除(不推荐)。
更彻底的清理是用 docker image prune:
docker image prune # 删除所有悬空镜像
docker image prune -a # 删除所有未被使用的镜像
我习惯在每个项目结束后跑一次 docker image prune -a。项目里构建的临时镜像一下子全没了,磁盘空间瞬间释放十几 GB。但要注意,如果某个镜像虽然没被容器用,但它被你后续构建引用为基础镜像,-a 会把它也干掉。所以我建议先确认清楚再用。
⚠️ 曾经我踩过的坑: 有一次在团队服务器上,我执行了 docker image prune -a,没仔细看它列出的待删镜像。结果把团队成员正在用的基础镜像删了,害得他们重新拉取了一整遍。建议在删除之前,先用 docker images --filter "dangling=true" 确认哪些是真正要清理的。
2.4 构建镜像:从无到有定制你的环境
现成的镜像不一定能满足你的需求。比如你要在容器里运行一个质谱数据处理流程,需要装特定的 R 包和命令行工具。这时候就得自己构建镜像了。
构建命令是 docker build:
docker build -t my-ms-processor:v1 .
这里的 -t 指定镜像名和标签,最后的 . 是构建上下文路径(通常是 Dockerfile 所在的目录)。构建过程中,Docker 会逐行执行 Dockerfile 里的指令,生成一层层的缓存。
我自己遇到最多的问题是构建上下文里放了太多无关文件(比如原始质谱数据文件)。这些文件不会被复制进镜像,但 Docker 必须把它们打包发给构建守护进程,导致构建速度特别慢。我建议用 .dockerignore 文件排除它们:
# .dockerignore 文件内容
*.raw
*.mzML
data/
temp/
.git/
2.5 Dockerfile 基础:写在刀尖上的配方
Dockerfile 就是构建镜像的“配方”。每一行指令对应镜像的一层。把常用的几个指令列出来:
| 指令 | 作用 | 示例 |
|---|---|---|
FROM |
指定基础镜像 | FROM python:3.10-slim |
RUN |
在构建时执行命令(安装软件等) | RUN apt-get update && apt-get install -y procps |
COPY |
把本地文件/目录复制到镜像 | COPY ./scripts /opt/scripts |
WORKDIR |
设置工作目录 | WORKDIR /app |
CMD |
容器启动时的默认命令 | CMD ["python", "/app/run.py"] |
EXPOSE |
声明容器监听的端口(仅文档用途) | EXPOSE 8000 |
ENV |
设置环境变量 | ENV DEBIAN_FRONTEND=noninteractive |
举个实际的例子——构建一个质谱数据预处理镜像:
FROM python:3.10-slim
# 设置工作目录
WORKDIR /work
# 复制需求文件和脚本
COPY requirements.txt .
COPY scripts/ ./scripts/
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 设置环境变量(避免 apt 交互)
ENV DEBIAN_FRONTEND=noninteractive
# 安装系统工具
RUN apt-get update && apt-get install -y --no-install-recommends \
wget \
curl \
&& rm -rf /var/lib/apt/lists/*
# 容器启动时自动运行预处理脚本
CMD ["python", "/work/scripts/preprocess.py"]
你想想看,为什么要把 COPY requirements.txt 写在 COPY scripts/ 前面?因为 Docker 在构建时对每一层都有缓存。如果 requirements.txt 没变,Docker 会直接复用缓存,跳过重新安装依赖。但如果你把代码文件放在前面,哪怕只改了一行代码,整个镜像的依赖层也得重装。
优化建议:
- 合并 RUN 指令: 用
&&连接多个命令,减少层数。 - 清理包缓存: 在同一个 RUN 里执行
apt-get clean或rm -rf /var/lib/apt/lists/*,让镜像更小。 - 利用 .dockerignore: 别把数据文件、源码仓库的 .git 文件夹打包进去。
2.6 构建与缓存:别忽视层的威力
镜像是由层堆叠起来的。每一层都是一个只读的文件系统快照。构建时,如果 Docker 发现当前指令没有变化,就直接复用之前的层。但一旦某层变了,它后面的所有层都必须重新构建。
所以理想的 Dockerfile 是把最稳定的部分(比如系统依赖、Python 基础包)写在前面,把最容易变的部分(比如你的脚本代码)写在最后。这样大部分情况你只需要重建最后几层,构建速度能快 10 倍不止。
我个人有个习惯:在 RUN pip install 后面加上 --no-cache-dir,否则 pip 会把下载的 wheel 包缓存也塞进镜像,白白增加几十 MB。对于质谱数据处理的容器来说,几十 MB 看起来不多,但如果做批量构建和分发,累计起来就很可观了。
docker build --no-cache -t my-ms-processor:v1 .
加上 --no-cache 会强制重新构建所有层,不依赖任何缓存。这在调试 Dockerfile 时很有用,但平时别乱用——效率太低了。我只有在基础镜像更新了,或者安装源发生变化时,才会这么干。
快捷清理与构建: 如果发现构建出的镜像有问题,可以用 docker system df 查看磁盘使用情况,然后 docker image prune -f 清理悬空镜像,最后重新构建。