Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >空中加油: Harbor 2.1的非阻塞垃圾回收

空中加油: Harbor 2.1的非阻塞垃圾回收

作者头像
Henry Zhang
发布于 2020-10-27 02:16:01
发布于 2020-10-27 02:16:01
1.1K0
举报
文章被收录于专栏:亨利笔记亨利笔记

题图摄于温哥华港

注:微信公众号不按照时间排序,请关注“亨利笔记”,并加星标置顶,以免错过更新。

本文介绍 Harbor 2.1 的非阻塞镜像垃圾回收功能,可以一边进行正常的镜像管理任务,一边默默地执行垃圾回收任务,如同飞机的空中加油,无需中断飞行。部分内容节选自最新出版的《Harbor权威指南》(详情参见文末),相关作者为 Harbor 开源项目维护者王岩,欢迎扫码或点击“阅读原文”购买。

垃圾回收(Garbage Collection)是计算机系统常见的一种资源管理方式,由John McCarthy 在 1959 年发明并在 Lisp 语言中应用。对,就是那位大名鼎鼎的、提出了人工智能概念并被誉为“人工智能之父”的 John McCarthy。

顾名思义,垃圾回收是指把系统中不再使用的资源(即垃圾)释放并且使其可被重新使用。程序员在 JavaPython、Go 等现代编程语言中可能都了解过垃圾回收的概念,即释放程序中不再使用的数据所占用的内存,从而腾出空间存放其他数据。垃圾回收由语言的运行时(runtime)在后台来处理,对程序员一般是透明的,程序员写代码的时候无需(或者无法)干预垃圾回收的过程。这样的设计避免了程序员去关注复杂的内存管理机制。

当然,垃圾回收也有个很大的不足之处:一般需要有阻塞的时间,就是不定期地暂停前台程序的执行,来处理后台的垃圾回收工作。取决于需要回收的垃圾数量,暂停程序的执行可能会持续比较长的时间,因此,对响应时间敏感的应用这个矛盾尤为突出。为此,人们设计了多种垃圾回收的执行方式,如并发回收、分代回收等,目的是减少程序执行“骤停”的时间或频率。

Harbor 作为镜像等云原生制品的仓库,为了释放不再使用的镜像所占的存储空间,也需要定期进行垃圾回收工作。 在 Harbor 2.0 及之前的版本中,垃圾回收一直是阻塞式的。也就是说,在 Harbor 系统执行垃圾回收任务时,系统处于只读状态,只能拉取而不能推送镜像。在部分用户的生产环境下,阻塞式的垃圾回收是不能被接受的,这会造成系统从几分钟到几十小时的阻塞状态。虽然建议用户定制周期垃圾回收任务在非工作日的夜间执行,但是并不能从根本上解决问题。

造成垃圾回收任务阻塞和执行时间较长的主要原因有如下两个。

1.层(layer)文件的引用计数

在阻塞式的垃圾回收任务中使用的是Docker Distribution(下简称Distribution)自带的垃圾回收功能,实现流程大致如下。

(1)遍历文件系统,得到每一个共享层文件的引用数量。当一个层文件的引用数量为0时,即为待删除层文件。

(2)在得到所有待删除的层文件后,调用存储系统的删除接口,依次删除层文件。

在计算层文件引用计数的过程中,如果此时用户正在上传镜像,则垃圾回收可能会删除正在上传的层文件,从而破坏镜像。因此,在垃圾回收任务执行时需要阻塞镜像的推送。

同时,因为 Distribution 并没有使用数据库记录层文件的引用关系,所以需要遍历整个存储系统的路径来获取每一个层文件的引用计数。这种遍历方式造成了很大的时间开销,并且所需时间随着层文件数量的增加而线性增加。

2.云存储的使用

在层文件引用关系的遍历和层文件的删除过程中,需要调用存储系统的接口来实现。如果用户使用云存储(如S3)作为存储系统,则存储系统接口调用的时间开销会比本地存储增加很多。

基于以上情况,Harbor 2.1 实现了非阻塞式的垃圾回收功能。该功能的目的是去除垃圾回收任务执行时的系统阻塞,同时提高垃圾回收任务的运行效率,使得 Harbor 可以一边进行正常的镜像管理任务,一边默默地执行垃圾回收任务,如同飞机的空中加油,无需中断飞行。本文将简要介绍非阻塞式的垃圾回收方案的基本思想。

1.Artifact(制品)数据库

