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 生命周期管理

镜像和容器都有自己的生命周期。我习惯把它们分开看,但实际使用中是串在一起的。

镜像的生命周期

  1. 构建:通过 Dockerfile 或 commit 生成镜像
  2. 存储:存在本地或推送到仓库
  3. 分发:从仓库拉取到其他主机
  4. 删除:清理不再使用的镜像

容器的生命周期

  1. 创建:docker create 从镜像创建一个容器,但不启动
  2. 启动:docker start 运行容器
  3. 运行中:容器内的进程正在执行
  4. 暂停:docker pause 冻结容器中的进程
  5. 停止:docker stop 给主进程发 SIGTERM,默认等待 10 秒后强杀
  6. 删除: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 核心概念关系图 镜像 (Image) 容器 (Container) 仓库 (Repository) Dockerfile docker pull docker build docker run / create docker push 流程说明 1. Dockerfile 构建→镜像 2. 镜像 push→仓库 / pull←仓库 3. 镜像启动→容器(可写层)

嗯,这张图把三个核心概念和它们之间的交互都串起来了。你照着这个思路去理解,Docker 就不再有那么多“玄学”了。