集成工具Jenkins部署全流程:從安裝到流水線實戰(zhàn))
我最早接觸持續(xù)集成工具就是因為被一次手動發(fā)版折磨到天亮。那次經(jīng)歷讓我下定決心必須把構(gòu)建、測試、部署這套流程徹底自動化。而聊到持續(xù)集成工具Jenkins 幾乎是繞不開的名字。這篇文章我就結(jié)合自己部署 Jenkins 的完整過程把從環(huán)境準備、安裝配置到流水線搭建的每個環(huán)節(jié)包括踩過的坑和處理方案原原本本寫出來。項目標題是“持續(xù)集成工具-Jenkins的部署流程”那我們就從最實際的問題開始Jenkins 到底怎么部署部署完該怎么用才能讓持續(xù)集成真正跑起來。這套內(nèi)容適合剛接手 CI 建設(shè)、準備在團隊里落地自動化構(gòu)建的開發(fā)者也適合那些已經(jīng)把 Jenkins 跑起來但總出問題、想搞清楚原理的運維同學(xué)。我會盡量少講空泛概念多給能直接照做的步驟和參數(shù)。1. 先把持續(xù)集成這件事想清楚1.1 持續(xù)集成到底解決了什么問題在聊 Jenkins 部署之前得先明確一個前提持續(xù)集成Continuous IntegrationCI不是裝個工具就完事它是一套工程實踐。核心思想是頻繁地把代碼變更合并到主干每次合并都自動觸發(fā)構(gòu)建和測試盡早暴露集成問題。舉個例子。沒有 CI 的時候團隊幾個人各自開發(fā)代碼合并集中在發(fā)版前。結(jié)果就是A 改的接口把 B 的調(diào)用弄掛了C 加的依賴和 D 用的版本沖突這些問題全在發(fā)版窗口集中爆發(fā)修一個冒一個。引入 CI 之后每次代碼推送到遠端自動觸發(fā)編譯、跑單測有問題當(dāng)場就發(fā)現(xiàn)定位成本低得多。需要說明的是CI 不等于 CD持續(xù)交付/持續(xù)部署。CI 側(cè)重“提交代碼后自動驗證”CD 側(cè)重“驗證通過后自動交付到環(huán)境”。Jenkins 兩者都能做但我個人建議先專注 CI把構(gòu)建和測試做扎實再考慮自動部署。1.2 Jenkins 憑什么成為首選工具持續(xù)集成工具不少市面上有 GitLab CI、GitHub Actions、Travis CI還有云廠商自帶的各種流水線服務(wù)。但 Jenkins 在自建場景下依然有不可替代的位置。第一插件生態(tài)太豐富了。從代碼拉取、編譯構(gòu)建、測試報告展示到釘釘/郵件通知幾乎你能想到的環(huán)節(jié)都有插件支持。我統(tǒng)計過我這邊生產(chǎn)環(huán)境用的插件常年保持在 30 個左右覆蓋了多語言構(gòu)建、憑證管理、Kubernetes 集成整個鏈路都很順。第二完全自托管。代碼倉庫是內(nèi)網(wǎng)的或者需要訪問內(nèi)網(wǎng)私有化環(huán)境的Jenkins 部署在公司內(nèi)網(wǎng)最省事。相比 SaaS 服務(wù)數(shù)據(jù)不出內(nèi)網(wǎng)合規(guī)問題也少。第三Pipeline 能力強。Jenkins 的 Pipeline 用代碼定義整個構(gòu)建流程可以做成 Jenkinsfile 放進代碼庫這樣構(gòu)建邏輯跟著項目走誰改了都看得見回滾也方便。當(dāng)然選型也不是非它不可。如果團隊規(guī)模小、代碼量不大且倉庫托管在某個代碼托管平臺上用平臺自帶的 CI 功能會更快。但如果你需要一個長期穩(wěn)定、可控性強的方案Jenkins 依然是自建 CI/CD 基礎(chǔ)設(shè)施中很扎實的選擇。2. 部署前的環(huán)境評估與版本選型2.1 硬件與操作系統(tǒng)選型建議部署 Jenkins 之前先把運行環(huán)境定下來。我踩過的一個最直接的坑就是低估了資源需求。Jenkins 本體是個 Java Web 應(yīng)用占用內(nèi)存不大但任務(wù)并發(fā)起來之后尤其是要同時構(gòu)建多個項目時內(nèi)存和 CPU 消耗漲得很快。我建議最低配置2 核 CPU、4GB 內(nèi)存、40GB 以上可用磁盤。如果你們還要在 Jenkins 上跑編譯、跑鏡像構(gòu)建這類重活那最好 4 核 8GB 起步。磁盤要留足因為構(gòu)建產(chǎn)物、插件、日志都會持續(xù)增長后期可以靠定期清理策略控制但初期空間別太摳。操作系統(tǒng)方面我推薦用某個長期維護的 Linux 發(fā)行版。Windows 和 macOS 也能裝但生產(chǎn)環(huán)境不推薦原因很簡單Linux 環(huán)境下做腳本調(diào)試、權(quán)限管理和 Docker 集成都更順手。我這里演示用的是 Linux 的環(huán)境具體發(fā)行版大家按自己熟悉的來。2.2 JDK 與 Jenkins 版本的配對策略Jenkins 是 Java 應(yīng)用JDK 版本必須匹配。這里有個非常關(guān)鍵的細節(jié)不同版本的 Jenkins 對 Java 版本的要求不同。以我部署時的情況為例當(dāng)時的 Jenkins 版本要求 JDK 17 或更新版本。如果你用的是老一點的 Jenkins 版本可能只支持 JDK 8 或 JDK 11。裝之前先去官網(wǎng)查一下版本兼容矩陣別裝完發(fā)現(xiàn) Jenkins 啟動不了。安裝 JDK 時還有個小細節(jié)盡量用發(fā)行版?zhèn)}庫里的包或者用某廠商的官方 JDK 發(fā)行版。有些公版腳本會默認裝一個版本路徑還不一定對后面排查起來很麻煩。裝完用 java -version 確認版本沒問題再進入下一步。注意Jenkins 安裝程序自帶的 setup 向?qū)z測 Java 環(huán)境檢測不通過會直接報錯。提前確認java -version輸出符合要求能省很多事。3. 安裝部署全流程實操3.1 基于安裝包的常規(guī)部署步驟部署方式上我優(yōu)先推薦用發(fā)行版的軟件倉庫安裝 Jenkins。這樣做的最大好處是后續(xù)升級方便Jenkins 出新版本直接執(zhí)行包管理器升級命令就能完成插件和新功能也能及時跟上。第一步添加官方軟件源。這步很關(guān)鍵如果不添加源直接用系統(tǒng)的默認源裝版本會非常老容易出現(xiàn)兼容性問題。不同發(fā)行版添加源的方式不同大概是導(dǎo)入官方的 GPG 密鑰然后把 Jenkins 的倉庫地址寫進源列表最后刷新源緩存。這個流程做完之后再安裝。第二步安裝。包管理器會自動把 Jenkins 依賴的組件拉下來。裝完檢查一下服務(wù)狀態(tài)是否正常運行。如果遇到啟動失敗多半是 JDK 版本不對回到上一節(jié)重新核對。第三步防火墻放行。Jenkins 默認監(jiān)聽 8080 端口你需要確保這個端口可訪問。如果是內(nèi)網(wǎng)機器且大家都用跳板機那防火墻規(guī)則自己按需調(diào)整。然后就可以通過http://服務(wù)器IP:8080訪問了。首次訪問會要求輸入一個初始密碼這個密碼在服務(wù)器上有明確的文件路徑取出來填進去就行。3.2 基于 Docker 的容器化部署方案如果你的團隊已經(jīng)在用容器或者希望 Jenkins 實例能快速遷移和重建那用 Docker 部署也是不錯的方案。這里我簡單說一下思路不展開具體命令因為不同環(huán)境差異較大。用 Docker 部署 Jenkins 一個大坑是權(quán)限問題。Jenkins 容器里的用戶不是 root如果要掛載宿主機的 Docker 套接字來實現(xiàn)“容器內(nèi)構(gòu)建再生成容器”的方案必須處理好用戶 ID 和權(quán)限映射否則會一直報權(quán)限不足。我的建議是如果只是驗證功能用 Docker 跑一個臨時實例沒問題如果要做生產(chǎn)級部署優(yōu)先用安裝包方式控制面更簡單排查問題也更直接。容器化雖然靈活但引入了額外的存儲、網(wǎng)絡(luò)、權(quán)限維度的問題初期容易勸退。3.3 初始化配置與插件安裝策略首次登錄后Jenkins 會讓你選擇安裝插件的方式。推薦的選項是“Install suggested plugins”也就是安裝官方推薦插件。這個組合穩(wěn)定覆蓋面也夠適合多數(shù)場景。但如果你明確知道自己不需要某些插件也可以選擇自由選擇安裝。我個人建議先裝推薦的跑通流程之后再按需增減。這一步追求的是快速可用的初始狀態(tài)。初始化時還會要求創(chuàng)建管理員賬號。這里有個容易忽略的問題初始密碼在首次登錄后可以用但創(chuàng)建完管理員賬號后后續(xù)登錄就用新賬號了。如果之后想給別人開賬號需要在系統(tǒng)管理里配置用戶權(quán)限。加載插件的速度有時會比較慢特別是需要訪問外網(wǎng)才能下載插件的場景。建議在系統(tǒng)配置里改一下插件更新中心地址換成國內(nèi)可訪問的鏡像源速度會明顯改善。這一步具體地址各環(huán)境不同但我建議大家都提前確認一下是部署流暢度的重要影響因素。4. 第一個自動化構(gòu)建任務(wù)的完整配置4.1 構(gòu)建任務(wù)的核心三要素拆解初始化完成后我們來創(chuàng)建一個實際可用的構(gòu)建任務(wù)。以我之前部署時用的模擬項目 X 為例這是一個常規(guī)的 Web 項目代碼托管在內(nèi)網(wǎng)的 Git 倉庫里。在 Jenkins 里創(chuàng)建任務(wù)時建議選擇“自由風(fēng)格”來入門。一個自由風(fēng)格任務(wù)離不開三個要素源碼管理、構(gòu)建觸發(fā)器、構(gòu)建步驟。源碼管理這塊選 Git然后把倉庫地址和憑證填進去。憑證可以先用用戶名密碼方式后續(xù)再升級成部署密鑰。需要注意分支配置默認是*/master如果你的默認分支是 main一定要改過來。構(gòu)建觸發(fā)器決定了什么時候跑。常用的觸發(fā)方式有三種定時輪詢代碼倉庫、Webhook 觸發(fā)、以及上游任務(wù)觸發(fā)。Webhook 是體驗最好的方式代碼一推就觸發(fā)不用等輪詢。但設(shè)置 Webhook 需要代碼倉庫平臺能訪問到 Jenkins 地址內(nèi)網(wǎng)環(huán)境通常沒問題外網(wǎng)就需要做端口映射。構(gòu)建步驟是最有文章的地方。對于 Java 項目可以加一個 Maven 構(gòu)建步驟執(zhí)行 clean package 命令來產(chǎn)出編譯包。這一步建議加上單元測試的閘門測試不通過就直接失敗避免壞代碼往下流。4.2 參數(shù)化構(gòu)建與構(gòu)建后動作實際使用中你會發(fā)現(xiàn)固定不變的構(gòu)建流程太少。很多時候要區(qū)分測試環(huán)境和正式環(huán)境要能手動指定版本號要選擇只跑某項測試。這時候就需要參數(shù)化構(gòu)建。在任務(wù)配置里勾選“參數(shù)化構(gòu)建過程”添加字符參數(shù)、選項參數(shù)或布爾參數(shù)。比如我加一個BRANCH_NAME參數(shù)構(gòu)建時就能臨時指定要構(gòu)建的分支。構(gòu)建步驟里的 Git 分支就用${BRANCH_NAME}這個變量來引用。這樣同一個任務(wù)既能跑最新代碼也能用來補發(fā)歷史版本。構(gòu)建后動作也別忽略。最基礎(chǔ)的是構(gòu)建結(jié)果通知可以郵件通知也可以推到即時通訊工具。另外強烈建議把構(gòu)建產(chǎn)物歸檔配置上這樣每次構(gòu)建后在任務(wù)頁面就能直接下載 war 包或 jar 包排查問題不用再到服務(wù)器上翻目錄。4.3 憑證管理與敏感信息處理第一次配置 Git 密碼的人很容易直接把密碼明文填在任務(wù)里。這樣做短期方便但后患無窮任務(wù)配置頁在瀏覽器里任何能看到頁面的人都能看到密碼多人協(xié)作后密碼改動又要跑到每個任務(wù)里去更新極其痛苦。正確做法是在系統(tǒng)配置中先創(chuàng)建“憑證”把用戶名密碼或部署密鑰存進去然后在任務(wù)里引用這個憑證 ID。這樣做的好處是密碼只存一份更換時不用逐個任務(wù)去改。還有一個細節(jié)如果在構(gòu)建腳本里需要用到私有源或者鏡像倉庫密碼不要直接硬編碼??梢栽趨?shù)化構(gòu)建的變量里引用或者用憑證綁定插件在構(gòu)建時動態(tài)注入環(huán)境變量。Jenkins 對這個場景支持得不錯關(guān)鍵是要養(yǎng)成敏感信息全部走憑證管理的習(xí)慣而不是圖省事。5. 從自由風(fēng)格任務(wù)到流水線的持續(xù)集成升級5.1 為什么要用 Pipeline 替代多條自由風(fēng)格任務(wù)用了一段時間的自由風(fēng)格任務(wù)后你會感受到它的局限。最明顯的問題是流程散落在各個任務(wù)的配置頁里沒法直觀看到整個鏈路。比如 commit 觸發(fā)構(gòu)建構(gòu)建完要手動去點部署或者靠多個任務(wù)之間的上下游關(guān)系串聯(lián)出了問題時很難定位是哪一段的問題。Pipeline 把整個流程用腳本描述出來從拉代碼、編譯、測試、鏡像構(gòu)建到推送制品、部署到某環(huán)境一條流程清晰可見。你可以直觀看到當(dāng)前卡在哪一步每步日志也獨立分段排查效率高很多。我這里強烈建議如果團隊流程已經(jīng)開始復(fù)雜化盡早轉(zhuǎn)到 Pipeline 上。雖然寫 Jenkinsfile 比在界面上點配置要多學(xué)一點 Groovy 語法但這個學(xué)習(xí)成本是必要的。更重要的是Jenkinsfile 是代碼像項目源碼一樣維護代碼評審和變更記錄都有了這才是工程化。5.2 一個可復(fù)用的多階段流水線示例分享一個我實際用過的流水線骨架拿來做參考非常合適。整個流程大概四個 stage拉取代碼、構(gòu)建與單測、產(chǎn)出鏡像并推送、按參數(shù)決定是否部署。拉取代碼階段直接用 checkout 步驟分支從參數(shù)里讀這樣手動構(gòu)建時可以指定分支。構(gòu)建與單測階段用 Maven 或前端構(gòu)建工具跑編譯然后歸檔測試報告。如果用了 GitLab 這類平臺還可以假裝測試報告讀回去。鏡像構(gòu)建和推送階段基礎(chǔ)是構(gòu)建產(chǎn)物已經(jīng)準備好。這里會用到 Docker 的構(gòu)建能力建議把鏡像倉庫地址和憑證做成環(huán)境變量避免寫死在 Jenkinsfile 里。鏡像 tag 可以用構(gòu)建號加上 Git 短 commit hash 拼接保證可追溯。這個 tag 命名習(xí)慣非常重要后期回溯線上版本全靠它。部署階段如果只是測試環(huán)境可以直接在 Jenkins 機器上執(zhí)行腳本或者調(diào)用遠端宿主機的部署接口。生產(chǎn)環(huán)境我建議加入人工確認步驟用 input 指令卡一下等有權(quán)限的人點擊“繼續(xù)”才執(zhí)行后續(xù)操作。初次嘗試時這個門檻能擋住很多誤操作。5.3 多分支流水線與分支策略匹配當(dāng)你把 Jenkinsfile 提交到代碼庫之后你會發(fā)現(xiàn)“多分支流水線”這個功能特別香。它會自動掃描倉庫里的所有分支每個分支都能有獨立的構(gòu)建記錄和狀態(tài)展示保護分支額外設(shè)置超時與告警。這與團隊的 Git 工作流直接關(guān)聯(lián)。如果你們做主分支開發(fā)模式那每次合并到主干都會自動觸發(fā)構(gòu)建和測試如果做 Git Flow那 release 分支要額外跑一遍完整測試和部署到預(yù)發(fā)環(huán)境。這些策略都靠多分支流水線來落地每個分支類型用不同的構(gòu)建參數(shù)和發(fā)布目標。我實際用下來的體會是分支策略不用復(fù)雜但要明確“哪類分支觸發(fā)哪類構(gòu)建”。這一條在 Jenkinsfile 里用 when 條件就能實現(xiàn)不用維護一大堆任務(wù)配置清晰多了。6. 高頻問題排查與避坑實錄6.1 部署和日常使用中的高頻故障速查我把這些年遇到過的高頻問題按現(xiàn)象、原因、解決方案整理成一個表方便你在出問題時直接對照。問題現(xiàn)象常見原因處理思路啟動失敗日志提示 Java 版本不支持JDK 版本與當(dāng)前 Jenkins 版本不兼容查版本兼容矩陣更換 JDK 版本插件安裝極慢或超時默認插件中心地址訪問受限更換為可用的鏡像源再做一次插件更新構(gòu)建任務(wù)一直卡在拉取代碼憑證無效或機器人不允許訪問倉庫檢查憑證有效期用部署密鑰替換密碼Git 拉取失敗提示臨時文件目錄不可寫工作空間目錄權(quán)限不對檢查 Jenkins 進程用戶的目錄寫權(quán)限構(gòu)建產(chǎn)物找不到步驟里沒配置歸檔或產(chǎn)物路徑不對確認構(gòu)建結(jié)果的相對路徑并勾選歸檔Webhook 不觸發(fā)構(gòu)建出口 IP 限制、地址不通或憑證不匹配在倉庫端手動測試 hook檢查消息記錄定時任務(wù)到了時間不執(zhí)行時區(qū)配置錯誤服務(wù)器時區(qū)與預(yù)期不符修改 JVM 時區(qū)為指定的本地時區(qū)構(gòu)建歷史丟失頁面報空間不足磁盤占用超過閾值清理舊的構(gòu)建記錄和日志設(shè)置保留策略這張表涵蓋了我最常遇到的幾類問題。其中“構(gòu)建歷史丟失”這個現(xiàn)象初期我以為是 Jenkins 的 bug后來排查發(fā)現(xiàn)是磁盤滿了系統(tǒng)自動清理了部分舊數(shù)據(jù)。磁盤空間監(jiān)測對 Jenkins 來說真的很重要建議直接配置系統(tǒng)監(jiān)控插件空間低于閾值主動告警。6.2 并發(fā)構(gòu)建與權(quán)限控制的一些心得Jenkins 默認是支持并發(fā)構(gòu)建的但并發(fā)度過高會直接拖垮機器。我遇到過一臺 4 核 8GB 的機器上同時跑 6 個構(gòu)建任務(wù)每個都是前端編譯加單測結(jié)果內(nèi)存直接打滿構(gòu)建大面積失敗。后來給主節(jié)點設(shè)置了最大并發(fā)構(gòu)建數(shù)限制才算穩(wěn)定下來。并發(fā)控制還有一個容易被忽視的維度多個任務(wù)同時寫同一個代碼目錄或者同時操作同一個輸出路徑會產(chǎn)生不可預(yù)期的構(gòu)建結(jié)果。我后來把所有構(gòu)建任務(wù)都配置成“不允許并行執(zhí)行”也就是使用自定義工作空間把每個項目的構(gòu)建目錄完全隔離。雖然犧牲了一點并發(fā)度但換來的是構(gòu)建結(jié)果的確定性。權(quán)限方面我推薦一開始就啟用 Jenkins 自帶的基于角色的權(quán)限控制。團隊規(guī)模小的時候可能感覺不到一旦加了新人或需要外部同事看構(gòu)建日志沒有權(quán)限隔離就會很尷尬。我吃過這個虧一個臨時同事進來幫忙看問題順手就動了一個生產(chǎn)部署任務(wù)的配置雖然沒有造成事故但徹底改變了我對 Jenkins 權(quán)限管理的態(tài)度。提示Jenkins 默認是“任何人都可以操作所有任務(wù)”的模式這在開發(fā)環(huán)境無所謂只要你們的環(huán)境涉及生產(chǎn)部署操作就一定要做權(quán)限隔離。好的權(quán)限模型是“按角色分場景”開發(fā)角色只能觸發(fā)構(gòu)建和查看日志運維角色才有權(quán)限改配置和執(zhí)行部署。6.3 多環(huán)境發(fā)布的一個穩(wěn)定實踐Jenkins 部署還有一個進階問題怎么用一套流程管理開發(fā)、測試、預(yù)發(fā)、生產(chǎn)多個環(huán)境的發(fā)布。我的做法是用參數(shù)化構(gòu)建區(qū)分環(huán)境每個環(huán)境對應(yīng)的目標主機列表、配置文件模板不一樣。在 Jenkinsfile 中可以定義一個環(huán)境分支的判斷然后根據(jù)參數(shù)選擇不同的部署目標。這里有一個很關(guān)鍵的細節(jié)構(gòu)建產(chǎn)物應(yīng)該只構(gòu)建一次然后多次部署。不要在每個環(huán)境都重新編譯一遍那樣既有性能損耗又可能因為環(huán)境差異產(chǎn)生出不同產(chǎn)物。正確做法是構(gòu)建階段統(tǒng)一產(chǎn)出制品部署階段按不同環(huán)境參數(shù)拉取同一物件的不同配置。這套模式幫我解決了很大的維護成本。以前每個環(huán)境都要維護一套任務(wù)現(xiàn)在核心流程就一條環(huán)境差異只體現(xiàn)在參數(shù)和配置里加一個環(huán)境基本是幾分鐘的事。寫在最后一次部署帶來的長期收益從第一臺 Jenkins 部署到現(xiàn)在我最深的體會是工具本身不難難的是想清楚你要把它用成什么樣。很多人裝 Jenkins 只是為了“有個持續(xù)集成工具”結(jié)果裝完沒人維護任務(wù)三天兩頭掛最后反而不如手動構(gòu)建穩(wěn)當(dāng)。Jenkins 部署只是一個開始。它能發(fā)揮多大價值取決于你愿不愿意把構(gòu)建、測試、制品管理這些環(huán)節(jié)都接入進來。我建議第一周先只做一個項目的編譯加單測跑穩(wěn)了再加鏡像構(gòu)建再加自動部署到測試環(huán)境一步步來別貪多。等整套流程真正轉(zhuǎn)起來你會發(fā)現(xiàn)之前手動發(fā)版的那些擔(dān)驚受怕都會變成一次很輕松的流水線操作。