在 Harbor 2.0 中,在用户成功推送一个镜像后,Harbor系统会完整记录这个镜像的信息,如下图所示。

从上图可以看出,一个镜像的层文件和其引用关系都被记录在 Artifact 数据库中。同时,在一个镜像被删除后,其层文件的引用关系也被删除。这样一来非阻塞式垃圾回收任务可以通过数据库计算出存储系统中所有层文件的引用计数。当任何一个层文件的引用计数为都0时,该层文件即待删除层文件。相比存储系统的遍历,数据库的计算可以节省大量时间开销。

2.层文件和清单文件删除API

通过数据库得到待删除层文件后,下一步就是将其删除。Distribution 并没有提供删除层文件和清单(manifest)文件的 API,而是暴露公有函数供其自身的垃圾回收任务使用。在非阻塞垃圾回收任务实现中,需要引用 Distribution 的代码来实现层文件和清单文件的删除 API,而删除 API 仅供非阻塞垃圾回收任务使用,不暴露给用户,如下图所示。

3.非阻塞

非阻塞式垃圾回收的核心是在垃圾回收任务运行时,不阻塞用户的镜像等 Artifact的推送。为了达到此目的,这里引入了状态控制和时间窗口机制,下面以镜像为例加以说明。

1)状态控制

在层文件的数据库表中加入了版本和状态列,层文件的每一次状态改变都会增加版本,这样可以通过版本来实现乐观锁。当非阻塞垃圾回收任务执行删除时,会尝试将待删除的层文件标记为“deleting”状态。如果该待标记的层文件刚好被Docker 客户端正在推送的镜像引用,则非阻塞垃圾回收任务的“deleting”标记将会失败。原因是 Docker 客户端在推送过程中发起的 HEAD Blob 请求被 Harbor 中间件拦截,中间件会增加层文件的版本。而非阻塞垃圾回收任务在更新层文件状态为“deleting”时,层文件的版本已经不符合数据库里的最新版本信息,导致更新失败,如下图所示。

2)时间窗口

在推送 Docker 客户端的过程中,Docker 客户端首先会推送层文件,而此时的层文件在系统中的引用计数为0,只有当清单文件推送成功后,Harbor 才会建立引用关系,使得这些层文件的引用计数非0。为保证在非阻塞垃圾回收任务执行中,用户正在推送的层文件不被删除,需要引入时间窗口概念。在层文件的数据库表中加入更新时间列,非阻塞垃圾回收仅作用于更新时间早于非阻塞垃圾回收起始时间两小时的层文件。在时间窗口内推送的层文件都会被保留,如下图所示。

在 Harbor 2.1 推出了非阻塞垃圾回收之后,解决了镜像运维的一个痛点,得到许多用户的点赞,有用户发出了“期待已久”的感叹。正如上期文章所说,这又是运维工程师一个保护”发际线“的功能,快来试试吧,欢迎留言告诉我们你使用的情况如何。


