[TOC]
基本原理
Docker Registry是开源的镜像请求的处理端,用于封装的镜像的存取。Docker客户端通过访问Registry推送(push)或拉取(pull)镜像。
HTTPS
Registry可提供HTTPS或HTTP服务,而Docker客户端默认通过HTTPS访问Registry,因此有以下几种方案:
- Registry仅提供HTTP服务,客户端需在docker配置中添加
--insecure-registry
选项,即客户端无条件信任Registry,并需重启Docker。 - Registry添加经过CA认证的SSL证书,需通过CA付费申请。客户端无需修改任何配置。
- Registry添加自签名SSL证书,手动生成证书,客户端需在指定目录下存放证书。
- 针对方案2及方案3,在Registry前端提供一个代理用以处理SSL,代理与Registry之间通过HTTP通信。
注意:
- 目前采取的是方案4中对方案3的优化,即用户需下载并放置证书,服务端需搭配一套Nginx代理。
- HTTPS在镜像传输的过程中对数据加密,是客户端(Docker Client)信任服务端(Registry)。
- HTTPS与用户对Registry的访问鉴权没有关联。
鉴权
Docker Registry的鉴权基于OAuth2.0协议,具体的过程如下:
- Docker Client(Docker命令行或Pod拉取镜像时调用的Docker API)请求Registry,要求访问指定的镜像资源。
- Registry返回401错误,并在响应头部Www-Authenticate指示鉴权方式,包括鉴权服务器的地址、鉴权服务名、访问的资源范围等。
- Docker Client带着鉴权信息(用户名密码等),根据指示请求鉴权服务器。
- 如果鉴权通过,鉴权服务器返回token给Docker Client。
- Docker Client带着token进行与第一步类似的请求。
- Registry解析token后,对指定的资源请求予以放行。
注意:
- 过程中Registry与鉴权服务器不直接交互,他们共享一套密钥证书对。
- 鉴权服务器使用该密钥证书对将镜像资源请求的具体内容加密成token,Registry利用同样的密钥证书对将其解密。
通知
Docker Registry的通知机制允许Registry在其镜像资源被请求完成后(拉取、推送、列表等),对指定的地址发送HTTP请求来记录该次操作,因此我们可以利用这个功能,以收到通知的时机为准,将推送到Registry的镜像记入DB,或对镜像进行跨区域同步。对每个镜像来说,每个layer的推送均会发送通知,因此通知的量会较大,通知接收端需要对其进行过滤。只有最后推送完成时通知的target字段才不为空,从而可以根据此来过滤通知消息。
存储
Registry可以对接多种存储,如内存、硬盘(本地盘、NFS盘或NAS盘等)、对象存储(Swift、S3等)等,目前大部分采用的是NAS盘。
OBS
之前内部云尝试过对接OBS对象存储,希望将镜像数据的同步交由存储端完成,遇到了如下问题:
- 底层基于ceph搭建。
- ceph的gateway提供API访问,包括完整的Swift接口(已顺利对接,但由于OBS团队没有对Swift接口进行产品化封装,因此该方案被否决)及部分完整的S3接口。
- ceph的gateway提供的S3接口与标准的S3接口相比有明显缺失(如缺少Registry接到推送镜像时会调用S3的批量删除blob的接口),因此OBS团队在gateway上层又配置了一层代理,专门用于处理缺失的接口(如批量删除blob),但仍对接失败,后未查明原因。
公有云内部镜像库
- 用于云平台公有云内部使用,无鉴权,暂无通知,计划要有跨区域同步功能(需启用通知功能)。
- 每个地域(EC/SC/HK)一套Registry,同一个地域内不同可用区共用同一套(ECA/ECB)。
- 每套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
公有云外部镜像库
- 用于云平台公有云的客户及客户的k8s集群使用,有鉴权,有通知,计划要有跨区域同步功能。
- 每个地域(EC/SC/HK)一套Registry,同一个地域内不同可用区共用同一套(ECA/ECB)。
- 所有可用区的Registry共用同一套位于ECA云管区的镜像鉴权服务。
- 每套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
内部云镜像库
- 用于云平台内部云的内部、客户及客户的k8s集群使用,有鉴权、无通知、无需跨区域同步功能。
- 每个可用区一套Registry Proxy,请求最终由位于SZC云管区的Registry处理。
- Registry Proxy同时作为鉴权Proxy,将鉴权的请求转发到SZC云管区的Kube Manager(合并原Docker Server)。
- 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的通知功能针对内部云的跨区域镜像同步,后来随着内部云镜像库的架构优化,镜像同步功能在内部云已经暂时失去了意义。
过程
- 本区域Registry通知Docker Server,Docker Server解析通知,action为push,且target不为空,说明本区域Registry刚刚接收了一次镜像push。
- Docker Server检查通知内容中的推送者IP,如果该IP属于待分发列表中的某个远端目标Registry,则不进行分发,防止分发回环。
- Docker Server比对通知内容中的镜像名与配置的镜像分发白名单,如果镜像名符合白名单规则则继续处理。
- Docker Server调用分发服务器上的Docker 客户端的Socket接口:
- 从本区域Registry拉取刚刚推送的镜像,如local/tomcat:8。
- 将该镜像tag成远端目标Registry的镜像名,如remote/tomcat:8。
- 将tag后的镜像推送到远端目标Registry。
问题
目前在上述过程的第4步调用Docker客户端的接口有问题,预计是由于使用的Docker Client API的版本导致的问题。
思考
目前内部云通过Registry Proxy的方式将镜像汇总到同一个Registry,随着用户使用量的增大,Registry Proxy到Registry之间的链路压力会变得非常大。 因此针对内部云,后期可以考虑以下几个方向的探索:
- 采用Registry提供的Reigstry mirror/pull cache等配置,在各个区域搭建mirror/cache,mirror/cache按需与汇总区的Registry进行交互,减小目前总Registry的压力。
- 类似于迅雷的p2p方式,在同区域内点对点传输,但目前以云平台内部的网络架构来看不太可能。
而公有云方面,由于各区域有单独的Registry,因此网络压力不会太大,最大的问题是镜像跨区域同步的问题,根据需求来看,后期可以考虑:
- 利用Docker Server早期设计的简单同步功能,配置一些同步规则,进行按需同步。
- 搭建harbor进行同步。
上新区指引
公有云内部镜像库
- 在新区的云管区SF搭建Registry:
- 映射主机80端口。
- 多个实例使用同一块NAS盘。
- 根据用户所在区域(公共服务区或云管区SF)给Registry配置LVX:
- 需配置LVS VIP并由Nginx监听。
- Nginx需配置:监听443及80端口(443端口配置SSL证书秘钥)、转发到Registry实例所在主机的80端口、修改请求体上限、添加Upstream的ip_hash。
- 根据内部用户所在区域(公共服务区或云管区SF),配置相应区域的DNS,将hub.cloud.pub域名解析到VIP。
- 开通防火墙策略:
- 用户VPC网段 -> VIP 80/443端口。
- Nginx集群 -> Registry实例所在主机 80端口。
- 用户的机器需在
/etc/docker/hub.cloud.pub/ca.crt
放置SSL证书。
公有云外部镜像库
- 在新区的云管区SF搭建Registry:
- 映射主机80端口。
- 多个实例使用同一块NAS盘。
- notification的endpoint:
<Docker Server的VIP>:<端口>/<新区code>/notify
。 - auth的realm:
http://auth.cloud.papub/<新区code>/token
。
- 根据用户所在区域(通常为公共服务区)给Registry配置LVX:
- 需配置LVS VIP并由Nginx监听。
- Nginx需配置:监听443及80端口(443端口配置SSL证书秘钥)、转发到Registry实例所在主机的80端口、修改请求体上限、添加Upstream的ip_hash。
- Nginx需配置:监听8080端口、转发到Docker Server实例所在主机8080端口。
- 根据外部用户所在区域(通常是公共服务区),将hub.cloud.papub及auth.cloud.papub域名解析到VIP。
- 开通防火墙策略:
- 用户VPC网段 -> VIP 80/443端口。
- Nginx集群 -> Registry实例所在主机 80端口。
- Nginx集群 -> Docker Server实例所在主机 8080端口。
- 用户的机器需在
/etc/docker/hub.cloud.papub/ca.crt
放置SSL证书。
公有云互联网镜像库
- 在外部镜像库所在主机上启动新的Registry:
- 映射主机5000端口。
- 与外部镜像库共用NAS盘。
- notification的endpoint:
<Docker Server的VIP>:<端口>/<新区code>/notify
。 - auth的realm:
http://hub-auth.yun.pingan.com/<新区code>/token
。
- 申请新区的公网IP。
- 申请公网域名xxx-hub.yun.pingan.com,解析到新申请的公网IP。
- 在新区云管区DMZ区域内搭建枢纽代理Nginx:
- 监听某两个端口(如9443与9080),转发到Registry实例所在主机的5000端口、修改请求体上限、添加Upstream的ip_hash。
- 在新区F5上配置:
- caas镜像库资源池,指定IP为枢纽代理Nginx。
- 公网IP的443端口配置
*.yun.pingan.com
的SSL证书,可从其他区域复制。 - 公网IP的443及80端口转发到枢纽代理监听的端口(如80->9080, 443->9443)。
- 开通防火墙策略:
- 枢纽代理 -> Registry实例所在主机 5000端口。
- 用户的机器不需要在
/etc/docker/
放置任何SSL证书,因为证书与公有云的公网域名的证书有效性一致。
内部云镜像库
- 在新区的公共服务区搭建Registry Proxy,建议以后的用LVX替代:
- 配置LVS VIP并由Nginx监听。
- Nginx需配置:监听443及80端口(443端口配置SSL证书秘钥)、转发到Registry实例所在主机的80端口、修改请求体上限、添加Upstream的ip_hash。
- Nginx需配置:监听9090端口,转发到Manager实例所在主机的8080端口。
- 将整个新区的hub.yun.paic.com.cn及ds.yun.paic.com.cn域名解析到VIP。
- 开通防火墙策略:
- 用户VPC -> Proxy 80/443/9090端口。
- Proxy或Nginx集群 -> SZC云管区Registry实例所在主机 80端口。
- Proxy或Nginx集群 -> SZC云管区Manager实例所在主机 8080端口。
- 用户的机器需在
/etc/docker/hub.yun.paic.com.cn/ca.crt
放置SSL证书。