3. Docker 核心概念:镜像、容器、仓库与生命周期管理
Docker 的三个核心概念——镜像、容器、仓库,是所有操作的基石。搞懂它们之间的关系,比你记住几十条命令都管用。
我刚接触 Docker 时,总觉得镜像和容器差不多。后来踩了坑才明白——这俩东西,一个像“类”,一个像“实例”。
3.1 镜像(Image)
镜像是一个只读的模板。它包含了运行某个应用所需的全部东西:代码、运行时、系统库、环境变量、配置文件。
你想想看,没有 Docker 之前,部署一个 Java 应用要装 JDK、调环境变量、装依赖库……每台机器都得重复这套流程。镜像把这一切打包成了“不可变单元”。
每个镜像由多个只读层叠加而成。每一层代表一个 Dockerfile 指令。这样的设计让多个镜像可以共享相同的底层,节省磁盘,加速传输。
我个人习惯把镜像理解为一个“快照”。它一旦构建好,内容就不会变了。你在容器里做的任何修改,都不会影响镜像本身——除非你显式地提交新镜像。
查看本地镜像我常用:
docker images
想查看镜像的具体层结构?试试 inspect:docker inspect <image-name> | grep -A 10 "RootFS"
镜像是如何构建出来的?
最常见的方式是编写 Dockerfile,然后执行 build 命令:
# 一个简单的 Python 应用镜像
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
docker build -t my-python-app:v1 .
每一行指令生成一层。我建议你把经常变化的内容(比如代码)放在 Dockerfile 尾部,这样能充分利用缓存。我在项目中遇到过有人把 COPY . 写在 RUN pip install 前面,结果每次改代码都要重新装依赖——构建时间从几十秒变成了几分钟。
3.2 容器(Container)
容器是镜像的运行实例。镜像启动后,Docker 会在镜像的顶层加一个可写层,形成容器。
说白了,镜像是“菜谱”,容器是“做好的菜”。同一个菜谱可以做出无数盘菜,它们之间互不影响。
容器的本质是什么?
它是一个隔离的进程。Docker 利用 Linux 内核的 Namespace 和 Cgroups,让这个进程拥有自己独立的文件系统、网络栈、进程空间和资源限制。
容器和虚拟机不同。容器与宿主机共享内核,所以你在 Windows 上用 Docker 必须跑一个 Linux 虚拟机。容器启动快、资源开销小,但隔离性不如虚拟机。
我见过有些团队把容器当虚拟机用,在里面跑 systemd、装 sshd、手动改配置……这其实违背了容器的设计理念。容器应该是“一次运行、快速销毁”的临时进程,而不是需要长期维护的“小服务器”。
3.3 仓库(Repository)
仓库用来存储和分发镜像。你可以把它想象成 Git 仓库——不过存的是镜像,而不是代码。
Docker 官方有 Docker Hub,那是最大的公共仓库。当然,你也可以搭建私有仓库,比如 Harbor、Nexus,或者用云厂商的镜像服务。
镜像的命名格式
[仓库地址]/[命名空间]/[镜像名]:[标签]
示例:
docker.io/library/nginx:1.25
mycompany.com/platform/api-server:3.2.1
标签(Tag)是镜像的版本号。我个人习惯用语义化版本号加上环境标识,比如 api:v2.3.1-prod。千万不要只用 latest——你永远不知道它指向的是哪个版本。
| 来源 | 示例地址 | 说明 |
|---|---|---|
| Docker Hub | docker.io/library/ubuntu:22.04 |
官方镜像,省略仓库地址时默认使用 |
| 私有仓库 | registry.mycompany.com/app/backend:latest |
自行搭建的内部仓库 |
| 云服务商 | xxx.dkr.ecr.us-east-1.amazonaws.com/my-service:v1 |
AWS ECR、阿里云 ACR 等 |
3.4 生命周期管理
镜像和容器都有自己的生命周期。我习惯把它们分开看,但实际使用中是串在一起的。
镜像的生命周期
- 构建:通过 Dockerfile 或 commit 生成镜像
- 存储:存在本地或推送到仓库
- 分发:从仓库拉取到其他主机
- 删除:清理不再使用的镜像
容器的生命周期
- 创建:
docker create从镜像创建一个容器,但不启动 - 启动:
docker start运行容器 - 运行中:容器内的进程正在执行
- 暂停:
docker pause冻结容器中的进程 - 停止:
docker stop给主进程发 SIGTERM,默认等待 10 秒后强杀 - 删除:
docker rm移除容器
我曾经在线上环境犯过一个错误:直接
docker stop 一个数据库容器,结果数据丢失了。因为容器默认没有数据持久化。从那以后我养成了两个习惯:- 重要数据用 volume 或 bind mount 挂载到宿主机
- 停止容器前先检查是否使用了持久化存储
还有一个容易被忽略的点:容器终止后,文件系统仍然存在(除非你用了 --rm)。你可以用 docker start 再次启动终止的容器——但我很少这么做。我通常把容器当作“不可变基础设施”,销毁后重新创建而不是复用。
# 镜像相关
docker pull nginx:alpine # 拉取镜像
docker images # 列出镜像
docker rmi nginx:alpine # 删除镜像
docker push myuser/myapp:v1 # 推送镜像到仓库
# 容器相关
docker create --name myapp nginx # 创建容器
docker start myapp # 启动容器
docker stop myapp # 停止容器
docker rm myapp # 删除容器
docker ps -a # 查看所有容器(包括已停止的)
最后,我用一张图来总结镜像、容器、仓库之间的关系,以及它们的工作流程:
嗯,这张图把三个核心概念和它们之间的交互都串起来了。你照着这个思路去理解,Docker 就不再有那么多“玄学”了。