Harbor权威指南:容器镜像、Helm Chart等云原生制品的管理与实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.2 组件简介

本节将对Harbor的架构、组件和典型处理流程做简要介绍。

2.2.1 整体架构

在早期的版本中,Harbor的功能主要围绕Docker镜像的管理展开。Harbor的开发者希望让用户通过一个统一的地址同时进行推送和拉取,以及利用图形界面对镜像进行浏览和其他管理工作。关于推送和拉取这一部分功能,Docker公司开源的Distribution项目应用广泛,可以支持不同类型的存储,而且比较成熟和稳定。因此,Harbor选择由Distribution处理客户端镜像的推送和拉取请求,并通过围绕Distribution增加其他组件的方式来提供管理功能。这种方式一方面减少了开发工作量;另一方面由于Distribution基本上是镜像仓库的事实标准,所以保证了镜像的推送和拉取功能的稳定。后来,随着版本的迭代,Harbor逐渐减少了对Distribution的依赖,但是在镜像的读写、存取等功能上,Distribution仍然是Harbor和用户存储之间的桥梁。

如图2-11所示是Harbor 2.0的架构示意图,从上到下可分为代理层、功能层和数据层。

img

图2-11

其中代理层功能比较简单,可将其理解为Harbor的门户(gateway)。代理层实质上是一个Nginx反向代理,负责接收不同类型的客户端的请求,包括浏览器、用户脚本、Docker及其他Artifact命令行工具如Helm、ORAS等,并根据请求类型和URI转发给不同的后端服务进行处理。它保证了Harbor的所有功能都是通过单一的主机名(hostname)暴露的。

功能层是一组HTTP服务,提供Harbor的核心功能,包括核心功能组件Core、Portal、JobService、Docker Distribution和RegistryCtl,以及可选组件Notary、ChartMuseum和镜像扫描器。在实际部署中,除了Notary组件包括server和signer这两个容器,其他组件都由一个容器组成。这些组件被设计为无状态的组件,以便通过多实例的方式进行水平扩展。

数据层包括PostgreSQL关系型数据库、Redis缓存服务,以及用户提供的存储服务(文件系统、对象存储)。这些服务被各个功能组件共享,用于存储不同场景的应用数据。由于功能组件都是无状态的,所以在规划 Harbor 的高可用部署时,只要保证应用数据一致而且不会丢失就可以了。

组件可划分为两大类:核心组件和可选组件,将在下面两节中介绍。

2.2.2 核心组件

核心组件是安装Harbor时的必选组件,是完成Harbor主要功能所必需的,包括核心功能组件和数据存储组件。其中,核心功能组件包括反向代理 Nginx、Portal、Core、RegistryCtl、Docker Distribution和JobService等,数据存储组件包含数据库(PostgreSQL)、缓存(Redis)和Artifact存储。反向代理Nginx已经在上一节解释过了,下面结合实际使用场景对其他组件逐一进行介绍。

1.核心功能组件

◎ Portal:这是一个基于Angular的前端应用,对应的容器为portal,由容器内置的Nginx 服务器提供静态资源的服务。用户在用浏览器访问Harbor的用户界面时,代理层的Nginx反向代理会将请求转发到portal容器中的Nginx服务,以便浏览器获得运行前端界面所需的Javascript文件及图标等静态资源。

◎ Core:这是Harbor中的核心组件,封装了Harbor绝大部分的业务逻辑。它基于Beego框架提供了中间件和RESTful API处理器(API Handlers)来处理界面及其他客户端发来的API请求。一个HTTP请求到达Core进程后,首先会被请求的地址(URI)对应的一组中间件做预处理,进行安全检查、生成上下文等操作;之后,API处理器会解析请求数据对象,并调用内部的业务逻辑模块。Harbor内部的业务逻辑由不同的控制器(controller)接口暴露,如 Artifact、项目的增删改查都有相应的控制器负责。某些功能,如查询镜像签名,或对Artifact进行扫描,需要调用其他组件的接口完成,这部分工作也是由控制器完成的。Core组件还负责连接内部数据库或外部身份认证服务(如LDAP)对用户输入的用户名和密码进行校验。

此外,由命令行工具发来的推送和拉取Artifact的请求也到达这个组件,由中间件进行权限检查、扣除配额等操作,之后把请求转发到Docker Distribution组件,由它对存储进行读写。

