LoginSignup
0
0

More than 5 years have passed since last update.

镜像仓库

Last updated at Posted at 2019-01-31

[TOC]

基本原理

Docker Registry是开源的镜像请求的处理端,用于封装的镜像的存取。Docker客户端通过访问Registry推送(push)或拉取(pull)镜像。

HTTPS

Registry可提供HTTPS或HTTP服务,而Docker客户端默认通过HTTPS访问Registry,因此有以下几种方案:
1. Registry仅提供HTTP服务,客户端需在docker配置中添加--insecure-registry选项,即客户端无条件信任Registry,并需重启Docker。
2. Registry添加经过CA认证的SSL证书,需通过CA付费申请。客户端无需修改任何配置。
3. Registry添加自签名SSL证书,手动生成证书,客户端需在指定目录下存放证书。
4. 针对方案2及方案3,在Registry前端提供一个代理用以处理SSL,代理与Registry之间通过HTTP通信。

注意
1. 目前采取的是方案4中对方案3的优化,即用户需下载并放置证书,服务端需搭配一套Nginx代理。
2. HTTPS在镜像传输的过程中对数据加密,是客户端(Docker Client)信任服务端(Registry)。
3. HTTPS与用户对Registry的访问鉴权没有关联。

鉴权

Docker Registry的鉴权基于OAuth2.0协议,具体的过程如下:

docker-registry_workflow

  1. Docker Client(Docker命令行或Pod拉取镜像时调用的Docker API)请求Registry,要求访问指定的镜像资源。
  2. Registry返回401错误,并在响应头部Www-Authenticate指示鉴权方式,包括鉴权服务器的地址、鉴权服务名、访问的资源范围等。
  3. Docker Client带着鉴权信息(用户名密码等),根据指示请求鉴权服务器。
  4. 如果鉴权通过,鉴权服务器返回token给Docker Client。
  5. Docker Client带着token进行与第一步类似的请求。
  6. Registry解析token后,对指定的资源请求予以放行。

注意
1. 过程中Registry与鉴权服务器不直接交互,他们共享一套密钥证书对。
2. 鉴权服务器使用该密钥证书对将镜像资源请求的具体内容加密成token,Registry利用同样的密钥证书对将其解密。

通知

Docker Registry的通知机制允许Registry在其镜像资源被请求完成后(拉取、推送、列表等),对指定的地址发送HTTP请求来记录该次操作,因此我们可以利用这个功能,以收到通知的时机为准,将推送到Registry的镜像记入DB,或对镜像进行跨区域同步。对每个镜像来说,每个layer的推送均会发送通知,因此通知的量会较大,通知接收端需要对其进行过滤。只有最后推送完成时通知的target字段才不为空,从而可以根据此来过滤通知消息。

存储

Registry可以对接多种存储,如内存、硬盘(本地盘、NFS盘或NAS盘等)、对象存储(Swift、S3等)等,目前大部分采用的是NAS盘。

OBS

之前内部云尝试过对接OBS对象存储,希望将镜像数据的同步交由存储端完成,遇到了如下问题:
1. 底层基于ceph搭建。
2. ceph的gateway提供API访问,包括完整的Swift接口(已顺利对接,但由于OBS团队没有对Swift接口进行产品化封装,因此该方案被否决)及部分完整的S3接口。
3. ceph的gateway提供的S3接口与标准的S3接口相比有明显缺失(如缺少Registry接到推送镜像时会调用S3的批量删除blob的接口),因此OBS团队在gateway上层又配置了一层代理,专门用于处理缺失的接口(如批量删除blob),但仍对接失败,后未查明原因。

公有云内部镜像库

  1. 用于云平台公有云内部使用,无鉴权,暂无通知,计划要有跨区域同步功能(需启用通知功能)。
  2. 每个地域(EC/SC/HK)一套Registry,同一个地域内不同可用区共用同一套(ECA/ECB)。
  3. 每套Registry的存储对接一块NAS盘,两个实例高可用。

架构

配置

启动脚本:

docker run -d -p 80:5000 \
--restart=always --name registry \
-v /docker/regsitry:/var/lib/registry \
-v /root/registry/config:/etc/registry \
registry:2.5.0 \
serve "/etc/registry/config.yml"

