重新審視 Docker 和 Jenkins

  • Part I: Thinking Inside the Container (深入思考容器)
  • Part II: Putting Jenkins in a Docker Container (將 Jenkins 放入 Docker 容器中)
  • Part III: Docker & Jenkins: Data That Persists (Docker&Jenkins:持久化數據)
  • Part IV: Jenkins, Docker, Proxies, and Compose
  • Part V: Taking Control of Your Docker Image(控制你的 Docker 鏡像)
  • Part VI: Building with Jenkins Inside an Ephemeral Docker Container (在臨時 Docker 容器中使用 Jenkins 構建)
  • Part VII: Tutorial: Building with Jenkins Inside an Ephemeral Docker Container(教程:在臨時 Docker 容器中使用 Jenkins 構建)
  • Part VIII: DockerCon Talk and the Story So Far(DockerCon 談話及目前的故事)

今天,我想介紹下我們在使用 Docker 和 Jenkins 兩年後在 Riot 中學到的經驗。我還會為那些熟悉的人員詳細介紹我為該系列文章做出的所有更新和改動,並介紹 Docker 容器和 Jenkins 的變遷。

開發者生態系統

早在2016年,當我第一次將筆放在紙上,安裝 Docker 並在桌面上使用起來並不是很方便。當時 Docker Toolbox 是桌面設置中的最好的工具。該工具在桌面上設置 Virtualbox 並在 Linux 虛擬機中運行 Docker 。Docker 提供了一個叫做 Docker Machine 的具有創造性的客戶端應用程序來創建和管理改 VM(如果你願意的話,可以使用幾個其他的)。這種設置運行良好,但是非常的重量級。

現在,Docker 為 Windows 和 OSX 提供了本地安裝程序。儘管虛擬機管理程序現在更加本地化,並且不需要 Virtualbox ,但他們仍然依賴於虛擬機管理程序樣式的解決方案。作為獎勵,Windows 版的 Docker 可以在“本地窗口”模式下運行,這使得 Windows Docker 容器得益於微軟的合作開發成果。這兩種解決方案都像集成的桌面客戶端,通過方便的圖形用戶界面驅動的設置菜單和選項,使得 Docker 的工作變得更加容易。我已經更新了一系列的博客來反映這些增加的內容,這反過來使得設置和運行更容易解釋!

DOCKER的改變

在2016年Docker的版本是1.10左右,Docker composer幾乎不支持Windows。我們的部署也依賴於Docker Swarm版本0.3.0。Docker 1.12於當年年末時候在DockerCon上宣佈,標誌著一個重大的進化。在這兩年中對Docker的更改太多了,無法詳細介紹,但是有幾個更改影響了教程。

DOCKER VOLUMES

對Docker最重要的更改可能是添加了Docker Volumes。在Docker的1.10版本中,如果不希望從主機裝載數據,您仍然需要創建一個“Docker數據容器”來封裝存儲持久性。這在Docker中創建了一個半永久volume,其他容器可以從中裝載和共享數據。雖然很方便,但也有一定的風險,因為如果您希望維護存儲,至少有一個容器必須指向數據容器內的volumes。這使得在生產Docker主機上使用這樣的設置不太理想,實際上我們只在Jenkins的本地開發環境中使用這種設置。

通過引入真正的volume支持,您可以創建、命名和管理volumes,這些volumes可以獨立於容器或包含大量集成命令的映像。volumes一直存在,直到您有意地從Docker主機中刪除它們,並且它們甚至與存儲插件集成在一起,以支持跨集群的共享數據volumes(如果您願意的話)。我更新了博客關於消除笨重的數據volumes容器,併為數據存儲定義和創建Docker卷。我想你會發現這種方法更加直觀。事實上,你們中的許多人都寫過,並提供反饋,認為這樣的轉換是長期需要的。

您可以在這裡閱讀關於Docker Volumes及其各種存儲特性的更多信息。

DOCKER NETWORKS

在2016年Docker的時代,pre Docker1.12,讓兩個容器相互通信,需要手動識別他們所暴露的端口,知道Docker主機的IP地址或使用Docker鏈接,這在同一個主機上是有效的。在最初的教程中,我建議為啟用Docker的虛擬機安裝檢索您的IP地址,並將其提供給幾個腳本,以便您的Jenkins安裝可以修改NGINX和內部configs等內容。它使得像“localhost”這樣的東西作為簡單的DNS引用幾乎是不可能的。這也意味著我們必須使用容器連接兩個容器,以便它們能夠很容易地找到彼此。

