Skip to content
Belier Blog
Go back

使用 Docker Compose 搭建私有镜像仓库

1. 生成认证文件 htpasswd

使用 docker 生成认证文件,避免本地安装 htpasswd 工具:

mkdir -p auth
docker run --rm --entrypoint htpasswd httpd:2 -Bbn <username> <password> > auth/htpasswd

2. 创建 app_net 网络

方便容器间通信(registry-ui、registry-server 与 nginx 可以互相通过容器名访问):

docker network create app_net

3. 创建 docker-compose.yml

使用 Joxit/docker-registry-ui 进行镜像管理

services:
  registry-ui:
    image: joxit/docker-registry-ui:main
    restart: unless-stopped
    environment:
      - SINGLE_REGISTRY=true
      - REGISTRY_TITLE=Docker Registry UI
      - DELETE_IMAGES=true
      - SHOW_CONTENT_DIGEST=true
      - NGINX_PROXY_PASS_URL=http://registry-server:5000
      - SHOW_CATALOG_NB_TAGS=true
      - CATALOG_MIN_BRANCHES=1
      - CATALOG_MAX_BRANCHES=1
      - TAGLIST_PAGE_SIZE=100
      - REGISTRY_SECURED=true
      - CATALOG_ELEMENTS_LIMIT=1000
    container_name: registry-ui

  registry-server:
    image: registry:2
    restart: unless-stopped
    environment:
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Origin: '[https://hub.example.com]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Methods: '[HEAD,GET,OPTIONS,DELETE]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Credentials: '[true]'
      REGISTRY_HTTP_HEADERS_Access-Control-Allow-Headers: '[Authorization,Accept,Cache-Control]'
      REGISTRY_HTTP_HEADERS_Access-Control-Expose-Headers: '[Docker-Content-Digest]'
      # REGISTRY_PROXY_REMOTEURL: 'https://registry-1.docker.io'
      REGISTRY_STORAGE_DELETE_ENABLED: 'true'
      REGISTRY_AUTH_HTPASSWD_PATH: '/auth/htpasswd'
      REGISTRY_AUTH_HTPASSWD_REALM: 'Registry Realm'
      REGISTRY_AUTH: 'htpasswd'
    volumes:
      - ./data:/var/lib/registry
      - ./auth:/auth
    container_name: registry-server

networks:
  default:
    external: true
    name: app_net

4. 启动

docker compose up -d

启动后可通过日志确认两个容器均正常运行:

docker compose logs -f registry-server
docker compose logs -f registry-ui

5. 配置 nginx

server {
    listen 80;
    server_name hub.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name hub.example.com;
    ssl_certificate      /etc/nginx/ssl/example.com/full.pem;
    ssl_certificate_key  /etc/nginx/ssl/example.com/key.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    client_max_body_size 1g;
    chunked_transfer_encoding on;

    location / {

        proxy_pass http://registry-ui:80;

        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_set_header X-Forwarded-Proto $scheme;

        # 确保 Nginx 不会截断或修改用于认证的 Authorization 头部
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header Authorization;

        # 增加超时时间,防止上传大镜像时断开
        proxy_read_timeout               900;
        proxy_connect_timeout            900;
        proxy_send_timeout               900;

        # 禁用代理缓冲区,直接流式传输大文件
        proxy_buffering off;
        proxy_request_buffering off;
    }
}

配置生效后重新加载 nginx:

docker exec -it nginx service nginx reload

6. 验证

登录(使用步骤 1 中设置的用户名密码):

docker login hub.example.com

推送一个测试镜像,确认仓库可正常读写:

docker pull alpine:latest
docker tag alpine:latest hub.example.com/alpine:latest
docker push hub.example.com/alpine:latest

浏览器访问 https://hub.example.com,应能看到 Docker Registry UI 并列出刚推送的镜像。

常见问题



Next Post
群晖运行 Jellyfin 并开启硬件转码