5大最新雲原生鏡像構建工具全解析,3個來自Google,你瞭解幾個?

1雲原生大背景下的鏡像構建

在分享開始,我想先跟大家簡單聊一下雲原生,可能不會詳細展開,而是帶領大家瞭解一下雲原生對鏡像構建方面的影響。

第一,在接觸雲原生相關的技術時,無論是要解決開發、測試環境的問題,還是解決日常開發、測試等相關的操作和流程,我們經常都會談到持續集成。持續集成首先要做代碼的集成,不同的feature一起交付,使用持續集成的理念儘快把代碼合併,保證代碼沒有衝突,這是持續集成最簡單的一些理念。

在持續集成之後,要考慮做哪些業務的驗證。驗證之外,還需要有一些安全相關的策略。比如,在開發過程中是否使用了不安全的代碼或依賴包。在構建的過程中,還要生產許多不同的製品。

那麼問題就來了,雲原生技術確實能通過容器化、K8s集羣編排等提供能夠複製的應用環境。無論什麼語言,Java或Python等都可以非常簡單地去使用docker鏡像,或者Kubernetes yaml去部署環境,解決開發和生產環境的區別。

在傳統開發模式下,常常會遇到在開發環境里程序好好的,到生產環境就出現各種各樣的問題。K8s集羣編排可以說很好地解決了這個問題,變成複用地資源。以前部署很多實例需要開很多虛擬機的情況,也成爲我們不必再關心的問題。

這些聽起來很多都是跟運維相關的,那麼開發、測試爲什麼要去關心這個事情?其實大家是在合作,爲了達成一個共同的目標。不管用DevOps,還是敏捷開發,我們都要去考慮從代碼交付到真正上線要做哪些事情,並予以解決,而不是總面對“在我的環境裏沒有問題”這樣的問題。同時,儘量統一使用環境的配置、資源、方案。

剛纔談到從持續集成到雲原生,大量使用雲原生技術,這時候要考慮的是安全。簡單來說,我可以使用K8s鏡像把docker socket直接部署到自己的Pod容器裏。但實際上要做更多的考慮,這個環境可能不同的業務來使用,環境本身是共享的,所以不光要使用環境,還要考慮它是否穩定,有沒有CVE漏洞以及容器本身的一些權限等。只有搞清楚這幾件事,才能去考慮後面要講的內容。

2無Dockerfile構建鏡像工具

首先來思考爲什麼會有鏡像構建這方面的需求?第一,對很多開發者來說,需要不斷學習新的框架,新的技術,新的理念,這其實是很多開發者都不希望面臨的狀態。開發者希望開發環境和生產環境一致,構建的結果無差距,從而避免在後期發現問題。在沒有Dockerfile的情況下,會發現不同的語言使用的構建鏡像的方案完全不同,需要去這個語言的生態裏面尋找相關的方案。

還有一種場景,在構建一個應用時不知道它到底安不安全,也沒法控制它裏面有沒有Dockerfile。比如說先有一個jar包,jar包是第三方開發的,我先上傳到服務器,然後再去下載、構建。這時候可能直接用大的jar包去做。

另外,企業本身對產出物或者製品要求非常高,可能會有專門的人去維護,沒有那麼開放。

我在這裏介紹三個無Dockerfile鏡像構建方案。

1、KO

Ko是Google發明的一個工具,它主要服務Golang的用戶,比較容易使用。除了構建Golang本身,它還往前進了一步,集成了Kubernetes的使用。比如構建鏡像,構建程序,相關的yaml部署到開發環境等等,一切一個命令就可以搞定,不需要再去執行其他的命令。

Ko也應用了Golang語言本身的一些特性,比如用Golang package構建不同的鏡像,用package構建二進制文件。我們自己的項目裏也會使用Golang,編譯很多不同的二進制文件。
 

PIC1.jpg



這塊怎麼使用呢?第一,我們有對應的k8s yaml,比如開發環境可能是yaml的方案,默認的代碼倉庫可能會帶一些k8s yaml文件,pod、deployment、services等資源。如果不使用ko,還得去做一些replace,ko能夠完全解決這個問題。

在鏡像地址裏直接放go module的名字,或者對應的二進制文件的名字,二進制文件可能在後面再加相關的package就可以了。ko apply也會構建,根據config的一些配置來更新對應的環境。

此外,它還支持前端比較喜歡的一些技術,比如live server這樣的概念,對開發體驗效率非常有幫助。