下面以用户推送镜像的场景为例(为了说明原理,略去了认证过程),讲解Nginx、Core、Docker Distribution组件是如何处理客户端的请求的,如图2-12所示。

img

图2-12

首先,Docker向Harbor发送请求,调用两个API“POST/v2/<name>/blobs/uploads”和“PATCH/v2/<name>/blobs/uploads/<session_id>”上传镜像的数据层,请求被Nginx转发给Core组件,Core的各个中间件进行各类检查,如查询数据库检查请求的权限及目标项目配额是否用尽等。在这些检查通过后,请求被发送给Docker Distribution,后者将数据写入存储中。在写入成功后,响应会再次经过Core的中间件,这时它会更新数据库中项目配额的使用量并将成功信息返回给客户端。

如此往复,在所有数据层都上传成功后,Docker客户端会向Harbor发送请求,调用API“PUT/v2/<name>/manifests/<reference>”上传镜像的清单(manifest),这是一个JSON格式的数据对象。请求经过Core的预处理,被转发给Docker Distribution,后者写存储成功后,Core的中间件会调用镜像对应的Artifact功能模块中的元数据处理器(Processor),根据JSON对象的内容向数据库中插入记录,保存镜像的元数据信息。

在上传结束后,推送请求完成。镜像内容被Docker Distribution写入存储中,它的元数据则被Core写入数据库中,可供后续查询。

上面提到的上传和处理过程也适用于包括Helm Chart、CNAB、镜像索引等其他与OCI格式兼容的Artifact。除了镜像,在Harbor 2.0中还为镜像索引、Helm Chart和CNAB提供了专用的Artifact元数据处理器,可以为它们提取各自特有的元数据。其余类型的Artifact,如ORAS打包的本地文件,则由默认(Default)Artifact元数据处理器提取类型、大小等基本信息。更详细的Artifact处理流程可参考第4章。

◎ Docker Distribution:为由Docker公司维护的Distribution镜像仓库,实现了镜像推送和拉取等功能。Harbor通过Distribution实现了Artifact的读写和存取等功能。

◎ RegistryCtl:Docker Distribution的控制组件,与Docker Distribution共享配置,并提供了RESTful API以便触发垃圾回收动作。

◎ JobService:JobService是异步任务组件,负责Harbor中很多比较耗时的功能,比如 Artifact 复制、扫描、垃圾回收等,都是以后台异步任务的方式运行的。JobService提供了管理和调度任务的功能。它首先定义了公共的接口(interface),通过实现这些接口,可以提供不同类型任务的执行逻辑。JobService 也提供了RESTful API,通过调用这些API,指定任务的类型、运行参数及执行的时间表,JobService会实例化任务,把它们放到Redis队列中,并根据时间表调度执行,并以回调方式将任务执行的结果通知给调用方。

以垃圾回收为例,用户在通过界面触发垃圾回收之后,Core组件会向JobService提交一个任务,这个任务会被异步调度,因此请求会立即返回。当这个任务被调度执行时,JobService内处理垃圾回收任务的代码会调用RegistryCtl服务的API,后者会调用Docker Distribution的命令进行垃圾回收。在回收成功后,JobService会以Webhook方式通知Core组件。Core组件在收到通知后,会更新数据库里任务记录的状态,此时,用户通过刷新界面就可以查看垃圾回收结果。流程如图2-13所示。

img

图2-13

2.数据存储组件

上面的核心组件只负责处理业务逻辑和用户请求,都是以无状态服务的形式运行的,而对业务数据和Artifact内容的保存和持久化都是通过数据存储组件完成的。

Harbor的数据存储组件可分为以下三部分。

◎ 数据库(PostgreSQL):Harbor的应用数据,比如项目信息、用户与项目的关系、管理策略和配置信息等,都保存在这个关系型数据库中。Artifact 的元数据,比如类型、大小、Tag等也会保存在这里。此外,一些可选组件如负责管理签名的Notary及镜像扫描器Clair,在默认的安装方式下也会和Harbor的核心应用组件共享这个数据库服务。在安装时也可以配置外置的数据库服务,并创建相应组件的数据库,Harbor会在启动时完成数据库表结构的初始化。此外需要注意的是,目前Harbor只支持PostgreSQL作为后台数据库,并不能完全兼容MySQL等其他数据库。

