Skip to content

WangJie-Mant/Docker-It-Yourself

Repository files navigation

Docker It Yourself

目录


设计思路

题目的要求是根据 Docker Registry HTTP API v2 规范来设计一个简化的后端服务。运行逻辑的设计和BYR Arichive的思路差不多,也是基于P-C模型的多并发预生成线程池处理并发任务。

遇到的难点

  • 如何读懂文档
  • 搞清楚api规定的不同uri的对应操作
  • 从uri中正确解析出不同的param/header
  • 什么是base64解密?怎么用openssl的函数进行SHA256计算digest以及生成uuid
  • 由于没能及时修正拼写错误导致的各种小问题

文档解读

Registry主要维护三种东西:blob manifestmanifest list 。根据不同的uri,通过路由转发到不同的端点进行处理,最底层的是各种文件操作。

对blob,主要有如下几种操作

method URI 函数 描述
GET /v2/ *router.c 检查registry的api版本,在router中解析uri时构成响应
HEAD /v2/{repository_name}/blobs/{digest} blob_exists 检查blob是否存在
GET /v2/{repository_name}/blobs/{digest} retrieve_blob 下载blob内容
POST /v2/{repository_name}/blobs/uploads initiate_blob_upload 初始化一个上传会话,获得一个会话uuid
PATCH /v2/{repository_name}/blobs/uploads/{uuid} upload_blob_chunk 追加body中包含的数据块,返回Range作为累计大小
PUT /v2/{repository_name}/blobs/{uuid}?digest=sha256: complete_blob_upload 结束上传并校验digest
GET /v2/{repository_name}/blobs/uploads/{uuid} get_blob_upload_status 获取当前会话的状态
DELETE /v2/{repository_name}/blobs/uploads/{uuid} cancel_blob_upload 取消上传,删除会话数据

对manifest, 主要有如下几种操作

当参数为reference时,一律都需要strcmp(uri, "sha256:")来判断是否为digest

method URI 函数 描述
PUT /v2/{repo_name}/manifests/{ref} put_image_manifest - tag:将请求体解析为manifest,若config.data存在,则将config.database64作为镜像的config,校验后更新manifesttags存储
(前置要求:请求header中必须带有Content-Length) - digest:将请求体解析为manifest,若config.data存在,则将config.database64作为镜像的config,校验后更新manifestdigest存储
GET /v2/{repo_name}/manifests/{ref} get_image_manifest - tag:返回manifest list,若指定了Accept,则会从list中返回最佳匹配linux/amd64
(响应体中均携带Docker-Content-Digest - digest:返回对应的manifest,响应头中的mediaType从对应的manifest中获取
HEAD /v2/{repo_name}/manifests/{ref} check_manifest_exist GET方法一致,但只返回响应头,用于检查blob的每层layer的存在性
DELETE /v2/{repo_name}/manifests/{digest} delete_image_manifest digest删除单个manifest,若manifest仍被任一tagmanifest list引用则删除失败

一个image带有多个layer,通常是一个压缩的tar,描述这个文件相对于它的上一层的文件系统改动。在manifest中,layers是一个数组,每个layer都有:digest size mediaType。除了layers,manifest里也有config字段,也可以用digest来引用。

心路历程

解决这类问题的首要任务是读懂文档里描述的两件事:

  • 维护的文件和它们的文件结构,它们之间的关系
  • 对这些文件进行增删改查的操作api的定义

在Docker文档中,每个接口都有非常详尽的描述。入口参数在参数表中设定,请求Headers需要通过readhdr和get_hdr函数来存储到对应的变量中。通过rio进行读取请求体/请求头操作,从而得到对manifest/manifest list/blobs进行操作的先决条件。

manifest与manifest list两类文件的入与出都与json相关,cJSON库提供了很好的支持,让遍历、查找等工作简单快捷。

blob文件则对应二进制文件的操作,fopen返回的文件流配合rio能够对这些文件进行修改和添加。struct stat能够对文件的大小、可访问性等等进行便捷的操作。lseek可以覆盖到任意位置。这些好用的工具都为操作任意二进制字节流提供了安全可靠的接口。

优缺点分析

优点 缺点
使用了模块化组织工程的结构,每个部分分工明确 token鉴权没有完整实现
blob分片上传可靠 断点续传似乎一直有问题
data目录结构清晰,工程二次阅读时能分清楚 缺少对大文件下载的管理机制,完全信任Content-Length
缺少清理机制,过期的uploads、blobs会堆积
多并发情况下的互斥操作缺少测试

使用工具辅助

  • Gemini 2.5 Pro:能够在这样代码量较大的工程里帮助我在不同的时间厘清思路,区分 已经实现尚未实现 的功能。在生成uuid、计算SHA256等陌生领域给出可靠的支持。在代码开发过程中,能够自动帮助校验已经写过的代码,从而在开发过程中就已经避免了大多数bug
  • GPT-5:在测试阶段给出了很多好的本地测试方法。agent可以代劳测试-反馈-debug循环,两手一摊只等出结果。特别好用。test文件夹下的各种测试脚本都是他写的。

项目整体结构及说明

项目结构

路径 描述
📁 DockerItYourself -
├── 📄 server.c 服务器的主程序
├── 📄 blob_handler.c/h 处理与blob增删查改下载等的接口(endpoint)
├── 📄 manifest_handler.c/h 处理与manifest增删查改的接口
├── 📄 router.c/h 根据uri分发请求,并提供两个错误返回函数统一调用
├── 📄 server_doit.c/h 供worker线程调用的服务器处理函数
├── 📄 dockutils.c/h 普遍使用的重要功能,如获取headers 根据字段查找headers 解析请求参数等等
├── 📄 cJSON/csapp/sbuf json文件解析/c语言网络通信和并发等等相关依赖
├── 📘 README.md readme
├── 📁 Authserver 假冒的鉴权服务器
├──├── 📄 authserver.c .c
├── 📁 data 所有服务器相关数据存储
└── 📁 test 测试用例或测试bash

data存储路径

路径 说明
./data/blobs/ 存储所有blobs
./data/blobs/repository_name/digest/ 根据repo/digest存储不同的blob
./data/blobs/repository_name/uuid/data/ 存放上传会话进行过程中的临时文件
./data/manifests/ 分两类存储所有manifets
./data/manifests/digest/ 按digest存放manifest
./data/manifests/tags/ 按tags存放manifest list

About

BYRteam 2025秋季后端考核仓库

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published