欢迎关注微信公众号“云原生手记”

文章目录

  • 背景
  • dragonfly的原理
    • dragonfly中的概念
  • dragonfly运行原理
    • dfdaemon
      • dfdaemon的拦截
      • dfdaemon拦截后作甚
    • dfget
      • dfget获取blob过程
    • supernode
      • 注册接口
  • 总结

背景

今天要分享的是Dragonfly,这是阿里开源的一款用于镜像分发的工具,大家最早了解到这款工具,可能是因为调研大规模容器镜像分发的解决方案,最初这款工具确实是用于解决镜像分发问题的,当然,现在仍用于镜像分发,但是将来这块工具的定位将是企业级文件工具。

dragonfly解决大规模容器镜像分发问题的关键是采用了p2p技术,p2p翻译为perr to peer,译为端到端。这边稍微介绍下容器镜像分发的背景,大家都知道镜像仓库一般简单的可以使用registry搭建,当然,也有企业级的镜像仓库harbor。正常使用,这两款工具都没什么问题,但是当有大规模拉取镜像需求时(比如1000个节点在拉取镜像),harbor极有可能会支撑不住而挂掉或者获取文件的时间过长导致很多应用都在等待。有人会说,可以水平扩容harbor,但这不是永久性的解决方案,再说harbor的存储(如s3存储)能不能扛住还两说。基于此,阿里推出了dragonfly,而Uber推出了Kraken。本文的重点是dragonfly,Kraken有机会再分享。

dragonfly的原理

dragonfly大规模容器镜像分发问题的关键是采用了p2p技术,那么到底是怎么解决的呢?我们稍微深入分析下大规模容器镜像分发问题,存在以下几个需要解决的问题:

  • 1000个节点都要向镜像仓库拉取镜像,假设仓库服务没有奔溃,但是获取镜像的时间会很长,这个在下面的性能图中会有体现。
  • 1000个节点都要向镜像仓库拉取镜像,其中很多可能拉取的镜像是相同的,或者说他们中很多拉取的层文件是相同的。所以这边的小问题是如何解决同一个镜像/层文件重复拉取的问题。在编程中,相同的代码出现2-3次,就需要抽出来作为一个函数;
  • 高并发拉取镜像时,镜像仓库的存储可能会崩掉,这是由存储系统决定的。存储系统本身有瓶颈。水平扩展harbor,并不能减轻存储的压力。

对于以上两个问题,dragonfly的解决策略如下:

  • 不再对相同镜像/层文件重复拉取,以减轻harbor不必要的并发压力;
  • 使用拉取的客户端节点作为存储,对外提供文件服务,以减轻harbor端存储的压力。
  • 通过去其他客户端节点获取层文件,而不是去镜像仓库获取文件,获取文件的速度会有所提高。

那么dragonfly解决大规模镜像分发的效果如何呢?
对于Dragonfly,无论有多少客户端开始下载文件,平均下载时间几乎都没有增加(实验中为12s,这意味着所有客户端总共只需要12s即可完成文件/图像的下载)。
对于wget,当您有更多客户端时,下载时间会不断增加。当wget客户端数量达到1200(在以下实验中)时,文件源将崩溃,因此它不能为任何客户端提供服务。
从统计结果来看,dragonfly的使用绝对是加快了镜像获取的时间。这是dragonfly的官方的统计结果,样本数是从100个节点开始统计的,确实优化效果很明显,足够说明在100节点及以上的情况下并发拉取镜像,是有效率上的提高的。

测试环境 统计结果
Dragonfly 服务器 2 * (24core 64GB 2000Mb/s)
文件源服务器 2 * (24core 64GB 2000Mb/s)
客户端 4core 8GB 200Mb/s
目标文件大小 200MB

dragonfly中的概念

