您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“AWS上云原生Jenkins的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學習一下“AWS上云原生Jenkins的示例分析”這篇文章吧。
我們使用 Jenkins 搭建持續(xù)交付流水線,和其他很多團隊一樣,這些年我們圍繞 Jenkins 創(chuàng)建了很多工作流程和自動化。Jenkins 是我們團隊取得成功的關(guān)鍵,讓我們能夠在上一季度順利進入生產(chǎn)677次,搭建及部署時長平均為12分鐘。
我們的大部分應(yīng)用和基礎(chǔ)設(shè)施可以看作云原生,但當時 Jenkins 服務(wù)并不完全適合這個分類:服務(wù)在單個服務(wù)器上運行,同時很多任務(wù)直接在 master 上運行,其部分手動配置包括 secret、插件、定時任務(wù)和 startup hacking 的普通膨脹,該膨脹是自2014年首次搭建起不斷累積而成。
Jenkins 不僅變成了單體服務(wù)和單點故障,而且拆除及重建 Jenkins 對企業(yè)也是很大的風險。
我們決定必須做出改變。這篇博客說明了我們?nèi)绾芜\用 Terraform、Packer、Docker、Vault、和 ELB、ASG、ALB 或 EFS 等 AWS 服務(wù)實現(xiàn) Jenkins Cloud-native,以及我們一路走來的收獲。
當時不得不面對的關(guān)鍵問題是:如果我們將 Jenkins 服務(wù)置于一個容器/自動縮放實例中,我們需要恢復(fù)何種狀態(tài)?
問題的答案并不簡單,值得一提的是,有個 Jenkins 特別興趣小組(SIG)已經(jīng)識別出所有導(dǎo)致這一 Jenkins 狀態(tài)的存儲組件。這是一個很棒的起點,因為我們至少得確保那篇文章列出的所有存儲類型都考慮在內(nèi)。
這不是新問題。很多團隊使用 Docker 容器運行 Jenkins,官方 Jenkins Docker 鏡像也得到良好維護。如《Jenkins Dokcer 鏡像》文檔中解釋的:
docker run -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
這會把 workspace 存在 /var/jenkins_home。所有的 Jenkins 數(shù)據(jù)(包括插件和配置)都存在上述目錄里。創(chuàng)建一個明確的 volume 可以方便管理和附加到另一個容器進行升級。
上述示例裝載主機上的 jenkins_home,其中包括所有 Jenkins 狀態(tài)。然后該目錄可以存在一個外部磁盤上,比如 Kubernetes 持久化存儲卷。或者,如果 Jenkins 在 EC2 上運行,該目錄可存在一個外部 EBS 或 EFS 卷上。
這是一種有效的方法,但我們認為這個方法不能達到我們的標準,因為 jenkins_home 不僅包括狀態(tài),還包括配置。Block storage 擁有大量用戶案例,但一個小小的配置修改就必須進行 snapshot 恢復(fù)操作,這似乎并不算是好的解決方案。此外,我們并不是想轉(zhuǎn)移問題:外部存儲無法免去手動配置、憑據(jù)儲存在文件系統(tǒng)等問題。
過去,我們用了 Jenkins 備份插件,該插件基本上把配置修改備份在源碼控制里,允許配置恢復(fù)。這個插件的設(shè)計想法很棒,但我們決定不使用它,因為我們無法輕松控制哪些數(shù)據(jù)實現(xiàn)備份,而且該插件自2011年就沒有任何更新了。
這樣的話,如果我們把 jenkins_home 創(chuàng)建成個人 Git repo,并自動提交對 Jenkins 所做的修改呢?此處的關(guān)鍵是排除單獨儲存的任何二進制文件、secrets 或大型文件(稍后詳細介紹)。我們的 .gitignore 文件如下所示:
/.bash_history /.java/ /.kube/ /.ssh/ /.viminfo /identity.key.enc /jobs/ /logs/ /caches/ # Track static worker and exclude ephemeral ones /nodes/** !nodes/static-node/config.xml /org.jenkinsci.plugins.github_branch_source.GitHubSCMProbe.cache/ /plugins/ /saml-idp-metadata.xml /saml-jenkins-keystore.jks /saml-jenkins-keystore.xml /saml-sp-metadata.xml /scm-sync-configuration/ /scm-sync-configuration.success.log /secret.key /secret.key.not-so-secret /secrets/ /updates/ /workspaces/
幾乎所有的純文本配置都正在 Git 實現(xiàn)持久化。為了給 Jenkins 提供這一配置,我們要做的就是檢查 startup 上的 repo;事情漸漸成形。
Jenkins 要訪問很多地方,也就是說我們需要一個安全的 secret 存儲空間。因為我們是 HashiCorpVault 的重度用戶,所以自然而然就選了這個工具,不過遺憾的是,Vault 無法涵蓋所有場景。比如,scm-branch-source 流水線插件需要 SCM 的認證憑據(jù),并默認為 Jenkins 憑據(jù)插件。每次從 Vault 動態(tài)檢索這些,我們都需要同步一個倉庫,這可能導(dǎo)致錯誤,也會需要額外的精力去維護。
這就是為什么我們采用 Vault 與 Jenkins 憑據(jù)混合的方法: 1. 在 startup 實例中,Jenkins 進行認證,VAult采用 IAM 認證方法。 2. 一個引導(dǎo)腳本檢索 Jenkins master.key 和憑據(jù)插件所用的其他加密密鑰。更多詳情請參閱這篇文章。 3. 儲存在 jenkins_home/credentials.xml 上的憑據(jù)現(xiàn)在可由 Jenkins 解密和訪問。
用 Vault 完全取代憑據(jù)插件是我們未來可能探索的問題,不過我們很開心這個方法滿足了安全性要求, 同時能輕松與 Jenkins 的其余功能實現(xiàn)集成。
問題從這一步開始變得棘手:jenkins_home/jobs and jenkins_home/workspaces 都含有介于非結(jié)構(gòu)化數(shù)據(jù)、創(chuàng)建制品和純文本之間的混合體。這個信息很有價值,可以幫助我們審計、理解之前的流水線 build。這些 build 尺寸很大,而且不太適合 SCM 同步,因此這兩個目錄都排除在 .gitignore 之外了。
那我們把這些儲存在哪兒呢?我們認為 block storage 最適合存儲這種數(shù)據(jù)。作為 AWS 的重度用戶,使用 EFS 完全說得通,因為 EFS 的文件存儲可擴展、可用性高并可以通過網(wǎng)絡(luò)訪問,非常易于使用。我們使用 Terraform 整合了 AWS EFS資源,并用 AWS 備份服務(wù)制定了一份定期備份計劃。
在 startup,我們將 EFS 卷 、符號鏈接 jenkins_home/jobs 和 jenkins_home/workspaces 裝載到 EFS 目錄上,然后啟動 Jenkins 服務(wù)。
接下來,Jenkins 服務(wù)是唯一可以讀寫任務(wù) /workspace 數(shù)據(jù)的界面。值得一提的是,我們有一個 Jenkins 任務(wù)定期刪除幾周前的任務(wù)和 workspace 數(shù)據(jù),這樣數(shù)據(jù)不會一直增加。
你可能想知道這些是如何湊在一起的?我甚至沒說過在哪里運行 Jenkins!我們廣泛使用 Kubernetes,花了一些時間思考將 Jenkins 作為容器來運行,可我們決定使用 Packer 和 EC2 來運行 Jenkins master,用短暫 EC2 實例運行這些任務(wù)。
盡管將 master 和 worker 雙雙作為容器運行的想法很有用,但我們在當前 Kubernetes 集群里沒有找到存儲 Jenkins 的地方。而且只是為了 Jenkins 就新建一個集群似乎有點兒“殺雞用牛刀”。此外,我們想保留從其余服務(wù)中解耦的基礎(chǔ)設(shè)施的關(guān)鍵部分。這樣的話,如果 Kubernetes 升級對我們的 app 有影響,我們希望至少可以運用 Jenkins 進行回滾。 運行“Docker in Docker”還有另一個問題,這個問題有解,不過還是需要說明一下,因為我們的 build 經(jīng)常用到 Docker 命令。
其體系架構(gòu)如下:
能使用 EC2 實例讓過渡更順暢:我們當時通過 Jenkins EC2 插件用臨時 worker node 運行流水線工作,并在聲明式流水線代碼上調(diào)用了這一邏輯,所以不必重構(gòu)就能用 Dokcer 代理節(jié)點是一個加分項。其余工作就是 Packer 和 Terraform 代碼,這是我們已經(jīng)很熟悉的部分了。
因為插件也是狀態(tài)!我們在這個項目里想要解決的問題之一就是更好地審計、管理插件。在手動場景中,插件管理可能不受控制,很難了解安裝插件的時間和原因。
大多數(shù) Jenkins 級別的插件配置可以在常規(guī) Jenkins 配置 xml 文檔中找到,但安裝插件也導(dǎo)致 jar 制品、元數(shù)據(jù)、圖片和其他文件存在 jenkins_home/plugin 目錄。
一種方法是在 EFS 中存儲插件,不過我們想將 EFS 使用率保持在最低水平,這無法解決問題,只是轉(zhuǎn)移問題。這就是為什么我們選擇對插件安裝進行“Packer 化”。
基本上,在我們的 AMI 定義中,有一個插件文件羅列了插件和版本,大致如下:
# Datadog Plugin required to send build metrics to Datadog datadog:0.7.1# Slack Plugin required to send build notifications to Slack slack:2.27
然后,我們的 AMI provision 腳本解析該文件,用 Jenkins CLI 安裝插件和所選版本:
# Wrapper function for jenkins_cli jenkins_cli() { java -jar "$JENKINS_CLI_JAR" -http -auth "${user}:${pw}" "$@" }for plugin in "${plugins[@]}"; do echo "Installing $plugin" jenkins_cli install-plugin "$plugin" -deploy done
然后,任何需要安裝的新插件或升級到當前安裝版本的版本升級都需要 GitHub Pull Request,這會觸發(fā)搭建新 AMI。完美!
根據(jù)定義,Jenkins 要安裝很多軟件才能創(chuàng)建、測試和部署。首先,我們不想讓 master node 運行任何任務(wù),所以我們避免安裝任何與任務(wù)相關(guān)的軟件。Master 的主要任務(wù)是在其他短暫 worker node 上提供界面、編排 builds。
這意味著我們可以在 worker node 上安裝所需工具,但我們決定盡可能多地使用 docker run。這是因為我們是使用 Scala、Java、Node、Golang、Python等其他編程語言的多語言組織。為所有這些軟件棧維護不同 build 工具可能讓 worker node 設(shè)置變得有點兒復(fù)雜。
以 JavaScript 為例,我們想讓 Jenkins 針對 install 和 test 等 app 運行 yarn 命令。簡單將加載檢查過的 repo 目錄作為一個 volume 安裝到 Docker 容器里,從該容器中運行任何命令。以下為運用 Groovy 工作流代碼的例子:
def node(command, image) { def nodeCmd = [ 'docker run -i --rm', '-u 1000', // Run as non-root user '-v ~/.npmrc:/home/node/.npmrc:ro', '-v ~/.yarn:/home/node/.yarn', '-e YARN_CACHE_FOLDER=/home/node/.yarn/cache', "-v ${env.WORKSPACE}:/app", '--workdir /app', "${image}" ].join(' ') sh "${nodeCmd} ${command}" }
然后,我們檢查倉庫后可以調(diào)用這個功能:
checkout scm node('yarn install --frozen-lockfile', 'node:12.6.0-alpine')
漂亮收尾!因為除了 Docker 后臺程序或 kubectl,我們不必在 worker machine 上安裝、維護所用工具的多個版本。我們也相信 build 命令在本地和 CI 環(huán)境之間是一致的,因為用的是同一個 Docker 鏡像。
運用臨時 node 創(chuàng)建時要記得緩存依賴。比如,一個 worker node 重建后,我們丟失了 sbt 緩存,由于緩存必須重建,這導(dǎo)致創(chuàng)建時間變慢。如果外部依賴不可用,這甚至會導(dǎo)致失敗。我們決定將相關(guān)依賴緩存在另一個外部 EFS 上,以求獲得更快、更可靠的 build。
Jenkins 是一個很棒的工具,但在管理外部狀態(tài)上略有不足,因此以 cloud native 的方式創(chuàng)建 Jenkins 較有難度。我們的方法并不完美,但我們相信這個方法結(jié)合了兩者的精華,而且確保安全性、操作簡單、有彈性。令人高興的是,我們完成這個項目,并把所有的生產(chǎn) build 遷移到新的 Jenkins 服務(wù)之后,可以終止 master server,讓自動縮放在幾分鐘內(nèi)完成重建,而不會影響以前儲存的狀態(tài)。
以上是“AWS上云原生Jenkins的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。