作者 | Magalix
譯者 | 火火醬 責編 | Carol
來源 | 架構師技術聯盟
封圖 | CSDN付費下載於IC photo
CI/CD要解決的是什麼問題?
CI/CD(CI全名Continuous Integration,持續集成;CD全名Continuous Deployment,持續部署)這個術語常常和DevOps、Agile、Scrum以及Kanban、自動化等其他術語一起出現。
有時,人們只將它看作是工作流的一部分,而沒有真正理解它是什麼或採用它的意義是什麼。年輕的DevOps工程師經常將CI/CD視作理所當然的事情,他們可能沒有見過“傳統的”軟件發佈週期,因此,Get不到CI/CD的好處。
CI/CD代表持續集成、持續交付和部署。未實現CI/CD的團隊在創建新軟件產品時必須經歷以下幾個階段:
產品經理(代表客戶的利益)提供產品應該具有的特性和應該實現的行為。該環節必須儘可能全面和具體。
開發人員藉助業務分析師的幫助,通過編寫代碼,運行單元測試並將結果提交到版本控制系統(例如Git)。
開發階段結束後,項目將被轉移到QA(Quality Assurance,質量保證)。針對產品運行幾個測試,比如用戶驗收測試、集成測試、性能測試等等。在此期間,在QA階段完成之前,不會對代碼庫進行任何更改。如果有任何bug,他們會返還給開發人員進行修復,然後修改後的產品將被再次交給QA。
QA結束後,操作團隊會將代碼部署到生產環境中。
但上述工作流程存在很多缺點:
首先,從產品經理提出要求到產品準備投入生產之間,需要花費很長的時間。
對於開發人員來說,要想解決一個月或更久以前編寫的代碼中的bug可不容易。要記得,只有在開發階段結束和QA階段開始之後才會發現bug。
當需要緊急更改代碼(比如需要熱修復的嚴重bug)時,因為需要儘快部署,所以往往會縮短QA階段。
由於不同的團隊之間的溝通協作很少,所以當bug發生時,人們就會開始相互指責。每個人都只會關心他自己負責的那部分,而忽略了共同的目標。
CI/CD通過引入自動化來解決上述問題。每次將代碼更改推送到版本控制系統後,都將進行測試,然後將其部署到stagingUAT環境,進行進一步的測試,之後才會將其部署到生產環境中供用戶使用。自動化能夠確保整個過程快速、可靠、可重複,並且減少出錯的概率。
既然如此,什麼是CI/CD?
一些相關書籍已經介紹得很清楚了。如何、為什麼以及何時在你的基礎架構中實現CI/CD。然而,我們總是喜歡少一點理論,多一點實踐。既然如此,我們將簡要描述在提交代碼更改後應該執行的自動化步驟:
持續集成(CI):第一步中不包括QA。換句話說,它並不關注代碼是否提供了客戶要求的特性。相反,它關注的是要確保代碼質量。通過單元測試、集成測試,可以將任何有關代碼質量的問題通知給開發人員。我們可以進一步通過代碼覆蓋率和靜態分析來強化測試,從而進一步保證代碼質量。
用戶驗收測試:這是CD流程中的第一部分。在此階段,將對代碼進行自動化測試,以確保其滿足客戶的期望。例如,一款web應用程序或許能夠正常運行,不引發任何錯誤,但是客戶希望訪客能夠在進入主頁之前先經過一個包含推廣的登陸頁。當前的代碼會將訪客直接帶到主頁面,這與客戶的實際要求是有偏差的,UAT測試會可以指出這類問題。在非CD環境中,該工作需要QA測試人員人工完成。
部署:這是CD流程中的第二部分。它涉及到對將承載應用程序的服務器/容器進行更改,使其能夠反映更新後的版本。這項工作應該以自動化的方式完成,最好是通過諸如Ansible、Chef或Puppet等配置管理工具來完成。
什麼是管道(Pipeline)?
管道是一個擁有簡單概念的高級術語。當你為實現一個共同目標,可能需要按照一定的順序執行很多個腳本,而這些腳本被統稱為“管道”。
例如,在Jenkins中,一個管道可能包含一個或多個必須全部完成才能實現成功構建的階段。使用多個階段能夠將整個過程可視化,瞭解每個階段所花費的時間,並清楚瞭解構建在何處失敗。
實驗室:為Golang App創建管道
在該實驗室中,我們將構建一個持續交付(CD)管道。我們使用的是一個用Go語言編寫的非常簡單的應用程序。為了簡單起見,我們僅對代碼運行一種類型的測試。該實驗的前提條件如下:
正在運行的Jenkins實例。可以是雲實例、虛擬機、裸機或docker容器。它必須能夠從網絡上進行公開訪問,以便存儲庫可以通過webhook連接到Jenkins。
鏡像註冊表:你可以使用Docker Registry,這是一種基於雲的產品,如ECR或GCR,甚至也可以使用自定義註冊表。
ECR:https://aws.amazon.com/ecr/
GCR:https://cloud.google.com/container-registry/
一個GitHub上的賬戶。雖然我們在本例中使用了GitHub,但是這個過程可以與其他存儲庫(比如Bitbucket)一樣進行細微的修改。
該管道可描述如下:
步驟一:應用文件
我們的示例應用程序將對任何GET請求作出“Hello World”響應。創建一個名為main.go的新文件,並在文件中添加以下內容:
由於我們要構建一個CD管道,所以應該進行一些測試。我們的代碼非常簡單,只需要一個測試用例即可,確保我們在點擊根URL時能夠收到正確的字符串。在相同目錄中創建一個名為main_test.go的新文件,並添加以下內容:
還有其他幾個文件可以幫助我們部署應用程序,這些文件名為:
The Dockerfile
我們在此對應用程序進行打包:
Dockerfile是一個多階段構建文件,它可以使鏡像儘可能得小一些。它開始於一個以golang:alpine為基礎的構建鏡像。生成的二進制文件將被用於第二個鏡像,這只是一scratch鏡像,不包含依賴項或庫,只包含啟動應用程序的二進制文件。
scratch: https://hub.docker.com/_/scratch/
The Service
由於我們使用Kubernetes作為承載此應用程序的平臺,因此至少需要一項服務和部署。我們的service.yml文件如下所示:
這沒有什麼特別之處。只是一個使用NodePort作為其類型的服務。它將監聽任何集群節點的IP地址上的32000端口。傳入的連接將中繼到8080端口上的吊艙(pod)。內部通信方面,服務將監聽80端口。
The deployment
一旦應用程序本身進行了docker化,就可以通過Deployment資源將其部署到Kubernetes。該deployment.yml文件如下所示:
這個部署定義最有趣的地方是鏡像部分。我們沒有硬編碼圖像名稱和標籤,而是使用了一個變量。稍後,我們將看到如何使用該定義作為Ansible的模板,並通過命令行參數替換鏡像名稱(以及部署的任何其他參數)。
The playbook
在該實驗中,我們使用Ansible作為部署工具。部署Kubernetes資源有許多種方法,包括Helm Charts,但我認為使用Ansible更簡單一些。Ansible使用playbook來組織其指令。我們的playbook.yml文件如下所示:
Ansible已經包含了用於處理與Kubernetes API服務器通信的k8s模塊。因此,我們不需要安裝kubectl,但是我們需要一個有效的kubeconfig文件來連接到集群(稍後會詳細介紹)。我們來快速瀏覽一下playbook:
k8s模塊: https://docs.ansible.com/ansible/latest/modules/k8s_module.html
kubectl: https://kubernetes.io/docs/reference/kubectl/overview/
playbook的主要功能是用於將服務和資源部署到集群。
因為我們需要在執行時將數據動態地注入到定義文件中,所以需要使用我們的定義文件作為模板,其中可以從外部提供變量。
為此,Ansible提供了查找功能,你可以在其中傳遞有效的YAML文件作為模板。Ansible支持多種將變量注入到模板的方法。在本特定實驗中,我們將使用命令行方法。
步驟二:安裝Jenkins、Ansible和Docker
現在,我們可以安裝Ansible,並通過它來自動部署一個Jenkins服務器和Docker運行時環境。我們還需要安裝openshift Python模塊以啟用與Kubernetes的Ansible連接。
Ansible的安裝過程非常簡單,只需安裝Python,然後使用pip安裝Ansible就可以了:
1. 登錄到Jenkins實例。
2. 安裝Python 3、Ansible和openshift模塊:
sudo apt update && sudo apt install -y python3 && sudo apt install -y python3-pip && sudo pip3 install ansible && sudo pip3 install openshift
3. 默認情況下,pip會將二進制文件安裝在用戶主文件夾中的一個隱藏目錄下。我們需要將此目錄添加到$PATH變量中,以便輕鬆調用命令:
echo "export PATH=$PATH:~/.local/bin" >> ~/.bashrc && . ~/.bashrc
4. 安裝部署Jenkins實例所需的Ansible角色:
ansible-galaxy install geerlingguy.jenkins
5. 安裝Docker角色:
ansible-galaxy install geerlingguy.docker
6. 創建一個playbook.yaml文件並添加以下內容:
7. 通過以下命令運行該playbook文件:
ansible-playbook playbook.yaml.
這裡要注意,我們實例使用的公共IP地址作為Jenkins使用的主機名。如果你使用的是DNS,那麼可能需要替換成實例的DNS名稱。另外,請注意,在運行playbook之前,必須在防火牆上啟用8080端口 (如果有的話)。
8. 幾分鐘後,Jenkins應該就安裝好了。你可以通過導航到計算機的IP地址(或者DNS名稱)並指定8080端口來進行檢查:
9. 單擊“登陸”,填寫“admin”作為用戶名和密碼。注意,這些是我們使用Ansible角色設置的默認憑據,在生產環境中使用Jenkins時,你可以(也應該)對其進行更改。可以通過設置角色變量來完成。詳情參見角色官方頁面。
官方頁面:https://galaxy.ansible.com/geerlingguy/jenkins
10. 你最後要做的是安裝將在實驗中使用到的以下插件:git、pipeline、CloudBees Docker Build和Publish、GitHub
步驟三:配置Jenkins用戶以連接到集群
如上所述,本實驗假設你已經安裝並運行了一個Kubernetes集群。為了使Jenkins能夠連接到這個集群,我們需要添加必要的kubeconfig文件。在本特定實驗中,我們將使用一個託管在Google Cloud上的Kubernetes集群,因此我們將使用gcloud命令。你的具體情況可能有所不同。但在所有情況中,我們都必須將kubeconfig文件複製到Jenkins的用戶目錄,如下所示:
請注意,你在此處使用的帳戶必須具有創建和管理部署(Deployments)和服務(Services)所需的權限。
步驟04:創建Jenkins Pipeline 作業
創建一個新的Jenkins作業並選擇管道類型。作業設置如下所示:
我們更改的設置是:
我們使用Poll SCM作為構建觸發器,此設置將會指導Jenkins定期檢查Git存儲庫(按*****指示的每分鐘檢查一次)。如果倉庫自上次poll以來已經更改過,那麼將觸發作業。
在管道本身,我們指定了存儲庫URL和憑據。分支是master。
在本實驗中,我們將作業的所有代碼添加到一個Jenkinsfile中,該文件與代碼存儲在相同的存儲庫中。本文稍後將討論該Jenkinsfile。
步驟五:為GitHub和Docker Hub配置Jenkins憑據
轉到/credentials/store/system/domain/_/newCredentials並將憑據添加到兩個目標中。確保提供有效的ID和描述,因為稍後將引用它們:
/credentials/store/system/domain/_/newCredentials:
http://35.238.224.64:8080/credentials/store/system/domain/_/newCredentials
步驟六:創建Jenkinsfile
Jenkinsfile指導Jenkins如何構建、測試、docker化、發佈並交付我們的應用程序。我們的Jenkinsfile如下所示:
pipeline {
agent any
environment {
registry = "magalixcorp/k8scicd"
GOCACHE = "/tmp"
}
stages {
stage('Build') {
agent {
docker {
image 'golang'
}
}
steps {
// Create our project directory.
sh 'cd ${GOPATH}/src'
sh 'mkdir -p ${GOPATH}/src/hello-world'
// Copy all files in our Jenkins workspace to our project directory.
sh 'cp -r ${WORKSPACE}/* ${GOPATH}/src/hello-world'
// Build the app.
sh 'go build'
}
}
stage('Test') {
agent {
docker {
image 'golang'
}
}
steps {
// Create our project directory.
sh 'cd ${GOPATH}/src'
sh 'mkdir -p ${GOPATH}/src/hello-world'
// Copy all files in our Jenkins workspace to our project directory.
sh 'cp -r ${WORKSPACE}/* ${GOPATH}/src/hello-world'
// Remove cached test results.
sh 'go clean -cache'
// Run Unit Tests.
sh 'go test ./... -v -short'
}
}
stage('Publish') {
environment {
registryCredential = 'dockerhub'
}
steps{
script {
def appimage = docker.build registry + ":$BUILD_NUMBER"
docker.withRegistry( '', registryCredential ) {
appimage.push
appimage.push('latest')
}
}
}
}
stage ('Deploy') {
steps {
script{
def image_id = registry + ":$BUILD_NUMBER"
sh "ansible-playbook playbook.yml --extra-vars \\"image_id=${image_id}\\""
}
}
}
}
}
文件構建起來比看上去容易。管道基本上包含四個階段:
1. 在Build階段構建Go二進制,並且確保在構建過程中沒有任何錯誤。
2. 在Test階段應用簡單的UAI測試來確保應用程序按預期工作。
3. 在Publish階段構建Docker鏡像,並將其推送到註冊表。此後,其可供任何環境使用。
4. 在Deploy階段調用Ansible來聯繫Kubernetes並應用定義文件。
現在,我們來討論一下此Jenkinsfile的關鍵部分。
前兩個階段大致相似。它們都用了Golang Docker鏡像來構建應用程序。讓階段在已經準備好所有必需的構建和測試工具的Docker容器中運行始終是一個好習慣。另一個選擇是在主服務器上或者從服務器上安裝這些工具。當你需要針對不同的工具版本進行測試時,問題就來了。例如,我們可能希望使用Go 1.9構建和測試我們的代碼,因為我們的應用程序還沒有準備好使用最新版本的Golang。鏡像中包含所有內容,因此更改版本甚至鏡像類型就變得像更改字符串一樣簡單。
Publish階段(從第42行開始)首先指定一個環境變量,該變量將在後面的步驟中使用。該變量指向在先前步驟中添加到Jenkins的Docker Hub憑據的ID。
第48行:我們使用docker插件來構建鏡像。它默認在註冊表中使用Dockerfile,並將構建編號添加為鏡像標籤。當你需要確定哪個Jenkins構建是當前正在運行的容器的源代碼時,這就變得非常重要。
第49-51行:鏡像構建成功之後,我們使用構建編號將其推入Docker Hub。此外,我們將“最新的”標籤添加到鏡像中(第二個標籤),這樣一來,如果用戶需要,就可以在不指定構建編號的情況下提取鏡像。
第56-60行: 在部署階段,我們將部署和服務定義文件應用到集群。我們使用之前討論過的playbook來調用Ansible。需要注意,我們將image_id作為命令行變量傳遞。此值將自動替換部署文件中的鏡像名稱。
測試我們的CD管道
本文的最後一部分進行了實例測試。我們將把代碼提交給GitHub,並確保我們的代碼能夠在管道中順利運行到集群:
1. 添加文件:
git add *
2. 提交更改:
git commit -m "Initial commit"
3.推送至GitHub:
git push
4. 在Jenkins上,我們可以等待作業自動觸發,也可以直接點擊“立刻構建(Build Now)”。
5. 如果作業運行成功,我們可以使用以下命令來檢查部署的應用程序。
6. 獲取節點IP地址:
7. 現在,讓我們嚮應用程序發起一個HTTP請求:
可以看到,我們的應用程序運行正常。下面,讓我們在代碼中故意犯一個錯誤,並確保管道不會將錯誤代碼發送到目標環境:
將應該顯示的消息更改為“Hello World!”(我們將每個單詞的第一個字母大寫,並在後面加上感嘆號)。因為我們的客戶可能不希望消息以這種方式顯示,所以管道應該在Test測試階段停止。
首先,我們進行了更改。現在,main.go文件應如下所示:
接下來,讓我們提交併推送代碼:
回到Jenkins,我們可以看到最後一次構建失敗了:
通過單擊失敗的作業,我們可以看到其失敗的原因:
該錯誤代碼永遠不會到達目標環境。
TL;DR
CI/CD是所有遵循Agile方法的現代環境的組成部分。
通過管道,你能夠確保代碼從版本控制系統到目標環境(testing/staging/production/etc.)的平穩過渡,同時應用所有必要的測試和質量控制實踐。
-
在本文中,我們進行了真實的實驗,構建了一個持續交付管道來部署Golang應用程序。
通過Jenkins,我們能夠從存儲庫中提取代碼,使用相關的Docker鏡像對其進行構建和測試。
接下來,我們對應用程序進行Docker化並將其推送到Docker Hub——因為它通過了我們的測試。
最後,我們使用Ansible將應用程序部署到運行Kubernetes的目標環境中。
使用Jenkins管道和Ansible令更改工作流變得非常簡單和靈活,幾乎不會產生什麼摩擦問題。例如,我們可以在Test階段中添加更多的測試,可以更改用於構建和測試代碼的Go版本,還可以使用更多的變量來更改部署和服務中的其他方面。
最棒的一點是我們使用了Kubernetes部署,這能夠確保在更改容器鏡像時,應用程序的停機時間為0。因為Deployments在默認情況下使用滾動更新方法來終止和重新創建容器。只有當新容器啟動並且運行狀況良好時,Deployment才會終止舊容器。
原文:https://hackernoon.com/how-to-create-a-cd-pipeline-with-kubernetes-ansible-and-jenkins-i6c03yp2
本文為 CSDN 翻譯,轉載請註明來源出處。
在全民抗疫的特殊時期下,在人員複雜、流動量大地方的出入口處都設置了無接觸式無感紅外人體測溫系統。
在這次疫情防控中,無感人體測溫系統發揮了怎樣的作用?高精準的無感人體測溫系統的核心技術武器是什麼?對於開發者們來說,大家應該瞭解哪些技術?
明晚7點《多場景疫情防控:解讀雲邊端聯動下的全棧 AI 技術應用》
閱讀更多 CSDN程序人生 的文章