Docker Hub 只能传一个私有 repo 也太抠了.

前言

最近有帮人写蛮多乱七八糟的小项目, 也都使用 CI/CD 实现了自动化.

但总是使用 SCP 把文件传到服务器上再 SSH 连上去跑运行脚本实在是太难受了, 版本管理也不好做, 就想着用打包成 Docker 的 Image 去管理.

最开始是想着把 Image 传到 Docker Hub, 但 Docker Hub 不付费只能储存 1 个私有 repo…

又看了别的几家提供了 Docker Registry 服务的服务商, 基本上不是要备案就是太贵用不起.

自托管 Docker Registry 服务

既然嫖不到免费的, 那只能自己搭建了.

Docker 官方有提供简单的 Registry 的 Image. 但坑还是蛮多的, 这个后面会说到.

生成身份认证文件

既然是私有的仓库, 自然是需要设置好身份认证的. 这里我使用简单地Basic Auth进行认证.

现在我们遇到了第一个坑, 按照 官方文档 提供的方法并不能成功地生成密码.

我们需要自己安装htpasswd工具去生成 htpasswd 文件, 或者用下面的命令去生成.

docker run --rm -ti xmartlabs/htpasswd <账号> <密码> > auth/htpasswd

需要先手动创建auth目录.

编写 docker-compose.yaml 文件

认证文件生成好之后把下面的内容写到auth相同目录下的docker-compose.yaml文件.

然后执行docker-compose up -d就能把服务跑起来了.

# docker-compose.yaml
version: '2'

services:
  registry:
    image: registry:2.7.1
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_STORAGE_DELETE_ENABLED: 'true'
    volumes:
      - ./data:/var/lib/registry
      - ./auth:/auth
    networks:
      - registry-net

  ui:
    image: joxit/docker-registry-ui:static
    environment:
      - REGISTRY_TITLE=Private Docker Registry
      - REGISTRY_URL=http://registry:5000
      - PULL_URL=docker.liuli.lol
      - DELETE_IMAGES=true
    ports:
      - '8101:80'
    depends_on:
      - registry
    networks:
      - registry-net

networks:
  registry-net:

配置反向代理

因为上面配置了Basic Auth, 不配置好 https 会存在泄露账号密码的风险, 所以我使用了Caddy2来实现自动申请证书配置 https

Caddy2具体怎么安装可以参考 Caddy2 官方安装方法

# /etc/caddy/Caddyfile
<你的域名>:443 {
  encode gzip

  header / {
    Strict-Transport-Security "max-age=31536000;"
    X-XSS-Protection "1; mode=block"
    X-Frame-Options "DENY"
    X-Robots-Tag "none"
    -Server
  }

  reverse_proxy localhost:8101 {
    header_up X-Real-IP {remote_host}
  }
}

反向代理配置好后就能通过配置的域名使用docker login <你的域名>进行登录了, 浏览器访问https://<你的域名>并输入账号密码能看到已上传的 Image.

一些官方 Registry 的坑

除了上面提到的htpasswd的坑外, 官方的 Registry 的删除 Image 也有坑.

删除 Tag 默认不会删除磁盘上的文件, 当一个 repository 不存在任何 tag 的时候 repository 还会存在.

下面提到的方法请确保在没有任何 repo 被 push 的情况下操作.

删除磁盘文件的坑比较好解决, 可以通过在docker-compose.yaml同目录下执行下面的命令执行 GC 清除磁盘文件.

docker-compose exec registry \
  /bin/registry garbage-collect -m /etc/docker/registry/config.yml

删除 repository 的话需要到data/docker/registry/v2/repositories目录下删除与 repo 同名的目录解决, 删除后需要重启一下容器.

sudo rm -rf data/docker/registry/v2/repositories/<repo名>
docker-compose restart