OCI即Open Container Initiative,开放容器计划,是一套有关容器与镜像的标准
想要实现一个容器镜像仓库,就要符合OCI的标准与规定的接口
该服务主要维护blob与manifest两种对象,其中blob记录了镜像文件的实际存储路径,manifest(货单)则是用来记录存在这样一个blob
此外还有一个Manifest List,是一个 Tag 所对应所有 Manifest 的元数据列表。digest是manifest和blob的标识符,然而在不同平台digest并不相同,且digest并不方便阅读,于是增加一个tag做标识
建表语句如下:
CREATE TABLE IF NOT EXISTS blobs (
digest TEXT PRIMARY KEY NOT NULL,
size INTEGER NOT NULL,
storage_path TEXT,
content_type TEXT,
created_at TEXT DEFAULT (datetime('now')),
accessed_at TEXT DEFAULT (datetime('now')),
CHECK(digest LIKE 'sha256:%')
);
CREATE TABLE IF NOT EXISTS uploads (
uuid TEXT PRIMARY KEY NOT NULL,
repository TEXT NOT NULL,
tmp_path TEXT NOT NULL,
offset INTEGER NOT NULL DEFAULT 0,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS manifests (
digest TEXT PRIMARY KEY NOT NULL,
repository TEXT NOT NULL,
tag TEXT,
media_type TEXT NOT NULL,
schema_version INTEGER NOT NULL DEFAULT 2,
config_digest TEXT,
data TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY(config_digest) REFERENCES blobs(digest),
CHECK(digest LIKE 'sha256:%')
);
CREATE TABLE IF NOT EXISTS manifest_layers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
manifest_digest TEXT NOT NULL,
layer_digest TEXT NOT NULL,
layer_order INTEGER NOT NULL,
media_type TEXT,
size INTEGER,
FOREIGN KEY (manifest_digest) REFERENCES manifests(digest),
FOREIGN KEY (layer_digest) REFERENCES blobs(digest),
UNIQUE(manifest_digest, layer_order)
);
CREATE TABLE IF NOT EXISTS manifest_lists (
digest TEXT PRIMARY KEY NOT NULL,
repository TEXT NOT NULL,
tag TEXT,
media_type TEXT NOT NULL DEFAULT 'application/vnd.docker.distribution.manifest.list.v2+json',
schema_version INTEGER NOT NULL DEFAULT 2,
data TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
CHECK(digest LIKE 'sha256:%')
);
CREATE TABLE IF NOT EXISTS manifest_list_manifests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
list_digest TEXT NOT NULL,
manifest_digest TEXT NOT NULL,
architecture TEXT NOT NULL,
os TEXT NOT NULL,
variant TEXT,
features TEXT,
os_version TEXT,
os_features TEXT,
size INTEGER,
FOREIGN KEY (list_digest) REFERENCES manifest_lists(digest),
FOREIGN KEY (manifest_digest) REFERENCES manifests(digest),
UNIQUE(list_digest, manifest_digest)
);
CREATE INDEX IF NOT EXISTS idx_blobs_digest ON blobs(digest);
CREATE INDEX IF NOT EXISTS idx_manifests_repo_tag ON manifests(repository, tag);
CREATE INDEX IF NOT EXISTS idx_manifest_lists_repo_tag ON manifest_lists(repository, tag);
blobs表以SHA256算出来的digest为主键,在上传完成后才新增条目,uploads表用作中间状态,以uuid为主键,manifests表与镜像一一对应,其中的层信息存在manifest_layers表中。两个manifest_list相关的表,建出来摆着看的
对blob,主要有三种操作,上传,存在性检查和下载
对于上传,流程是先blob再manifest:
- 上传过程分三步,第一步是一个POST请求,api:
/v2/{name}/blobs/uploads/,给定一个仓库,可能选择挂载一个别的仓库下的镜像需要特殊处理。新建一个upload项,返回一个location URL,用uuid标识 - 第二步是一个PATCH请求,请求体里就是上传的镜像了,这一步是分片进行的,可能包含一个
bytes 0-65535样的Content-Range。从数据库获取upload项,继续追加内容 - 第三步是一个PUT请求,通知上传完成,可能有最后一部分blob内容,完成追加,清理临时文件,记录blob
- 另外还有一个获取上传状态的GET请求,获知已经传了多少数据以便重传,去查offset字段并返回即可
存在性检查时根据digest检查是否有blob存在,取消上传是删除临时文件和upload记录,很直观
下载,读取blob并返回
然后是manifest相关的四个api,增删读查
增在所有层和blob上传之后,直接验证后写入对应字段即可
删只有未被tag或引用的可以,否则403。引用是指多仓库同镜像不重复存的机制,Rc减1即可不能删
读要获知configuration 和 layer digests,以便拉取blob
查存在性和digest/大小