Docker 私有镜像仓库 Registry 部署

概述

在容器化技术普及的今天,Docker镜像已成为应用分发和部署的核心载体。然而,依赖公共仓库(如Docker Hub)存在安全隐患、网络依赖、速率限制等问题。私有Docker镜像仓库不仅能提升安全性,还能优化CI/CD流程、控制镜像版本、降低网络依赖,尤其适合企业级开发、离线环境或需要严格权限管理的场景。

Registry 是 Docker 的镜像存储服务,DockerHub 上的 registry 镜像见 Registry 官方镜像,更多详细信息请转至 GitHub 查看最新源码。

本文档介绍如何通过 Docker Compose 搭建一个简单的 registry 环境。使用 DockerHub 官方镜像,registry 镜像版本为 registry:3。这里主要介绍 registry 环境的搭建及使用,更详细的企业级 registry 服务器的搭建可参阅开源的 Harbor

方案选型:Docker Registry vs Harbor

在搭建私有仓库前,需明确技术选型。当前主流方案有两种:

  1. Docker 官方 Registry:轻量级、开源免费,适合小型团队或快速验证场景。
  2. Harbor:企业级镜像仓库,基于Registry扩展,提供权限管理、镜像扫描、UI界面等高级功能。

对比结论

  • 若追求快速搭建且功能需求简单,选择Docker Registry;
  • 若需企业级管理功能(如RBAC权限、漏洞扫描),选择Harbor。

本文以Docker Registry为例,因其部署更简单,适合“快速搭建”的核心目标。

环境准备

本次部署环境为内网树莓派,系统架构为 ubuntu/aarch64

基础环境如下:

项目配置
主机名Amadeus
内网地址192.168.0.10
Registry 域名registry.amadeus.lan
CPU4 核
内存8GB
Docker26.1.5
Docker Compose2.26.1
OpenSSL3.5.6
Registry 端口基础直连端口 5000;域名访问使用 HTTPS 默认端口 443
访问方式HTTPS

目录规划如下:

用途路径
Compose 工程目录/home/lyra/workspace/registry
证书目录/home/lyra/workspace/registry/certs
认证目录/home/lyra/workspace/registry/auth
镜像数据目录/srv/registry/data

初始化目录:

mkdir -p /home/lyra/workspace/registry/{certs,auth} sudo mkdir -p /srv/registry/data

进入 Compose 工程目录:

cd /home/lyra/workspace/registry

确认 Docker、Docker Compose 和 OpenSSL 可用:

docker --version docker compose version openssl version

HTTPS 证书采用内网私有 CA 签发,证书需要包含 Registry 的访问域名和内网 IP:

registry.amadeus.lan 192.168.0.10

基础直连访问地址:

https://registry.amadeus.lan:5000

如果需要通过 https://registry.amadeus.lan 直接访问,按“配置域名访问”章节修改端口映射和代理配置。

搭建 Registry

构建配置文件

目录结构:

/home/lyra/workspace/registry ├── docker-compose.yml ├── config └── config.yml ├── certs ├── ca.crt ├── registry.crt └── registry.key └── auth └── htpasswd /srv/registry └── data

创建配置目录:

mkdir -p /home/lyra/workspace/registry/{config,certs,auth} sudo mkdir -p /srv/registry/data sudo chown -R 1000:1000 /srv/registry/data

config/config.yml 是 Registry 服务配置文件,控制存储路径、HTTP 监听、HTTPS 证书和认证方式。下面是基础直连模式的配置;如果需要通过域名直接访问,后续需要按“配置域名访问”章节调整 http.host 和 TLS 入口。

version: 0.1 log: fields: service: registry storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true http: addr: :5000 host: https://registry.amadeus.lan:5000 tls: certificate: /certs/registry.crt key: /certs/registry.key headers: X-Content-Type-Options: [nosniff] auth: htpasswd: realm: basic-realm path: /auth/htpasswd

