我之前发版一直是手工:
1
2
3
4
5
| 本地 build 镜像
docker save 成 tar
FTP 上传到服务器
服务器 docker load
重启容器
|
能用,但每次都很烦。镜像稍微大一点,上传和 load 都慢,也没有一个清楚的版本记录。所以根据以上步骤整理出来的需求如下:
- CI 能把业务镜像推上去
- 生产机器能拉下来
- 最好还能缓存一下 Docker Hub 的基础镜像
这种场景用 registry:3 就够了。
由于 registry:3 限制,一个 registry cache 只能代理一个上游,所以私有镜像仓库和 Docker Hub 缓存分开运行。
比如:
1
2
| registry.example.com # 存自己的业务镜像
mirror.example.com # 缓存(加速) Docker Hub 镜像
|
私有 Registry#
先建目录:
1
2
| mkdir -p /opt/registry/{data,auth}
cd /opt/registry
|
生成登录账号:
1
2
| docker run --rm --entrypoint htpasswd httpd:2 \
-Bbn woodpecker 'your-password' > auth/htpasswd
|
docker-compose.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| services:
registry:
image: registry:3
container_name: private-registry
restart: always
ports:
- "127.0.0.1:5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
REGISTRY_STORAGE_DELETE_ENABLED: "true"
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
OTEL_TRACES_EXPORTER: none
volumes:
- ./data:/var/lib/registry
- ./auth:/auth
|
启动:
这里只监听 127.0.0.1,外面统一走 Nginx / Caddy 反代 HTTPS。
Nginx 大致配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| server {
listen 443 ssl http2;
server_name registry.example.com;
ssl_certificate /etc/letsencrypt/live/registry.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;
client_max_body_size 0;
location / {
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 $scheme;
proxy_read_timeout 900;
}
}
|
client_max_body_size 0 不要漏。镜像层可能比较大,限制太小 push 会失败。
测试:
1
2
3
| docker login registry.example.com
docker build -t registry.example.com/my-app/backend:latest .
docker push registry.example.com/my-app/backend:latest
|
生产服务器只需要登录一次:
1
| docker login registry.example.com
|
之后更新服务:
1
2
| docker compose pull
docker compose up -d
|
到这里,原来的 save tar + FTP + load 就可以扔掉了。
Docker Hub 缓存(加速)#
单独建一个 mirror:
1
2
| mkdir -p /opt/registry-mirror/data
cd /opt/registry-mirror
|
config.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| version: 0.1
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
proxy:
remoteurl: https://registry-1.docker.io
ttl: 168h
|
docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
| services:
registry-mirror:
image: registry:3
container_name: registry-mirror
restart: always
ports:
- "127.0.0.1:5001:5000"
volumes:
- ./data:/var/lib/registry
- ./config.yml:/etc/docker/registry/config.yml
environment:
OTEL_TRACES_EXPORTER: none
|
启动:
然后给 mirror.example.com 配 HTTPS 反代到 127.0.0.1:5001。
使用方式#
方法1: 客户端配置 Docker daemon:#
1
2
3
| {
"registry-mirrors": ["https://mirror.example.com"]
}
|
重启 Docker:
1
2
| sudo systemctl daemon-reload
sudo systemctl restart docker
|
测试:
1
| docker pull alpine:latest
|
第一次还是会去 Docker Hub 拉,后面相同镜像就会走本地缓存。
方法2: 临时拉取镜像#
镜像名称前加上自己的域名。
1
| docker pull mirror.example.com/alpine:latest
|
一个 registry cache 只能代理一个上游。如果想代理 ghcr.io、quay.io 这些地址,则需要建立多个容器。
私有 Registry 最好走 HTTPS。HTTP 也能配,但每台机器都要单独加 insecure registry。
registry:3 + htpasswd 的权限很简单,基本就是能登录就能 push/pull。如果要项目级权限、Web UI、漏洞扫描、保留策略,那就需要 Harbor 之类的。
另外,Registry 的数据不会因为 tag 改了就自动变小。镜像多了以后要考虑清理策略。