一、镜像基础概念:从零理解容器镜像

说实话,我刚接触容器技术那会儿,也被「镜像」这个词搞得有点懵。什么镜像不镜像的,不就是个文件包吗?后来踩了不少坑才明白——镜像远不止是个安装包那么简单。

今天咱们就把镜像这个概念彻底掰开揉碎。我保证,看完这一章,你对镜像的理解会比大多数人都深。

1.1 什么是镜像

镜像,说白了就是一个只读的、分层存储的模板文件。它包含了运行某个应用所需的一切:代码、运行时环境、系统库、环境变量、配置文件……几乎就差一个操作系统内核了。

你可以把镜像想象成一个「快照」——把某个时刻的完整文件系统拍下来,存成一份不可修改的静态文件。我习惯把它比作光盘:你拿到一张光盘,可以反复用它启动系统,但没法直接修改光盘里的内容。

核心定义:镜像是容器的静态模板,容器是镜像的运行实例。没有镜像,就没有容器。

1.2 镜像与容器的关系

很多新手会混淆镜像和容器。我当年也犯过这个错——以为 docker run 就是直接跑了个镜像。其实不是。

它们的关系,用一句话就能说清:

  • 镜像是「类」(Class)——定义好了结构,但还没实例化
  • 容器是「对象」(Object)——镜像的一个具体运行实例

举个例子:

# 查看本地镜像
docker images

# 从镜像启动一个容器
docker run -it ubuntu:22.04 /bin/bash

# 查看运行中的容器
docker ps

同一个镜像可以启动多个容器,每个容器都有自己的读写层。你改了一个容器里的文件,其他容器完全不受影响。这就是「镜像只读、容器可写」的精髓。

我的经验:我曾经在生产环境同时用同一个 Nginx 镜像启动了 50 个容器做负载均衡。每个容器只改了配置文件,镜像本身没动。这种「一次构建,到处运行」的能力,就是镜像最大的价值。

1.3 镜像分层原理(UnionFS)

这是镜像最核心、也最容易被忽略的部分。为什么镜像能那么小?为什么拉取镜像时可以复用已有层?答案都在 UnionFS 里。

UnionFS(联合文件系统)是一种将多个目录挂载到同一个挂载点的文件系统技术。Docker 用它把镜像的每一层叠加起来,对外呈现为一个完整的文件系统。

来看一个典型的镜像分层结构:

+---------------------------+
|  容器读写层(可写)         |  ← 容器启动后创建
+---------------------------+
|  镜像层 3:CMD/ENTRYPOINT  |  ← 应用配置
+---------------------------+
|  镜像层 2:应用代码         |  ← 你的业务代码
+---------------------------+
|  镜像层 1:依赖库           |  ← apt install 的包
+---------------------------+
|  基础层:Ubuntu 22.04      |  ← 操作系统基础
+---------------------------+
        ↓ 叠加后
  容器看到的完整文件系统

每一层都是只读的。容器运行时,UnionFS 把各层「叠」在一起,最上面加一个可写层。你修改文件时,实际上是在可写层做了一份拷贝(Copy-on-Write)。

避坑指南:我曾经在 Dockerfile 里写了大量 RUN 命令,每一条都生成一个新层。结果镜像体积飙到 2GB。后来我把多个 RUN 合并成一条,用 && 连接,体积直接降到 800MB。记住:层数越少,镜像越小。

常见的 UnionFS 实现有:

实现 特点 适用场景
OverlayFS 性能好,内核原生支持 Docker 默认驱动(Linux)
AUFS 稳定,但未合入内核主线 早期 Docker 默认驱动
Device Mapper 基于块设备,性能一般 CentOS/RHEL 默认
ZFS 支持快照、压缩 需要高级存储特性时

现在 Docker 在 Linux 上默认用 OverlayFS。你可以在宿主机上查看镜像层的实际存储:

# 查看镜像的层信息
docker image inspect ubuntu:22.04 | jq '.[].RootFS'

# 查看 OverlayFS 挂载点
mount | grep overlay

输出大概长这样:

{
  "Type": "layers",
  "Layers": [
    "sha256:2dc39ba059dcd42ade30aae30147b5692777ba9ff0779a62ad93a74de02e3e1f",
    "sha256:5b3b991d6d3e3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f"
  ]
}

每一层对应一个 SHA256 哈希值。Docker 用这些哈希值做缓存——如果你拉取的镜像某层已经存在,就直接复用,不用重新下载。

1.4 镜像仓库介绍

镜像做好了,总得有个地方存吧?这就是镜像仓库干的事。

镜像仓库(Registry)是存储和分发镜像的服务。最常用的当然是 Docker Hub,但生产环境我建议你用私有仓库。

仓库的命名规则很简单:

[仓库地址]/[命名空间]/[镜像名]:[标签]

# 例子
docker.io/library/nginx:1.25
myregistry.example.com/myteam/myapp:v2.0.1

常见的镜像仓库:

仓库 类型 特点
Docker Hub 公共 官方镜像多,但拉取有限速
Harbor 私有 企业级,支持 RBAC、漏洞扫描
GitHub Container Registry 公共/私有 与 GitHub 集成好
Amazon ECR 私有 AWS 生态,安全
Azure Container Registry 私有 Azure 生态,支持地理复制

我的建议:个人项目用 Docker Hub 没问题。但公司项目,一定要搭私有仓库。我见过不止一次因为 Docker Hub 限速导致 CI/CD 流水线卡死的惨案。Harbor 是我用得最多的,功能全、社区活跃。

拉取和推送镜像的基本操作:

# 登录仓库
docker login myregistry.example.com

# 拉取镜像
docker pull myregistry.example.com/myapp:latest

# 给镜像打标签
docker tag myapp:latest myregistry.example.com/myapp:v1.0

# 推送镜像
docker push myregistry.example.com/myapp:v1.0

1.5 本章知识体系总览

为了让你对本章内容有个整体印象,我画了一张结构图:

镜像基础概念知识体系 镜像基础概念 什么是镜像 镜像与容器关系 分层原理(UnionFS) 镜像仓库介绍 只读模板 分层存储 类 vs 对象 一次构建多处运行 OverlayFS Copy-on-Write Docker Hub Harbor 理解这四块内容,镜像基础就稳了 后续章节会深入校验、加载、安全等实战

嗯,这张图基本把本章的知识点串起来了。你仔细看看,每个分支之间其实都有联系——比如分层原理直接影响了镜像仓库的存储效率,镜像和容器的关系又决定了你如何设计 CI/CD 流程。

好了,镜像基础就聊到这儿。记住一句话:镜像就是容器的蓝图,分层是镜像的灵魂,仓库是镜像的家。后面几章,咱们会一步步深入镜像的加载、校验和分发实战。到时候你会发现,今天打下的基础,全都能用上。