文章

自建 Docker 私有仓库

GitHub Packages 免费提供 500M 空间 1G 外网流量的私有容器仓库。Pro 用户可达 2G 存储 10G 流量。如果没有特殊需要不建议自己折腾了。

这是只是一篇比较水的记录

快速开始

Docker 官方提供了 Registry 镜像,可以快速部署服务。这里使用 Docker Compose 来运行:

version: "3" 
services:
  registry:
    image: registry:2
    container_name: registry
    restart: always
    ports:
      - "5000:5000"
    volumes:
      - ./registry:/var/lib/registry
    environment:
      REGISTRY_HTTP_ADDR: 0.0.0.0:5000
      REGISTRY_STORAGE_DELETE_ENABLED: "true"

/var/lib/registry 是容器内用户上传镜像的保存位置,根据需求映射到本地。环境变量 REGISTRY_HTTP_ADDR 控制其监听的地址与端口,注意把端口映射出来。

现在在本机已经可以使用啦,试试看吧:

docker pull hello-world
docker tag hello-world:latest localhost:5000/custom:latest
docker push localhost:5000/custom:latest

Tips: 下面命令可以执行 Docker Registry 的垃圾清理:

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

# registry 是容器的名字

HTTPS

上面的最小实例只能在本机使用,要是在其他机器通过 ip 或域名访问默认会被阻断,因为 Docker 默认不允许使用明文 HTTP 推送。这是客户端的限制,与服务器 Registry 无关。

我们当然可以通过配置修改这个行为,但这不是最佳实践。下面还是配一下 HTTPS 证书。非常简单,只需要加几个环境变量:

version: "3" 
services:
  registry:
    image: registry:2
    container_name: registry
    restart: always
    ports:
      - "5000:443"
    volumes:
      - ./registry:/var/lib/registry
      - /www/wwwroot/ssl/docker:/certs
    environment:
      REGISTRY_HTTP_ADDR: 0.0.0.0:443
      REGISTRY_HTTP_TLS_CERTIFICATE: /certs/fullchain.pem
      REGISTRY_HTTP_TLS_KEY: /certs/key.pem
      REGISTRY_STORAGE_DELETE_ENABLED: "true"

注意映射一下包含证书文件的文件夹。

建议使用 acme.sh 来自动化获取与更新免费的 https 证书。

Nginx 反代

Registry 地址带个端口号真是太难看了。而我们的服务器通常也会部署普通的 web 服务,443 端口已经被占用,那么最好的办法就是反向代理一下。

server
{
  listen 80;
  listen 443 ssl http2;
  server_name docker.example.com;

  # -------------------- https
  ssl_certificate     /www/wwwroot/ssl/docker/fullchain.pem;
  ssl_certificate_key /www/wwwroot/ssl/docker/key.pem;
  ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
  ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /www/wwwroot/ssl/docker/fullchain.pem;
  # -------------------- https END

	# .well-known 下文件不走反代以免影响 https 证书签发
  location ^~ .well-known{
    allow all;
  }

  # 请求搜索引擎屏蔽
  location = /robots.txt {
    default_type text/html;
    add_header Content-Type "text/plain; charset=UTF-8";
    return 200 "User-Agent: *\nDisallow: /";
  }

  # 反代
  location / {
    proxy_pass http://localhost:5000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_cache off;
  }
}

上面配置有几个注意点:

  • proxy_cache off; 用于关闭反代缓存。反正 Docker 储存库就在本地,没有缓存的必要,反而在服务端清除一些垃圾后客户端重新 push 可能遇到下面的诡异报错:

    upload manifest blob unknown: blob unknown to registry
    
  • proxy_pass 目前用了 http 协议,同样因为 Docker 仓库服务就在本地,没有必要启用 https。

Nginx 配置 https 后 Docker Registry 就可以不配置 https 了。

来试试我们的反代:

docker pull hello-world
docker tag hello-world:latest docker.example.com/custom:latest
docker push docker.example.com/custom:latest