config.yml:
```
version: 0.1
log:
level: debug
formatter: json
fields:
service: registry
storage:
cache:
layerinfo: inmemory
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
maintenance:
uploadpurging:
enabled: false
delete:
enabled: true
http:
addr: :5000
secret: placeholder
# must be https + domain name to avoid http patch 401 error
host: https://hub.cloud.pub
debug:
addr: :5001

notifications:
endpoints:
- name: notify
disabled: false
# endpoint url must be internal ip or vip
# zone VIP
# 实验性地开启通知功能,通知发送到Docker Server
url: http://100.68.3.22:8080/PUB/notify
timeout: 300s
threshold: 5
backoff: 30s
```

公有云外部镜像库

  1. 用于云平台公有云的客户及客户的k8s集群使用,有鉴权,有通知,计划要有跨区域同步功能。
  2. 每个地域(EC/SC/HK)一套Registry,同一个地域内不同可用区共用同一套(ECA/ECB)。
  3. 所有可用区的Registry共用同一套位于ECA云管区的镜像鉴权服务。
  4. 每套Registry的存储对接一块NAS盘,两个实例高可用。

架构

配置

启动脚本:

docker run -d -p 80:5000 \
--restart=always --name registry \
-v /docker/regsitry:/var/lib/registry \
-v /root/registry/config:/etc/registry \
registry:2.6.2 \
serve "/etc/registry/config.yml"

config.yml:
```
version: 0.1
log:
level: debug
fields:
service: registry
storage:
cache:
layerinfo: inmemory
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
maintenance:
uploadpurging:
enabled: false
delete:
enabled: true
http:
addr: :5000
secret: placeholder
# must be https + domain name to avoid http patch 401 error
host: https://hub.cloud.papub
debug:
addr: :5001

auth:
token:
issuer: caas-issuer
# realm must be domain name
realm: http://auth.cloud.papub:8080/ECA/token
rootcertbundle: /etc/registry/root.crt
service: caas-registry

notifications:
endpoints:
- name: notify
disabled: false
# endpoint url must be internal ip or vip
# zone VIP
url: http://100.68.3.15:8080/ECA/notify
timeout: 300s
threshold: 5
backoff: 30s
```

互联网访问

在公有云外部Registry的实例宿主机上,5000端口也起了Registry实例,但配置略微不同,通过与枢纽代理配合提供对互联网的镜像服务。互联网用户访问的镜像数据及鉴权服务的最终提供方是相同的,但两者的访问链路不同。

架构

配置

启动脚本:

docker run -d -p 5000:5000 \
--restart=always --name registry_external \
-v /docker/regsitry:/var/lib/registry \
-v /root/registry_external/config:/etc/registry \
registry:2.6.2 \
serve "/etc/registry/config.yml"

config.yml:
```
version: 0.1
log:
level: debug
formatter: json
fields:
service: registry
storage:
cache:
layerinfo: inmemory
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
maintenance:
uploadpurging:
enabled: false
delete:
enabled: true
http:
addr: :5000
secret: placeholder
# must be https + domain name to avoid http patch 401 error
host: https://eca-hub.yun.pingan.com

auth:
token:
issuer: caas-issuer
# realm must be domain name
realm: http://hub-auth.yun.pingan.com/ECA/token
rootcertbundle: /etc/registry/root.crt
service: caas-registry

notifications:
endpoints:
- name: notify
disabled: false
# endpoint url must be internal ip or vip
# zone VIP
url: http://100.68.3.15:8080/ECA/notify
timeout: 300s
threshold: 5
backoff: 30s
```

内部云镜像库

  1. 用于云平台内部云的内部、客户及客户的k8s集群使用,有鉴权、无通知、无需跨区域同步功能。
  2. 每个可用区一套Registry Proxy,请求最终由位于SZC云管区的Registry处理。
  3. Registry Proxy同时作为鉴权Proxy,将鉴权的请求转发到SZC云管区的Kube Manager(合并原Docker Server)。
  4. Registry的存储对接一块NAS盘,两个实例高可用。

架构

配置

Registry docker-compose.yml:

version: '2'
services:
registry:
ports:
- 80:80
image: docker.io/registry:2.6.2
restart: always
volumes:
- /Docker/registry:/var/lib/registry
- ./config/:/etc/registry/
environment:
- GODEBUG=netdns=cgo
command:
["serve", "/etc/registry/config.yml"]