如今,Docker已經引入了Docker網絡。隨著Docker在Mac和Windows上的出現,Docker主機現在只使用機器的本地IP地址;容器鏈接被認為是過時的,並已被棄用。與Volumes一樣,網絡可以獨立地創建、命名和維護,而不需要任何容器或圖像。容器可以連接到網絡,即使是最簡單的Docker也提供服務發現,並通過DNS公開網絡中的所有容器。

這使我們能夠完全重新考慮教程和本地Jenkins設置的許多方面。為了使Jenkins和NGINX能夠彼此對話我取消了使用容器鏈接,而是將它們放在Docker網絡上。同樣,我重新配置了Jenkins配置中的構建Docker的設置,以便它們連接到相同的網絡,並且可以輕鬆找到Jenkins主服務器。

整個設置現在是自包含的,不再需要您提供一個IP地址。我們還可以消除在Jenkins配置中用於重命名字段的繁瑣啟動腳本。

新特性是廣泛的,並且在整個Docker群集群中工作,允許您輕鬆地在主機之間橋接容器。您可以在這裡閱讀更多Docker網絡特性。

DOCKER COMPOSE

在Docker Compose上也看到了許多變化。除了對Volumes和網絡的完全支持之外,還獲得了對Windows的完全原生的支持。這意味著,我現在幾乎只在創建多容器應用程序時使用Docker composer在我的桌面上運行。Compose使名稱變得超級簡單,並創建多個網絡、Volumes和“服務”(多容器應用程序)。規範文件也進行了一些修訂,現在已經在版本3上了。您將發現,我已經完全接受了Docker composer的最新版本,相關的配置文件都已更新。

您可以在這裡閱讀有關最新的Compose更改的更多信息。

DOCKER PROXYING

隨著對Docker進行如此多的更改以及切換到原生Docker安裝版,一個問題出現了。為了讓Jenkins安裝動態創建構建從機能夠和Docker容器一樣,它需要在系統桌面上與Docker對話,就好像它是Docker主機一樣。具有諷刺意味的是,當Docker主機在Virtualbox內的虛擬機中運行時,這相當簡單,因為它有自己的本地IP地址,並可配置為不安全的監聽端口2375或使用TLS證書監聽端口2376。舊的教程讓你通過獲得這些證書並安全地與該終端通信。

現在Dockerhost因為智能安全的原因不會將自己暴露給公共互聯網,而這種變化使Jenkins開發配置變得複雜,並在Windows和Mac上創造了獨特的挑戰。在OSX上,我選擇通過使用socat,通過Docker網絡上的端口2375(這仍然是不會暴露此文件到公開網絡的)來公開Docker套接字文件來解決此問題。這使得與Jenkins的配置通信變得更加容易。這個技巧在Windows上不起作用,因為沒有Docker套接字文件。因此,我選擇了暴露公開端口2375的作為僅在Windows上的選項。這從技術上意味著本教程中的Windows配置比OSX更不安全。

你將在上一篇教程中找到有關這些更改的詳細演練,其中的Jenkins安裝是完全針對臨時用例進行了優化。

其他DOCKER的變化

正如你所看到的,兩年來Docker發生了很多變化。在本教程系列中,您會發現許多小細節,例如Docker構建時間參數,標籤以及Dockerfiles中反映的其他小調整。其中有很多強大的新功能,但它們並沒有徹底改變教程的性質,所以我會把它們留給你自己去發現。

JENKINS變化

就像Docker一樣,Jenkins的時間也在不斷前進。當我寫這篇教程時,Jenkins 2剛剛上線。 Docker Plugin正在經歷非常早的迭代,代碼庫被分入了另一個Docker插件。最終Jenkins仍然像兩年前一樣工作,但是,Jenkins的Dockerfile設置已經發生了很多變化,配置和設置也發生了改變,它允許與Docker主機進行通信並構建從屬設備。

JENKINS版本

本教程現在使用Jenkins v2.112(撰寫本文時為最新版本)。大部分重大更改都在Dockerfile演練中,演示了Cloudbees如何設置Dockerfiles - 例如底層OS容器,Jenkins dockerfiles內部暴露的變量,啟動以及Java選項,還有腳本。

DOCKER PLUGIN(S)

