1. 生成认证文件 htpasswd
使用 docker 生成认证文件,避免本地安装 htpasswd 工具:
mkdir -p auth
docker run --rm --entrypoint htpasswd httpd:2 -Bbn <username> <password> > auth/htpasswd
-B使用 bcrypt 加密(registry 的 htpasswd 认证要求 bcrypt)-b允许在命令行直接传入密码,避免交互输入-n不将结果写入文件,而是直接输出到屏幕,通过 > 保存到指定文件- 生成的
auth/htpasswd会被registry-server容器挂载读取
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 并列出刚推送的镜像。
常见问题
- 推送镜像 401 Unauthorized:检查
auth/htpasswd是否正确挂载,以及 nginx 是否透传了Authorization头(见第 5 步配置)。 - 配置
REGISTRY_PROXY_REMOTEURL后无法推送镜像:该配置会让 registry 变为拉取代理(pull-through cache)模式,此模式下不支持推送,因此需要保持注释状态。 - 大镜像上传中断:确认 nginx 的
client_max_body_size、超时时间和proxy_buffering off配置已生效。