它的概念也非常簡單,Golang本身構建出來的是一個二進制文件,需要基礎鏡像的支持,在基礎鏡像裏面做一些配置,就可以運行起來。谷歌前段時間發明了一個項目叫distroless, distroless會有一些工具,例如busybox、ls,這些是在容器裏面通常會使用的工具,但是並沒有package,沒有辦法安裝新的包。如果能修改,我們不希望使用distroless默認的鏡像,而是通過.ko.yaml文件去覆蓋,做默認的全局配置,或者根據不同的package去覆蓋。

Ko沒有把目標docker鏡像倉庫的地址放在配置裏面,它是一個環境變量,這是因爲,同城市不同的人在開發時會使用自己的鏡像倉庫,並不希望這個信息是共享的。用戶加上自己的Docker registry地址之後,可以直接開始構建。這裏可以分成兩部分,剛纔我們提到apply、K8s相關的操作,如果只想構建一個鏡像並且推送到registry,可以用一個publish命令,publish一個或者多個二進制文件或者鏡像。

這個鏡像本身非常簡單,Golang對環境的依賴非常少。所以,除了基礎鏡像的這些層級,還有你的應用。構建鏡像層面並不依賴Docker,我們使用docker時,會通過docker執行docker build命令,docker build會訪問docker daemon,接到主機裏面。用Ko的話,完全不需要docker daemon,容器本身沒有docker也是可以構建的。同時,也不需要很高的成本,不需要高權限,直接就可以配置。所以從上手或者用戶使用來說,又快又簡單。Ko也會大量使用GO本身的緩存,以及鏡像緩存。

缺點方面,Google的這個基礎鏡像還在國外,如果應用本身是比較複雜的,需要額外投入時間去學習它的構建。另外,也沒有特別好的網站,或者文檔,所有的信息都在GitHub裏面。

2、Jib

國內大部分開發者可能都是基於Java的,谷歌也在前段時間發明了一個Java專用鏡像工具,叫Jib。Maven、gradle、core(庫)都支持Jib這個鏡像工具,它可以用於任何一個java項目。它本身也是完全基於Java來實現,對java開發者來說是非常熟悉的一種用法。
 

PIC2.jpg



這是我們的目標鏡像倉庫,基礎鏡像可以寫入,不寫入的話,也會跟Ko一樣用distroless的鏡像去做。也可以定製,無論更簡單還是更復雜的鏡像,都可以做到。

除了使用Java的生態本身,Jib也考慮了對Java應用的優化。這裏包含三層:依賴、靜態資源和應用jar包。

Jib還有幾個優點,對Java開發者來說它非常容易,jib不是特別複雜的一個概念,就是一個插件。第二,Java容器化很佔用時間。容器化的Java方案在不斷優化,因爲Java除了容器化,它使用jvm本來就是爲了解決應用環境的一些問題,相當於帶有兩種虛擬化的概念。我們要去做非常優化的Java鏡像要投入不少時間和精力,這塊jib是一步到位的。Jib和Ko一樣,不依賴docker daemon,也可以在容器裏運行,不需要什麼高權限。

另外Jib和Ko相比較的話,沒有做和K8s的集成,如果進一步優化的話,可能會往這個方向考慮。只是不好理解的是,maven或者gradle這塊的生態可能已經有相關的插件。

3、s2i

如果我不是Java,又不是Go開發的,我應該怎麼做呢?s2i是紅帽發明的簡化的Docker構建方案 。它的主要理念是,使用builder pattern 的概念去構建程序,再用運行時的鏡像包一層。s2i需要在一個專門的鏡像裏做構建,然後再包一個運行的環境去做鏡像,這個可以用任何語言去做。

s2i的使用,需要一些經驗去考慮,同時還需要有專門的人去維護。因爲它需要一個鏡像做構建,在這個鏡像裏面要維護一堆東西。簡單來說,s2i的理念是有這個流程去聲明一下,所以相比前面的Ko、Jib兩個工具,s2i有點複雜。

第一,要有一個二進制文件把流程編排起來,除了目標鏡像倉庫之外,代碼還需要builder一個鏡像。builder 鏡像有幾個要求,要有assemble腳本和run腳本,這幾個腳本都是爲了支持要做哪些事情,包括在鏡像裏面做單元測試都是支持的,還有其他一些附加功能。
 

PIC3.jpg



從使用來說,一個小的命令就可以搞定,但是,這個命令背後的很多事情是怎麼做到的,比如builder鏡像的流程、文檔等,有很多需要學習的概念。