docker-compose.yml 用于定义 Registry 容器、端口映射和数据挂载。下面的配置会把宿主机 5000 端口映射到 Registry 容器,适合先完成基础服务验证;如果要改为域名无端口访问,后续需要按“配置域名访问”章节调整 ports

services: registry: image: registry:3 container_name: registry restart: unless-stopped user: "1000:1000" ports: - "5000:5000" volumes: - /srv/registry/data:/var/lib/registry - ./config/config.yml:/etc/distribution/config.yml:ro - ./certs:/certs:ro - ./auth:/auth:ro command: - serve - /etc/distribution/config.yml

auth/htpasswd 用于保存 Registry 用户认证信息,文件在后续步骤中生成。

生成 HTTPS 证书

进入 Compose 工程目录:

cd /home/lyra/workspace/registry

生成内网私有 CA:

openssl genrsa -out certs/ca.key 4096 openssl req -x509 -new -nodes \ -key certs/ca.key \ -sha256 \ -days 3650 \ -out certs/ca.crt \ -subj "/CN=Amadeus Registry CA"

生成 Registry 服务端私钥:

openssl genrsa -out certs/registry.key 4096

生成证书签名请求配置文件:

cat > certs/registry.cnf <<'EOF' [req] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [dn] CN = registry.amadeus.lan [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = registry.amadeus.lan IP.1 = 192.168.0.10 EOF

生成 Registry 证书签名请求:

openssl req -new \ -key certs/registry.key \ -out certs/registry.csr \ -config certs/registry.cnf

生成证书扩展配置文件:

cat > certs/registry.ext <<'EOF' authorityKeyIdentifier = keyid,issuer basicConstraints = CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = registry.amadeus.lan IP.1 = 192.168.0.10 EOF

使用内网私有 CA 签发 Registry 服务端证书:

openssl x509 -req \ -in certs/registry.csr \ -CA certs/ca.crt \ -CAkey certs/ca.key \ -CAcreateserial \ -out certs/registry.crt \ -days 3650 \ -sha256 \ -extfile certs/registry.ext

查看证书信息:

openssl x509 -in certs/registry.crt -noout -subject -issuer -dates openssl x509 -in certs/registry.crt -noout -text | grep -A 2 "Subject Alternative Name"

调整证书文件权限:

chmod 600 certs/ca.key certs/registry.key chmod 644 certs/ca.crt certs/registry.crt

生成认证文件

使用 httpd:2 镜像生成 htpasswd 文件:

docker run --rm \ --entrypoint htpasswd \ httpd:2 \ -Bbn registry '<registry-password>' > auth/htpasswd

调整认证文件权限:

chmod 600 auth/htpasswd

其中 registry 为登录用户名,<registry-password> 替换为实际密码。

启动 Registry 服务

docker-compose.yml 文件目录下启动 Registry 服务:

cd /home/lyra/workspace/registry docker compose up -d

查看容器状态:

docker compose ps

查看服务日志:

docker compose logs -f registry

常用管理命令:

docker compose stop docker compose restart docker compose down

配置域名访问

通过域名直接访问 Registry 时,客户端使用默认 HTTPS 端口 443,登录地址不再携带端口:

docker login registry.amadeus.lan

方案一:直接映射 443 端口

该方案由 Registry 容器直接处理 HTTPS,将宿主机 443 端口映射到容器内 5000 端口。

需要修改 docker-compose.yml。核心变化是把宿主机 443 映射到容器 5000,其余数据目录、配置文件和认证文件仍沿用基础配置。

services: registry: image: registry:3 container_name: registry restart: unless-stopped user: "1000:1000" ports: - "443:5000" volumes: - /srv/registry/data:/var/lib/registry - ./config/config.yml:/etc/distribution/config.yml:ro - ./certs:/certs:ro - ./auth:/auth:ro command: - serve - /etc/distribution/config.yml

需要修改 config/config.yml。核心变化是把 http.host 改为不带端口的 HTTPS 域名,Registry 仍然由容器自身加载证书并处理 TLS。

version: 0.1 log: fields: service: registry storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true http: addr: :5000 host: https://registry.amadeus.lan tls: certificate: /certs/registry.crt key: /certs/registry.key headers: X-Content-Type-Options: [nosniff] auth: htpasswd: realm: basic-realm path: /auth/htpasswd

客户端证书信任目录需要改为:

/etc/docker/certs.d/registry.amadeus.lan/ca.crt

应用配置:

cd /home/lyra/workspace/registry docker compose up -d

方案二:Nginx 反向代理

该方案由 Nginx 监听宿主机 443 端口并处理 HTTPS,Registry 容器只监听本机 127.0.0.1:5000

需要修改 docker-compose.yml。核心变化是只把 Registry 暴露到宿主机回环地址,避免局域网客户端绕过 Nginx 直接访问 5000 端口。

services: registry: image: registry:3 container_name: registry restart: unless-stopped user: "1000:1000" ports: - "127.0.0.1:5000:5000" volumes: - /srv/registry/data:/var/lib/registry - ./config/config.yml:/etc/distribution/config.yml:ro - ./auth:/auth:ro command: - serve - /etc/distribution/config.yml

需要修改 config/config.yml。核心变化是移除 Registry 自身的 TLS 配置,由 Nginx 统一处理 HTTPS;Registry 只保留 HTTP 服务、存储和认证配置。

version: 0.1 log: fields: service: registry storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true http: addr: :5000 host: https://registry.amadeus.lan headers: X-Content-Type-Options: [nosniff] auth: htpasswd: realm: basic-realm path: /auth/htpasswd

应用 Registry 配置:

cd /home/lyra/workspace/registry docker compose up -d

创建 Nginx 证书目录:

sudo mkdir -p /etc/nginx/certs/registry sudo cp /home/lyra/workspace/registry/certs/registry.crt /etc/nginx/certs/registry/registry.crt sudo cp /home/lyra/workspace/registry/certs/registry.key /etc/nginx/certs/registry/registry.key sudo chmod 600 /etc/nginx/certs/registry/registry.key sudo chmod 644 /etc/nginx/certs/registry/registry.crt

新增 Nginx 配置文件 /etc/nginx/conf.d/registry.conf。该配置负责监听 443,加载 Registry 证书,并把 /v2/ 请求转发给本机 Registry 服务。

server { listen 443 ssl; server_name registry.amadeus.lan; ssl_certificate /etc/nginx/certs/registry/registry.crt; ssl_certificate_key /etc/nginx/certs/registry/registry.key; client_max_body_size 0; chunked_transfer_encoding on; location /v2/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $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 https; proxy_set_header X-Forwarded-Host $host; proxy_read_timeout 900; add_header Docker-Distribution-Api-Version registry/2.0 always; } }

