
pnpm pnpr 的 OCI 容器镜像服务用 docker / podman / skopeo 向 pnpr 推送镜像【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读本指南讲解 pnpm 项目新引入的 pnprpnpm 的 Rust 实现容器镜像服务能力只需在注册表配置中声明ecosystem: ocipnpr 就会以标准 OCI Distribution 协议对外提供镜像仓库可用docker、podman、skopeo直接推送与拉取。读完本文你将掌握如何在 pnpr 中声明 OCI 注册表、用docker login pnpr token 完成认证、理解/v2/API 与镜像命名规则并深入看到这一功能背后的 OCI 协议实现manifest、digest、镜像文档、referrers 等。功能总览pnpr 从包注册表到镜像仓库在 pnpm 仓库的变更说明 .changeset/serve-container-images.md 中pnpm/pnpr以minor版本引入了如下能力pnpr now serves container images. Declare a registry withecosystem: oci. Push to it withdocker,podman, orskopeo. The distribution API answers at/v2/on the host root. An image keeps its own name, with no registry key in the path. Sign in withdocker login, using a pnpr token as the password.翻译并展开这一变更意味着pnpr 不仅仅能代理 npm / cargo / pypi 等软件包生态还能直接充当容器镜像仓库注册表通过ecosystem: oci声明纳入 pnpr 统一的注册表路由与访问控制体系服务端实现的是标准OCI Distribution Specification因此 Docker 生态的任意客户端docker、podman、skopeo都能开箱即用认证复用 pnpr 自身的 token 体系用户不需要额外引入独立的镜像仓库服务。OCI 协议层的纯数据实现集中在 pnpr/crates/oci/src/lib.rs其模块注释明确写道这是pnpr 所讲的 OCI distribution 协议The OCI distribution protocol pnpr speaks包含三部分命名所有 blob 的Digest、客户端推送的Manifest形状以及托管注册表为每个仓库保存的ImageDocument。配置如何声明一个ecosystem: oci的注册表配置项解析在 pnpr/crates/config/src/config_file.rs 中hosted托管型注册表与upstream上游代理型注册表都带有一个ecosystem字段注释说明该字段决定此注册表服务的包生态进而选择它使用的协议。当取值是oci时pnpr 就以 OCI Distribution 协议对外服务。从 pnpr/crates/config/src/registry_graph.rs 的生态组ecosystem group展开逻辑看配置既可以采用组语法也可以采用单条声明语法组语法oci:组下的所有注册表自动继承ecosystem: oci并且org存储归属默认取oci~namedefaultRegistry等默认路由也会带上oci/前缀单条语法显式写ecosystem: oci。例如 pnpr/crates/config/src/tests/registry_graph.rs 中的测试就使用了ecosystem: oci的 hosted 注册表并验证resolve_default(Ecosystem::Oci, repository)的路由行为pnpr/crates/config/src/tests/storage.rs 则验证了生态组的名称作用域、默认注册表与存储组织规则。一个最小可用的声明大致如下以测试中出现的生态组语法为基础# pnpr 配置文件config.yaml registries: oci: images: # 注册表名存储 org 默认成为 oci~images type: hosted ecosystem: oci # 组内可省略此处显式写出更清晰说明具体的access/packages权限规则沿用 pnpr 统一的注册表访问控制模型ecosystem: oci的键名同样会经过生态相关的规范化校验见 pnpr/crates/config/src/registry_graph/namespace.rs。协议选择如何影响路由在 pnpr/crates/config/src/registry_graph.rs 中Npm之外的生态会被逐一登记进路由表oci作为独立生态获得自己的默认注册表与来源解析pnpr/crates/config/src/lib.rs 中还提供了独立的OciConfig位于http.oci下其中包含max_manifest_bytes、max_blob_bytes等大小限制供服务端在读取 manifest / blob 请求体时做上限校验。推送容器镜像docker / podman / skopeo配置好ecosystem: oci的注册表后即可用任意 OCI 兼容客户端推送镜像。以docker为例# 1. 登录密码使用 pnpr token见下文认证一节 docker login pnpr.example.com # 2. 给本地镜像打上 pnpr 仓库的 tag docker tag my-app:1.0.0 pnpr.example.com/my-app:1.0.0 # 3. 推送 docker push pnpr.example.com/my-app:1.0.0podman、skopeo用法等价podman push pnpr.example.com/my-app:1.0.0skopeo copy docker-daemon:my-app:1.0.0 docker://pnpr.example.com/my-app:1.0.0。推送过程本质上是分两步完成的这与 pnpr 的存储模型深度绑定见 pnpr/crates/oci/src/lib.rs 的模块注释上传 blob层与配置镜像的 config 与每一层 layer 都是内容寻址、不可变的字节块客户端先把它们上传到blobs/uploads/写入 manifest发布提交点上传完成后客户端PUT一份 manifest引用这些 blob 的 digest。只有 manifest 写入才算发布了一个版本——没有任何 manifest 引用的 blob 只是待回收的垃圾而不是发布了一半的版本。镜像清单的形态校验pnpr 在 pnpr/crates/oci/src/manifest.rs 定义了接受的 manifest 结构schemaVersion仅接受 2Docker schema 1 早已废弃而被拒绝、mediaType、config一个 Descriptor、layers、manifests用于镜像索引、subjectreferrer 机制、artifactType与annotations。校验规则manifest.rs包括schemaVersion必须是 2mediaType未声明时回退到请求头Content-Type再回退到默认的 OCI image manifest非索引类 manifest 必须携带configdescriptor带subject的 referrer 若本身不是索引则必须携带artifactType或可推导的 config mediaType。支持的媒体类型为了兼容大多数客户端仍然推送 Docker 旧类型的现实见 pnpr/crates/oci/src/media_type.rspnpr 同时接受 OCI 与 Docker 两类 manifest 媒体类型lib.rs媒体类型含义application/vnd.oci.image.manifest.v1jsonOCI 镜像 manifestapplication/vnd.oci.image.index.v1jsonOCI 镜像索引application/vnd.docker.distribution.manifest.v2jsonDocker 镜像 manifestapplication/vnd.docker.distribution.manifest.list.v2jsonDocker manifest 列表命名与路径/v2/与镜像自带名字Distribution API 挂载点OCI Distribution 协议要求所有端点位于镜像引用主机名下的/v2/路径。在 pnpr/crates/oci/src/lib.rs 中这个段被定义为常量/// The path segment every distribution endpoint sits under. pub const API_SEGMENT: str v2;客户端根据镜像引用的 host 推导出 API 地址因此该段不能移动或重命名。pnpr/crates/pnpr/src/server/oci/tests.rs 的测试验证了这一点例如api_base(/v2/acme/app/manifests/1.0, ...)解析出的 API 根就是/v2。镜像名中不含 registry key变更说明特别强调An image keeps its own name, with no registry key in the path. 也就是说镜像引用形如pnpr.example.com/acme/app:1.0.0路径中的仓库名是acme/app不会把注册表的 key如oci/images塞进镜像名。registry key 只影响 pnpr 内部的路由与存储归属org默认为oci~name不影响对外协议层的命名。服务端对/v2/name/type/reference的路由实现位于 pnpr/crates/pnpr/src/server/oci/manifest_request.rsHEAD/GET/PUT/DELETE四种方法的 manifests 端点与 pnpr/crates/pnpr/src/server/oci/discovery.rsGET /v2/_catalog仓库列表、GET /v2/name/tags/list标签列表。标签与引用规则pnpr/crates/oci/src/lib.rs 实现了 distribution 规范的 tag 语法[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}最大长度MAX_TAG_LEN 128首字符必须是 ASCII 字母数字或下划线后续字符仅允许字母数字、.、_、-。manifest 引用只有两种形态tag 或 digest除此之外一律拒绝——既防止存下任何符合规范的客户端都无法寻址的元数据也避免sha256:short这类长得像 digest的 tag 混入标签列表。标签是仓库里唯一的可变对象pnpr/crates/oci/src/document.rs 对TagEntry的设计值得关注每个 tag 记录tag、指向的digest以及updatedUnix 毫秒时间戳。标签是仓库中唯一可变的东西因此已存在即优先的不可变合并规则不适用于 tag——崩溃恢复后重放旧事务会把 tag 拖回旧 manifest。改为按时间戳比较后合并是单调的旧写入重放变成空操作同毫秒平局时新写入获胜因为同一仓库的活跃写入被其包锁串行化。这为跨副本、崩溃恢复场景下标签的最终一致性提供了保证。认证docker login pnpr tokenOCI 客户端通过标准的 registry 认证流程登录pnpr 复用其自身的 token 体系docker login pnpr.example.com # Username: 你的 pnpr 用户名 # Password: 你的 pnpr token变更说明明确密码处填 pnpr token。服务端的认证接入点在 pnpr/crates/pnpr/src/server/authentication.rs请求会先尝试按 OCI token 解码pnpr/crates/pnpr/src/server/oci/tokens/ 模块解码失败则走常规认证路径同文件第 346 行 通过检查路径段中是否出现pnpr_oci::API_SEGMENT即v2来识别 OCI 请求。tokens/tests.rs 中的测试展示了 token 的作用域模型claims 携带audience如/oci/~images与scopes如acme/v2/app上的pull/push权限随后按路径与 HTTP 方法逐条判定是否放行。深度实现pnpr 如何存储一个镜像仓库ImageDocument仓库的持久化形状pnpr/crates/oci/src/document.rs 定义了每个镜像仓库的持久化文档ImageDocument包含name仓库名manifests按 digest 排序的 manifest 条目ManifestEntry记录 digest、mediaType、size以及可选的 referrer 元数据tags按标签名排序的TagEntrygeneration代际计数用于隔离显式 blob 删除之前已预备的日志化发布deleting_blob待删除的 blob digest删除完成前会阻止新的发布。层与配置 blob 刻意不记录在文档里它们是内容寻址、不可变、且在有人引用之前就先上传的字节manifest 才是发布动作因此文档只记录 manifest 与 tag。两个集合始终保持有序因为这里的所有查找都是二分查找manifest()、tag()、resolve()均如此。合并语义让事务重放安全merge()document.rs用于把日志化写入合并进已存储文档manifest 内容寻址已存在即同字节、保持不变tag 按时间戳单调合并。generation 不匹配或存在待删除 blob 时拒绝合并。这样被新写入取代的旧日志化写入可以安全重放而不破坏状态。延伸能力referrers、分页、blob 删除与在线回收围绕容器镜像服务仓库中还有多个配套的变更说明与实现可作为实战参考Referrers APIGET /v2/name/referrers/digest列出挂接到某镜像上的工件签名、SBOM、扫描结果等支持artifactType过滤实现位于 pnpr/crates/pnpr/src/server/oci/referrers.rs并在.changeset/pnpr-oci-protocol-surface.md中说明跨仓库 blob 挂载与分段下载跨仓库 blob 挂载复用可读仓库中的层支持 Range 请求下载 blob_catalog与tags/list支持n/last分页见 discovery.rs 与.changeset/pnpr-oci-protocol-surface.md删除与在线 GCDELETE /v2/name/manifests/reference由 deletion.rs 实现与 manifest_request.rs 的删除端点配合通过deleting_blob门闩阻止删除期间的新发布oci-gc离线回收pnpr oci-gc --registry name收集旧的无引用镜像 blob要求先停止该注册表的写入--dry-run可先预览核心逻辑在 pnpr/crates/pnpr/src/oci_maintenance.rs命令入口在 pnpr/crates/pnpr/src/main.rs见.changeset/oci-registry-operations.md跨副本上传恢复使用 S3 存储时跨副本的上传会话可恢复24 小时无活动的废弃共享上传会话在启动时被清理.changeset/oci-registry-operations.md。小结从.changeset/serve-container-images.md出发可以看到pnpr 的容器镜像服务不是另起炉灶而是把 OCI Distribution 协议作为一类新的生态ecosystem: oci接入其既有的注册表路由、访问控制、token 认证与存储体系。对外它是标准的/v2/镜像仓库docker/podman/skopeo开箱即用对内它依靠内容寻址 blob、以 manifest 写入为发布提交点、用时间戳单调合并保证标签的崩溃安全。无论你是想用 pnpr 统一托管内部 npm 包与容器镜像还是希望深入理解 OCI 注册表的工程实现都可以从 pnpr/crates/oci 与 pnpr/crates/pnpr/src/server/oci 这两个目录开始阅读源码。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考