這個統一構建的過程,開發者統一去管理,對最終用戶來說是無感知的。包括第三方的軟件依賴、builder鏡像,除了這些腳本之外,還要寫dockerfile,dockerfile是統一在一個鏡像裏面管理的。有自己的緩存機制。支持直接克隆代碼構建,支持任何語言。

s2i缺點就是,太依賴docker環境。其次,上手非常複雜,拿現成的s2i工具做業務的話會很好用,但是這之前的學習成本還是很高的。

3 Dockerfile構建鏡像工具

我們其實還是更希望在K8s環境下去執行構建,這部分我也跟大家介紹兩個工具。

其實更多的情況,現在很多團隊學習能力很強,願意擁抱新的技術。希望深入研究容器化的最佳實踐,探索怎麼優化才能做到更好,怎麼進一步使用工具減少建構的時間。

從社區的角度,現在開源社區越來越龐大,很多企業會選擇使用開源社區的方案去做。開源社區也提供了很多的資源,你的團隊可能忽然之間就變得很大了。在使用這些工具時,企業裏的不同團隊也會互相分享經驗。

另外,影響Dockerfile鏡像工具使用的,在交付物這塊一些團隊定製要求很高。

1、Kaniko

第一個是kaniko,也是谷歌發明的,因爲K8s是谷歌的,所以他們在這塊做了不少東西,kaniko不需要docker daemon,在容器裏面去構建鏡像。Kaniko目的是推廣在K8s裏構建鏡像,任何容器化的方案都支持。除代碼倉庫,還可以使用對象存儲的方案。
 

PIC4.jpg



Kaniko也有一些自己的概念,有本地緩存,還會用registry鏡像作爲緩存。構建過程中,每完成一個multistage docker構建,結束一個stage,會把這個鏡像上傳到registry。Kaniko還有一個基礎鏡像的緩存,爲了減少在拉取時的時間,比如說在k8s裏面用PVC來保存緩存,可以用warmer鏡像來維護這個緩存。

Kaniko構建時,一個命令一步到位就可以完成,完全兼容docker的config.json。那麼Kaniko是怎麼在容器裏面構建的呢?其實,它的原理也很簡單,它把鏡像的內容保存在自己的容器裏面,所有的run命運都在容器裏面運行,再構建文件。

Kaniko支持推送到多個registry.Docker需要一個個去推送,但kaniko可以用一個命令全部搞定。除了推送到registry本身,還可以保存成一個tar包,load到docker本身,其他操作都可以使用。Kaniko有多個上下文支持,可以做的場景非常多,可以用bucket或者其他類似的協議做非常複雜的構建。

Kaniko的缺點,鏡像也是在谷歌那邊gcr.io,不支持v1 image tag格式,構建原理不直觀,無法直接load到docker daemon。

2、makisu

Makisu是Uber公司發明的,從18年開始使用,和Kaniko要解決的問題一樣,在一個容器化的環境裏面執行構建,不依賴docker daemon,也不需要做很多複雜的操作。能夠直接load到docker daemon。Makisu跟Kaniko不同之處在於,除了支持dockerfile,還做了一些優化,加入commit機制,可以選擇最終鏡像要幾個層級,通過dockerfile去解決。
 

PIC5.jpg



它的緩存除了本地,還有registry Jason。還可以把layer dockerfile裏面的命令和相關 layer的信息保存到redis,從而減少構建操作,也可以大量複用。Makisu有一個特點,它沒有選擇兼容docker的配置,而是自己發明了一個配置文件,執行命令時指定具體文件。

這兩個都有它的好處,在不同的構建環境裏面維護這個配置,也不需要配置參數,構建參數都是一樣的。另外,可以按照它的路徑去做不同的配置。比如在docker.io裏面,可以用不同的用戶名和密碼。這點docker配置本身其實沒有這方面的支持。Makisu另外一個跟kaniko不太一樣的地方,是可以在主機裏面直接構建。

Makisu的優點剛纔也提到了,Docker使用比較快,支持優化鏡像層級和空間,直接load到docker daemon,支持鏡像壓縮,緩存對接redis,Kubernetes官方的pod模版。

缺點部分跟Kaniko一樣,鏡像在國外Google,其次,它不支持其它構建上下文類型。第三,不兼容docker配置,儘管它能夠搞定這些事情提供更強化的配置,但不兼容總歸是一個缺陷。另外一個不容易理解的點是,makisu把push 和 –t 分開成爲兩個參數,來拼接最終目標鏡像倉庫地址。

4總結

今天這個探索並不是爲了選擇最優的工具,或者得出哪個工具更好更牛的結論。而是說,這些不同的工具有自己不同的使用場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章