在深入介绍dragonfly前,需要先向大家介绍dragonfly中的一些概念:

  • SuperNode(超级节点):这是一个长期运行的进程,主要提供以下两个功能:

    • 为每个peer节点调度下载piece的网络路径(你可以理解为种子文件,该文件告诉客户端去哪些节点获取哪些数据),superNode是个调度者也是追踪者, 因为peer节点会通知supernode本节点piece文件的信息,也就是说超级节点拥有其下属所有peer节点上的piece文件信息。
    • CDN 服务器,从源处缓存数据,避免从源处下载重复的数据。deget在下载文件之前,都会注册到超级节点,告诉超级节点dfget需要下载的信息,超级节点会立刻去源处下载这些目标文件。
  • Dfget:是dragonfly中获取文件的工具,负责下载文件数据。类似于wget,同时,他还担任peer角色(dfget server 命令就是peer服务),peer角色的节点可以为p2p网络中的其他使用dfget命令的客户端传输数据。
  • Dfdaemon:是个代理,Dfdaemon在容器引擎(docker daemon)和注册表(registry或者harbor)之间的代理,也是一个本地长期运行的进程。当拉取镜像时,他会拦截docker daemon发送出去的请求,然后对于非层文件的请求直接转发,而对于层文件获取请求会拦截后使用Dfget下载这些层文件。docker需要配置proxy参数,接入dfdaemon,才能让dfdaemon起效。
  • P2P:peer to peer,一种分布式应用程序体系结构。
  • Task:任务将存储有关taskFile的一些元信息,任务将存储有关taskFile,片段(piece)和其他内容的一些元信息。任务与任务ID标识的磁盘上的文件具有一一对应关系,如果要从Supernode下载文件,则应注册一个任务,这意味着在实际执行操作之前先告诉服务器要下载的文件信息。
  • DfgetTask: DfgetTask表示由Dfget或其他客户端启动的下载过程。当Dfget尝试从p2p网络下载文件时,Supernode将创建一个DfgetTask对象,用于管理下载过程的生命周期。
  • Peer: 在P2P网络中,peer节点双方既是资源的提供者又是消费者。因此,在Dfget开始从Supernode进行下载任务之前,Dfget将启动一个Web服务器,该服务器提供下载的文件服务以供其他peer在P2P网络中下载,并向Supernode发送peer/Register请求以加入P2P网络。只有这样,Dfget才能从P2P网络下载文件。
  • Piece: 一个piece是将要下载的文件的一部分,可以解释为一块文件。在dragonfly中,下载文件不是完整传输,而是分段传输。

dragonfly运行原理

dragonfly中有三个组件:supernode,dfdaemon和dfget,下面就这几个组件进行讲解。

dfdaemon

dfdaemon到底做了哪些事情?起到了什么作用?

在使用dragonfly的节点上,都会配置docker的proxy参数,如下:

vi /usr/lib/systemd/system/docker.service
[Service]
Environment="HTTP_PROXY=http://10.154.12.120:65001"

其中http://10.154.12.120:65001地址时dfdaemon服务的地址,一般每个节点上都会启一个dfdaemon的服务,而节点上的docker proxy 参数会指向dfdaemon的服务。docker的proxy参数设置的作用就是docker daemon向外发送的所有请求都会发送到http://10.154.12.120:65001即dfdaemon服务,由dfdaemon处理。

dfdaemon的拦截

dfdaemon不会拦截docker daemon发出来的所有的请求,他只对含blobs/sha256.*的请求做拦截处理,其他请求它只做转发。也就是说当拉取镜像时,对于获取镜像出blob文件的请求,dfdaemon是制作转发的。

dfdaemon拦截后作甚

