背景是最近AI编程,我自己搞了不少服务,每个服务部署想着用docker镜像方便点。但是拉docker镜像的时候遇到了一些问题。

是本地环境越来越乱:测试镜像散落在不同的开发机上,docker images 里躺着十几二十个大小不一的镜像,有的甚至好几个 G。想在团队内共享镜像,就需要一个私有仓库,让各端统一从它拉取。

需求很朴素:

  • 能存自建/测试镜像,支持推送和拉取
  • 有基本鉴权,不能裸奔公网
  • 走 HTTPS,证书用已有域名

遇到什么问题

问题一:不知道私有仓库该怎么搭

一开始根本没想到现成的方案。第一反应是"自己写一个"。对 Docker 的直觉是:镜像仓库本质上是一个能接收 blob 上传、按 digest 存储、再按 digest 分发文件的 HTTP 服务。那就自己写一个最小的试试。

当时的大致想法是:

  • 用 Python 写一个 HTTP 服务
  • 接收 POST /v2/xxx/blobs/uploads 的层上传
  • 把 tar 包按 sha256 存到磁盘
  • 提供下载接口

于是真的用 Python + 标准库(http.server)拼了个"小仓库"出来,能存能取,几兆的镜像勉强能推。但很快发现两个问题:

  1. 协议细节太多。镜像推送不只是"传个文件"那么简单,涉及 manifest 校验、blob 去重、上传会话(分片/状态)、HEAD 检查等。手写只会越写越糙,处处是边角。
  2. 认证是另一个大坑。即使协议写对了,鉴权、HTTPS、证书、并发,全是自己造轮子,风险和成本都不划算。

写了一半,隐约觉得"这事儿 Docker 生态里应该有现成的东西"。

问题二:发现 registry:2

后来查了下,Docker 官方早就有独立镜像仓库项目,就是 registry:2(Docker Distribution)。一个容器、一份配置就能跑起来,HTTPS、鉴权、存储驱动、GC 全内置。

于是方向从"自己写"转向"用现成的,把配置和部署自动化"。这就是 registry.sh 脚本的由来:一个封装了部署、停止、状态查询和配置生成的 shell 脚本,目标是"一条命令拉起私有仓库"。

部署与鉴权的相关尝试

尝试一:裸跑 registry,无鉴权

最初的版本最省事:docker run -d -p 5000:5000 registry:2,能推能拉,局域网内自测没问题。

但仔细一想不对劲——这台服务器是公网的,无鉴权等于任何人都能往你的仓库里推镜像,还能白嫖你的磁盘。不行,必须加鉴权。

尝试二:htpasswd 基础认证

Docker Distribution 支持内置的 htpasswd 认证(Basic Auth)。步骤是:

  1. htpasswd -Bbn <user> <pass> 生成认证文件(注意要 -B 用 bcrypt,旧版 -bn 生成的 md5/crypt 在部分场景会出问题)
  2. 挂载进容器,指向 /auth/htpasswd
  3. 配置 auth.htpasswd.realmpath

当时本机没装 htpasswd 命令,于是用官方 httpd:2 镜像来生成:

docker run --rm --entrypoint htpasswd httpd:2 -Bbn registry "$PASSWORD" > /srv/registry/auth/htpasswd

这种"用容器生成凭证"的做法顺手解决了一台干净机器上没有工具的问题,后来一直保留在脚本里。

密码也不硬编码——脚本里用 tr -dc 'A-Za-z0-9' < /dev/urandom 随机生成 24 位,写进 .env,避免明文密码散落在各处。

尝试三:Nginx 反代 + HTTPS

鉴权有了,还差 HTTPS。两个选择:

  • registry 自带的 TLS 配置
  • Nginx 反代终止 TLS

选了 Nginx,理由是这个服务器上本来就有 Nginx 在跑多个服务,证书复用、端口统一(443),后续加路径也方便。反代只暴露 /v2/ 一个路径,其余全部不透传:

location /v2/ {
    proxy_pass http://127.0.0.1:5000;
    proxy_set_header Host $host;
    ...
}

尝试四:顺手配了 Docker Hub 回源缓存

“反正都要搭了”,顺手在 config.yml 里加了:

proxy:
  remoteurl: https://registry-1.docker.io

想法是:以后 docker pull registry.example.com/library/xxx 可以直接缓存 Docker Hub 官方镜像,给服务器省带宽、给内网提速。这个决定当时看起来是加分项,后来却成了坑二(见排障篇)。

阶段性结论

  • 别重复造轮子:镜像仓库这种基础设施,先搜有没有现成方案再动手。Python 手写版虽然能跑通最小闭环,但协议细节和鉴权把它拖到了不可维护的地步。
  • 用容器化的方式解决环境依赖:本机没有 htpasswd?用镜像跑。没有 Python?用镜像跑。这套思路贯穿始终。
  • 公网服务的第一优先级是鉴权:无鉴权能跑 ≠ 能上线。
  • 配置里"顺手"加的东西,往往是最容易忽略的坑:回源缓存就是个例子,详见排障篇。

更多推荐