3. Docker 镜像管理:拉取、构建、标签与推送

镜像管理,说白了就是 Docker 使用者的基本功。你想想看,不管你的单细胞分析流程写得再漂亮,最后都得打包成镜像才能到处跑。这一章,我就带你把这四个核心操作——拉取、构建、标签、推送——彻底捋清楚。

3.1 镜像拉取:从 Registry 下载现成镜像

docker pull 是你最常用的命令。它的作用是从镜像仓库(比如 Docker Hub、GitHub Container Registry)把别人做好的镜像拉到本地。

我刚开始用 Docker 时犯过个错误:每次都 docker pull ubuntu:latest,后来发现这个 latest 标签在不同时间拉到的可能是不同版本,导致分析结果对不上。后来我学乖了——拉镜像时必须指定版本标签。

我的习惯做法:拉取镜像总带上具体版本号,除了初次测试时用 latest。

基本命令长这样:

# 拉取官方 Ubuntu 22.04
docker pull ubuntu:22.04

# 拉取带有 CUDA 的 R 基础镜像
docker pull rocker/tidyverse:4.3

# 拉取空间转录组分析常用工具
docker pull quay.io/biocontainers/seurat:4.4.0

拉取的镜像存放在本地,可以用 docker images 查看。如果你在墙内或者学校网络受限,记得配置镜像加速器。我曾经因为没配加速,pull 一个 2GB 的镜像等了三个小时才超时——太折磨了。

# 查看本地所有镜像
docker images

# 输出示例
REPOSITORY      TAG       IMAGE ID       CREATED       SIZE
ubuntu          22.04     a04f64a1d8a4   2 weeks ago   77.8MB
rocker/tidyverse 4.3      e3b0c44298fc   5 days ago    1.2GB

3.2 镜像构建:从 Dockerfile 生成镜像

拉取是拿别人的,构建才是做自己的。docker build 根据你写的 Dockerfile 来一步步生成镜像。这是整个封装过程中最核心的一步——你的单细胞分析环境好不好用,全看 Dockerfile 写得怎么样。

先看一个简单的 Dockerfile 示例:

FROM rocker/tidyverse:4.3

RUN apt-get update && apt-get install -y \
    libhdf5-dev \
    libxml2-dev \
    libssl-dev

RUN R -e "install.packages('Seurat', repos='https://cloud.r-project.org')"

WORKDIR /workspace
COPY scripts/ /workspace/scripts/

CMD ["Rscript", "scripts/run_analysis.R"]

构建命令很简单:

# 在当前目录下找 Dockerfile 构建,-t 指定镜像名:标签
docker build -t spatial-analysis:v1 .

# 如果 Dockerfile 名字不一样,用 -f 指定
docker build -f Dockerfile.cpu -t spatial-analysis:cpu-v1 .

注意缓存问题:Docker 构建会缓存每一层。如果你改了 Dockerfile 的前几行,后面的层会用缓存,可能导致旧依赖混进来。我遇到过好几次——改了 Seurat 版本但忘记清理缓存,结果还是旧版。稳妥做法是用 --no-cache:

docker build --no-cache -t spatial-analysis:v2 .

为什么会这样?Docker 对每一条 RUN、COPY 指令都会生成一个层。只要指令文本没变,它就复用之前的缓存层。但你想想看,如果上游的基础镜像更新了,缓存层可能就不对了。所以定期用 docker build --pull 强制拉取最新的基础镜像是个好习惯。

3.3 镜像标签:给你的镜像起个靠谱的名字

docker tag 为镜像添加额外标签。标签就是镜像的别名,本身不复制数据。我见过很多新手直接 push 一个没有标签的镜像,最后仓库里一堆 latest,根本分不清哪个是哪个。

# 语法:docker tag 源镜像[:标签] 新镜像名[:新标签]

# 给本地镜像加标签
docker tag spatial-analysis:v1 docker.io/yourname/spatial-analysis:v1.0

# 加 latest 标签
docker tag spatial-analysis:v1 docker.io/yourname/spatial-analysis:latest

命名规范很重要。我个人习惯按这样的规则打标签:

