2. Docker 基础入门:安装、拉取、运行、进入、提交

说到 Docker,我第一次接触还是几年前搭生信分析平台的时候。那时候被依赖环境折腾得够呛,一个工具装完发现 glibc 版本不对,另一个又跟 Python 包冲突。后来发现 Docker 可以把整个环境打包带走,嗯,从那以后我就彻底回不去了。这一章咱们先过一遍最基础、也最常用的几个命令——pull、run、exec、commit。你只要把这四个练熟,后面做镜像校验和质控就有了操作基础。

核心思路:镜像(image)是模板,容器(container)是运行实例。我们从仓库拉一个镜像,用 run 创建容器跑起来,用 exec 进去改东西,最后用 commit 把改动保存成新镜像。这个闭环就是日常操作的主干。

2.1 安装 Docker

不同系统安装方式不太一样。我个人习惯在 Ubuntu 上用官方脚本自动装,省事干净。你执行这个就行了:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

装完之后,记得把你的用户加入 docker 组,不然每次都要加 sudo:

sudo usermod -aG docker $USER
newgrp docker  # 或者重新登录
验证安装:运行 docker --version 和 docker run hello-world。如果看到 "Hello from Docker!" 就说明环境搞定了。
注意:Windows/macOS 用户请直接安装 Docker Desktop。我遇到过有人非要在 WSL 里手动装原生 Docker,结果网络代理搞了一天……直接装 Desktop 搭配 WSL 2 后端才是最稳的。

2.2 docker pull:拉取镜像

装好 Docker 后,第一件事就是拉个镜像下来。我习惯先拉一个比较小的 base 镜像做测试,比如 Ubuntu:

docker pull ubuntu:22.04

这个命令会从 Docker Hub 下载 Ubuntu 22.04 的镜像。你可能会想,有没有其他 tag?当然有。常用的 tag 有这些:

  • latest:当前最新版,但不建议在生产环境用,因为每次拉可能不同。
  • 22.04、20.04:具体版本号,可重复性好。
  • jammy:代号,跟版本号一样。
  • slim:精简版,体积小,生信环境也够用。

如果你想拉一个生信常用的镜像,比如 bioconda 的:

docker pull biocontainers/biocontainers:latest

避坑提醒:我曾经天真地拉了一个 3GB 的镜像,结果公司网慢,半天没拉完。建议你先用 docker search 看看镜像大小,或者优先选 slim 版。网络不好的时候,可以配置国内镜像加速器。

2.3 docker run:运行容器

镜像拉下来,就该跑容器了。这是最常用的命令,我先给个典型用法:

docker run -it --rm --name my_container ubuntu:22.04 bash

拆开讲一讲:

  • -i 保留标准输入(STDIN),-t 分配伪终端。两个合起来 -it 就能让你像在 SSH 里一样交互。
  • --rm 容器退出后自动删除。测试时加上它,不会留下垃圾容器。
  • --name 给容器起个名字,方便后面 exec 或 stop。
  • ubuntu:22.04 是镜像名 + tag。
  • bash 是要执行的命令,这里打开一个 shell。

你还可以添加端口映射、挂载目录、设置环境变量:

docker run -it --rm \
  -p 8080:80 \
  -v /home/user/data:/workspace \
  -e MY_ENV=hello \
  ubuntu:22.04 bash
注意:容器退出后,如果没有用 --rm,它会一直留在磁盘里。用 docker ps -a 能看到全部容器。时间久了会有几十上百个停止的容器,赶紧用 docker container prune 清理掉。

2.4 docker exec:进入运行中的容器

在实际操作中,我们经常需要在已经运行的容器里执行命令。比如你启动了一个后台服务容器,想检查日志,或者临时装个软件。这时候 docker exec 就上场了。

docker exec -it my_container bash

这个命令会打开 my_container 容器里的一个新终端。你可以在里面做任何事——装包、改配置、跑脚本。注意 -it 同样不能少,否则你无法交互。

跟 docker attach 的区别:attach 会接入当前容器的标准输出,如果容器里只有一个前台进程,attach 进去后你可能会卡住。而 exec 是创建一个新进程,不影响主进程。我 100% 推荐用 exec,attach 只用来看日志。

举个例子:假如你有个容器跑着 RStudio Server,你想进去手动装个 R 包:

docker exec -it rstudio_container R -e "install.packages('ggplot2')"

这样不用进入 shell 也能直接执行命令,适合在 CI 脚本里用。

2.5 docker commit:创建新镜像

你通过 exec 进到容器里,安装了某些工具,改了配置文件。怎么把这个修改后的环境保存下来?docker commit 就是做这个的。

docker commit my_container my_new_image:v1

执行后,当前容器里的文件系统变化会被打包成一个新镜像。你可以用 docker images 看到它。我的经验是,这个命令适合快速迭代,但不要把它当作长期方案——因为 commit 过的镜像没有 Dockerfile,别人很难复现你每一步做了什么。

慎用 commit:我见过有人每次安装工具后都 commit 一次,最后镜像变得臃肿不堪,还藏了临时文件。正确的做法是:先用 commit 快速保存一个中间版本,然后立即用 docker history 查看,再写对应的 Dockerfile 正式构建。commit 只用来「抓拍」,不是「存档」。

顺便提一下,commit 时可以加 -a 指定作者,-m 写注释:

docker commit -a "yourname" -m "installed samtools and bcftools" my_container bio_env:1.0

2.6 四个命令的工作流

上面四个命令其实就构成了一个完整的「拉取 ➔ 运行 ➔ 修改 ➔ 导出」循环。我画了一张图帮你理清关系:

Docker 基础命令工作流 docker pull docker run docker exec docker commit 说明 pull:从仓库取镜像 run:基于镜像启动容器 exec:进入运行中的容器 commit:将容器保存为镜像 形成一个闭环

2.7 常用命令速查表

下面这个表格总结了我们刚讲过的所有命令,以及它们的核心参数。你可以打印出来贴在工位上,非常实用。

命令 用途 常用选项
docker pull <image> 从 registry 拉取镜像 无特殊选项,可指定 tag
docker run [OPTIONS] IMAGE [CMD] 创建并启动容器 -it 交互、--rm 自动删除、-v 挂载、-p 端口
docker exec -it <container> <cmd> 在运行中的容器里执行命令 -it 必须有,可加 -u 指定用户
docker commit [OPTIONS] CONTAINER [REPOSITORY:TAG] 将容器保存为新镜像 -a 作者、-m 提交信息
个人小习惯:我每天开始工作前,会先用 docker pull biocontainers/samtools:latest 把最近常用的镜像拉一遍,然后 docker run --rm -it 起一个临时容器,在里面干活。干完活直接退出(容器自动删掉),第二天再重新拉最新的版本,这样保证每次用的都是新环境。

好啦,这一章的核心就是四个命令:pull、run、exec、commit。你想想看,它们是不是把整个 Docker 生命周期都串起来了?镜像仓库拿到手,跑成容器,进去修改,再保存成新镜像。后面章节讲校验和质控,这些操作就是基本功。如果你现在手边还没有 Docker 环境,赶紧用安装命令装一个,然后跟着上面的例子动手敲一遍。只有亲手跑过,才记得牢。