這個空間中最大的變化來自插件,用於在構建從服務器動態地派生Docker容器。2016年,當我完成博客系列時,另一個Docker插件作為Docker插件的一個分支剛剛上線。從這個插件看到了持續的改進和新版本的誕生,而最初的Docker插件在大約一年的時間裡都沒有改變。Docker插件直到最近才被Cloudbees的開發人員復興,現在這兩個插件都有非常不同的配置。我已經進行了廣泛的測試,並且兩者都運行良好。可以說Docker插件在本教程中更容易設置,因此在整個教程中都是突出顯示的。

無論您使用哪個插件,我都主張放棄我在2016年寫過的SSH連接設置。相反,這兩個插件都提供了一種不同的方式來使用與Jenkins的反向JNLP連接,這將大大提高速度和性能。同樣,它使配置和設置臨時構建變得更容易。因此,本教程已經進行了更新,以消除SSH連接方法,並堅持使用更簡單的方法。

那麼你最終應該選擇哪個插件呢?在2016年,我開始喜歡另一個Docker插件,因為當涉及到Jenkins時,我總是喜歡正在開發中的插件。最近,隨著Cloudbees對Docker插件的支持,我懷疑這可能是最好的長期使用,我們的下一個升級將集中在最新的版本上。

更多已學到的經驗教訓

兩年對處理Riot Jenkins部署等大型複雜系統的運營問題來說是一個相當長的時間。很可能,我會忘記我們一路上所學的所有微小的事情。這就是說,我認為這裡有一些重要的經驗教訓值得一提。

規模

我們的Jenkins集群自發布第一篇文章以來就一直在增長。在之前文章撰寫過程中,我相信我們所描述的Docker Jenkins設置定義了大約1,000-2,000個作業,並且峰值為每小時以120-200個作業。現在我們的服務器平均每小時平均可以處理200-400個作業,峰值為600個左右,並且定義了超過7,000個作業。

這當然會給系統帶來很大的壓力。我們仍然在該服務器上使用0.15版本的Docker Plugin(我們將隨後升級),但我們必須在容器上進行多輪實例上限設定。我們的用戶開始對Jenkins非常熟練,並且會創建多個流程,同時啟用5個,10個甚至20個容器,以並行處理事務。較舊版本的插件無法處理所有這些情況,特別是在使用SSH時。我們發現立即構建環境實例數量控制在5到10之間對我們用例的最佳配置。

構建環境

在我們運行這個設置的兩年時間裡,我們的用戶創建了大約240個獨特的構建環境,最多可以有7000多個工作機會。經過培訓、教育、交談和反饋,這個數字已經縮減到150-180個左右。我們發現很多人在創建獨特的環境時,實際上他們可以使用通用的設置,比如Java或Go軟件開發。我們繼續提倡合併。我們發現,在Riot的各個團隊中,大多數不同的設置仍然存在於Python和NPM開發社區中,並且大部分由包管理和環境設置組成。

作為一種和平服務,我們為各種操作系統(Alpine, Ubuntu和Centos)提供基礎構建服務,因此使用我們工具的團隊可以與他們的包管理人員一起工作。除此之外,我們還提供Python、Java和Go從用例以及一組基本的實用程序腳本。我們得到了一個有力的教訓,那就是升級這些系統實際上是非常痛苦的。當Jenkins切換到使用Java 1.8的強制性需求時,我們必須在幾天內更新每個人的構建環境,在此期間許多團隊的構建都無法工作。對於擁有核心服務的團隊來說,這從來都不是一個好時機。

自從更新以來,我們提倡最小化基本構建環境中的內容。我們已經開始使用許多預先安裝的腳本和工具,並在運行時通過從服務器的簡單下載將它們安裝或安裝到容器中。在許多情況下,我們將實用程序轉移到共享Jenkins管道的Groovy庫(到目前為止,將代碼放到環境中是最容易/最快的選擇)。

JENKINS PIPELINE

在我們的Docker Jenkins配置的7,000個作業中,有近4400個是Jenkins管道作業。我們使用了Pipeline的Jenkins全局庫功能創建了一套通用管道函數。這些自定義庫中的大部分可以更容易地與各種Riot artifact商店和系統進行交互。有些只是通過最小化語法和使用常規默認值來使管道更容易使用。我將在不久之後寫一篇關於我們的通用函數的博客。