字段 示例 说明
应用名 spatial-single-cell 表明用途,别起太通用
版本号 v1.2.3 语义化版本,方便回溯
基础镜像 r4.3-cuda12.0 标明底层环境,方便兼容性判断
日期 20250115 哪天构建的,排查问题时好用

一个完整的镜像名可以很长:

docker tag spatial-base:latest \
  ghcr.io/your-lab/spatial-single-cell:r4.3-cuda12.0-v1.0.0-20250115

避坑指南:我曾有一次给镜像打错了 tag,结果 push 到了别人的仓库名下。你想想看,这后果多严重?所以推送前一定要用 docker images 确认镜像名和标签是否正确。

3.4 镜像推送:把镜像分享到仓库

构建好、打好标签,最后一步就是推送。推送前要先登录目标仓库:

# 登录 Docker Hub
docker login

# 登录 GitHub Container Registry
docker login ghcr.io -u your-github-username

# 登录私有仓库
docker login registry.yourlab.edu.cn

认证信息会被缓存,存在 ~/.docker/config.json。如果你用 CI/CD 自动推送,建议用 echo "PAT" | docker login ... --password-stdin 的方式,不要明文写密码。

推送命令:

# 推送已打好标签的镜像
docker push docker.io/yourname/spatial-analysis:v1.0

# 推送到 GitHub Container Registry
docker push ghcr.io/your-lab/spatial-single-cell:r4.3-cuda12.0-v1.0.0-20250115

# 推送多个标签
docker push yourname/spatial-analysis:latest
docker push yourname/spatial-analysis:v1.0

推送技巧:大镜像可以先试试 docker save 看看压缩后多大。超过 10GB 的镜像推送很容易超时。我一般会把基础依赖和数据分析工具分开成两个镜像,用 multi-stage build 或者共享 base 镜像来减小每次推送的负担。

镜像大小和推送时间的关系,我统计过一次:

镜像大小(压缩后) 推送耗时(10Mbps 上行) 建议策略
< 500MB 1-2 分钟 直接推送
500MB - 2GB 5-15 分钟 分两次推送不同标签
2GB - 10GB 20-90 分钟 用 base + app 拆分
> 10GB 可能超时失败 考虑用 .sif 或者压缩后手动传

嗯,这里要注意。推送大镜像时网络断了怎么办?Docker 支持断点续传——它会缓存已经推送的层。所以中断后重新 docker push 就好,不需要从头推。

3.5 全流程实战:从 Dockerfile 到仓库

把上面四个步骤串起来,一个典型的工作流是这样的:

# 1. 拉取基础镜像
docker pull rocker/tidyverse:4.3

# 2. 构建镜像(假设当前目录有 Dockerfile)
docker build -t spatial-seurat:dev .

# 3. 打标签
docker tag spatial-seurat:dev \
  docker.io/wanglab/spatial-seurat:r4.3-v1.0.0

# 4. 登录仓库
echo "ghp_xxxxxx" | docker login ghcr.io -u wanglab --password-stdin

# 5. 推送
docker push docker.io/wanglab/spatial-seurat:r4.3-v1.0.0

# 6. 本地验证:删除后重新拉取
docker rmi docker.io/wanglab/spatial-seurat:r4.3-v1.0.0
docker pull docker.io/wanglab/spatial-seurat:r4.3-v1.0.0

最后一步的本地验证很重要。我吃过亏——push 上去的镜像因为 Dockerfile 里用了相对路径,COPY 指令没执行成功,结果在另一台机器上 pull 下来跑不了。所以推送完一定要在干净的本地重新拉取并测试一遍。

3.6 本章知识结构

下面这张图概括了镜像管理的完整流程和四个核心操作之间的关系:

Docker 镜像管理:核心流程 1. 选择基础镜像 FROM rocker/tidyverse:4.3 2. 拉取镜像 docker pull 3. 构建镜像 docker build 4. 打标签 docker tag 5. 推送至仓库 docker push 6. 本地验证 pull & test 核心原则 指定版本标签 · 构建后先验证再推送 · 大镜像考虑拆分 · 定期用 --no-cache 重建