--- tags: notes --- # 學習 Jenkins Jenkins 是由 java 撰寫的 CI/CD 工具,可以透過網頁 UI 來設定編譯、佈署的參數,並觸發整個 CI/CD 流程。除了透過手動觸發外還提供了 Cron-Style 排程、透過 git push 時觸發、透過 webhook 觸發。 ## 安裝 按照官方教學,先安裝 openjdk-17-jre,然後從 jenkins 的 PPA 下載 jenkins,它會自動透過 systemd 執行在 localhost:8080。首次進入時選擇安裝官方推薦的 plugins,裡面包含 git、pipelines 插件。 ## Lab 1: Free-Style 佈署 docker-flask 首先要把 jenkins 使用者加入 docker 群組並重新啟動,否則它將無法執行 docker 指令: ``` $ sudo usermod -aG docker jenkins $ sudo systemctl restart jenkins ``` 新增一個 free-style 專案,隨意命名該專案。 然後設定用 https://github.com/shekhargulati/python-flask-docker-hello-world 來拉取原始碼。 ![](https://i.imgur.com/HOnLw7H.png) 接著在 Build Step 中新增 `執行 shell` 輸入以下指令: ``` docker rm -f flask-app docker build -t simple-flask-app:latest . docker run --name flask-app -d -p 5000:5000 simple-flask-app ``` ![](https://i.imgur.com/qI2S1d3.png) 最後回到專案頁面,按下 `馬上建置` 即可佈署一個簡單的 flask container,在終端機中用以下指令測試: ``` $ curl localhost:5000 Flask inside Docker!! ``` ## Lab 2: Free-Style 帶參數建置 在 Lab 1 的專案中找到 `參數化建置` 新增一個 `PORT` 字串參數,代表 flask app 的需要被導向的連接埠。 ![](https://i.imgur.com/sGlLp2b.png) 然後將`執行 shell` 的指令改為: ``` docker rm -f flask-app docker build -t simple-flask-app:latest . docker run --name flask-app -d -p $PORT:5000 simple-flask-app ``` 接著就可以在 `帶參數建置` 中指定想要使用的連接埠,而不是固定使用 5000。 ## Lab 3: Pipeline 首先新增一個 Pipline 專案,然後按照 Lab 2 新增一個 `PORT` 參數。 接下來會有一個 Pipeline 區塊取代原本 free-style 專案中的 Build Step 區塊,這邊需要透過 Groovy 語法來定義 Pipeline 中的步驟。如果對 Groovy 語法不熟,可以點擊 **Pipeline Syntax** 來自動將一些常用功能轉換為 Groovy 腳本。 在 Pipeline 區塊填入以下腳本: ```groovy pipeline { agent any stages { stage('Pull') { steps { git 'https://github.com/shekhargulati/python-flask-docker-hello-world.git' } } stage('Docker Run') { steps { sh '''docker rm -f flask-app docker build -t simple-flask-app:latest . docker run --name flask-app -d -p $PORT:5000 simple-flask-app''' } } stage('Smoke Test') { steps { sh 'curl localhost:$PORT' } } } } ``` 儲存好專案後,按下帶參數建置,就會根據 stages 產生以下表格: ![](https://i.imgur.com/0AytFRl.png) ## Lab 4: 透過 Git 管理 Jenkinsfile (Multibranch Pipeline) 透過 Git 管理 Jenkinsfile 有許多好處:首先 Jenkinsfile 由 git 存放,所以當 Jenkins Server 壞掉時不怕 Pipeline 的腳本也一起搞丟。再者,可以透過版控系統維護 Pipeline,在新版本 Pipline 上線之前可以透過 git branch 來區別不同的版本進行測試。 這次我 fork 先前 Lab 中所使用的 Repo,並且在專案的根目錄新增了一個 Jenkinsfile 在 https://github.com/blueskyson/python-flask-docker-hello-world.git Jenkinsfile 的內容如下,可以注意到比起 Lab 3 的腳本多一個 parameter 區塊: ```groovy pipeline { agent any parameters { string(name: 'PORT', defaultValue: '5000', description: 'Forwarded Port') } stages { stage('Pull') { steps { git 'https://github.com/shekhargulati/python-flask-docker-hello-world.git' } } stage('Docker Run') { steps { sh '''docker rm -f flask-app docker build -t simple-flask-app:latest . docker run --name flask-app -d -p $PORT:5000 simple-flask-app''' } } stage('Smoke Test') { steps { sh 'curl localhost:$PORT' } } } } ``` 新增一個 Multibranch Pipeline,在 `Branch Sources` 中選擇 GitHub,貼上上方給的 repo 連結。這個 repo 是公開的,所以 Credential 的部分留空不用填。其他設定也都用預設值就行了。 ![](https://i.imgur.com/bictN4u.png) 接下來 Jenkins 會自動掃瞄 Repo 的所有 branch,或是手動按下 `Scan Repository Now` 手動掃描。之後就如下圖有兩個 branch,我們可以進到 branch 中帶參數建置。 ![](https://i.imgur.com/CiqeOrC.png) ### 設定 Git Credential 如果 Repo 是不公開的,在 Jenkins 中就要給他一些資訊讓 Jenkins 登入 Repo,再拉取 Jenkinsfile。這裡以 Azure DevOp 為例,首先在 Azure DevOps 的 Repo 中新增一個 Personal Access Token,給予讀取程式碼的權限: ![](https://i.imgur.com/TWHmrue.png) 然後在 Jenkins 的 `Branch Sources`->`Credentials`->`Add` 新增基於 Username 和 Password 的 Credential,這邊 Username 填入 Azure DevOps 的使用者名稱,Password 填入剛剛在 Azure DevOps 拿到的 Personal Access Token: ![](https://i.imgur.com/w76nuA2.png) 這樣就能讓 Jenkins 透過 https 存取 Private Repo 了。 ## Lab 5: Shared Library 隨著越來越多的專案採用 Pipeline,每個 Jenkinsfile 裡會出現大量且重複的邏輯,此時可以透過 Shared Library 將部分 Groovy 語法寫成獨立的函式,然後讓 Jenkinsfile 來呼叫函式。Shared Library 必須要寫在一個 `vars` 的子目錄中,必且 Groovy 檔的檔名即為函式的名稱,詳見官方文件:https://www.jenkins.io/doc/book/pipeline/shared-libraries/ 。 一般來說 Library 的 Repo 會直接定義在 `資訊主頁`>`管理 Jenkins`>`設定系統` 中的 Global Pipeline Libraries,因為所有專案都共同引用 Global Libraries 在維護上會比較方便。如果真的有需求為每個 Pipline 引入 Local Libraries,也是可以在個別 Pipeline 中指定要去哪個 Repo 中下載 Library。見下圖。 ![](https://i.imgur.com/XaDmevi.png) 設定好 Library 後,在 Lab 4 的同一個 Repo,的 `use-jenkins-library` 即可藉由 Shared Library 來執行。 ![](https://i.imgur.com/EhQxCLw.png) ## Lab 6: 使用 agent 當小組規模變得龐大,一台 Jenkins Server 無法同時乘載所有人的 CI/CD 工作量,或是不同專案必須佈署在不同機器上時,我們就需要透過 Agent 來完成這些需求。 首先在`管理 Jenkins`>`管理節點和雲端`>`新增節點`選擇 Permanent Agent,然後填入類似以下資訊: ![](https://i.imgur.com/NlyVP9A.png) 設定完成後回到 Agent 的狀態頁,會有一個 Jenkins 自動產生的透過 `curl` 執行 Agent 的整令,將它複製起來: ``` curl -sO http://localhost:8080/jnlpJars/agent.jar java -jar agent.jar -jnlpUrl http://localhost:8080/manage/computer/my%2Dagent/jenkins-agent.jnlp -secret YOUR_SECRET -workDir "/home/lin/jenkins_agent/workdir" ``` 在你要佈署這個 agent 的機器中新增這個 Agent 的工作目錄 (以上圖為例就是 `/home/lin/jenkins_agent`),然後打開終端機執行前面複製的這個指令就可以啟動 agent 了。 接下來,檢視在 Lab 5 的同一個 Repo,的 `use-custom-agent` 的 Jenkinsfile 會發現 `agent any` 被替換成: ``` agent { label "my-agent" } ``` 最後,在執行這個 branch 的 Pipeline 時你可以觀察 Jenkins 網頁的左下角,將會由 my-agent 來執行。 ## 延伸閱讀 https://blog.amis.com/%E7%B2%BE%E9%80%9A-jenkins-pipeline-part1-e8ef48d3543e https://www.jenkins.io/doc/book/pipeline/getting-started/#global-variable-reference