Docker Registry 私有映像倉庫建置

Docker Private Registry Setup and Configuration

前言

在企業環境中,將容器映像檔存放於公開的 Docker Hub 並非總是合適的選擇。基於安全性、網路效能及合規要求,建置私有的 Docker Registry 成為許多團隊的必要選項。本文將詳細介紹如何從零開始建置一個安全可靠的私有映像倉庫。

私有 Registry 概述

Docker Registry 是一個開源的映像檔儲存與分發系統。透過自建 Registry,您可以:

  • 安全控管:完全掌控映像檔的存取權限
  • 網路效能:在內網環境中加速映像檔的推送與拉取
  • 合規需求:滿足資料不出境的法規要求
  • 成本控制:避免 Docker Hub 的速率限制與付費方案

基本安裝與執行

最簡單的方式是使用官方提供的 registry 映像檔:

1
2
3
4
5
6
7
8
9
# 拉取官方 registry 映像檔
docker pull registry:2

# 啟動基本的 registry 服務
docker run -d \
  --name registry \
  -p 5000:5000 \
  -v /opt/registry/data:/var/lib/registry \
  registry:2

驗證服務是否正常運作:

1
2
curl http://localhost:5000/v2/_catalog
# 預期輸出:{"repositories":[]}

設定 TLS 加密

為確保傳輸安全,建議為 Registry 設定 TLS 加密。首先產生自簽憑證:

1
2
3
4
5
6
7
8
9
# 建立憑證存放目錄
mkdir -p /opt/registry/certs

# 產生自簽憑證
openssl req -newkey rsa:4096 -nodes -sha256 \
  -keyout /opt/registry/certs/domain.key \
  -x509 -days 365 \
  -out /opt/registry/certs/domain.crt \
  -subj "/CN=registry.example.com"

啟動具備 TLS 的 Registry:

1
2
3
4
5
6
7
8
9
docker run -d \
  --name registry-tls \
  -p 443:443 \
  -v /opt/registry/data:/var/lib/registry \
  -v /opt/registry/certs:/certs \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry:2

設定基本認證

為 Registry 加上帳號密碼認證:

1
2
3
4
5
6
# 建立認證目錄
mkdir -p /opt/registry/auth

# 產生 htpasswd 檔案
docker run --rm --entrypoint htpasswd \
  httpd:2 -Bbn admin secretpassword > /opt/registry/auth/htpasswd

啟動具備認證的 Registry:

1
2
3
4
5
6
7
8
9
docker run -d \
  --name registry-auth \
  -p 5000:5000 \
  -v /opt/registry/data:/var/lib/registry \
  -v /opt/registry/auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  registry:2

登入私有 Registry:

1
2
3
docker login localhost:5000
# 輸入帳號:admin
# 輸入密碼:secretpassword

儲存後端設定

Registry 支援多種儲存後端,可透過設定檔進行配置。建立 /opt/registry/config.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
version: 0.1
log:
  level: info
storage:
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
  cache:
    blobdescriptor: inmemory
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]

若要使用 S3 作為儲存後端:

1
2
3
4
5
6
7
8
storage:
  s3:
    accesskey: YOUR_ACCESS_KEY
    secretkey: YOUR_SECRET_KEY
    region: ap-northeast-1
    bucket: your-registry-bucket
    encrypt: true
    secure: true

推送與拉取映像檔

將本地映像檔推送至私有 Registry:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 標記映像檔
docker tag nginx:latest localhost:5000/my-nginx:v1.0

# 推送至私有 Registry
docker push localhost:5000/my-nginx:v1.0

# 查看倉庫內容
curl http://localhost:5000/v2/_catalog

# 拉取映像檔
docker pull localhost:5000/my-nginx:v1.0

垃圾回收

隨著映像檔的更新,舊版本的 layer 會佔用儲存空間。透過垃圾回收機制清理無用資料:

1
2
3
4
5
6
7
# 執行垃圾回收(dry-run 模式)
docker exec registry bin/registry garbage-collect \
  --dry-run /etc/docker/registry/config.yml

# 實際執行垃圾回收
docker exec registry bin/registry garbage-collect \
  /etc/docker/registry/config.yml

建議定期排程執行垃圾回收:

1
2
# 加入 crontab,每週日凌晨 3 點執行
0 3 * * 0 docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml

使用 Docker Compose 部署

建立 docker-compose.yml 整合所有設定:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: '3.8'

services:
  registry:
    image: registry:2
    container_name: docker-registry
    restart: always
    ports:
      - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_STORAGE_DELETE_ENABLED: "true"
    volumes:
      - ./data:/var/lib/registry
      - ./auth:/auth
      - ./certs:/certs
      - ./config.yml:/etc/docker/registry/config.yml:ro

volumes:
  registry-data:

啟動服務:

1
docker-compose up -d

與 Nginx 反向代理整合

透過 Nginx 作為前端反向代理,提供更彈性的存取控制。建立 nginx.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
upstream docker-registry {
    server registry:5000;
}

server {
    listen 443 ssl;
    server_name registry.example.com;

    ssl_certificate /etc/nginx/certs/domain.crt;
    ssl_certificate_key /etc/nginx/certs/domain.key;

    # 限制上傳大小
    client_max_body_size 0;

    # 關閉 chunked transfer 以支援大型映像檔
    chunked_transfer_encoding on;

    location /v2/ {
        # 基本認證
        auth_basic "Registry Realm";
        auth_basic_user_file /etc/nginx/auth/htpasswd;

        proxy_pass http://docker-registry;
        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;
        proxy_read_timeout 900;
    }
}

更新 docker-compose.yml 加入 Nginx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: '3.8'

services:
  registry:
    image: registry:2
    container_name: docker-registry
    restart: always
    volumes:
      - ./data:/var/lib/registry

  nginx:
    image: nginx:alpine
    container_name: registry-nginx
    restart: always
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs:/etc/nginx/certs:ro
      - ./auth:/etc/nginx/auth:ro
    depends_on:
      - registry

常見問題排除

問題:推送時出現 http: server gave HTTP response to HTTPS client

解決方案:將 Registry 加入 Docker 的 insecure-registries 設定:

1
2
3
4
// /etc/docker/daemon.json
{
  "insecure-registries": ["localhost:5000"]
}

重啟 Docker 服務後生效:

1
sudo systemctl restart docker

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy