2026.5.15 更新:
写考核题的那会最强的大模型还是 Gemini 2.5 pro,Agent 大时代还没来。可惜不到一年的时间,Vibe Coding 的发展已经让古法编程的意义消失殆尽了。
最近把题目扔给 GPT-5.5 让他一口气写完,结果是没做任何分层一个文件洋洋洒洒了 700 行写完了,也许只有这种时候才能肯定一下程序员本身的价值吧!又让 AI 审了一下当时的代码,存在不少问题,顺便修复了。
无限进步。
BYRTeam 2025 考核题 Docker It Yourself 报告
一个用 Go 语言实现的轻量级 Docker Registry HTTP API V2 服务器,支持完整的 manifest 和 blob 管理功能。
项目采用分层架构设计:
- Handler层:HTTP请求处理
- Storage层:存储驱动接口和文件系统实现
- Types层:数据结构和错误处理
由于是第一次接触这种偏向于对着文档造轮子的项目,还是挺痛苦的。前期瞎几把整理的文档贴在 这里 了, yysy 写的非常非常乱,感觉还不如直接对着官方文档看。等到初步完工了才想起来可以用 APIFOX 做管理和测试,所以交了问卷又返工,这玩意真是比自己截图方便 998244353 倍啊。
前期花了几个小时读文档写文档,大概理清思路之后设计项目结构,然后花费大量时间设计接口,剩下的存储层和处理层的代码大部分为 Vibe Coding 主导 + 人工 Review。AI 生成代码使用 vsc 集成 copilot,模型为 Claude Sonnet 4 以及 Gemini 2.5 pro。
这道题开始拿到的时候感觉“除了 Docker 这个词之外没几个认识的”,加上自己不能做 BYR-Archive 那题只能做这个其实挺恐慌的。不过在群里 xdd 老师提示读懂文档就完成大部分的时候找回了信心,花了几个小时用 DeepL + Gemini 把文档通读一遍大致理解 Registry 到底是干什么之后,其实本身已经没什么特别可怕的地方了。
不过本人其实没啥后端开发经验,好在 Vibe Coding 已经把写代码的难度从 “精通才能写好” 降到了 “理解需求 + 会说话能写个差不多” ,加之时间给的也很充裕(导致我做的很松弛),边学边做也并非不可能。
/internal/types & /internal/storage/interface.go -> /internal/storage/filesystem.go -> /internal/handler/handler.go
花费时间最大的部分大概是第一步(types 设计和接口设计)。众所周知 Golang 是一门没有类和继承概念的静态编译型语言,意味者 struct 设计和品味的重要性。
对于这个项目多达 11 个的 api,为了提高代码复用,本人设计时经常左脑搏击右脑,几个不同 api 可以实现复用的结构捋不清楚,加上我把文档喂给 AI 写的还和文档要求大相径庭,于是乎自己推翻重来对着官方文档和自己写的文档改了好几次。哎还是太菜了。
贯彻 Vibe Coding 主导 + 人工 Review ()。其实接口写好、参数写好,代码本身的实现问题并非很大,基本都交给了 AI 来写然后我再 Review。
首先完全没考虑并发问题。希望这点不计入考量();其次最大的缺点是本人太菜 Vibe 有点多...
图省事部署到了 render 上。
# API 访问
curl https://mydockerregistry.onrender.com/v2/- Manifest 管理 - 支持获取、上传、检查和删除
- Blob 管理 - 支持分片上传、下载和跨仓库挂载
- 错误处理 - 符合 Docker Registry API 规范的标准错误响应
- 分层架构 - Handler → Storage → FileSystem
- 接口驱动 - 可扩展的存储驱动设计
- 参数对象模式 - 类型安全的接口设计
- 完整的错误处理 - 标准化的错误码和响应格式
| 方法 | 路径 | 描述 |
|---|---|---|
GET |
/v2/{name}/manifests/{reference} |
获取 manifest |
PUT |
/v2/{name}/manifests/{reference} |
上传 manifest |
HEAD |
/v2/{name}/manifests/{reference} |
检查 manifest 是否存在 |
DELETE |
/v2/{name}/manifests/{reference} |
删除 manifest |
| 方法 | 路径 | 描述 |
|---|---|---|
HEAD |
/v2/{name}/blobs/{digest} |
检查 blob 是否存在 |
GET |
/v2/{name}/blobs/{digest} |
获取 blob 内容 |
POST |
/v2/{name}/blobs/uploads/ |
初始化 blob 上传 |
GET |
/v2/{name}/blobs/uploads/{uuid} |
获取上传状态 |
PATCH |
/v2/{name}/blobs/uploads/{uuid} |
上传 blob 数据块 |
PUT |
/v2/{name}/blobs/uploads/{uuid}?digest={digest} |
完成 blob 上传 |
DELETE |
/v2/{name}/blobs/uploads/{uuid} |
取消 blob 上传 |
my_docker_registry/
├── cmd/registry/
│ └── main.go # 应用程序入口
├── internal/
│ ├── handler/
│ │ └── handler.go # HTTP 请求处理层
│ ├── storage/
│ │ ├── interface.go # 存储驱动接口定义
│ │ └── filesystem.go # 文件系统存储实现
│ └── types/
│ ├── errors.go # 标准错误定义
│ ├── blob.go # Blob 相关数据结构
│ └── manifest.go # Manifest 相关数据结构
├── go.mod
├── go.sum
└── registry_data/ # 数据存储目录(运行时创建)
├── blobs/ # Blob 存储(按摘要组织)
│ └── sha256/
│ └── {xx}/ # sha256 前两位作为索引
│ └── {hash}
└── repositories/ # 仓库数据
└── {name}/
├── _manifests/
│ ├── revisions/sha256/{hash}
│ └── tags/{tag}/current/link
└── _uploads/{uuid}/
| API | 成功响应 | 错误响应 |
|---|---|---|
| GET Manifest | 200 获取成功 | 400 无效名称或引用 404 Repository 或 manifest 不存在 |
| PUT Manifest | 201 创建成功 | 400 无效名称、引用或 manifest 404 Repository 不存在 |
| HEAD Manifest | 200 Manifest 存在 | 404 Manifest 不存在 |
| DELETE Manifest | 202 删除成功 | 404 Manifest 或 repository 不存在 |
| API | 成功响应 | 错误响应 |
|---|---|---|
| HEAD Blob | 200 Blob 存在 | 404 Blob 不存在 |
| GET Blob | 200 内容返回 | 404 Blob 不存在 |
| POST Upload Init | 201 挂载成功 202 上传初始化成功 |
404 Repository 不存在 |
| GET Upload Status | 204 上传进行中 | 404 上传会话不存在 |
| PATCH Upload Chunk | 202 Chunk 接受 | 400 格式错误或范围无效 404 上传会话不存在 |
| PUT Complete Upload | 201 上传完成 | 400 无效摘要或缺少参数 404 上传会话不存在 |
| DELETE Cancel Upload | 204 会话取消成功 | 404 上传会话不存在 |
| 错误码 | HTTP 状态 | 消息 | 使用场景 |
|---|---|---|---|
BLOB_UNKNOWN |
404 | blob unknown to registry | Blob 不存在 |
MANIFEST_UNKNOWN |
404 | manifest unknown | Manifest 不存在 |
NAME_UNKNOWN |
404 | repository name not known to registry | Repository 不存在 |
BLOB_UPLOAD_UNKNOWN |
404 | blob upload unknown to registry | 上传会话不存在 |
NAME_INVALID |
400 | invalid repository name | 无效的仓库名称 |
MANIFEST_INVALID |
400 | manifest invalid | 无效的 manifest 格式 |
DIGEST_INVALID |
400 | provided digest did not match uploaded content | 摘要不匹配 |
RANGE_INVALID |
400 | invalid content range | Content-Range 头无效 |
BLOB_UPLOAD_INVALID |
400 | blob upload invalid | 无效的上传参数 |
- 路径:
blobs/sha256/{前两位}/{完整摘要} - 示例:
blobs/sha256/ab/abcdef123... - 特点:全局去重,跨仓库共享
- 内容文件:
repositories/{name}/_manifests/revisions/sha256/{hash} - 标签链接:
repositories/{name}/_manifests/tags/{tag}/current/link - 临时上传:
repositories/{name}/_uploads/{uuid}/
MIT License