要想了解云原生、区块链和人工智能等技术原理,请立即长按以下二维码,关注本公众号亨利笔记 ( henglibiji ),以免错过更新。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 亨利笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Harbor制品仓库资源配额的使用
本文节选自《 Harbor权威指南》 一书第8章,作者为 Harbor 开源项目维护者王岩。
Henry Zhang
2021/04/21
2.9K0
玩懂Python垃圾回收机制,又有时间可以摸鱼了
我们写过C语言、C++的朋友们都知道,C语言是没有垃圾回收这种说法的。手动分配以及释放内存都是需要我们的程序员自己动手完成。不管是“内存泄漏” 还是野指针都是让开发者非常头疼的问题。所以C语言开发提及讨论最多的话题就是内存管理了。but对于其他高级语言来说,例如Java、C#、Python等高级语言,已经具备了垃圾回收机制。这样可以屏蔽内存管理的复杂性,使开发者可以更好的关注核心的业务逻辑。
查理不是猹
2021/12/12
9390
Java 虚拟机:垃圾回收(上)
Java 虚拟机的自动内存管理,将原本需要由开发人员手动回收的内存,交给垃圾回收器来自动回收。不过既然是自动机制,肯定没法做到像手动回收那般精准高效 [1] ,而且还会带来不少与垃圾回收实现相关的问题。
码农架构
2021/02/18
4481
Java 虚拟机:垃圾回收(上)
浅析垃圾回收
Java虚拟机的自动内存管理使开发人员不必手动回收内存,而是将其自动交给垃圾回收器来自动回收。然而,由于自动机制的限制,垃圾回收无法像手动回收那样精确高效。此外,垃圾回收的实现过程还可能产生一些问题。今天,我们简单地探讨一下垃圾回收的概念。
架构狂人
2023/08/16
2240
浅析垃圾回收
JVM之垃圾回收相关算法
当p的指针断开的时候,内部的引用形成一个循环,这就是循环引用,从而造成内存泄漏
Java微观世界
2025/01/20
1000
JVM之垃圾回收相关算法
JavaScript的垃圾回收机制
JavaScript是使用垃圾回收的语言,也就是很大的解决了跟踪内存对开发者造成的负担(毕竟这是很多问题的来源)。而卸下这个甜蜜的负担(一点也不甜蜜好嘛),通过自动内存管理实现内存分配和闲置资源回收。(下面会简单的讲述内存泄漏)
肥晨
2023/02/16
3K1
java — 垃圾回收
1. 垃圾回收的意义   在java中,当没有对象指向原先分配给某个对象的内存的时候,这片内存就变成了垃圾,JVM的一个系统级线程就会自动释放这个内存块,垃圾回收意味着程序不再需要的对象是“无用的信息”,这些信息会被丢弃。当一个对象不再被引用的时候,内存回收它所占用的空间,以便将空间用来存放后续的新对象。   除了①释放没用的对象,垃圾回收还可以②清除内存记忆碎片,由于创建对象和垃圾回收期释放丢弃对象所占的内存空间,内存会出现碎片,碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的对内存移动到堆
Mister24
2018/05/14
1.5K0
python进阶(7)垃圾回收机制
现在的高级语言如java,c#等,都采用了垃圾回收机制,而不再像c,c++里,需要用户自己管理内存。自己管理内存及其自由,可以任意申请内存,但这如同一把双刃剑,可能会造成内存泄漏,空指针等bug。 python中也同java一样采用了垃圾回收机制,不过不一样的是:python采用的是引用计数机制为主,标记清除和分代回收两种机制为辅的策略
全栈程序员站长
2022/09/19
7780
Harbor功能特点看这一篇就够了
本文由“GO开源说”第七期 《Harbor助你玩转云原生》直播内容修改整理而成,视频内容较长,本文内容有所删减和重构。 注:微信公众号不按照时间排序,请关注“亨利笔记”,并加星标以置顶,以免错过更新。 相关视频: 视频回放:Harbor助你玩转云原生(1) 视频回放:Harbor助你玩转云原生(2) 云原生技术的兴起为企业数字化转型带来新的可能。作为云原生的要素之一,带来更为轻量级虚拟化的容器技术具有举足轻重的推动作用。其实很早之前,容器技术已经有所应用,而 Docker 的出现和兴起彻底带火了容器。其
Henry Zhang
2023/04/04
2.5K0
Harbor功能特点看这一篇就够了
golang 系列:啥是垃圾回收?
golang 的三色标记法虽然没有 java 的内存回收机制成熟,但它细分了回收过程,通过写屏障技术,能和用户程序并发进行,这也一定程度的提高了内存回收速度。
lincoln
2021/08/01
4310
golang 系列:啥是垃圾回收?
php 的垃圾回收策略
此前我们了解过 java 和 python 如何管理内存以及运行过程中的垃圾收集。 python 的内存管理与垃圾收集 java 的存活判定与垃圾收集
用户3147702
2022/06/27
4260
php 的垃圾回收策略
JVM垃圾回收(上)
Java 中的垃圾回收,常常是由 JVM 帮我们做好的。虽然这节省了大家很多的学习的成本,提高了项目的执行效率,但是当项目变得越来越复杂,用户量越来越大时,还是需要我们懂得垃圾回收机制,这样也能进行更深一步的优化。
健程之道
2019/11/02
5200
JVM之垃圾回收相关算法
JVM自学指南已经开源到GIthub项目 JVM自学指南 欢迎star fork 万分感谢!
程序员阿杜
2021/07/05
4521
JVM之垃圾回收相关算法
python垃圾回收机制原理
#python垃圾回收机制详解 一、概述:   python的GC模块主要运用了“引用计数(reference counting)”来跟踪和回收垃圾。在引用计数的基础上,还可以通过标记清除(mark and sweep)解决容器(这里的容器值指的不是docker,而是数组,字典,元组这样的对象)对象可能产生的循环引用的问题。通过“分代回收(generation collection)”以空间换取时间来进一步提高垃圾回收的效率。 二、垃圾回收三种机制   1、引用计数   在Python中,大多数对象的生命周期都是通过对象的引用计数来管理的, 广义上讲,它也是一种垃圾回收机制,而且是一种最直观最简单的垃圾回收机制。   原理:当一个对象被创建引用或者被复制的时候,对象的引用计数会加一,当一个对象的引用被销毁时,对象的引用计数会减一,当对象的引用计数减为0的时候,就意味着对象已经没有被任何人使用了,可以将其所占用的内存释放了。   虽然引用计数必须在每次分配和释放内存的时候加入管理引用计数的这个动作,然而与其他主流垃圾收集机制相比, 最大的一个优点是实时性, 及任何内存,一旦没有指向他的引用,就会立即被回收,其他的垃圾回收机制必须在某种特殊条件下(内存分配失败)才能进行无效内存的回收。   执行效率问题: 引用计数机制带来的维护引用计数带来的额外操作与python运行中所运行的内存分配和释放,引用赋值的次数是成正比的。相比其他机制,比如“标记-清除”,“停止-复制”,是一个弱点,因为这些技术所带来的操作基本上只是与待回收的数量有关。 引用计数还存在的一个致命的弱点是循环引用,这使得垃圾回收机制从来没有将引用计数包含在内。这就需要我们用新的方法了, 即标记清除。 2、标记清除 标记清除主要是用来解决循环引用产生的问题的,循环引用只会在容器对象中才会产生,比如数组、字典、元组等,首先是为了追踪对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个链表,指针分别指向前后两个容器对象,这样就可以将对象的循环引用环摘除,就可以得出两个对象的有效计数。 问题说明:   循环引用可以使得一组对象的引用计数不是0, 然而这些对象实际上并没有被外部对象所引用,这就意味着不会再有人使用这组对象, 应该回收这组对象所占用的内存空间,然而由于相互引用的存在,每一个对象的引用计数不为0,因为这些对象所占用的内存永远不会被释放。比如下面的代码:
全栈程序员站长
2022/09/12
4290
python垃圾回收机制原理
jvm之垃圾回收标记相关算法解读
在堆里存放着几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。
一个风轻云淡
2023/10/15
2690
jvm之垃圾回收标记相关算法解读
PHP 垃圾回收与内存管理指引
php 的变量存储在「zval」变量容器(数据结构)中,「zval」属性包含如下信息:
柳公子
2018/09/17
1.3K0
PHP 垃圾回收与内存管理指引
golang 垃圾回收 gc
摘要 在实际使用go语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究。本文对研究的结果进行一下总结。 什么是垃圾回收? 曾几何时,内存管理是程序员开发应用的一大难题。传统的系统级编程语言(主要指C/C++)中,程序员必须对内存小心的进行管理操作,控制内存的申请及释放。稍有不慎,就可能产生内存泄露问题,这种问题不易发现并且难以定位,一直成为困扰开发者的噩梦。如何解决这个头疼的问题呢?过去一般采用两种办法: 内存泄露检测工具。这种工具的原理一般是静态代码扫描,通过扫描
李海彬
2018/03/26
4.9K0
深入Go:垃圾回收的演进
Stop the world 是讨论垃圾回收(Garbage Collection,GC)时绕不开的话题,曾经Go语言的GC机制也威胁着服务的响应时间——Discord技术团队的文章Why Discord is switching from Go to Rust讨论了Go语言GC带来的问题。Go通过版本迭代已经极大地改善了GC的问题,平均每次STW时间从100+ms降低到了0.5ms——是什么神奇的魔法使得世界几乎无需暂停?在本文中,我们通过提问、解答的方式尝试对该演进的主要过程进行梳理。
wenxing
2021/12/10
1.5K0
【黄啊码】垃圾回收可以赚钱,那php的垃圾回收机制你懂多少?
大家好,我是黄啊码,相信java的垃圾回收机制,任何java入门的码农们多多少少已经接触过了,那么php的垃圾回收机制又有多少知道,知道的评论区打个1呗。
黄啊码
2022/11/14
4080
【黄啊码】垃圾回收可以赚钱,那php的垃圾回收机制你懂多少?
Python 垃圾回收机制与原理解析
Python 作为一门解释型语言,以代码简洁易懂著称,我们可以直接对名称赋值,而不必声明类型,名称类型的确定、内存空间的分配与释放都是由 Python 解释器在运行时进行的
公众号机器学习与AI生成创作
2021/10/20
1.2K0
Python 垃圾回收机制与原理解析
相关推荐
Harbor制品仓库资源配额的使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档