Pipeline作業突飛猛進的主要原因是我們開始構建我稱之為的“持續交付即服務”的部分。Riot的大部分後端軟件棧已經整合到Java或Go服務中,其中大多數使用相同的內核軟件庫。因此,我們要求團隊在代碼倉庫中放置了一個簡單的Jenkinsfile,其中僅包含這些構建的配置數據。我們使用Jenkins共享庫來使用這些文件,並使用通用管道來構建我們的軟件。因此,使用這些框架構建的團隊不需要構建工程師。實際上,團隊不需要編寫單個構建腳本來發布他們的軟件,因為即使他們的部署工作是自動化的。更多關於這個介紹將在未來的博客中!JENKINS重新開始並恢復工作

雖然情況越來越好,但是使用臨時構建環境的一個代價是缺少永久性的工作空間。這確實給許多Jenkins插件帶來了一個循環,因為他們期望一個公共的工作空間存在於一個永久的構建上。雖然這在一些地方影響了我們(特別是源代碼控制插件),但是最大的影響是Jenkins管道庫本身及其恢復功能。

Jenkins有一個方便的重新啟動特性,但是因為我們在瞬息萬變的環境中使用了太多的管道,所以工作進程無法重新啟動,如果我們不從磁盤上刪除所有的恢復文件,那麼重新啟動常常會使Jenkins無法使用。我們創建了一個自定義的Jenkins重新啟動過程,以禁用所有試圖在重新啟動後重新啟動的Jenkins作業。

從那以後,Cloudbees引入了管道耐久性,或者全局禁用管道的能力。他們還修復了幾個bug以提高Jenkins的穩定性。話雖如此,如果在重新開始之前,管道短暫的構建環境已經消失,那麼管道仍然不太可能恢復,所以emptor警告說,使用這種方法意味著構建恢復特性可能沒有那麼有用。

不友好的容器和鏡像設置

當我第一次寫博客時,我討論了這個問題,它仍然是值得注意的。 Docker 的插件在創建容器後嘗試清理時做得相當不錯,但容器在作業時會洩漏其他的容器。 尤其是長期運行的容器,我們必須調整我們的設置來消除。

因此,我們的 Docker Jenkins 環境在背景剔除正在運行的容器之前,最長運行時間約為24小時。 這使得該設置對於任何長時間運行作業(如性能測試)的任何人都不那麼有用,除非他們在不到24小時的時候變更 Jenkins 節點

一般的維護和自動化工作

雖然我們構建這個服務器是一個可擴展的軟件構建平臺,但是我們並沒有預料到它會有多受歡迎,因為一般的自動化團隊想要自動化各種維護過程或其他小的工作。

因為我們不需要團隊提供傳統的構建裝置,所以我們的Jenkins平臺成為一個有吸引力的選擇。Jenkins是一個強大的任務運行系統,因為它集成了源代碼控制和cron-like的調度功能。自動化作業是Jenkins服務器最主要的用途之一;Jenkins很容易實現從小數據ETL負載到跨幾十個系統的健康檢查的自動化。

自動化作業的負載配置文件與構建作業非常不同。當一個構建作業可能在工作時間提交源代碼時觸發時,自動化作業往往按照計劃運行,從每15分鐘到24小時不等。這些工作全天候24小時運行,為團隊創建維護窗口的挑戰。我們正在積極地考慮創建兩個Jenkins環境,一個用於自動化風格的工作,一個用於軟件構建。然而,我們還沒有設計出一種清晰的方法來執行該策略,如果不進行簡單的執行,它可能會給我們的Jenkins用戶造成一些迷惑。

結論

自2016年以來,看到這一體系的發展,是令人難以置信的收穫。隨著時間的推移,由於Docker特性的改進,基本的開發設置變得更容易維護和使用。Jenkins也變得更健壯了更容易的整合了。Jenkins管道的成熟使它成為我們目前在Riot所擁有的最強大的自動化和構建系統之一。

我們的未來充滿了這樣的問題,即我們的Jenkins設置將擴展到什麼程度,以及我們應該在什麼時候進行分片,以便我們能夠更容易地創建維護窗口和用戶導航。問題不再是“這行得通嗎?”但是“我們能推多遠?”

最初的嘗試是讓構建環境完全實現自服務,並消除自定義構建裝置的需要,而現在的系統已經開始消除自定義構建腳本和作業的需要,同時節省工程時間。一如既往,我熱切期待您在下面的評論中的反饋,經驗教訓,和想法。我會思考並且回答問題,並從您的Docker和Jenkins的經歷中學習。


分享到:


相關文章: