實例演示:如何在Kubernetes上大規模運行CI/CD

本週四晚上8:30,第二期k3s在線培訓如約開播!本期課程將介紹k3s的核心架構,如高可用架構以及containerd。一起來進階探索k3s吧!

報名及觀看鏈接:http://z-mz.cn/PmwZ
 

本文來自Rancher Labs
 
在雲原生領域中,Kubernetes累積了大量用例。它能夠在雲中部署應用容器、安排批處理job、處理工作負載以及執行逐步升級。Kubernetes使用高效的編排算法來處理這些操作,即便是大規模集羣這些算法依舊錶現良好。

 
此外,Kubernetes主要用例之一是運行持續集成或持續交付(CI/CD)流水線。也就是說,我們部署一個CI/CD容器的唯一實例,該實例將監控代碼版本控制系統。所以,每當我們推送到該倉庫時,該容器都會運行流水線步驟。其最終目標是達到一個“true or false”的狀態。True即在集成階段commit通過了各種測試,False即未通過測試。

 

除了以上描述的CI流水線之外,在CI測試通過之後,另一個流水線可以接管餘下的過程,以處理髮布過程的CD部分。在這一階段,流水線將嘗試將應用程序容器交付到生產中。

 

需要明白的是,這些操作是按需運行或者是由各種行爲(如代碼check-in、測試觸發器、流程中上一步的結果等)自動觸發的。因此我們需要一種機制來增加單個節點以運行那些流水線的步驟,並在不需要它們時將其淘汰。這種管理不可變基礎架構的方法有助於我們節省資源並降低成本。

 

當然,最關鍵的機制就是Kubernetes,它具有聲明式的結構和可定製性,因此可以讓你在任何場景下高效地調度job、節點以及pod。

 

本文包括3個部分:第一部分我們將探討目前在Kubernetes上運行最受歡迎的CI/CD平臺。

 

接着我們將會看兩個用例:第一個例子中,我們將簡單地在Kubernetes上安裝Jenkins以及對其進行配置以讓我們可以在Kubernetes上使用這個流行的開源工具來運行我們的CI流水線;第二個例子中,我們將把這個Jenkins部署提高到一個新的水平。我們將會提供一些在Kubernetes中擴展CI/CD流水線的tips和建議。

 

最後,我們將會討論在Kubernetes上大規模運行CI/CD的最合理的方法和實踐。
 

本文的目標是讓你徹底瞭解Kubernetes處理這些工作負載的效率。
 
實例演示:如何在Kubernetes上大規模運行CI/CD
 

適用於Kubernetes的CI/CD平臺

 

Kubernetes是一個運行CI/CD的理想平臺,因爲它擁有許多特性使得在上面運行CI/CD更爲簡單。那麼,到底有多少CI/CD的平臺可以在Kubernetes上運行呢?可以這麼說,只要它們能夠被打包爲一個容器,Kubernetes都能夠運行它們。以下是幾個最爲流行的CI/CD平臺:

 

  • Jenkins:Jenkins是最爲流行也最爲穩定的CI/CD平臺。在世界範圍內有數以千計的企業都在使用它,因爲它擁有強大的生態和可擴展性。如果你打算要在Kubernetes上使用它,非常建議你安裝它的官方插件。JenkinsX是專門爲雲原生領域設計的Jenkins版本。它與Kubernetes更加兼容,並且提供了更好的集成功能,如GitOps、自動CI/CD和預覽環境。
     
  • Spinnaker:Spinnaker是一個可擴展的多雲部署的CD平臺,得到了Netflix的支持。使用相關的Helm Chart即可安裝它。

    https://github.com/helm/charts/tree/master/stable/spinnaker
     

  • Drone:這是有多種功能的通用雲原生CD平臺。可以使用關聯的Runner在Kubernetes中運行它。
     
  • GoCD:Thoughtworks的另一個CI/CD平臺,提供了適用於雲原生部署的各種工作流程和功能。它可以在Kubernetes中作爲Helm Chart運行。

 

此外,還有一些與Kubernetes緊密合作的雲服務,並提供諸如CircleCI和Travis的CI/CD流水線。如果你不打算託管CI/CD平臺,那麼這些也十分有用。

 

現在,我們來看看如何在Kubernetes集羣上安裝Jenkins。
 

如何在Kubernetes上安裝Jenkins

 

首先,我們需要安裝Helm,它是Kubernetes的軟件包管理器:

 

$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 > get_helm.sh
$ chmod 700 get_helm.sh
$ ./get_helm.sh -v v2.15.0

 
同樣,我們還需要安裝Tiller,以讓Helm正常運行:
 

$ kubectl -n kube-system create serviceaccount tiller
serviceaccount/tiller created

~/.kube
$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
clusterrolebinding.rbac.authorization.k8s.io/tiller created

~/.kube
$ helm init --service-account tiller
$HELM_HOME has been configured at /Users/itspare/.helm.

 
完成這些步驟之後,我們需要運行檢查命令,以查看deployment的配置值:
 

$ helm inspect values stable/jenkins > values.yml

 
仔細檢查配置值並在需要的時候進行更改。然後安裝Chart:
 

$ helm install stable/jenkins --tls \
--name jenkins \
--namespace jenkins

 
安裝過程中會有一些關於下一步操作的說明:

 

注意:

 

  1.  運行以下命令獲取”admin“用戶的密碼:
     
    printf $(kubectl get secret --namespace default my-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo

     

  2. 在相同的shell中獲取Jenkins URL以訪問這些命令:
     
    export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=my-jenkins" -o jsonpath="{.items[0].metadata.name}")
    echo http://127.0.0.1:8080
    kubectl --namespace default port-forward $POD_NAME 8080:8080

     
    遵循這些步驟,它們將在http://127.0.0.1:8080 啓動代理服務器。
     

到那裏輸入你的用戶名和密碼。你將會擁有自己的Jenkins 服務器:
 
實例演示:如何在Kubernetes上大規模運行CI/CD
 
不過,請記住,還有許多配置選項尚未修改,你可以訪問chart文檔以瞭解更多信息:
 
https://github.com/helm/charts/tree/master/stable/jenkins

 

在默認情況下,服務器會安裝好最基本的插件,如Git和Kubernetes-Jenkins,我們可以根據自己的需要安裝其他插件。

 

總而言之,使用Helm安裝Jenkins十分輕鬆。
 

使用K8S擴展CI/CD Jenkins流水線

 

既然我們已經大致瞭解CI/CD如何在Kubernetes上運行的,那麼我們來看一個在Kubernetes中部署高度可擴展的Jenkins部署的示例用例。人們通常用它(進行了少量修改)來處理基礎結構的CI/CD,開始吧!

 

使用Jenkins固定發行版

 

雖然官方Jenkins鏡像很適合入門,但它需要的配置超出了我們的期望。許多用戶會選擇一個固定的發行版,如my-bloody-jenkins(https://github.com/odavid/my-bloody-jenkins ),它提供了一個較爲完整的預安裝插件以及配置選項。在可用的插件中,我們使用saml插件、SonarQubeRunner、Maven和Gradle。

 

它能夠使用以下命令通過Helm Chart安裝:
 

$ helm repo add odavid https://odavid.github.io/k8s-helm-charts
$ helm install odavid/my-bloody-jenkins

 
我們選擇使用以下Dockerfile部署自定義鏡像:
 

FROM odavid/my-bloody-jenkins:2.190.2-161

USER jenkins

COPY plugins.txt /usr/share/jenkins/ref/
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt

USER root

 
其中plugins.txt文件是我們要預安裝到鏡像中的其他插件列表:
 

build-monitor-plugin
xcode-plugin
rich-text-publisher-plugin
jacoco
scoverage
dependency-check-jenkins-plugin
greenballs
shiningpanda
pyenv-pipeline
s3
pipeline-aws
appcenter
multiple-scms
Testng-plugin

 
然後,只要dockerfile發生更改,我們就使用此通用Jenkinsfile來構建master:
 

#!/usr/bin/env groovy

node('generic') {
try {

def dockerTag, jenkins_master

stage('Checkout') {
checkout([
$class: 'GitSCM',
branches: scm.branches,
doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations,
extensions: [[$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']],
userRemoteConfigs: scm.userRemoteConfigs,
])

def version = sh(returnStdout: true, script: "git describe --tags `git rev-list --tags --max-count=1`").trim()
def tag = sh(returnStdout: true, script: "git rev-parse --short HEAD").trim()
dockerTag = version + "-" + tag
println("Tag: " + tag + " Version: " + version)
}

stage('Build Master') {
jenkins_master = docker.build("jenkins-master", "--network=host .")
}

stage('Push images') {
docker.withRegistry("https://$env.DOCKER_REGISTRY", 'ecr:eu-west-2:jenkins-aws-credentials') {
jenkins_master.push("${dockerTag}")
}
}

if(env.BRANCH_NAME == 'master') {

stage('Push Latest images') {
docker.withRegistry("https://$env.DOCKER_REGISTRY", 'ecr:eu-west-2:jenkins-aws-credentials') {
jenkins_master.push("latest")
}
}

stage('Deploy to K8s cluster') {
withKubeConfig([credentialsId: 'dev-tools-eks-jenkins-secret',
serverUrl: env.TOOLS_EKS_URL]) {
sh "kubectl set image statefulset jenkins jenkins=$env.DOCKER_REGISTRY/jenkins-master:${dockerTag}"
}
}
}
currentBuild.result = 'SUCCESS'
} catch(e) {
currentBuild.result = 'FAILURE'
throw e
}
}

 
我們所使用的專用集羣由AWS中的一些大中型實例組成,用於Jenkins jobs。接下來,我們進入下一個部分。
 

使用專用的Jenkins Slaves和標籤(label)

 

爲了擴展我們的一些Jenkins slaves,我們使用Pod模板並將標籤分配給特定的agent。因此在我們的Jenkinsfiles中,我們可以爲jobs引用它們。例如,我們有一些需要構建安卓應用程序的agent。因此,我們引用以下標籤:
 

pipeline {
agent { label "android" }
…

 
並且將使用特定於安卓的pod模板。我們使用這一Dockerfile,例如:
 

FROM dkr.ecr.eu-west-2.amazonaws.com/jenkins-jnlp-slave:latest

RUN apt-get update && apt-get install -y -f --no-install-recommends xmlstarlet

ARG GULP_VERSION=4.0.0
ARG CORDOVA_VERSION=8.0.0

# SDK version and build-tools version should be different
ENV SDK_VERSION 25.2.3
ENV BUILD_TOOLS_VERSION 26.0.2
ENV SDK_CHECKSUM 1b35bcb94e9a686dff6460c8bca903aa0281c6696001067f34ec00093145b560
ENV ANDROID_HOME /opt/android-sdk
ENV SDK_UPDATE tools,platform-tools,build-tools-25.0.2,android-25,android-24,android-23,android-22,android-21,sys-img-armeabi-v7a-android-26,sys-img-x86-android-23
ENV LD_LIBRARY_PATH ${ANDROID_HOME}/tools/lib64/qt:${ANDROID_HOME}/tools/lib/libQt5:$LD_LIBRARY_PATH/
ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools

RUN curl -SLO "https://dl.google.com/android/repository/tools_r${SDK_VERSION}-linux.zip" \
&& echo "${SDK_CHECKSUM} tools_r${SDK_VERSION}-linux.zip" | sha256sum -c - \
&& mkdir -p "${ANDROID_HOME}" \
&& unzip -qq "tools_r${SDK_VERSION}-linux.zip" -d "${ANDROID_HOME}" \
&& rm -Rf "tools_r${SDK_VERSION}-linux.zip" \
&& echo y | ${ANDROID_HOME}/tools/android update sdk --filter ${SDK_UPDATE} --all --no-ui --force \
&& mkdir -p ${ANDROID_HOME}/tools/keymaps \
&& touch ${ANDROID_HOME}/tools/keymaps/en-us \
&& yes | ${ANDROID_HOME}/tools/bin/sdkmanager --update

RUN chmod -R 777 ${ANDROID_HOME} && chown -R jenkins:jenkins ${ANDROID_HOME}

 
我們還使用了Jenkinsfile,該文件與上一個文件類似,用於構建master。每當我們對Dockerfile進行更改時,agent都會重建鏡像。這爲我們的CI/CD基礎架構提供了極大的靈活性。
 

使用自動伸縮

 

儘管我們爲deployment分配了特定數量的節點,但我們還可以通過啓用cluster autoscaling,來完成更多的事情。這意味着在工作負載增加和峯值的情況下,我們可以增加額外的節點來處理job。目前,如果我們有固定數量的節點,那麼我們只能處理固定數量的job。基於以下事實,我們可以進行粗略地估計:每個slave通常分配500ms CPU和256MB內存,並且設置一個很高的併發。這根本不現實。

 

舉個例子,當你的版本被大幅削減並且需要部署大量微服務時,可能會發生上述情況。然後,大量的job堆積在流水線,造成嚴重的延誤。

 

在這種情況下,我們可以增加該階段的節點數。例如,我們可以添加額外的VM實例,然後在過程結束時將其刪除。

 

我們可以在命令行中使用自動伸縮選項來配置“Vertical”或“集羣”自動伸縮選項。但是,此方法需要仔細計劃和配置,因爲有時會發生以下情況:

  

  1. 越來越多的job達到平穩階段

  2. Autoscaler增加新的節點,但是需要10分鐘來進行部署和分配

  3. 舊的job已經完成任務,新的job將填補空白,進而減少了對新節點的需求

  4. 新節點可用,但需要X分鐘保持穩定且未利用,X由–scale-down-unneeded-time標誌定義

  5. 同樣的事情每天發生很多次

     

在這種情況下,最好是根據我們的特定需求進行配置,或者只是增加當天的節點數,並在流程結束後將其還原。所有這些都與尋找最佳方法來利用所有資源並使成本最小化有關。

 

在任何情況下,我們都應該有一個可伸縮且易於使用的Jenkins集羣。對於每個job,都會創建一個pod來運行特定的流水線,並在完成後將其銷燬。
 

大規模使用K8s進行CI / CD的最佳實踐

 

現在我們已經瞭解了Kubernetes有哪些CI/CD平臺以及如何在你的集羣上安裝一個平臺。接下來,我們將討論一些大規模運行它們的方法。

 
首先,選擇Kubernetes發行版是我們需要考慮的最關鍵因素之一。找到最合適的解決方案才能夠進行下一步。

 
其次,選擇合適的Docker鏡像倉庫和應用程序包管理器同樣重要。我們需要尋找可以按需快速檢索的安全可靠的鏡像管理。至於軟件包管理器,Helm是一個不錯的選擇,因爲它可以發現、共享和使用爲Kubernetes構建的軟件。

 

第三,使用現代集成流程,如GitOps和ChatOps,在易用性和可預測性方面提供了顯著優勢。將Git用作單一數據源,使我們可以運行“通過拉取請求進行操作”,從而簡化了對基礎架構和應用程序的部署控制。使用諸如企業微信或釘釘之類的團隊協作工作來觸發CI/CD流水線的自動化任務,有助於我們消除重複勞動並簡化集成。

 

總體而言,如果我們想更深入地瞭解,你可以自定義或開發自己的K8S Operator,與K8S API配合更緊密。使用自定義operator的好處很多,因爲它們可以建立更好的自動化體驗。
 

最後,我們可以說Kubernetes和CI/CD平臺是天合之作。如果你剛剛入門Kubernetes生態系統,那麼你可以嘗試集成一個CI/CD流水線。這是瞭解Kubernetes內部運作方式的好方法,關鍵是要留出機動空間,方便將來容易更改。

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