dfaemon会将docker daemon的请求进行拦截,但是除了blob文件的请求会被拦截做额外处理外,其他请求只会做转发。比如docker pull 镜像时会需要获取Manifest文件信息,像这个请求dfdaemon是直接转发的,然后根据转发结果返回给docker daemon。而在获取到manifest文件后,docker daemon会请求获取Blob文件,这类请求将被dfdaemon拦截住,去做其他处理。本来docker daemon获取blob文件,一般是去registry或者harbor获取,现在被dfdaemon拦截,由dfdaemon去获取blob文件。那么dfdaemon是如何获取Blob文件的呢?其实dfdaemon并没有直接去获取blob文件,而是使用dfget命令去获取blob文件,执行的dfget命令如下:

"/opt/dragonfly/df-client/dfget" "-u" "http://10.154.12.121:7999/v2/library/rabbitmq/blobs/sha256:04f8f8815c88ec1ed64a013b039aef36b2ebc09c66101a35a916a2f73bab6ae3" "-o" "/root/.small-dragonfly/dfdaemon/data/5e096c2a-a93c-42c1-a2b6-9f88585a3d92" "--node" "10.142.113.43" "--expiretime" "30m0s" "--alivetime" "5m0s" "-f" "Expires&Signature" "--dfdaemon" "-s" "20MB" "--totallimit" "20MB" "--node" "10.154.12.127,10.154.12.127" "--header" "User-Agent:docker/18.09.9 go/go1.11.13 git-commit/039a7df kernel/4.14.78-300.el7.bclinux.x86_64 os/linux arch/amd64 UpstreamClient(Docker-Client/18.09.2 \\(linux\\))" "--header" "X-Forwarded-For:10.154.12.127"

dfdaemon将拉取blob文件的任务交给dfget,在dfget命令执行完后,dfdaemon会从本地读取下载下来的blob文件内容(dfget获取文件后将文件保存在本地),然后将文件内容返回给docker daemon,那么docker daemon就算获取到了Blob文件。

dfget

上面讲到dfdaemon是使用dfget去获取blob文件的,那么dfget是如何获取的呢?跟docker daemon直接去获取文件有什么区别呢?

dfget获取blob过程

其实当前版本的dragonfly主要用于镜像分发,它默认只会对blob文件的请求进行拦截并处理,dragobfly的dfget负责去获取blob文件,下面这张图就是dfget获取Blob文件的全过程:

  • dfget命令启动后,先注册到supernode,告知本地需要哪个blob文件,并且通知supernode本地起了一个peer服务,地址多少,服务端口多少。supernode会返回一个taskId给dfget;这边需要注意一个细节,一个dfclient本地可能存在多个Peer服务,peer服务是会占用端口的,那么k8s中使用时是否应该注意?
  • dfget根据taskId再次请求supernode,获取下载blob文件的种子文件,种子文件就是告诉你去哪些peer节点获取哪些piece,把这些piece全下载下来,你就可以拼成一个完成的blob文件。然后将blob文件存储到临时目录,外部程序会将临时目录的blob文件转移到正确的目标目录(dfdaemon会去目标目录读取文件并返回给docker daemon)。

注意:这边是否有个疑问,万一supernode挂了,是不是dfget就获取不到Blob文件了呢?这个问题已经被考虑到了,源码中已经解决该问题了,要是dfget通过dragonfly的方式无法获取完整的数据,那么dfget会直接去源仓库处获取blob文件,并保存到临时目录。

supernode

超级节点在整个dragnfly的系统的,起着中央调度作用。dfget都需要从超级节点获取种子文件,失去超级节点,dfget只能使用原始的方式去获取blob文件,回到原始的请求。那么超级节点会做哪些操作呢:

  • 将自己注册成为peer服务,supernode使用nginx作为自己的文件服务器。
  • 提供注册、获取种子文件、汇报节点状态等服务,当然还有相关的监控接口。

注册接口

