2. Docker基础概念:镜像、容器、仓库、Docker架构

学MetaPhlAn部署,Docker是绕不开的坎。但别怕——核心概念其实就四个:镜像、容器、仓库,再加上它的整体架构。搞懂这些,后面搭建流程就顺了。

我刚开始接触Docker时,也觉得这些术语很绕。什么镜像容器的,跟虚拟机有啥区别?用过几次之后才发现,这玩意儿的设计思路其实非常简洁。说白了,就是把你的应用和它需要的环境打包成一个“箱子”,走到哪儿都能跑。

2.1 镜像——只读的“模板”

镜像是什么?你可以把它理解成一个只读的模板。就像装系统用的ISO文件,或者虚拟机用的镜像文件——但它比那些轻量得多。

镜像里包含了运行应用所需的一切:代码、运行时环境、系统库、环境变量、配置文件等等。你想想看,传统部署方式里,每次换台机器都要重新装一堆依赖,多折腾。用Docker镜像,这些依赖全部打包在一起了。

我记得刚开始学Docker时,对“分层存储”这个概念琢磨了好久。镜像不是一块整的,而是由一层一层堆叠起来的。每一层都是只读的,修改时只在最上层做增量。这样做的好处是——多个镜像可以共用底层,节省磁盘空间,拉取时也只传输差异部分。

小提示:生产环境中拉取镜像时,你会发现“Already exists”的字样。那些就是已经被其他镜像缓存的层,Docker不会重复下载。

拉取一个MetaPhlAn镜像试试看:

docker pull metaphlan/metaphlan:latest
docker images

docker images会列出你本地所有镜像。你会看到镜像的仓库名、标签(tag)、镜像ID、创建时间和大小。标签很重要,同一个镜像不同标签代表不同版本。

2.2 容器——运行的“实例”

镜像毕竟只是个静态模板,要让它干活,就得把它跑起来——跑起来的东西就叫容器。

镜像和容器的关系,说白了就像程序与进程。镜像是硬盘上的可执行文件,容器是内存里正在运行的进程。你可以从一个镜像启动多个容器,每个容器都是独立的运行环境,互不干扰。

容器在镜像的只读层之上,加了一层可写层。你在容器里做的所有修改——创建文件、安装软件包、修改配置——都写在这一层。但要注意,这一层是临时的。我就在项目里遇到过一件事:容器重启后,里面生成的分析结果全没了。嗯,从那以后,我再也不敢把重要数据放在容器内部了。

避坑指南:容器内生成的数据一定要通过数据卷(volume)或挂载(bind mount)持久化到宿主机上。否则容器一删,数据就跟着没了。我曾经因为这个吃过亏,通宵跑完的MetaPhlAn分析结果付之东流……教训惨痛。

启动一个MetaPhlAn容器看看:

docker run --rm metaphlan/metaphlan:latest --help
docker ps -a

docker run从镜像启动容器。--rm表示容器退出后自动删除,适合一次性任务。docker ps -a列出所有容器(包括已停止的)。你会看到每个容器都有唯一的容器ID和名称。

2.3 仓库——镜像的“集散地”

镜像做好了,怎么分享给团队?怎么在不同机器之间传输?这就是仓库(Registry)要做的事。

仓库就是存镜像的地方,你可以把它想象成代码的GitHub——用来存储、分发和管理Docker镜像。最常用的是Docker Hub,它是Docker官方的公共仓库。企业里也常用自建的私有仓库,比如Harbor或Nexus。

我见过不少团队因为镜像标签管理混乱出过问题。曾经有个同事,没看清标签就把测试环境的镜像推到生产仓库了,结果线上服务挂了半小时。所以我的习惯是:给镜像打标签时一定要规范,比如project/env:version这种格式。

从仓库拉取和推送镜像的命令:

docker pull metaphlan/metaphlan:latest
docker tag metaphlan/metaphlan:latest my-registry:5000/metaphlan:v1
docker push my-registry:5000/metaphlan:v1

docker pull从仓库拉镜像到本地,docker tag给镜像打新标签,docker push把镜像推送到指定仓库。公共仓库不需要认证就能拉取,但推送需要登录。

关键点:仓库地址的格式通常是 域名:端口/命名空间/镜像名:标签。如果是Docker Hub,可以省略域名部分。

2.4 Docker架构——各组件怎么配合

搞清楚了镜像、容器、仓库各自是什么,再来看它们之间怎么配合。Docker采用的是客户端-守护进程-仓库的架构,非常清晰。

我习惯用“点外卖”来比喻:你(客户端)用手机下单,餐厅厨房(守护进程)接到指令后开始做菜,食材不够了就从供应商(仓库)那里进货。做好的菜放在餐台上(镜像),送餐员取走一份(容器)送到你桌上。

下面这张图把核心组件和它们之间的关系串起来了:

Docker 核心架构图 Docker Client Docker Daemon Docker Registry 镜像 (Images) 容器 (Containers) API 指令 push / pull 管理 管理 实例化 工作流程说明 用户通过 CLI 向 Daemon 发送指令,Daemon 管理本地镜像和容器, 需要时从 Registry 拉取或推送镜像。

整个流程其实很简单:你通过docker命令行(Client)发送指令,守护进程(Daemon)负责执行——它管理着本地的镜像和容器,需要时从仓库(Registry)拉取镜像。所有的核心工作都由Daemon完成,Client只是发号施令。

2.5 概念对比

这四个概念容易搞混,我把它们放在一张表里对比一下,你一看就明白:

概念 类比 是否可变 生命周期 核心作用
镜像 (Image) ISO安装盘 / 类模板 只读 长期存在,可反复使用 提供应用运行的环境模板
容器 (Container) 运行中的进程 / 实例 可写(临时层) 启动→运行→停止,短暂 执行应用的实际环境
仓库 (Registry) GitHub / 应用商店 可读写(推送时) 长期存在,用于分发 存储和分发镜像
Daemon 操作系统内核 —— 持续运行 管理镜像、容器、网络、存储

一句话总结:镜像是打包好的环境模板,容器是模板跑起来后的实例,仓库是存模板的地方,Daemon是背后干活的引擎。

记牢这点:MetaPhlAn的流程部署,本质上就是——拉取MetaPhlAn镜像(从仓库),启动容器跑分析(从镜像),结果持久化到宿主机(用数据卷)。这三个动作贯穿始终。