Registry config.yml:
```
version: 0.1
log:
level: debug
formatter: json
fields:
service: registry
accesslog:
disabled: true
storage:
cache:
layerinfo: inmemory
filesystem:
rootdirectory: /var/lib/registry
maintenance:
uploadpurging:
enabled: false
delete:
enabled: true
http:
addr: :80
host: https://hub.yun.paic.com.cn
secret: placeholder

auth:
token:
issuer: caas-issuer

realm: http://ds.yun.paic.com.cn:8080/api/token

realm: http://ds.yun.paic.com.cn:9090/api/token
rootcertbundle: /etc/registry/root.crt
service: caas-registry

Proxy docker-compose.yml:

nginx:
log_driver: json-file
image: "library/nginx:1.10-auth-request"
ports:
- 443:443

- 8080:8080

- 9090:9090
- 80:80

volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./config:/etc/nginx/conf.d
- ./log:/var/log/nginx
```

Proxy reg_443.conf:
```
upstream docker_registry {
server 30.16.232.63:80;
}
server {
access_log /var/log/nginx/reg_443_access.log;
listen 443 ssl;
server_name hub.yun.paic.com.cn;
ssl_certificate /etc/nginx/conf.d/domain.crt;
ssl_certificate_key /etc/nginx/conf.d/domain.key;
ssl on;
ssl_protocols TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

client_max_body_size 0;
chunked_transfer_encoding on;
location /v2/ {
  proxy_pass         http://docker_registry;
  proxy_redirect     off;
  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;
}

}
```

Proxy reg_80.conf:

upstream docker_registry_80 {
server 30.16.232.63:80;
}
server {
access_log /var/log/nginx/reg_80_access.log;
listen 80;
server_name hub.yun.paic.com.cn;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://docker_registry_80;
proxy_redirect off;
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;
}
}

Proxy ds_9090.conf:

upstream docker_server {
server 30.16.232.67:8080;
}
server {
access_log /var/log/nginx/ds_access.log;
listen 9090;
server_name ds.yun.paic.com.cn:9090;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass http://docker_server;
proxy_redirect off;
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 http;
}
}

同步设计

Docker Server早期设计的镜像同步功能比较简单,主要利用Registry的通知功能针对内部云的跨区域镜像同步,后来随着内部云镜像库的架构优化,镜像同步功能在内部云已经暂时失去了意义。

过程

  1. 本区域Registry通知Docker Server,Docker Server解析通知,action为push,且target不为空,说明本区域Registry刚刚接收了一次镜像push。
  2. Docker Server检查通知内容中的推送者IP,如果该IP属于待分发列表中的某个远端目标Registry,则不进行分发,防止分发回环。
  3. Docker Server比对通知内容中的镜像名与配置的镜像分发白名单,如果镜像名符合白名单规则则继续处理。
  4. Docker Server调用分发服务器上的Docker 客户端的Socket接口:
    1. 从本区域Registry拉取刚刚推送的镜像,如local/tomcat:8。
    2. 将该镜像tag成远端目标Registry的镜像名,如remote/tomcat:8。
    3. 将tag后的镜像推送到远端目标Registry。

问题

目前在上述过程的第4步调用Docker客户端的接口有问题,预计是由于使用的Docker Client API的版本导致的问题。

思考

目前内部云通过Registry Proxy的方式将镜像汇总到同一个Registry,随着用户使用量的增大,Registry Proxy到Registry之间的链路压力会变得非常大。 因此针对内部云,后期可以考虑以下几个方向的探索:
1. 采用Registry提供的Reigstry mirror/pull cache等配置,在各个区域搭建mirror/cache,mirror/cache按需与汇总区的Registry进行交互,减小目前总Registry的压力。
2. 类似于迅雷的p2p方式,在同区域内点对点传输,但目前以云平台内部的网络架构来看不太可能。

而公有云方面,由于各区域有单独的Registry,因此网络压力不会太大,最大的问题是镜像跨区域同步的问题,根据需求来看,后期可以考虑:
1. 利用Docker Server早期设计的简单同步功能,配置一些同步规则,进行按需同步。
2. 搭建harbor进行同步。

上新区指引

