天天看點

Harbor項目概覽

Harbor項目概覽

容器應用的開發和運作離不開可靠的鏡像管理。從安全和效率等方面考慮,在企業私有環境内部署的Registry服務是非常必要的。Harbor(https://github.com/vmware/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鏡像的源碼)

| – docker-compose.yml (運作Harbor的docker compose檔案)

| – docs (文檔)

| – log (log工具)

| – models (資料庫映射的模型代碼)

| – routers (路由相關代碼)

| – service (服務)

| – notification.go (處理Registry發來的鏡像上傳或下載下傳等事件)

| – token.go (為Registry提供鑒權服務)

| – static (js、css等檔案)

| – utils (工具類)

| – vendor (依賴的第三方源碼)

| – views (html模版檔案)

| – Dockerfile (建構Core Services鏡像的Dockerfile)

| – main.go (入口函數)

源碼分析

下面以擷取項目清單和擷取某個項目的詳細資訊為例來分析Harbor源碼。

Harbor項目使用了go語言開發,WEB架構采用beego。main.go、routers目錄和controllers目錄分别對應了入口函數、路由函數目錄和控制器函數目錄。當Core Services啟動時,routers目錄下的相應函數會将各個控制器與其所對應的使用者請求URL進行注冊,這樣當不同的使用者請求到達的時候,不同的控制器邏輯就會被觸發。主要處理流程如下圖所示:

當擷取項目清單時會發送請求http://hostname/api/projects/,該請求首先到達Nginx。Nginx的配置檔案如下:

server {
listen 80;

location / {
proxy_passhttp://ui/;
…
}

location /v1/ {
return 404;
}

location /v2/ {
proxy_passhttp://registry/v2/;
…
}

location/service/ {
proxy_passhttp://ui/service/;
…
}
}
           

根據配置檔案該請求會被轉發到http://ui/,也即Core Services中的UI。根據UI中routers/router.go中定義的規則:

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) Prepare() {

p.userID = p.ValidateUser()

}

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資料,此次使用者請求處理完畢。

當擷取某個項目的詳細資訊時會發送請求http://hostname/registry/detail,該請求同樣會經過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檔案并傳回。