◎ 缓存服务(Redis):主要作为缓存服务存储一些生命周期较短的数据,如在水平扩展时多个实例共享的状态信息等。另外,出于性能等方面的考虑,JobService组件有极少量的持久化数据也保存在Redis中。Redis中不同的索引号对应的数据库面向不同的组件,0号数据库对应Core组件,存储用户的会话信息,以及只读、Artifact数据层上传状态等临时信息;1号数据库被Docker Distribution用来存储数据层的信息以加速API;2号数据库面向JobService,存储任务的信息,并实现了类似队列的功能,多个JobService都可以以它为依据,对任务进行调度并更新状态。对于可选组件,如ChartMuseum及镜像扫描软件Clair和Trivy,也在默认情况下以Redis作为缓存,存储临时数据。与数据库服务类似,用户也可以在安装时配置相应的参数将Harbor指向外部的Redis服务。

◎ Artifact存储:这是存储Artifact本身内容的地方,也就是每次用命令行工具推送容器镜像、Helm Chart或其他Artifact时,数据最终的存储目的地。在默认的安装情况下,Harbor会把Artifact写入本地文件系统中。用户也可以修改配置,将第三方存储服务,如亚马逊的对象存储服务S3、谷歌云存储GCS或阿里云的对象存储OSS等作为后端存储来保存Artifact。正如前面提到的,Harbor通过Docker Distribution对Artifact的内容进行读写,因此对各种存储服务的适配,完全是通过Docker Distribution中不同的驱动(driver)完成的。

目前,默认安装的持久化服务组件都不是高可用的,在部署高可用的 Harbor 时,用户需要自己根据环境的需要搭建高可用的数据服务,并将 Harbor 指向这些服务,具体可参考第3章。

2.2.3 可选组件

核心组件实现了基本的Artifact管理功能。在此基础上,Harbor通过与第三方开源软件集成来提供诸如镜像签名、漏洞扫描等功能,这部分功能服务叫作可选组件。在Harbor提供的安装方式中,用户可以根据需要选择是否安装这些组件。为了降低部署的复杂度,安装程序会对这些可选组件进行配置,让它们与Harbor的核心组件共用数据库、Redis等服务,并更好地与Harbor核心组件一起工作。

Harbor的可选组件有以下几个。

◎ Notary:基于TUF提供了镜像签名管理的功能。用户选择安装Notary后,在安装部署Harbor时会额外安装notary-server和notary-signer两个组件,其中notaryserver负责接收客户端的请求管理签名,notary-signer负责对签名的元数据再进行签名,以提高安全性。在默认安装的情况下,它们会与Harbor的Core组件共用一个数据库。在开启Docker Content Trust后,Docker 客户端会在推送镜像后将签名发到Harbor服务的4443端口,Harbor的Nginx组件接到4443端口的请求时,会把请求转发给notary-server。当用户向API发送请求查询镜像时,Core组件会通过内部网络向notary-server发送请求,查询镜像是否被签名,并将这个结果返回给用户。

◎ 扫描器:在Harbor 2.0版本中,支持将Clair或Trivy作为镜像扫描器和Harbor一起安装。它们的工作机制不尽相同,但是在部署时会同时安装适配器(adapter),这些适配器根据规范实现了相同的RESTful API。这些扫描器在安装过程中会被自动注册到Harbor中。在扫描镜像时,Harbor的JobService组件通过调用适配器上的API扫描镜像,得到漏洞报告,并将它们存储在数据库中。

◎ ChartMuseum:提供了API管理非OCI规范的Helm Chart。在安装了ChartMuseum组件后,当用户使用“helm”命令向Harbor推送或拉取Chart时,Harbor的Core组件会首先收到请求,在校验后将请求转发给ChartMuseum进行Chart文件的读写。随着兼容OCI规范的Helm Chart在社区上被更广泛地接受,Helm Chart能以Artifact的形式在Harbor中存储和管理,不再依赖ChartMuseum,因此Harbor可能会在后续版本中移除对ChartMuseum的支持。

以上是对Harbor架构及各个组件的概述,关于在不同使用场景中各个组件如何交互和协同工作,在后面的章节中将有更详细的介绍。