开源Registry项⽬Harbor源代码结构解析
上周我们介绍了Harbor开源企业级容器Registry的架构,获得了社区很多朋友的反馈和建议,再次⼀并感谢,希望和⼤家⼀起,共同建设⼀个优秀的开源项⽬。本⽂请Harbor项⽬⼯程师尹⽂开介绍源码结构,帮助开发和运维⼈员理解代码的⼯作原理。
Harbor项⽬概览
容器应⽤的开发和运⾏离不开可靠的镜像管理。从安全和效率等⽅⾯考虑,在企业私有环境内部署的Registry服务是⾮常必要的。
Harbor()是由VMware中国研发团队为企业⽤户设计的Registry Server开源项⽬,包括了权限管理(RBAC)、图形管理界⾯、LDAP/AD 集成、审计、⾃我注册、HA等企业必需的功能,同时针对中国⽤户的特点,原⽣⽀持中⽂,并计划实现镜像复制(roadmap)等功能。
本⽂主要介绍Harbor项⽬的源码结构,帮助开发和运维⼈员理解其⼯作原理。
主要组件
Harbor系统由五个容器组成:Proxy、Core Services(包含UI, tokenservice和webhook)、Database、Registry和Log Collector。
Proxy提供反向代理服务,⽤户的不同请求由Proxy分发到后端的UI或者Registry。Harbor中使⽤的是官⽅的nginx镜像。
Core Services是Harbor项⽬的核⼼组件,主要提供权限管理、审计、管理界⾯UI、token service以及可供其他系统调⽤的API等功能。
Database提供数据持久化服务,采⽤了官⽅的mysql镜像。
Registry是Docker官⽅的开源的Registry镜像,主要提供镜像的存储和分发功能。
Log Collector负责收集其他容器的⽇志并进⾏⽇志轮转。
各个容器之间的关系如下图所⽰:
源码结构
以下所述主要为Core Services组件的源码结构,通过根⽬录下的Dockerfile可以构建出Core Services的镜像。另外Deploy⽬录下的db 和log分别对应Database和Log Collector的Dockerfile镜像构建⽂件,⽽Nginx和Registry则都是采⽤的官⽅镜像。
| – api (Harbor提供的外部调⽤的API)
| – auth (认证模块,⽬前提供两种⽅式:数据库和LDAP)
| – db (数据库认证)
| – ldap (LDAP认证)
| – controllers (控制器相关代码)
| – dao (数据持久层)
| – Deploy (部署相关代码)
| – db (构建Database镜像的源码)
| – log (构建Log Collector镜像的源码)
| – l (运⾏Harbor的docker compose⽂件)
| – docs (⽂档)
| – log (log⼯具)
| – models (数据库映射的模型代码)
| – routers (路由相关代码)
| – service (服务)
| – (处理Registry发来的镜像上传或下载等事件)
| – (为Registry提供鉴权服务)
| – static (js、css等⽂件)
| – utils (⼯具类)
| – vendor (依赖的第三⽅源码)
| – views (html模版⽂件)
| – Dockerfile (构建Core Services镜像的Dockerfile)
| – (⼊⼝函数)
源码分析
下⾯以获取项⽬列表和获取某个项⽬的详细信息为例来分析Harbor源码。
Harbor项⽬使⽤了go语⾔开发,WEB框架采⽤beego。、routers⽬录和controllers⽬录分别对应了⼊⼝函数、路由函数⽬录和控制器函数⽬录。当Core Services启动时,routers⽬录下的相应函数会将各个控制器与其所对应的⽤户请求URL进⾏注册,这样当不同的⽤户请求到达的时候,不同的控制器逻辑就会被触发。主要处理流程如下图所⽰:
当获取项⽬列表时会发送请求,该请求⾸先到达Nginx。Nginx的配置⽂件如下:
server {
listen 80;
location / {
proxy_passui/;
}
location /v1/ {
return404;
}
location /v2/ {
proxy_passregistry/v2/;
}
location/service/ {
proxy_passui/service/;
}
}
根据配置⽂件该请求会被转发到,也即Core Services中的UI。根据UI中中定义的规则:
beego.Router( "/api/projects/?:id",&api.ProjectAPI{} )
可知该请求最终是由api包中的ProjectAPI的Get⽅法来处理的。ProjectAPI结构体的定义如下:
typeProjectAPI struct {
BaseAPI
userID  int
projectID int64
}
在beego中,控制器处理⽤户请求的⽅法执⾏之前⾸先会执⾏Prepare()⽅法来进⾏⼀些准备或者校验操作,ProjectAPI定义的Prepare()⽅法如下:
func(p *ProjectAPI) P repare() {
p.userID = p.V alidateUser()
}
Prepare()中调⽤BaseAPI中的ValidateUser⽅法检查⽤户的合法性,并将⽤户ID赋值给ProjectAPI的userID属性。之后执⾏Get⽅法来处理⽤户的请求:
func(p *ProjectAPI) Get() {
queryProject :=models.Project{UserID: p.userID}
projectList, err :=dao.QueryProject(queryProject)
for i := 0; i < len(projectList); i++{
if isProjectAdmin(p.userID,projectList[i].ProjectID) {
projectList[i].Togglable= true
}
}
p.Data["json"] = projectList
源代码下载开源社区p.ServeJSON()
}
Get⽅法中调⽤dao包中的QueryProject()⽅法来获取项⽬列表,之后遍历列表判断该⽤户是否对此项⽬具有administrator的权限,最终返回项⽬列表的JSON数据,此次⽤户请求处理完毕。
当获取某个项⽬的详细信息时会发送请求,该请求同样会经过Nginx和Router并最终到达其对应的Controller的处理⽅法如下:
func (idc *ItemDetailController) Get() {
//具体处理逻辑
idc.ForwardTo("page_title_item_details","item-detail")
}
具体的处理逻辑此处忽略。该⽅法的最后⼀步调⽤idc.ForwardTo(“page_title_item_details”,”item-detail”)定位对应的HTML模版⽂件。模版⽂件默认的存放⽬录为views,“item-detail”为模板⽂件名,因此该语句最终定位到views/item-detail.tpl的⽂件。经过数据填充最终⽣成对应的HTML⽂件并返回。
本⽂作者:尹⽂开/Henry

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。