上面一个了解到,dfget每次启动时,都会向supernode注册,告知超级节点本地启动的peer服务信息,超级节点在这个接口中会去做什么操作呢?

  • dfget注册信息中会带有期望下载的文件信息,supernode会解析出来;
  • supernode根据需要下载的文件信息拼成一个唯一的taskId返回给客户端。
  • supernode会获取期望下载的文件大小,并计算piece大小。
  • supernode会异步去下载期望的文件:
    • 检查本地是否已经有该文件了,有即退出;
    • 本地不存在该文件,那就去源仓库处获取该文件,并存储在supernode本地。
  • dfget获取到taskId后,会再次请求supernode获取种子文件,超级节点会根据文件的分块大小和编号,最后形成一个种子文件:每个块去哪个peer节点下载。一个Blob文件若是196M,那么每个piece有4M,总共分为49个piece。超级节点会告诉deget 这49个piece去哪边下载。
  • dfget下载完每个piece都会向超级节点汇报下载情况,超级节点就知道其所管辖的peer节点处都有哪些piece了。

以上这张是我画的有关dragobfly的交互图,下面说明下其中的交互。先介绍下部署环境:

  • 1个节点上使用docker部署supernode;
  • 2个节点上均使用docker部署了dfclient,dfclient由dfdaemon和dfget组成。两个节点的docker 都配置proxy参数。

获取Blob文件的描述:

  • 在一个节点上执行docker pull 命令。
  • 节点的docker daemon将请求转发给本地的dfdaemon
  • dfdaemon根据过滤条件选择转发或者拦截处理;
  • dfdaemon拦截blob文件请求;
  • dfdaemon使用dfget去获取Blob文件;
  • dfget注册成为Peer;
  • dfget请求supernode获取种子文件;
  • dfget根据种子文件去获取Blob文件,并保存在临时目录;
  • dfget将下载情况汇报给supernode;
  • dfget经临时文件移动到目标目录,dfget执行完;
  • dfdaemon从目标目录获取文件内容,并返还给docker daemon;
  • 一个blob文件下载完,开始下一个blob文件下来。

总结

dragonfly的优点:

  • 很明显加快拉取速度了,尤其是客户端多的情况下,优化效果很明显,所以优点就不多说了。

dragonfly的风险点:

  • 我们可以看到dragonfly存在很多数据落盘行为,且有很多数据从磁盘读取,那么对磁盘的性能要求就高了。
  • 频繁的落盘和读取行为,阻碍了性能的进一步提升。
  • 因为使用到了本地磁盘,那么磁盘容量管理也很重要,完了撑爆了。当然dragonfly都会定时定量做磁盘清理的。

注意点:dragondly适用于大规模并发场景,要是你的使用场景没有大搞100个节点在并发拉取镜像,建议不要使用Dragonfly,使用水平扩展的harbor就足够了。使用dragonfly毕竟是会带来一定的复杂度的。

对于以上的风险点,会在dragonfly2.0中解决,到时会有个stream模式。此外,dragonfly2.0开始将会有预热功能。总之,该组件的发展刚刚踏上正轨,路还很长。

