资讯动态

pnpr OCI Blob 删除:授权用户清理未引用层,并发发布下的可达性保护机制

发布时间:2026/9/20 1:55:36 来源:尧图企业网站定制
包管理器开发工具CLI【免费下载链接】pnpmFast, disk space efficient package manager项目地址https://gitcode.com/gh_mirrors/pn/pnpm点击查看免费下载导读本文围绕 pnprpnpm/pnpr在本次 minor 变更中引入的OCI Blob 删除能力展开被授权的用户可以对托管 OCI 仓库中未被任何保留清单manifest引用的 blob发起删除而仍被保留清单可达的 blob 在并发发布期间继续受到保护。文章将结合仓库中的 HTTP 路由、授权策略、两阶段删除协议、离线回收与端到端测试完整还原这一功能从请求到落盘的实现链路使读者既能掌握DELETE /v2/name/blobs/digest的调用方式与前置条件也能理解其并发安全设计。变更内容速览变更集 .changeset/pnpr-oci-blob-deletion.md 记录了本次pnpm/pnpr的 minor 变更核心语义只有两点授权用户可删除未引用的 OCI Blob——删除操作受既有权限体系约束并非开放给任意调用者可达性保护——凡是被保留清单含 manifest 索引、配置、层等递归引用的对象可达的 blob即使在并发发布进行中也不会被误删。下面从授权、路由、保护判定、两阶段删除与离线回收五个层面展开。授权模型删除不是默认开放的破坏性写操作Blob 删除在 pnpr 中属于“破坏性写操作”与包/清单删除共用同一套权限判定。在 pnpr/crates/pnpr/src/server/authentication.rs 中authorize根据Action选取对应的访问列表Action::Access→effective.accessAction::Publish→effective.publishAction::Unpublish→effective.unpublish删除 blob 走的是Action::Unpublish见 deletion.rs。权限不足时匿名调用者得到401 Unauthenticated已认证但不被允许的用户得到403 Forbidden。在 OCI 的 token 协议中请求 scope 的映射在 pnpr/crates/pnpr/src/server/oci/tokens.rs 中定义scope 中的delete动作被映射为Action::Unpublish。也就是说客户端需要在 token 请求中携带repository:name:pull,push,delete之类的 scope 才能获得删除资格。默认拒绝的注册表级策略测试辅助函数 oci_registry.rs 展示了两种配置形态——普通app不开放破坏性写入而app_allowing_deletes通过以下方式显式开启hosted.rules std::mem::take(mut hosted.rules) .with_default_unpublish(AccessList::from_tokens([$authenticated]));即只有显式为托管注册表配置了unpublish默认规则此处允许所有已认证用户后DELETE 请求才会被接受。对应地测试 manifests.rs 验证了“未开放破坏性写入的注册表会拒绝删除”。HTTP 路由DELETE /v2/name/blobs/digestpnpr 的 OCI 分发接口按 Distribution 规范组织blob 端点解析在 pnpr/crates/pnpr/src/server/oci.rs请求分发处 oci.rs 依据方法分流async fn blob(self, name: str, digest: str) - Response { let Ok(digest) Digest::parse(digest) else { return error(ErrorCode::DigestInvalid, not a supported digest); }; match self.method { Method::GET | Method::HEAD self.read_blob(name, digest).await, Method::DELETE self.delete_blob(name, digest).await, _ method_not_allowed(), } }调用形态DELETE /v2/repository/blobs/sha256:digest Authorization: Bearer token其中repository必须是托管仓库即该注册表自身托管的命名空间digest必须能被Digest::parse解析为受支持的摘要格式否则返回DigestInvalid。delete_blob的处理步骤deletion.rs解析托管目标通过hosted_source(name)确认目标仓库属于当前注册表托管若指向上游则拒绝解析命名空间hosted_read_namespace得到组织org确定存储分区授权以Action::Unpublish执行authorize加包级锁locks.packages.lock(key)串行化同一包上的并发写防止与发布/删除清单相互交错带重试执行两阶段删除try_delete_blob循环执行最多DOCUMENT_WRITE_RETRIES次冲突则重试。成功删除返回202 Acceptedblob 不存在返回BlobUnknown“no such blob”被保留清单引用则返回BadRequest原因串为{digest} is referenced by a retained manifest。可达性保护保留清单可达的 blob 不可删这是本次变更的关键安全语义。判定逻辑位于ensure_blob_unreferenceddeletion.rslet reachable referenced_document_blobs(storage, key, document, ...).await?; if reachable.contains(digest.blob_filename()) { return Err(RegistryError::BadRequest { reason: format!({digest} is referenced by a retained manifest), }); }可达性计算在 oci_maintenance.rs 的referenced_document_blobs中实现从文档ImageDocument中记录的所有保留 manifest出发含其媒体类型构成初始待处理队列逐个读取清单 blob校验其字节的摘要与所声称的 digest 一致防止用错误内容冒充将清单引用的所有对象配置、层、子清单等的 digest 记入reachable集合若清单是索引media_type::is_index则把其引用递归加入队列继续展开使用visited集合去重保证循环引用不会死循环最后有一个防御性检查若文档中残留的“待删除标记”deleting_blob本身指向某个可达 blob则拒绝该回收操作避免破坏一致性。因此只要目标 blob 仍被任何一个保留清单无论直接还是间接引用删除都会被拒绝。这保证了并发发布期间——即新清单已提交但相关 blob 尚未被标记为可达的状态——已发布内容的数据完整性。两阶段删除协议标记 → 删除 → 清除标记try_delete_blobdeletion.rs实现了一个可中断、可恢复的两阶段流程快照读取read_hosted_document_for_update读取当前文档及其版本号若文档中已存在deleting_blob标记说明另一处删除正在进行或上次删除中断直接返回DocumentWriteConflict存在性检查open_hosted_blob找不到目标 blob 则返回BlobUnknown可达性检查如上节所述写入删除标记将文档generation加一把待删 digest 写入document.deleting_blob并通过write_hosted_document_if_current带版本条件地提交——若返回Conflict说明其他写入者抢先修改了文档本次尝试返回None触发外层重试物理删除remove_hosted_blob移除 blob 文件清除标记clear_deletion_markdeletion.rs通过update_hosted_document_with_retry原子地将deleting_blob复位为None并校验当前标记仍指向本次删除的 digest防止误清其他删除的标记。这个“先标记后删除”的设计使得若在标记写入之后、blob 删除之前进程崩溃留下的半成品可以由后续请求或离线回收完成——文档中明确记录着“有一个 blob 待删除”。这解释了为何并发发布同一包上的发布也持同一把包级锁见 manifest_request.rs与 blob 删除不会互相撕裂标记既保护了删除目标的一致性也让中断可恢复。离线回收collect_oci_blobs与min_age/dry_run除在线删除外pnpr 还提供离线回收通道用于批量清理长期未引用的 blob。入口为 oci_maintenance.rs 的collect_oci_blobs其约束条件源码文档注释原文语义是调用前必须停止所有共享该存储的写入者包括其他副本并先恢复各副本临时卷上的发布日志。函数签名与参数registry目标托管注册表名必须映射到Ecosystem::Oci且为 hosted 注册表否则返回BadRequestmin_age新生 blob 的保留期限——比该时长更新的 blob 一律保留以便“被中断的推送在维护后可以继续”dry_run为true时只统计候选而不真正删除日志中记录dry_run字段。单次回收流程collectoci_maintenance.rs在临时文件中建立 SQLite 库存库Inventory登记每个仓库下所有 digest 命名的 blob 文件、大小与“是否达到 min_age”inventory_hosted_blobs扫描存储跳过被排除的其他 org 与package.json文档文件mark_reachable_blobs对每个仓库复用referenced_document_blobs把所有可达 blob 标记为keep 1remove_unreferenced_blobs逐条删除old 1 AND keep 0的候选dry_run时只计数、记日志非dry_run时执行finish_pending_deletions收尾在线删除可能中断的半成品——检查每个文档的deleting_blob标记若 blob 仍存在则物理删除并清除标记oci_maintenance.rs。测试 oci_maintenance/tests.rs 中的offline_collection_finishes_interrupted_explicit_deletion正是对这一收尾路径的验证模拟deleting_blob残留后运行回收断言 blob 被移除且标记被清空而corrupt_or_missing_manifests_prevent_deletion则验证当保留清单缺失或摘要校验失败时回收会被阻止read_manifest_blob中的 digest 校验oci_maintenance.rs避免破坏数据。端到端验证测试如何证明“删后可再传”仓库中的集成测试完整复现了本次变更的两个核心断言1. 未引用 blob 可删除且删除后仍可正常发布—— uploads.rsunreferenced_blobs_can_be_deleted_and_uploaded_againlet digest push_blob(app, auth, acme/app, bconfig).await; // DELETE /v2/acme/app/blobs/{digest} → 202 Accepted // GET /v2/acme/app/blobs/{digest} → 404 Not Found push_image(app, auth, acme/app, after-delete).await; // GET /v2/acme/app/manifests/after-delete → 200 OK该用例在开启删除的注册表app_allowing_deletes上验证了未被清单引用的 blob 删除后立即不可读而后续完整推镜像含引用新 blob 的清单不受影响。2. 删除需要显式授权开关—— manifests.rsa_delete_is_refused_unless_the_registry_opens_destructive_writes验证了默认未开启unpublish规则注册表拒绝删除。实践建议与注意事项基于上述实现使用该能力时建议注意删除前先确认引用状态在线 DELETE 本身会拒绝被引用的 blob但会返回400 Bad Request自动化清理流程可先通过GET /v2/name/manifests/ref获取当前清单集合或用离线dry_run预览候选再决定删除范围并发发布安全由于可达性检查与清单提交共享同一把包级锁删除与发布不会交叉破坏但多副本/多写入者场景下的离线回收必须停写含其他副本否则应只使用在线 DELETE中断恢复若删除中途失败deleting_blob标记会保留在文档中可通过再次触发该 blob 的删除或运行离线回收完成收尾权限最小化删除复用unpublish权限位建议按仓库粒度配置unpublish访问列表仅授予确有清理需求的账号避免$all一类过宽默认值测试中仅以$authenticated演示开启方式。小结本次pnpm/pnprminor 变更为托管 OCI 注册表补齐了 blob 生命周期管理的最后一块拼图授权用户在线删除未引用 blobDELETE /v2/name/blobs/digest受unpublish权限约束保留清单可达性保护在并发发布下依然生效并通过“标记 → 删除 → 清除标记”的两阶段协议与离线回收通道保证了中断可恢复。相关实现与测试可在 deletion.rs、oci_maintenance.rs、uploads.rs 中继续深入研读。赞分享包管理器开发工具CLI【免费下载链接】pnpmFast, disk space efficient package manager项目地址https://gitcode.com/gh_mirrors/pn/pnpm点击查看免费下载相关推荐Seerr 删除用户完全指南删除流程、权限保护与数据库级联清理机制Seerr 删除用户完全指南删除流程、权限保护与数据库级联清理机制 本文围绕 Seerr 官方文档 docs/using seerr/users/deleti后端前端认证鉴权pnpm pnpr 跨生态 OCI 批处理发布指南PUT /-/pnpr/v0/publish 使用与实现原理pnpm pnpr 跨生态 OCI 批处理发布指南 PUT / /pnpr/v0/publish 使用与实现原理 导读 本文围绕 pnprpnpm 的下一代包管理器开发工具CLIStatsD认证机制确保只有授权用户能发送指标StatsD认证机制确保只有授权用户能发送指标 为什么StatsD需要认证控制 你是否曾担心未经授权的服务向StatsD发送虚假指标导致监控数据失真在多团可观测性指标监控创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价