更现代的 OCI 镜像格式,采用了 OCI Image Format Specification,其中文件引用使用 blobs/sha256/<hash>
的形式,层和配置存储在 blobs/sha256/
目录下,并且包含 LayerSources
字段。这种格式在较新的 Docker 版本和 OCI 兼容的镜像存储中更常见。
从零开始手动创建一个可以被 docker load
加载的 Docker 镜像的步骤。我们将构造一个简单的镜像,包含一个 shell 脚本,输出 “Hello from my custom image!”,并确保生成的 tar 文件符合你提供的 manifest.json
结构。
从零创建 Docker 镜像(基于 OCI 格式)
我们将创建一个包含单个文件系统层的镜像,运行一个 shell 脚本。最终生成一个 tar 文件,包含 blobs/sha256/
目录结构和相应的 manifest.json
。
步骤 1:准备文件系统层
创建一个简单的文件系统,包含一个可执行的 shell 脚本。
- 创建工作目录和文件系统结构:
mkdir -p my-image/rootfs/bin
cd my-image
- 创建一个 shell 脚本:
echo -e '#!/bin/sh\necho "Hello from my custom image!"' > rootfs/bin/hello.sh
chmod +x rootfs/bin/hello.sh
- 打包文件系统层为 tar 文件:
tar -cvf layer.tar -C rootfs .
- 计算层的 SHA256 校验值:
sha256sum layer.tar
假设输出为:
e4d7f1b4c3b2a1... layer.tar
将层文件移动到 blobs/sha256/
目录:
mkdir -p blobs/sha256
mv layer.tar blobs/sha256/e4d7f1b4c3b2a1...
步骤 2:创建镜像配置文件
创建一个 config.json
文件,描述镜像的运行时配置。需要注意的是,config.json
本身也会被存储为一个 blob,并以 SHA256 校验值命名。
在 my-image
目录下创建 config.json
:
{"architecture": "amd64","os": "linux","config": {"Entrypoint": ["/bin/hello.sh"],"Env": ["PATH=/bin"],"WorkingDir": "/"},"rootfs": {"type": "layers","diff_ids": ["sha256:e4d7f1b4c3b2a1..."]},"history": [{"created": "2025-04-19T00:00:00Z","created_by": "Manual creation"}]
}
- 计算
config.json
的 SHA256 校验值:
sha256sum config.json
假设输出为:
d41617c0b2d95b2... config.json
将 config.json
移动到 blobs/sha256/
目录:
mv config.json blobs/sha256/d41617c0b2d95b2...
步骤 3:创建 Manifest 文件
manifest.json
描述镜像的层和配置信息,引用 blobs/sha256/<hash>
路径。
在 my-image
目录下创建 manifest.json
:
[{"Config": "blobs/sha256/d41617c0b2d95b2...","RepoTags": ["my-custom-image:latest"],"Layers": ["blobs/sha256/e4d7f1b4c3b2a1..."],"LayerSources": {"sha256:e4d7f1b4c3b2a1...": {"mediaType": "application/vnd.oci.image.layer.v1.tar","size": 1024,"digest": "sha256:e4d7f1b4c3b2a1..."}}}
]
填写 LayerSources
的 size
字段
LayerSources
中的 size
是层文件的大小(字节)。运行以下命令获取 layer.tar
的大小:
ls -l blobs/sha256/e4d7f1b4c3b2a1... | awk '{print $5}'
假设大小为 1024
字节,更新 manifest.json
中的 size
字段。
步骤 4:创建 OCI 布局文件
OCI 镜像需要一个 oci-layout
文件,表明目录符合 OCI 镜像规范。
在 my-image
目录下创建 oci-layout
:
{"imageLayoutVersion": "1.0.0"
}
步骤 5:打包镜像
将所有文件打包成一个 tar 文件,符合 Docker/OCI 镜像格式。
在 my-image
目录下运行:
tar -cvf my-image.tar oci-layout blobs/sha256/e4d7f1b4c3b2a1... blobs/sha256/d41617c0b2d95b2... manifest.json
这会生成 my-image.tar
,即最终的 Docker 镜像文件。
步骤 6:验证和加载镜像
将 my-image.tar
传输到有 Docker 的环境中,运行:
docker load -i my-image.tar
加载后,检查镜像:
docker images
应该能看到 my-custom-image:latest
。
运行镜像:
docker run my-custom-image:latest
预期输出:
Hello from my custom image!
注意事项
- SHA256 校验值:确保
layer.tar
和config.json
的 SHA256 值正确,文件名和manifest.json
中的引用必须一致。 - 文件权限:确保
hello.sh
有可执行权限(chmod +x
)。 - LayerSources:
LayerSources
字段是可选的,但在某些 Docker 版本中需要。如果不需要,可以从manifest.json
中移除。 - mediaType:层使用
application/vnd.oci.image.layer.v1.tar
,配置使用application/vnd.oci.image.config.v1+json
(Docker 通常会推断)。 - 依赖问题:此例中,
hello.sh
依赖/bin/sh
。如果目标环境没有/bin/sh
,需要将sh
二进制文件包含在rootfs
中。 - 目录结构:确保
blobs/sha256/
目录和文件路径与manifest.json
中的引用一致。
扩展:支持多层
如果需要多个层,重复以下步骤:
- 创建新的文件系统目录,添加或修改文件。
- 打包成新的
layerN.tar
,计算 SHA256 值,移动到blobs/sha256/<hash>
。 - 更新
config.json
的rootfs.diff_ids
,添加新的sha256:<hash>
。 - 更新
manifest.json
的Layers
和LayerSources
,添加新的层引用。
例如,添加第二个层:
mkdir rootfs2
echo "Another file" > rootfs2/another.txt
tar -cvf layer2.tar -C rootfs2 .
sha256sum layer2.tar # 假设得到 sha256:abcdef...
mv layer2.tar blobs/sha256/abcdef...
ls -l blobs/sha256/abcdef... | awk '{print $5}' # 获取大小,假设为 2048
更新 config.json
:
"rootfs": {"type": "layers","diff_ids": ["sha256:e4d7f1b4c3b2a1...", "sha256:abcdef..."]
}
更新 manifest.json
:
"Layers": ["blobs/sha256/e4d7f1b4c3b2a1...", "blobs/sha256/abcdef..."],
"LayerSources": {"sha256:e4d7f1b4c3b2a1...": {"mediaType": "application/vnd.oci.image.layer.v1.tar","size": 1024,"digest": "sha256:e4d7f1b4c3b2a1..."},"sha256:abcdef...": {"mediaType": "application/vnd.oci.image.layer.v1.tar","size": 2048,"digest": "sha256:abcdef..."}
}
重新打包:
tar -cvf my-image.tar oci-layout blobs/sha256/e4d7f1b4c3b2a1... blobs/sha256/d41617c0b2d95b2... blobs/sha256/abcdef... manifest.json
总结
通过以上步骤,你可以完全不依赖 Docker 工具,从零创建一个符合 OCI 规范的 Docker 镜像,匹配你提供的 manifest.json
格式。最终的 my-image.tar
可以通过 docker load
导入并运行。如果你有更复杂的需求(如特定的软件、配置或多架构支持),请提供更多细节,我可以进一步优化!