某镜像站的运维日记 p1
Mon May 13 2024
# 某镜像站的运维日记
祖宗之法不可变 我tm直接全部重构(bushi
刚接手这个服务器的时候,硬盘是满的,运维文档是没有的,服务端版本是过期的,还全部是手动安装的,没用 docker,甚至不是包管理器装的
经过一番摸索,大概摸清了一部分服务的运行方式,因为要部署一些新的服务,同时有一些旧的服务需要更新,在考量之后,决定用 Docker 重构,将原有的服务移入容器,同时用容器部署新的服务
同时考虑到这些 compose 之后可能也会成为现在这种没有文档的状态,于是决定采用 git 来管理(至少有了个 commit 记录)
注意 这里是直接 git push server-hostname/srv/compose
将本地的仓库推到服务器上的,这个时候服务器上的仓库应该是一个 Bare Repository,但是这样就没有一个 work tree,也就是说没法直接访问到仓库里面的文件,这显然是不符合预期的,所以我没有用 Bare Repository,而是用了个有 work tree 的仓库,但是手动 git checkout -d <mainBranch>
,从分支上 detach,这样既可以将仓库推送到远程,又能直接访问 work tree(虽然好像要多一步,但是想想这好像也不是什么大问题,相当于多了一个确认应用更新后的配置文件的机会)
## 将 Minecraft 服务器移入容器
这个其实不是很有必要,但是想着更新一下服务端版本,顺手升级一下 Java Runtime 的版本,于是用了 ghcr.io/graalvm/jdk-community:21.0.2
这个镜像,然后随手写一个 compose 文件,手动修一下文件权限,直接就能跑😋
## 部署 Docker Registry 镜像
我同时部署了 Docker Hub 和 ghcr.io 两个 registry 的镜像
registry 用的是 docker 官方的镜像,具体配置在文档里也都有,这里不复制了(
这里用到了一个比较奇怪的trick,正常来说,一个 registry 实例是只能有一个上游的,而 registry 的 api endpoint 在 /v2
下,所以一般镜像站(如南京大学镜像站),都是用域名做分流的。但是,我们有且仅有一个域名,所以我们必须得用路径做分流。这里就涉及到 docker api 的格式了,如果你访问的是 xxx/user/image
这个镜像,对应到 api 路径就是 /v2/xxx/user/image
,那就可以写一个 nginx 的分流规则,把 /v2/docker(/.*)
重写成 /docker/v2$1
,然后反代到 docker hub 的镜像,同理把 /v2/ghcr(/.*)
重写成 /ghcr/v2$1
,这样就既支持在 docker 的 daemon.json 里面配置镜像,也支持 直接使用 docker pull
但是,你觉得到这里就结束了吗?
除了 docker 用户,还有不少的 podman 用户(甚至可能还有用 nerdctl 进行操作 containerd 的用户,但是我懒得测试了)
podman 的 cli 在进行 pull 操作的时候,会先向 /v2
发送一个请求,虽然这个响应是一个空的 json,没什么实际数据,但是 podman 就是要请求这一下,如果响应是 4xx 或者 5xx 就直接报错……而我们刚才的分流并没有考虑到这一点,所以还要对 podman 特别加一个 location 做支持,我这里是把 /v2
直接 302 重定向到 /v2/docker
最后 nginx 的配置如下:
location /v2/docker/ {
include proxy_params;
proxy_pass http://localhost:5000/v2/;
}
location /docker/ {
include proxy_params;
proxy_pass http://localhost:5000/;
}
location /v2/ghcr/ {
include proxy_params;
proxy_pass http://localhost:5001/v2/;
}
location /ghcr/ {
include proxy_params;
proxy_pass http://localhost:5001/;
}
location /v2 {
return 302 /v2/docker/;
}