检查 Nginx 配置并重载:

sudo nginx -t sudo systemctl reload nginx

确认端口监听状态:

sudo ss -ltnp | grep 443 sudo ss -ltnp | grep 5000

客户端证书信任目录需要改为:

/etc/docker/certs.d/registry.amadeus.lan/ca.crt

基础操作

配置客户端域名解析

客户端需要将 Registry 域名解析到树莓派内网地址:

192.168.0.10 registry.amadeus.lan

如果使用本机 hosts 文件解析,Linux 客户端写入 /etc/hosts

echo "192.168.0.10 registry.amadeus.lan" | sudo tee -a /etc/hosts

Windows 客户端写入:

C:\Windows\System32\drivers\etc\hosts

配置客户端信任证书

在需要访问私有仓库的 Docker 客户端上创建证书信任目录(以本机为例):

sudo mkdir -p /etc/docker/certs.d/registry.amadeus.lan

复制 CA 证书:

sudo cp /home/lyra/workspace/registry/certs/ca.crt /etc/docker/certs.d/registry.amadeus.lan/ca.crt

重启 Docker:

sudo systemctl restart docker

账号管理

Registry 使用 auth/htpasswd 保存账号密码。账号变更前先进入 Compose 工程目录:

cd /home/lyra/workspace/registry

