Docker 基础:从安装到多阶段构建

各位同学,开始干活前先聊聊 Docker。说实话,几年前我第一次接触 NGS 流程部署时,最头疼的就是环境依赖。你是不是也遇到过——换台服务器,Python 版本不对,某个 C 库没装,跑了一天的分析中途报错,想死的心都有。

Docker 就是解决这个问题的。它把整个运行环境打包成一个「集装箱」,你随身带着走,到哪都能跑。我个人习惯把 Docker 看作「轻量级虚拟机」,但它比虚拟机快得多、省得多。

4.1 Docker 安装:不同系统的坑与招

安装 Docker 本身不复杂,但不同系统有不同的坑。我踩过的雷,帮你先排一排。

Linux(Ubuntu / CentOS)

官方推荐用仓库安装,别去官网下 .deb 手动装,除非你网络实在太差。

# Ubuntu 22.04 示例
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
小提示:安装完记得把当前用户加入 docker 组,不然每次敲 docker 都要 sudo,烦死了。
sudo usermod -aG docker $USER
newgrp docker

macOS / Windows

桌面版直接去 Docker Hub 下 Docker Desktop,双击安装就行。不过我要提醒一句——Windows 用户,确保 BIOS 里开启了硬件虚拟化。我曾经帮一个同事远程调了半天,发现他主板 VT-x 没开,Docker Desktop 启动不了,白白浪费一小时。

系统 推荐方式 避坑点
Ubuntu 20.04+ APT 仓库安装 不要用 snap 安装,权限和版本都别扭
CentOS 7+ YUM 仓库安装 CentOS 7 内核旧,建议先升级内核或直接用 Rocky Linux
macOS / Windows Docker Desktop Windows 必须开 WSL 2 和虚拟化

4.2 镜像拉取与容器运行:日常三板斧

装好了 Docker,第一个动作就是拉个镜像跑跑看。说白了,镜像就是模板,容器是模板的实例。

拉取镜像

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

# 拉取指定 tag
docker pull ubuntu:22.04

# 不写 tag 默认 latest
docker pull ubuntu
警告:不要盲目相信 latest 标签。latest 不是你理解的「最新稳定版」,而是维护者随手打的一个标签。我见过好几次 latest 指向了开发分支的镜像,跑 NGS 流程时关键库版本不对,查了半天。建议永远指定具体版本号。

运行容器

# 最简单的交互式运行
docker run -it --rm ubuntu:22.04 /bin/bash

# 后台运行一个 Nginx 服务
docker run -d --name my-nginx -p 8080:80 nginx:1.25

参数解释一下:

  • -it:交互模式,让你进到容器里敲命令
  • --rm:容器退出后自动删除,临时测试用,省得留下一堆垃圾
  • -d:后台运行(detached)
  • --name:给容器起个名字,方便管理
  • -p:端口映射,宿主机端口:容器端口
记住一个原则:容器是临时的。配置、数据都写在宿主机挂载卷里,别依赖容器内的文件系统。比如跑 NGS 流程的输入输出文件,用 -v 挂载进来。
docker run -it --rm \
  -v /home/user/ngs_data:/data \
  ubuntu:22.04 /bin/bash

4.3 Dockerfile 编写:把环境写成代码

手工敲 docker commit 做的镜像,维护起来要命。Dockerfile 就是那个让你把环境「代码化」的东西。我带的团队,Dockerfile 必须进版本控制,和业务代码一起管理。

一个 NGS 流程的 Dockerfile 例子

# ===== 基础镜像选择 =====
FROM ubuntu:22.04 AS base

# ===== 元数据 =====
LABEL maintainer="your-email@example.com"
LABEL description="NGS 变异分析基础镜像"

# ===== 环境变量 =====
ENV DEBIAN_FRONTEND=noninteractive
ENV LANG=C.UTF-8

# ===== 系统依赖安装 =====
RUN apt-get update && apt-get install -y \
    wget \
    curl \
    git \
    build-essential \
    python3 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# ===== 生物信息学工具 =====
RUN pip3 install --no-cache-dir \
    cutadapt==4.4 \
    fastqc==0.12.1

# ===== 工作目录 =====
WORKDIR /workspace

# ===== 默认命令 =====
CMD ["/bin/bash"]
我的一点经验:Dockerfile 里的 RUN 命令尽量合并,少写几个层,镜像体积能小很多。但别走极端,为了合并把调试信息都丢了,得不偿失。

写 Dockerfile 的几个原则:

  • FROM 只有一个:除非你做多阶段构建,否则别叠罗汉
  • 尽量用标签版本:FROM ubuntu:22.04 比 FROM ubuntu 可靠一百倍
  • 清理缓存:apt-get install 后用 rm -rf /var/lib/apt/lists/*
  • 保证幂等:同样的 Dockerfile,任何时候构建结果应该一样
  • 用 .dockerignore:别把本地 .git、node_modules 带进镜像
# .dockerignore 示例
.git
*.md
__pycache__/
.env

4.4 多阶段构建:瘦身利器

嗯,这里要重点聊聊多阶段构建。为啥需要它?因为我们的 NGS 工具链里,很多软件是 C++ 写的,编译要一堆开发库,但运行的时候只需要编译好的二进制文件。以前的做法是,把编译用的工具链全塞进最终镜像,一个镜像能占几个 GB。

多阶段构建的思路很简单——一个 Dockerfile 里能写多个 FROM,前面的阶段负责编译,后面的阶段只拷贝产物。

# ===== 第一阶段:编译 =====
FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    libz-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /src
COPY bwa-0.7.17.tar.gz .
RUN tar xzf bwa-0.7.17.tar.gz && \
    cd bwa-0.7.17 && \
    make

# ===== 第二阶段:运行 =====
FROM ubuntu:22.04 AS runtime

RUN apt-get update && apt-get install -y \
    libz1 \
    python3 \
    && rm -rf /var/lib/apt/lists/*

# 只拷贝编译好的二进制
COPY --from=builder /src/bwa-0.7.17/bwa /usr/local/bin/

WORKDIR /workspace
CMD ["bwa"]
注意:--from 后面的名字要和前面阶段的 AS 名字一致,拼错了就白忙。我犯过这种低级错误,镜像构建报错说找不到文件,查了十分钟才发现阶段名写错了。

看看构建结果,感受一下差异:

# 构建命令
docker build -t ngs-bwa:multi-stage -f Dockerfile.multistage .

# 查看镜像大小
docker images ngs-bwa
# REPOSITORY   TAG           SIZE
# ngs-bwa      multi-stage   182MB
# ngs-bwa      single-stage  1.12GB

从 1.12GB 瘦身到 182MB,你说香不香?

4.5 实战:构建一个轻量级 NGS 分析镜像

来,我们把前面学的东西串一遍。假设你要制作一个包含 BWA、SAMtools、BCFtools 的迷你分析镜像。

# Dockerfile.ngs-mini
FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git \
    libz-dev \
    libbz2-dev \
    liblzma-dev \
    libcurl4-openssl-dev \
    wget \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /build

# 下载并编译 BWA
RUN wget https://github.com/lh3/bwa/archive/refs/tags/v0.7.17.tar.gz && \
    tar xzf v0.7.17.tar.gz && \
    cd bwa-0.7.17 && \
    make

# 下载并编译 SAMtools
RUN wget https://github.com/samtools/samtools/releases/download/1.17/samtools-1.17.tar.bz2 && \
    tar xjf samtools-1.17.tar.bz2 && \
    cd samtools-1.17 && \
    ./configure --prefix=/build/samtools_install && \
    make && \
    make install

# 下载并编译 BCFtools
RUN wget https://github.com/samtools/bcftools/releases/download/1.17/bcftools-1.17.tar.bz2 && \
    tar xjf bcftools-1.17.tar.bz2 && \
    cd bcftools-1.17 && \
    ./configure --prefix=/build/bcftools_install && \
    make && \
    make install

# 第二阶段:运行
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    libz1 \
    libbz2-1.0 \
    liblzma5 \
    libcurl4 \
    python3 \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /build/bwa-0.7.17/bwa /usr/local/bin/
COPY --from=builder /build/samtools_install/bin/* /usr/local/bin/
COPY --from=builder /build/bcftools_install/bin/* /usr/local/bin/

WORKDIR /data
CMD ["/bin/bash"]

构建并验证:

docker build -t ngs-mini:1.0 -f Dockerfile.ngs-mini .
docker run --rm ngs-mini:1.0 bwa   # 应该能看到 BWA 帮助信息
最终提醒:多阶段构建不只是减少空间,还能减少攻击面。你不会在运行镜像里留着一堆编译器,对吧?
Docker 核心技术栈与流程 Docker Engine 镜像仓库(Docker Hub) Dockerfile 镜像(只读模版) 多阶段构建 容器(可写实例) 构建 → 共享 → 运行 → 销毁 通过 Dockerfile 构建镜像 通过镜像运行多个容器 容器用完即弃,状态保存在卷中

这张图把 Docker 的完整工作流串起来了:你写 Dockerfile,构建成镜像,推送到仓库,别人拉下来运行容器。NGS 流程的自动化部署,其实就是把这个过程脚本化、参数化。