OCI 容器镜像格式标准
FreeGuideOnline
最新
2026-07-09
mermaid graph TD I[Image Index 索引] --> M1[Manifest 清单] I --> M2[Manifest 清单] M1 --> C[Config 配置] M1 --> L1[Layer 层] M1 --> L2[Layer 层] M1 --> L3[Layer 层]
### 1. 清单(Manifest)
清单文件(`manifest.json`)是整个镜像的**目录**。当你拉取一个镜像时,容器引擎会首先获取这个文件。
一个典型的 OCI 清单包含三项核心信息:
- **`config`**:指向镜像配置文件的描述符(descriptor)。
- **`layers`**:一个描述符数组,按顺序列出了所有文件系统层。
- **`mediaType`**:通常为 `application/vnd.oci.image.manifest.v1+json`。
**描述符(Descriptor)** 是 OCI 中反复使用的重要概念,它包含:
| 字段 | 含义 |
| ----------- | ---------------------- |
| `mediaType` | 内容的媒体类型 |
| `digest` | 内容的唯一标识(哈希) |
| `size` | 内容的字节大小 |
| `urls` | (可选)备选下载地址 |
清单本身也是一个内容可寻址的 JSON 文件,通过其 SHA256 摘要来唯一引用。
### 2. 配置(Config)
配置文件描述了**如何实例化一个运行中的容器**,相当于容器的“DNA”。它包含:
- **镜像创建时间和作者**(`created`, `author`)。
- **容器运行时的启动命令和参数**:`Entrypoint`、`Cmd`。
- **环境变量**:`Env`。
- **工作目录**:`WorkingDir`。
- **暴露的端口和挂载的卷**:`ExposedPorts`、`Volumes`。
- **层的历史记录**(可选):`history` 字段记录了每一层是如何产生的(如 `RUN`、`COPY` 指令)。
配置文件也遵循内容寻址原则,通过其 SHA256 摘要被清单引用。
### 3. 层(Layers)
层是容器镜像**文件系统的增量变化**,每一层都是一个打包后的文件系统变更集。
- 每一层通常是一个 **tar 压缩包**(常用 gzip 或 zstd 压缩)。
- 层与层之间采用**联合挂载(Union Mount)** 的方式叠加,形成最终的文件系统视图。
- 不同镜像可以共享相同的层,这是镜像快速分发和节约存储空间的基础。
OCI 镜像的层必须是**不可变的**。一旦构建完成,层的摘要就固定不变。
---
## 🔍 内容寻址与摘要:为什么安全可靠
OCI 镜像**完全基于内容寻址(Content Addressable)**。所有组件(清单、配置、每一层)都通过 **SHA256 摘要**来唯一标识。
一个典型的摘要字符串格式为:
sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
**这带来了两个巨大优势**:
1. **防篡改**:任何内容改变都会导致摘要变化,从而打破引用链。引擎在拉取时会校验摘要,防止中间人攻击。
2. **去重存储**:相同的层(摘要一致)只会在存储中保存一份,无论被多少个镜像引用。
> ⚠️ 注意:摘要是指**未压缩**内容的校验和,而不是压缩后 tar 包的校验和。这种设计避免了因压缩算法差异导致的摘要变化。
---
## 📦 多架构支持:镜像索引(Image Index)
一个简单的 OCI 镜像只能代表**单一平台**(如 `linux/amd64`)。为了让一个镜像标签(如 `nginx:latest`)自动适配不同的 CPU 架构和操作系统,OCI 提供了**镜像索引(Image Index)** 或叫“胖清单”(Fat Manifest)。
索引文件的媒体类型为 `application/vnd.oci.image.index.v1+json`。
它的结构是一个**清单列表**,每个条目包含:
- 一个平台描述(`platform` 字段,指定 `architecture` 和 `os`)。
- 指向具体**平台镜像清单**的描述符。
示例简化逻辑:
```json
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"platform": { "architecture": "amd64", "os": "linux" },
"digest": "sha256:aaa..."
},
{
"platform": { "architecture": "arm64", "os": "linux" },
"digest": "sha256:bbb..."
}
]
}
当你在 ARM 服务器上执行 docker pull 时,引擎会自动根据当前平台选取对应的清单。
🧪 动手实践:解构一个真实的 OCI 镜像
下面我们用工具将一个镜像保存到本地,然后逐步拆解它的内部结构。
前置条件
- 安装
docker或podman,以及skopeo工具。 - 一个测试镜像:这里以
alpine:latest为例。
步骤 1:使用 skopeo 下载镜像到目录
skopeo 可以干净地将远程镜像拷贝到本地目录,而不依赖 Docker 守护进程。
skopeo copy docker://alpine:latest oci:alpine-oci
执行后会生成一个 alpine-oci 目录,结构如下:
alpine-oci/
├── blobs
│ └── sha256
│ ├── <config-hash>
│ ├── <layer1-hash>
│ └── ...
├── index.json
└── oci-layout
步骤 2:查看顶层索引
打开 index.json,它就是这个镜像的入口:
cat alpine-oci/index.json
你将看到一个索引结构,包含指向实际清单的摘要。
步骤 3:追踪清单文件
根据索引中 manifests 里的摘要,去 blobs/sha256/ 目录下找到同名文件。它就是镜像清单。
# 假设摘要为 abc123...
cat alpine-oci/blobs/sha256/abc123...
你会看到 layers 数组和 config 的描述符。
步骤 4:查看配置内容
再根据清单中 config.digest 的值找到对应的 blob 文件,输出即为容器配置 JSON。
cat alpine-oci/blobs/sha256/<config-digest>
你会看到 Env、Cmd 等熟悉的字段。
步骤 5:查看层的内容
清单中的每个层都对应一个 blob 文件。这些文件是 tar+gzip 存档。
# 复制层并解压查看
cp alpine-oci/blobs/sha256/<layer-digest> layer.tar.gz
tar -xzf layer.tar.gz -C layer_root
ls layer_root # 你会看到经典的 Linux 目录结构