修改账号前备份认证文件:

cp auth/htpasswd "auth/htpasswd.bak.$(date +%Y%m%d%H%M%S)"

查看已有账号:

cut -d: -f1 auth/htpasswd

创建新账号:

docker run --rm \ --entrypoint htpasswd \ httpd:2 \ -Bbn <user-name> '<user-password>' >> auth/htpasswd

修改账号密码:

grep -v '^<user-name>:' auth/htpasswd > /tmp/htpasswd.new docker run --rm \ --entrypoint htpasswd \ httpd:2 \ -Bbn <user-name> '<new-password>' >> /tmp/htpasswd.new mv /tmp/htpasswd.new auth/htpasswd chmod 600 auth/htpasswd

删除账号:

grep -v '^<user-name>:' auth/htpasswd > /tmp/htpasswd.new mv /tmp/htpasswd.new auth/htpasswd chmod 600 auth/htpasswd

账号变更后重启 Registry:

docker compose restart registry

使用新账号登录:

docker login registry.amadeus.lan

镜像操作

登录私有仓库:

docker login registry.amadeus.lan

拉取测试镜像:

docker pull hello-world:latest

给测试镜像打标签:

docker tag hello-world:latest registry.amadeus.lan/hello-world:latest

推送测试镜像:

docker push registry.amadeus.lan/hello-world:latest

删除本地测试镜像:

docker rmi registry.amadeus.lan/hello-world:latest

从私有仓库重新拉取:

docker pull registry.amadeus.lan/hello-world:latest

通过 HTTPS 接口查看仓库目录:

curl --cacert certs/ca.crt -u <user-name>:<user-password> https://registry.amadeus.lan/v2/_catalog

查看指定镜像的标签:

curl --cacert certs/ca.crt \ -u <user-name>:<user-password> \ https://registry.amadeus.lan/v2/hello-world/tags/list

删除本地镜像:

docker rmi hello-world:latest docker rmi registry.amadeus.lan/hello-world:latest

删除仓库中的镜像

删除仓库中的镜像需要先获取 manifest digest:

IMAGE_NAME=hello-world IMAGE_TAG=latest DIGEST=$(curl -sI \ --cacert certs/ca.crt \ -u <user-name>:<user-password> \ -H 'Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json' \ "https://registry.amadeus.lan/v2/${IMAGE_NAME}/manifests/${IMAGE_TAG}" \ | awk -F': ' 'tolower($1) == "docker-content-digest" {print $2}' \ | tr -d '\r') echo "${DIGEST}"

根据 manifest digest 删除仓库中的镜像:

curl -X DELETE \ --cacert certs/ca.crt \ -u <user-name>:<user-password> \ "https://registry.amadeus.lan/v2/${IMAGE_NAME}/manifests/${DIGEST}"

再次查看标签:

curl --cacert certs/ca.crt \ -u <user-name>:<user-password> \ "https://registry.amadeus.lan/v2/${IMAGE_NAME}/tags/list"

删除 manifest 后,Registry 不会立即释放磁盘空间。需要停止服务后执行垃圾回收:

cd /home/lyra/workspace/registry docker compose stop registry docker run --rm \ --user "1000:1000" \ -v /srv/registry/data:/var/lib/registry \ -v /home/lyra/workspace/registry/config/config.yml:/etc/distribution/config.yml:ro \ -v /home/lyra/workspace/registry/certs:/certs:ro \ -v /home/lyra/workspace/registry/auth:/auth:ro \ registry:3 \ garbage-collect /etc/distribution/config.yml docker compose up -d

查看镜像数据目录占用空间:

sudo du -sh /srv/registry/data