前言
在企業環境中,將容器映像檔存放於公開的 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:
|
啟動服務:
與 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
|
參考資料