文|唐荦彦
深信服高级开发工程师,主要负责 SASE 云化架构以及基础设施建设
本文 3056 字,阅读 6 分钟
1|你将在本文学到什么
- 多 K8s 集群镜像分发方案
- Dragonfly 的理解
- Harbor 的预热机制
- Dragonfly 的使用以及排障
2|K8S 多集群镜像分发问题
在边缘云架构的生产环境下,演进过程中,一开始的镜像分发方案如下:
每个边缘集群都存在节点的 Harbor 仓库,进行缓存操作,当边缘集群集体崩溃重启过程中,不会引发所有 worker 上中心仓拉取镜像。
带来的问题是:
- 每套环境一个 Harbor,导致部署、维护的困难。
- Harbor 的复制策略比较简单,无法单例执行。并且重试非常占用中心仓带宽。
那么面对这种场景存在以下两种方案:
- Harbor 仓库分级复制策略
- P2P 镜像分发策略
Harbor 仓库分级复制策略,存在以下问题:
- 如何进行分级划分
- 升级过程,如果节点所处的是第三级,如何触发复制策略加速缓存
- 每个节点增加了安全暴露面
- 节点的不断增加,后续是否需要 3 级、4 级、5 级,维护管理成本指数增加。
所以在项目中,我的选择是 Dragonfly 的 P2P 镜像分发策略。
3|Dragonfly 是什么
在理解过程中,首先需要搞懂以下几个问题:
P2P 是什么?
镜像的分层拉取策略。
什么是 P2P
此 P2P 不是金融圈里面经常爆雷的,而是 Peer to Peer 网络技术。有几个比较突出的使用:
- 迅雷;
- 某夭折的播放器(*快 B*);
- 国内一些视频网站白嫖用户网络的 P2P CDN。
为什么需要 P2P 网络
P2P 网络对应的就是传统网络传输 C/S 模式。传统模式下,所有的客户端请求数据下载都需要访问服务器,那么服务器的压力会非常大,当客户端多的情况下,网络带宽也存在问题。
以下来自 WIKI 百科:
对等式网络(*英语:peer-to-peer,简称 P2P*),又称点对点技术,是去中心化、依靠用户群(*peers*)交换信息的互联网体系。它的作用在于,减低以往网路传输中的节点,以降低资料遗失的风险。与有中心服务器的中央网络系统不同,对等网络的每个用户端既是一个节点,也有服务器的功能,任何一个节点无法直接找到其他节点,必须依靠其户群进行信息交流。
镜像分层拉取
Docker 镜像通过分层进行资源共享,通过 copy-on-write 完成文件隔离。在执行 Pull 的时候,可以看到:
c1c792ed5250: Already exists
fcac137f6aa5: Already exists
c31aa26549dd: Already exists
04699d7e44fb: Pull complete
可以分析得出,在 Docker pull 时,先会判断本地是否存在当前层,如果没有则从远端服务器拉取层。
Dragonfly 架构**
组件包括:
Manager: 多 Dragonfly 调度节点进行管理,提供了 UI 管理界面、镜像预热机制。
Scheduler:
- 基于机器学习的多场景自适应智能 P2P 节点调度,为当前下载节点选择最优父节点;
- 构建 P2P 下载网络的有向无环图;
- 根据不同特征值评估节点下载能力, 剔除异常节点;
- 当下载失败情况,主动通知 Dfdaemon 进行回源下载。
Dfdaemon: (*分为 Peer、Seed Peer*)
- 基于 gRPC 提供下载功能, 并提供多源适配能力;
- 开启 Seed Peer 模式可以作为 P2P 集群中回源下载节点, 也就是整个集群中下载的根节点;
- 为镜像仓库或者其他 HTTP 下载任务提供代理服务;
- 下载任务基于 HTTP 或 HTTPS 或其他自定义协议。
使用场景流程说明如下:(*当需要下载某一层镜像时*)
Docker 在请求下载镜像时,通过配置 Docker http proxy 代理,将请求转发到 Peer 节点。
Peer 节点进行本地缓存判断,查看是否存在该层镜像;
- 是,则直接响应。如图:
如果当前 Peer 不存在,将当前请求转发到 Scheduler;
Scheduler 将判断 Seed Peer 中是否存在:
- 是,则将对应的地址返回,通知 Peer 去指定的 Seed Peer 拉取资源,如图:
- 否,则通知 Seed Peer 回源拉取,拉取成功后,Peer 再进行拉取。
最长路径:Docker -> Peer -> Seed Peer -> 源站 -> Seed Peer -> Peer -> Docker。
Dragonfly 操作实践(*Docker 版*)
由于 K8s 版本过于简单,封装了 Docker 手动操作部分,这里讲源码版本如何使用。
源码安装
从上面已经了解到:Docker pull 通过 http proxy 配置即可通过 Peer 拉取镜像,那么操作就简单了。
步骤如下:
配置 Docker;
安装依赖组件:MySQL、Redis、Jaeger(*为了研究操作路径以及代码*);
配置 Manager、Scheduler、Seed Peer、Peer;
详细步骤如下:
a.配置 Docker
- 配置 http proxy
vi /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:65001"
Environment="HTTPS_PROXY=http://127.0.0.1:65001"
- 私有仓库的话,配置忽略证书 insecure-registries;
vi /etc/docker/daemon.json
{
"insecure-registries": ["your.private.registry"]
}
重启 Docker:systemctl restart docker。
b.安装依赖
MySQL:
docker run -d --name dragonfly-mysql --restart=always -p 3306:3306 \
--env MARIADB_USER="dragonfly" \
--env MARIADB_PASSWORD="dragonfly" \
--env MARIADB_DATABASE="manager" \
--env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD="yes" \
mariadb:10.6
Redis:
docker run -d --name dragonfly-redis --restart=always -p 6379:6379 \
redis:6-alpine \
--requirepass "dragonfly"
Jaeger:
docker run --rm --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.48
c.配置组件
私有仓库的情况下,需要给 Dragonfly 代理也配置一个私有证书;如果不配置,在拉取过程中,会偶发 pull 失败的情况,报错如下:
-
http: server gave HTTP response to HTTPS client
生成一个 CA 证书私钥。
-
openssl genrsa -out ca.key 2048
打开 OpenSSL 配置文件 openssl.conf。设置 basicConstraints 为 true,然后你就能修改这些值。
[ req ]
#default_bits = 2048
#default_md = sha256
#default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
extensions = v3_ca
req_extensions = v3_ca
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (eg, fully qualified host name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
[ v3_ca ]
basicConstraints = CA:TRUE
生成 CA 证书。
openssl req -new -key ca.key -nodes -out ca.csr -config openssl.conf
openssl x509 -req -days 36500 -extfile openssl.conf \
-extensions v3_ca -in ca.csr -signkey ca.key -out ca.crt
⚠️注意:配置只说明需要改的地方。
配置 Peer
jaeger: "http://127.0.0.1:14268/api/traces"
console: true # 打开可从窗口查看日志
scheduler: # 配置调度器
..... 省略
netAddrs:
- type: tcp
addr: 127.0.0.1:8002
proxy:
security:
insecure: true
tcpListen:
listen: 0.0.0.0
port: 65001
proxies:
- regx: blobs/sha256.*
hijackHTTPS:
# CA certificate's path used to hijack https requests
cert: ca.crt
key: ca.key
hosts:
- regx: your.private.registry
insecure: true
配置 Seed Peer
jaeger: "http://127.0.0.1:14268/api/traces"
console: true # 打开可从窗口查看日志
scheduler:
enable: true
netAddrs:
- type: tcp
addr: 127.0.0.1:65003
# scheduler list refresh interval
refreshInterval: 10s
seedPeer:
# Dfdaemon enabled seed peer mode.
enable: true
# Seed peer type includes super, strong and weak.
type: super
# Seed peer cluster id.
clusterID: 1 # 全局唯一
配置 Scheduler
jaeger: "http://127.0.0.1:14268/api/traces"
console: true # 打开可从窗口查看日志
# Manager configuration.
manager:
# 配置manager 地址
addr: "127.0.0.1:65003"
# schedulerClusterID cluster id to which scheduler instance belongs.
schedulerClusterID: "1"
database: ##!!!!!! 必须和manager同一个redis
# Redis configuration.
redis:
addrs:
- "127.0.0.1:6379"
# Redis username.
username: ''
# Redis password.
password: dragonfly
# Redis brokerDB name.
brokerDB: 1
# Redis backendDB name.
backendDB: 2
server:
# # Advertise ip.
advertiseIP: 127.0.0.1
# # Listen ip.
# listenIP: 0.0.0.0
# Port is the ip and port scheduler server listens on.
port: 8002
配置 Manager
server:
# GRPC server configure.
grpc:
advertiseIP: 127.0.0.1
port:
start: 65003
end: 65003
# REST server configure
rest:
# REST server address
addr: :8888
# Database info used for server.
database:
# Database type, supported types include mysql, mariadb and postgres.
type: mysql
# Mysql configure.
mysql:
user: dragonfly
password: dragonfly
host: 127.0.0.1
port: 3306
dbname: manager
migrate: true
redis:
# Redis addresses.
addrs:
- "127.0.0.1:6379"
password: dragonfly
# Redis DB name.
db: 0
# Redis brokerDB name.
brokerDB: 1
# Redis backendDB name.
backendDB: 2
job:
preheat:
registryTimeout: 1m
tls: ##!!!!!预热必须将harbor仓库的证书放在这
caCert: "/etc/docker/certs.d/your.private.registry/ca.crt"
jaeger: "http://127.0.0.1:14268/api/traces"
console: true # 打开可从窗口查看日志
d.Harbor 预热机制
预热,顾名思义,需要使用的时候,它已经是可以使用的状态了。如,电热毯在我们上床前,它已经是热的了。对于我们 Harbor 的使用场景,则是在 K8S 需要使用镜像的时候,Harbor 已经以前将镜像分发到了对应集群内了。
如何使用预热
Dragonfly 的使用逻辑如下:
Harbor 配置策略 -> 通知 Manager -> 查询现有活动的 Scheduler 集群 -> 创建任务 -> Scheduler 调用 Seed Peer 拉取镜像。
Harbor 配置如下:
- 配置分发实例
- 为对应的项目配置 P2P 策略
基于事件,在有新的镜像时,会通知 Manager。
其他问题
我们怎么判断当前的镜像层已经缓存到了本地?
- Dfdaemon 将缓存默认存放在 /var/lib/Dragonfly 目录下,名称为 task 名称;
- 通过查看源码,task 名称的来源为:func taskIDV1(*url string, meta *commonv1.UrlMeta, ignoreRange bool*) 。即对每一层算法计算出来的。
- 快速:直接查看对应的日志即可。
Dragonfly Star 一下✨
https://github.com/dragonflyoss/Dragonfly2
扫描二维码查看 Dragonfly GitHub 页面