溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

AWS上云原生Jenkins的示例分析

發(fā)布時間:2021-12-22 16:31:21 來源:億速云 閱讀:203 作者:小新 欄目:云計算

這篇文章主要為大家展示了“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,以及我們一路走來的收獲。

Jenkins 狀態(tài)

當時不得不面對的關(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)等問題。

SCM 救援

過去,我們用了 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;事情漸漸成形。

Secrets

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)集成。

任務(wù)和 workspace 數(shù)據(jù)

問題從這一步開始變得棘手: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ù)不會一直增加。

Packer 和 Terraform 實現(xiàn)編碼化 Jenkins

你可能想知道這些是如何湊在一起的?我甚至沒說過在哪里運行 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)如下:architecture

能使用 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。

結(jié)語

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è)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(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)容。

AI