3、Docker核心概念:镜像(Image)、容器(Container)、仓库(Repository)的关系
说实话,刚接触Docker那会儿,我也被这三个概念绕得有点晕。镜像、容器、仓库,听起来都跟存储有关,到底有啥区别?
我习惯用一个比喻来理解:镜像就像做菜的菜谱,容器就是按菜谱做出来的一盘菜,仓库则是存放菜谱的图书馆。这个比喻虽然简单,但核心关系全在里面了。
3.1 镜像(Image)—— 只读的模板
镜像是什么?说白了,就是一个只读的、不可修改的文件系统快照。它包含了运行某个应用所需的一切:代码、运行时环境、系统工具、库文件、环境变量……
举个例子,我做一个Python Web应用,镜像里就包含了:
- Ubuntu 22.04 的基础系统文件
- Python 3.10 解释器
- Flask 框架的依赖库
- 我的应用代码
- 启动命令
关键点:镜像是分层构建的。每一层都是只读的,修改镜像只能通过重新构建。
我在项目中遇到过一个问题:有同事直接修改了容器里的文件,然后说「镜像怎么没变?」——嗯,这就是没搞懂镜像的只读特性。容器里的修改,跟镜像本身没关系。
3.2 容器(Container)—— 可运行的实例
容器,就是镜像的运行实例。你可以把镜像理解成一个类(Class),容器就是实例化出来的对象(Object)。
容器在镜像的基础上,加了一层可写层。这意味着:
- 你可以安装软件、修改配置、创建文件
- 但这些修改只存在于当前容器中
- 容器删除后,所有修改都会丢失
我的习惯:每次启动容器时,我都会用 -v 参数挂载数据卷。这样即使容器删了,数据还在。曾经有一次我忘了挂载,结果调试了一天的数据全没了……从那以后,挂载卷成了我的肌肉记忆。
你想想看,一个镜像可以启动多个容器。比如我同一个Nginx镜像,可以启动三个容器:一个跑测试环境,一个跑预发布环境,一个跑生产环境。每个容器互不干扰,配置也各自独立。
3.3 仓库(Repository)—— 镜像的集中存储
仓库就是存放镜像的地方。你可以把它想象成GitHub,只不过存的是Docker镜像而不是代码。
仓库分两种:
| 类型 | 说明 | 例子 |
|---|---|---|
| 公共仓库 | 所有人都能访问和下载 | Docker Hub、GitHub Container Registry |
| 私有仓库 | 需要认证才能访问 | Harbor、AWS ECR、自建Registry |
我建议团队内部一定要搭建私有仓库。为什么?因为公共仓库的镜像可能会被删除或篡改。我曾经见过一个项目,依赖的公共镜像突然被作者删了,结果CI/CD全部挂掉……那叫一个惨。
3.4 三者的关系——一张图说清楚
用命令来展示它们的关系,会更直观:
# 1. 从仓库拉取镜像
docker pull nginx:latest
# 2. 基于镜像创建并启动容器
docker run -d --name my-nginx -p 8080:80 nginx:latest
# 3. 在容器里做修改(比如改配置文件)
docker exec -it my-nginx bash
# 4. 把修改后的容器提交为新镜像
docker commit my-nginx my-nginx:custom
# 5. 将新镜像推送到仓库
docker push my-nginx:custom
看到没?整个流程就是:仓库 → 镜像 → 容器 → 新镜像 → 仓库。这是一个闭环。
注意:用 docker commit 创建镜像虽然方便,但不推荐在生产环境使用。因为它会丢失构建历史,导致镜像无法追溯。我一般只在调试时用,正式环境还是写 Dockerfile 来构建。
3.5 避坑指南——我踩过的三个坑
- 镜像越堆越大:每次
docker build都会产生新的层。如果不清理,本地会堆积大量无用镜像。我习惯每周跑一次docker system prune。 - 容器退出后数据丢失:容器删除后,里面的数据就没了。所以重要数据一定要用数据卷或绑定挂载。
- 标签混乱:很多人喜欢用
latest标签,但latest是会变的。我建议用语义化版本号,比如v1.2.3,这样能精确定位。
嗯,这三个概念其实不难。你只要记住:镜像是模板,容器是实例,仓库是存储。搞清楚了这三者的关系,Docker 的基本功就算打牢了。