云原生时代的镜像分发工具——Dragonfly简介相关推荐

  1. 云原生时代,如何保证容器镜像安全?

    目录 遵从最佳实践,编写 Dockerfile 选择合适的基础镜像 以非 root 用户启动容器 采用多阶段构建 选择来源可靠且经常更新的镜像 用安全的方式构建容器镜像 使用容器镜像扫描 和极狐 Gi ...

  2. CODING —— 云原生时代的研发工具领跑者

    本文为 CODING 创始人兼 CEO 张海龙在腾讯云 CIF 工程效能峰会上所做的分享. 文末可前往峰会官网,观看回放并下载 PPT. 大家上午好,很高兴能有机会与大家分享 CODING 最近的一些 ...

  3. 进击的 Java ,云原生时代的蜕变

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 作者 | 易立 来源 | 公众号「阿里巴巴云原生」 导读:云原生时代的来临,与Java 开发 ...

  4. 进击的.NET 在云原生时代的蜕变

    你一定看过这篇文章 <进击的 Java ,云原生时代的蜕变>,  本篇文章的灵感来自于这篇文章.北京时间9.24 就将正式发布.NET Core 3.0, 所以写下这篇文章让大家全面认识. ...

  5. 云原生时代的运维体系进化

    简介:基于容器.Kubernetes 等云原生技术,提供的开放社区标准.不可变基础设施.声明式 API 会成为企业 CloudOps 的最佳实践,也将在这个基础上推进数据化.智能化体系建设,将运维复杂 ...

  6. 快进键启动,一文带你了解云原生时代容器安全

    简介: 分享阿里云容器安全的治理能力与经验,致力保护生产环境安全. 都说国内需求离容器化还远,更谈不上关注安全,喊的热闹而落地困难.但总得有些声音面向未来向前看. 在2020年Forrester Ia ...

  7. 开源巨头 SUSE 收购 Rancher Labs,云原生时代来临

    作者 | 马超 责编 | 伍杏玲 封图 | rancher.com官网 出品 | CSDN(ID:CSDNnews) 近日,全球最大的独立开源公司SUSE宣布收购云原生初始公司Rancher Labs ...

  8. 阿里技术专家:进击的 Java ,云原生时代的蜕变

    作者| 易立 阿里云资深技术专家 导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的.然而,本文的作者却认为云原生时代,Java 依然可以胜任&qu ...

  9. 云原生时代,Java 的危与机

    今天,25 岁的 Java 仍然是最具有统治力的编程语言,长期占据编程语言排行榜的首位,拥有一千二百万的庞大开发者群体,全世界有四百五十亿部物理设备使用着 Java 技术,同时,在云端数据中心的虚拟化 ...

  10. 云原生时代的DevOps平台设计之道

    开发人员与运维人员是 IT 领域很重要的两大人群,他们都会参与到各种业务系统的建设过程中去.DevOps 是近年间火爆起来的一种新理念,这种理念被很多人错误的解读为"由开发人员(Dev)学习 ...

最新文章

  1. 推荐一个学习golang的地址
  2. 研究人工智能最应该注意的问题
  3. 两个排序数组合并第k或前k个最小值问题
  4. 人工智能-基于U^2-Net的肖像画生成算法
  5. iOS经典面试题之深入分析block相关高频面试题
  6. Android之开发性能优化简介
  7. 嵌入式全栈工程师_我花了半个月,整理出了这篇嵌入式开发学习指南(学习路线+知识点梳理)...
  8. [jQuery原理] jQuery入口函数
  9. selenium使用TestNG实现DDT
  10. C# 直接选择排序(史上最清晰,最通俗)
  11. 简明Github使用教程(桌面客户端与网页版)
  12. Arduino 例程编译错误 error: unknown type name uint_farptr_t did you mean uint_fast8_t
  13. 从零开始学 Web 之 Ajax(五)同步异步请求,数据格式
  14. I.MX6U嵌入式Linux应用编程学习
  15. 3D 空间中拟合曲线
  16. 【持续更新】树莓派启动与故障系列集锦
  17. 金山软件CEO张宏江:技术与经营,优化不同的参数而已
  18. Ubuntu 和 Debian 的关系
  19. Zabbix5.0网易邮箱163告警详细步骤
  20. 3分钟告诉你如何成为一名黑客?|零基础到黑客入门指南,你只需要掌握这五点能力

热门文章

  1. 女孩起名字:100个优秀的女孩名字大全
  2. linux下替代windows的软件列表
  3. android 开机动画 卡顿,开机动画没按帧率播放 有卡顿
  4. 大二学生基于Html+Css+javascript的网页制作——动漫设计公司响应式网站模板 (10个页面)
  5. SQLServer引擎安装失败
  6. 高中数学学习技巧,这几步你都做到了吗?
  7. Windows的重要服务端口135/137/138/139/445
  8. 百度火星WGS84坐标转换
  9. appium 使用sendkeys 设置6位数交易密码时,发现界面显示位数丢失
  10. SpringBoot 实现发送邮件