公有云内部镜像库

  1. 在新区的云管区SF搭建Registry:
    1. 映射主机80端口。
    2. 多个实例使用同一块NAS盘。
  2. 根据用户所在区域(公共服务区或云管区SF)给Registry配置LVX:
    1. 需配置LVS VIP并由Nginx监听。
    2. Nginx需配置:监听443及80端口(443端口配置SSL证书秘钥)、转发到Registry实例所在主机的80端口、修改请求体上限、添加Upstream的ip_hash。
  3. 根据内部用户所在区域(公共服务区或云管区SF),配置相应区域的DNS,将hub.cloud.pub域名解析到VIP。
  4. 开通防火墙策略:
    1. 用户VPC网段 -> VIP 80/443端口。
    2. Nginx集群 -> Registry实例所在主机 80端口。
  5. 用户的机器需在/etc/docker/hub.cloud.pub/ca.crt放置SSL证书。

公有云外部镜像库

  1. 在新区的云管区SF搭建Registry:
    1. 映射主机80端口。
    2. 多个实例使用同一块NAS盘。
    3. notification的endpoint:<Docker Server的VIP>:<端口>/<新区code>/notify
    4. auth的realm:http://auth.cloud.papub/<新区code>/token
  2. 根据用户所在区域(通常为公共服务区)给Registry配置LVX:
    1. 需配置LVS VIP并由Nginx监听。
    2. Nginx需配置:监听443及80端口(443端口配置SSL证书秘钥)、转发到Registry实例所在主机的80端口、修改请求体上限、添加Upstream的ip_hash。
    3. Nginx需配置:监听8080端口、转发到Docker Server实例所在主机8080端口。
  3. 根据外部用户所在区域(通常是公共服务区),将hub.cloud.papub及auth.cloud.papub域名解析到VIP。
  4. 开通防火墙策略:
    1. 用户VPC网段 -> VIP 80/443端口。
    2. Nginx集群 -> Registry实例所在主机 80端口。
    3. Nginx集群 -> Docker Server实例所在主机 8080端口。
  5. 用户的机器需在/etc/docker/hub.cloud.papub/ca.crt放置SSL证书。

公有云互联网镜像库

  1. 在外部镜像库所在主机上启动新的Registry:
    1. 映射主机5000端口。
    2. 与外部镜像库共用NAS盘。
    3. notification的endpoint:<Docker Server的VIP>:<端口>/<新区code>/notify
    4. auth的realm:http://hub-auth.yun.pingan.com/<新区code>/token
  2. 申请新区的公网IP。
  3. 申请公网域名xxx-hub.yun.pingan.com,解析到新申请的公网IP。
  4. 在新区云管区DMZ区域内搭建枢纽代理Nginx:
    1. 监听某两个端口(如9443与9080),转发到Registry实例所在主机的5000端口、修改请求体上限、添加Upstream的ip_hash。
  5. 在新区F5上配置:
    1. caas镜像库资源池,指定IP为枢纽代理Nginx。
    2. 公网IP的443端口配置*.yun.pingan.com的SSL证书,可从其他区域复制。
    3. 公网IP的443及80端口转发到枢纽代理监听的端口(如80->9080, 443->9443)。
  6. 开通防火墙策略:
    1. 枢纽代理 -> Registry实例所在主机 5000端口。
  7. 用户的机器不需要在/etc/docker/放置任何SSL证书,因为证书与公有云的公网域名的证书有效性一致。

内部云镜像库

  1. 在新区的公共服务区搭建Registry Proxy,建议以后的用LVX替代:
    1. 配置LVS VIP并由Nginx监听。
    2. Nginx需配置:监听443及80端口(443端口配置SSL证书秘钥)、转发到Registry实例所在主机的80端口、修改请求体上限、添加Upstream的ip_hash。
    3. Nginx需配置:监听9090端口,转发到Manager实例所在主机的8080端口。
  2. 将整个新区的hub.yun.paic.com.cn及ds.yun.paic.com.cn域名解析到VIP。
  3. 开通防火墙策略:
    1. 用户VPC -> Proxy 80/443/9090端口。
    2. Proxy或Nginx集群 -> SZC云管区Registry实例所在主机 80端口。
    3. Proxy或Nginx集群 -> SZC云管区Manager实例所在主机 8080端口。
  4. 用户的机器需在/etc/docker/hub.yun.paic.com.cn/ca.crt放置SSL证书。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0