踐)
簡(jiǎn)介這是一份面向 Azure DevOps 用戶的 Azure Pipelines 官方文檔操作手冊(cè)適合需要搭建持續(xù)集成與持續(xù)交付流程的開發(fā)、運(yùn)維及 DevOps 工程師閱讀。內(nèi)容系統(tǒng)覆蓋核心概念與落地操作從代理、條件、表達(dá)式、環(huán)境、作業(yè)、階段、任務(wù)、模板到觸發(fā)器均有說(shuō)明并針對(duì) GitHub、Azure Repos、Bitbucket 等倉(cāng)庫(kù)集成、多環(huán)境部署、容器構(gòu)建、測(cè)試執(zhí)行及代理池管理等場(chǎng)景給出操作指南可幫助讀者快速掌握管道編寫與發(fā)布配置。資源為 1 個(gè) PDF 文件整體大小 106.6MB頁(yè)面內(nèi)容為 Azure Pipelines 文檔的中文版節(jié)選既適合新手從零了解 CI/CD 理念也對(duì)已使用者查閱具體任務(wù)參數(shù)與排錯(cuò)思路有參考價(jià)值。目前已有 412 人學(xué)習(xí)瀏覽。通過(guò)閱讀這份手冊(cè)讀者可獲得一份結(jié)構(gòu)完整的 Azure Pipelines 速查資料便于在項(xiàng)目中落地自動(dòng)化構(gòu)建、測(cè)試與部署流程減少摸索時(shí)間。1. 從找不到代理開始為什么配置 Azure Pipelines 的第一步就讓人想退坑某次幫同事調(diào)一個(gè) Node.js 項(xiàng)目的持續(xù)集成YAML 文件寫了三行自認(rèn)為沒(méi)什么可錯(cuò)的結(jié)果 pipeline 一跑就報(bào)錯(cuò)No agent found in pool。折騰了兩個(gè)小時(shí)最后發(fā)現(xiàn)是自托管代理壓根沒(méi)裝代理池名字還對(duì)不上。這個(gè)報(bào)錯(cuò)幾乎每個(gè)剛接觸 Azure Pipelines 的人都會(huì)遇到——它不會(huì)告訴你該裝什么、該配什么只會(huì)給你一個(gè)毫無(wú)頭緒的錯(cuò)誤信息。Azure Pipelines 是 Azure DevOps 的核心 CI/CD 服務(wù)支持從 GitHub、Azure Repos、Bitbucket 拉代碼在 Windows、Linux、macOS 上并行構(gòu)建把 .NET、Java、Python、Node.js、Go 這類主流生態(tài)全部覆蓋。但它的配置鏈路很長(zhǎng)組織、項(xiàng)目、代理、服務(wù)連接、YAML 語(yǔ)法任何一環(huán)斷了都跑不通。這篇筆記就是按我實(shí)際裝過(guò)的順序從版本選型、注冊(cè)組織到寫第一條 YAML、配自托管代理再到排錯(cuò)清單一步步給你走完一遍。2. 準(zhǔn)備環(huán)境版本選型、免費(fèi)額度與注冊(cè)組織2.1 版本選型Azure DevOps Services、Server 2022 和 TFS 怎么選裝 Azure Pipelines 之前先要分清你用的是哪一套環(huán)境因?yàn)樽?cè)方式、功能范圍和踩坑點(diǎn)都不一樣。Azure DevOps Services 是微軟的云服務(wù)版本直接瀏覽器訪問(wèn)https://dev.azure.com/{你的組織名}就能用。它更新最快YAML 管道、環(huán)境、審批這些新特性都在這個(gè)版本上最先落地。私有項(xiàng)目每個(gè)月有 1800 分鐘30 小時(shí)的免費(fèi)管道運(yùn)行時(shí)長(zhǎng)公共項(xiàng)目可以直接免費(fèi)跑管道這個(gè)額度對(duì)個(gè)人學(xué)習(xí)和小團(tuán)隊(duì)試用完全夠。Azure DevOps Server 是自托管版本對(duì)應(yīng) 2019、2022 兩個(gè)大版本。它適合代碼必須留在內(nèi)網(wǎng)、不允許出域的場(chǎng)景。注意 Server 版本里某些 YAML 功能是缺失的比如環(huán)境的部署作業(yè)deployment job在 Server 2022 上支持但部分容器作業(yè)能力不如云版本。TFS2018 屬于老古董了除非你被老項(xiàng)目綁死否則不建議新裝因?yàn)楹芏辔臋n里的新特性它都沒(méi)有。判斷方法很簡(jiǎn)單你的代碼在 GitHub 或別人的云端倉(cāng)庫(kù)上選 Services代碼只能留在公司內(nèi)網(wǎng)選 Server。我一般建議個(gè)人開發(fā)者直接上 Services省掉自己維護(hù)服務(wù)器的成本。Server 版本要額外買許可證五個(gè)活躍用戶以內(nèi)用 Express 版是免費(fèi)的但安裝復(fù)雜度高不少。2.2 注冊(cè)與創(chuàng)建組織免費(fèi)啟動(dòng)流程無(wú)論用 Microsoft 賬戶還是 GitHub 賬戶都能注冊(cè) Azure DevOps。用 GitHub 賬戶注冊(cè)有個(gè)前提如果 GitHub 郵箱關(guān)聯(lián)的是支持 Azure AD 的組織就必須改用 Azure AD 賬戶登錄否則會(huì)卡在授權(quán)那一步。打開 Azure DevOps 官網(wǎng)選擇「免費(fèi)啟動(dòng)」會(huì)進(jìn)入組織創(chuàng)建頁(yè)。需要填三項(xiàng)組織名稱、主機(jī)位置、驗(yàn)證字符。組織名稱就是將來(lái)訪問(wèn) URL 的一部分比如填devops-learning最終地址就是https://dev.azure.com/devops-learning。主機(jī)位置一般選離你最近的數(shù)據(jù)中心國(guó)內(nèi)用戶通常選東南亞或東亞區(qū)域網(wǎng)絡(luò)延遲更友好。提示組織名創(chuàng)建后可以改顯示名稱但 URL 的標(biāo)識(shí)部分基本定了。起名別太隨意后面所有 SSH 克隆地址、服務(wù)連接、pipeline 配置都會(huì)帶上這個(gè)標(biāo)識(shí)。創(chuàng)建完成后系統(tǒng)會(huì)提示創(chuàng)建一個(gè)項(xiàng)目。項(xiàng)目有公共和專用兩種可見性CI/CD 學(xué)習(xí)階段選專用就好。項(xiàng)目名最好和倉(cāng)庫(kù)名對(duì)應(yīng)后面在 pipeline 里引用時(shí)會(huì)省事。接著用 Azure CLI 驗(yàn)證一下環(huán)境是否就緒az login az devops configure --defaults organizationhttps://dev.azure.com/devops-learning projectmy-first-project az devops user show --org https://dev.azure.com/devops-learning第一行是登錄 Azure 賬戶第二行設(shè)置默認(rèn)組織和項(xiàng)目讓后續(xù)命令不用每次都帶--org參數(shù)。第三行如果返回了用戶信息說(shuō)明命令行工具已經(jīng)能訪問(wèn)你的 DevOps 組織了這一步可以作為環(huán)境是否配通的快速自檢。2.3 代碼倉(cāng)庫(kù)準(zhǔn)備GitHub 還是 Azure ReposAzure Pipelines 支持的代碼源不少GitHub、Azure Repos Git、Bitbucket Cloud、TFVC、Subversion。絕大多數(shù)新項(xiàng)目用的是 GitHub 或 Azure Repos。選 GitHub 的話pipeline 可以從 GitHub 倉(cāng)庫(kù)拉代碼但需要在 Azure DevOps 里創(chuàng)建一個(gè) GitHub 服務(wù)連接。選 Azure Repos 省掉服務(wù)連接這一步因?yàn)橥谝粋€(gè)組織里天然打通。從實(shí)際操作體驗(yàn)來(lái)說(shuō)我建議第一個(gè) pipeline 用 Azure Repos 練手。原因有兩點(diǎn)一是少配一層 GitHub 授權(quán)二是 Azure Repos 的 PR 分支策略和 pipeline 觸發(fā)器的聯(lián)動(dòng)更直接。GitHub 集成可以等把 YAML 語(yǔ)法跑熟之后再加那時(shí)候就算遇到授權(quán)問(wèn)題也知道是哪里斷了。倉(cāng)庫(kù)里至少要先放一個(gè)代碼文件。不需要多復(fù)雜一個(gè) README 或者一個(gè)簡(jiǎn)單的main.py都行。因?yàn)楹竺鎰?chuàng)建 pipeline 時(shí)要能拉到一次代碼空倉(cāng)庫(kù)會(huì)導(dǎo)致管道創(chuàng)建失敗。3. 創(chuàng)建第一個(gè)管道從 YAML 到第一次綠勾3.1 兩種管道形態(tài)YAML 與經(jīng)典編輯器Azure Pipelines 提供兩種定義方式Y(jié)AML 管道和經(jīng)典管道。YAML 管道的核心是一個(gè)azure-pipelines.yml文件放在代碼倉(cāng)庫(kù)根目錄。管道定義跟著代碼走改了文件就是改了管道配置PR 里能看到管道變更的 diff這個(gè)特性在排查問(wèn)題時(shí)非常有用。當(dāng)前官方推薦的方式所有新功能基本都優(yōu)先支持 YAML。經(jīng)典管道是在網(wǎng)頁(yè)界面上用拖拽表單的方式配置構(gòu)建和發(fā)布配置存在 Azure DevOps 服務(wù)端不跟代碼走。它也有存在價(jià)值發(fā)布管道Release Pipeline里的 Gates 功能部署前自動(dòng)收集外部運(yùn)行狀況信號(hào)只在經(jīng)典發(fā)布管道里可用YAML 里沒(méi)有。另外任務(wù)組Task Group這個(gè)封裝復(fù)用能力也是經(jīng)典編輯器專屬YAML 里對(duì)應(yīng)的是模板Template。如果是新項(xiàng)目直接學(xué) YAML。但要知道經(jīng)典編輯器還活著老項(xiàng)目里大量存在遇到歷史管道維護(hù)時(shí)至少得能讀懂。3.2 最小可用的 azure-pipelines.yml逐字段拆解從空倉(cāng)庫(kù)開始第一個(gè)目標(biāo)不是復(fù)雜部署而是讓管道跑出一個(gè)綠勾。以下是我實(shí)際用過(guò)的最小 YAMLtrigger: - main pool: vmImage: ubuntu-latest steps: - script: echo Hello, Azure Pipelines! displayName: Print greeting - script: | python -c print(Python is ready) displayName: Check Python第一段trigger定義什么時(shí)候觸發(fā)管道m(xù)ain表示推送到 main 分支就跑。第二段pool指定代理池這里用的是微軟托管的 Ubuntu 鏡像不需要自己維護(hù)機(jī)器。第三段steps是任務(wù)列表每個(gè)script就是一條命令displayName是這個(gè)步驟在運(yùn)行日志里顯示的名字。把默認(rèn)分支改成main然后推到遠(yuǎn)端。推完之后去 Azure DevOps 頁(yè)面的 Pipelines 菜單里應(yīng)該能看到這次運(yùn)行已經(jīng)自動(dòng)啟動(dòng)。點(diǎn)擊進(jìn)入運(yùn)行詳情看到兩個(gè)步驟都變成綠色的對(duì)勾第一個(gè)管道就算跑通了。這里有個(gè)細(xì)節(jié)要注意YAML 的縮進(jìn)語(yǔ)法比較嚴(yán)格steps下的每一項(xiàng)都要保持相同的縮進(jìn)層級(jí)。我在實(shí)際使用中因?yàn)閐isplayName縮進(jìn)多了一格導(dǎo)致解析失敗報(bào)錯(cuò)信息非常抽象根本看不出來(lái)是縮進(jìn)問(wèn)題。如果遇到 YAML 解析錯(cuò)誤優(yōu)先檢查縮進(jìn)和冒號(hào)后面有沒(méi)有空格。3.3 從模板克隆與自定義頁(yè)面上的「創(chuàng)建管道」向?qū)鋵?shí)不是從零寫 YAML而是基于倉(cāng)庫(kù)類型和語(yǔ)言生成一個(gè)模板再讓你在編輯器里改。選擇 GitHub 或 Azure Repos 倉(cāng)庫(kù)后向?qū)?huì)掃描代碼推薦對(duì)應(yīng)模板。Node.js 項(xiàng)目會(huì)生成安裝 npm 依賴、跑測(cè)試、發(fā)布工件的模板。Python 項(xiàng)目會(huì)生成pip install和pytest的模板。從模板起步的好處是官方模板已經(jīng)幫你處理了緩存、依賴安裝、條件執(zhí)行這些細(xì)節(jié)。比如 Node.js 模板里會(huì)有- task: NodeTool0 inputs: versionSpec: 20.x displayName: Install Node.js這個(gè)NodeTool0任務(wù)專門負(fù)責(zé)在托管代理上安裝指定版本的 Node.js。versionSpec就是版本號(hào)可以寫成20.x這樣的通配符。自己寫的話得手動(dòng)調(diào)apt-get或nvm用任務(wù)更可靠。模板能覆蓋常見語(yǔ)言但一些特殊場(chǎng)景要自己加任務(wù)比如 Android 構(gòu)建要裝 JDK 和 Android SDKiOS 構(gòu)建必須用 macOS 代理。模板只是開始真正生產(chǎn)級(jí)的管道基本都要手改。3.4 推送觸發(fā)把第一個(gè)管道跑起來(lái)創(chuàng)建完 YAML 管道后管道會(huì)等代碼推送觸發(fā)。驗(yàn)證觸發(fā)邏輯是否正常只需要改任意一個(gè)文件提交然后在 Pipelines 頁(yè)面看運(yùn)行列表里有沒(méi)有新增一條記錄。如果要同時(shí)驗(yàn)證 GitHub 和 Azure Repos 兩種倉(cāng)庫(kù)源GitHub 那邊需要在項(xiàng)目設(shè)置里創(chuàng)建 GitHub 服務(wù)連接。創(chuàng)建過(guò)程會(huì)引導(dǎo)跳轉(zhuǎn)到 GitHub 授權(quán)頁(yè)面授權(quán)時(shí)需要選擇倉(cāng)庫(kù)訪問(wèn)范圍選「僅限選定倉(cāng)庫(kù)」更安全避免把整個(gè) GitHub 賬號(hào)暴露給 Azure DevOps。注意服務(wù)連接的名稱會(huì)在 YAML 里被引用。我習(xí)慣命名為github-read-only這種帶用途的名字比默認(rèn)的GitHub connection 1直觀得多。我一般會(huì)故意把一個(gè)錯(cuò)誤加進(jìn) YAML比如讓腳本退出碼非零確認(rèn)管道確實(shí)會(huì)失敗并展示紅色狀態(tài)然后再修回來(lái)。這一步能同時(shí)驗(yàn)證日志系統(tǒng)、失敗通知和修復(fù)流程是花五分鐘換來(lái)后面省半小時(shí)的做法。4. 把管道做厚階段、作業(yè)、任務(wù)、模板與自托管代理4.1 階段與作業(yè)從單步構(gòu)建到多環(huán)境部署單個(gè)管道要支持多環(huán)境部署靠的是階段stage和作業(yè)job兩層編排。階段是管道里最大的邏輯分組通常對(duì)應(yīng)環(huán)境build、test、deploy-dev、deploy-prod。默認(rèn)情況下階段按順序執(zhí)行但可以用dependsOn控制依賴關(guān)系。作業(yè)是階段內(nèi)的執(zhí)行單元一個(gè)階段可以跑多個(gè)作業(yè)這些作業(yè)可以并行。stages: - stage: build displayName: Build Stage jobs: - job: compile pool: vmImage: ubuntu-latest steps: - script: echo Compiling... displayName: Compile code - stage: deploy_dev displayName: Deploy to Dev dependsOn: build condition: succeeded() jobs: - job: deploy pool: vmImage: ubuntu-latest steps: - script: echo Deploying to dev... displayName: Deploy devdependsOn: build表示這個(gè)階段必須等 build 階段跑完condition: succeeded()表示只有前面階段全部成功后才會(huì)執(zhí)行。這兩個(gè)組合是最常見的部署閘門寫法。condition是一個(gè)很關(guān)鍵的表達(dá)式字段。常見的取值有succeeded()、failed()、always()還有基于變量的判斷如eq(variables[build.reason], PullRequest)后者可以區(qū)分推送觸發(fā)的運(yùn)行和 PR 觸發(fā)的運(yùn)行。多作業(yè)并行是托管代理最實(shí)用的能力。默認(rèn)情況下 Azure DevOps 會(huì)給每個(gè)作業(yè)分配一個(gè)代理實(shí)例免費(fèi)額度是按分鐘計(jì)的并行作業(yè)越多消耗越快。自托管代理不會(huì)消耗 Microsoft 托管額度這也是很多團(tuán)隊(duì)搭自托管代理的原因。4.2 任務(wù)與腳本跑命令、裝依賴、發(fā)工件任務(wù)是管道的最小執(zhí)行單元。Azure Pipelines 內(nèi)置了幾百個(gè)任務(wù)常見的有NodeTool0裝 Node、UsePythonVersion0選 Python 版本、DotNetCoreCLI2處理 .NET 命令、CopyFiles2復(fù)制文件和PublishBuildArtifacts1發(fā)布工件。通用任務(wù)無(wú)法覆蓋的場(chǎng)景用script直接跑命令行、PowerShell 或 Shell。比如拉依賴和跑測(cè)試steps: - checkout: self - script: | python -m venv .venv source .venv/bin/activate pip install -r requirements.txt displayName: Install Python dependencies - script: | source .venv/bin/activate pytest tests/ --junitxmltest-results.xml displayName: Run tests with pytest - task: PublishTestResults2 inputs: testResultsFormat: JUnit testResultsFiles: test-results.xml displayName: Publish test results多行命令用|拼接每行一條命令。checkout: self顯式拉取當(dāng)前倉(cāng)庫(kù)代碼默認(rèn)行為也會(huì)拉但寫出來(lái)更明確。PublishTestResults2把測(cè)試結(jié)果文件發(fā)布到管道運(yùn)行記錄里這樣頁(yè)面上能看到測(cè)試通過(guò)率、失敗用例列表比只看命令行輸出直觀得多。發(fā)布工件是 CI 和 CD 的銜接點(diǎn)CI 階段產(chǎn)出的文件通過(guò)PublishBuildArtifacts1保存CD 階段再用下載任務(wù)取回來(lái)。工件有保留期設(shè)置默認(rèn) 30 天生產(chǎn)環(huán)境上會(huì)設(shè)為 90 天或更長(zhǎng)方便回溯歷史版本。4.3 變量、變量組與運(yùn)行時(shí)參數(shù)寫死配置是管道維護(hù)的災(zāi)難源頭。Azure Pipelines 的三層變量機(jī)制能避免這個(gè)問(wèn)題。第一層是管道內(nèi)變量直接在 YAML 里定義variables: buildConfiguration: Release imageName: myapp:${{ parameters.tag }}第二層是變量組Variable Group存在 Azure DevOps 庫(kù)Library里可以跨管道共享。適合存不常變的配置比如服務(wù)器地址、告警 Webhook URL。同一個(gè)變量組可以被多個(gè)管道引用改一處就全部生效。第三層是密鑰變量存在變量組里并標(biāo)記為密鑰運(yùn)行時(shí)在日志里自動(dòng)打碼。數(shù)據(jù)庫(kù)密碼、API Key 這類敏感信息必須用這種方式不能明文寫在 YAML 里。引用變量組的寫法variables: - group: production-config - task: AzureCLI2 inputs: azureSubscription: prod-service-connection scriptType: bash scriptLocation: inlineScript inlineScript: | echo Deploying to $(serverUrl)$(serverUrl)會(huì)在運(yùn)行時(shí)從production-config變量組里取值。注意 YAML 里變量引用的兩種語(yǔ)法$(varName)是運(yùn)行時(shí)替換模板里的${{ varName }}是模板展開時(shí)替換。前者在任務(wù)執(zhí)行時(shí)解析后者在管道編譯階段就定下來(lái)了用錯(cuò)場(chǎng)景會(huì)導(dǎo)致取值不對(duì)。運(yùn)行時(shí)參數(shù)Runtime Parameters是更靈活的輸入方式parameters: - name: environment displayName: Deployment Target type: string default: dev values: - dev - staging - prod stages: - stage: deploy_${{ parameters.environment }} displayName: Deploy to ${{ parameters.environment }} jobs: - job: deploy_job steps: - script: echo Deploying to ${{ parameters.environment }}運(yùn)行時(shí)參數(shù)可以在手動(dòng)運(yùn)行管道時(shí)從頁(yè)面上選擇也可以被 API 調(diào)用時(shí)傳入。values限制了可選范圍避免手滑部署到錯(cuò)誤環(huán)境。我一般會(huì)給所有多環(huán)境部署管道都加這個(gè)參數(shù)省得每次改 YAML。4.4 模板與觸發(fā)器告別復(fù)制粘貼兩個(gè)以上的管道出現(xiàn)相同的任務(wù)序列時(shí)就應(yīng)該抽成模板。模板文件通常放倉(cāng)庫(kù)的pipelines/templates目錄下后綴還是.yml?;A(chǔ)模板示例# pipelines/templates/build-node.yml parameters: - name: nodeVersion default: 20.x steps: - task: NodeTool0 inputs: versionSpec: ${{ parameters.nodeVersion }} displayName: Install Node ${{ parameters.nodeVersion }} - script: | npm ci npm run test displayName: Install and test引用模板的管道trigger: - main pool: vmImage: ubuntu-latest extends: template: pipelines/templates/build-node.yml parameters: nodeVersion: 22.xextends是模板繼承管道本身不再寫steps全部由模板決定。另一種是steps里局部引用模板steps: - template: templates/build-node.yml parameters: nodeVersion: 20.x兩種方式的選擇標(biāo)準(zhǔn)管道結(jié)構(gòu)和模板完全一致用extends只有部分步驟復(fù)用用- template。模板可以嵌套但要控制層級(jí)三層以上排查問(wèn)題就很痛苦了。觸發(fā)器除了分支觸發(fā)還有計(jì)劃觸發(fā)和路徑觸發(fā)trigger: branches: include: - main - release/* paths: exclude: - docs/* schedules: - cron: 0 2 * * * displayName: Daily 2 AM build branches: include: - mainbranches.include是包含模式release/*會(huì)匹配所有 release 開頭的分支。paths.exclude排除docs/*目錄文檔變更不觸發(fā)構(gòu)建。schedules是定時(shí)觸發(fā)注意計(jì)劃觸發(fā)的時(shí)區(qū)默認(rèn)是 UTC國(guó)內(nèi)團(tuán)隊(duì)用的話要減 8 小時(shí)換算。PR 觸發(fā)器在 GitHub 場(chǎng)景里很重要pr: - main這個(gè)配置讓 PR 創(chuàng)建和更新時(shí)自動(dòng)跑管道驗(yàn)證防止壞代碼合并進(jìn)主干。4.5 自托管代理從下載、配置到加入代理池微軟托管代理省心但有兩個(gè)硬傷自定義軟件裝不了免費(fèi)分鐘數(shù)有限。跑大型編譯任務(wù)或者需要內(nèi)網(wǎng)資源時(shí)自托管代理幾乎是必須的。自托管代理的安裝流程在不同操作系統(tǒng)上一致以 Linux 為例mkdir myagent cd myagent curl -o agent.tar.gz -L https://vstsagentpackage.azureedge.net/agent/3.x/vsts-agent-linux-x64-3.x.tar.gz tar zxvf agent.tar.gz ./config.sh --url https://dev.azure.com/devops-learning --agentpool default --token XXXXX --unattended --acceptteeeula ./run.sh第一步創(chuàng)建目錄并下載代理包。第二步解壓。第三步是配置代理--url指向組織地址--agentpool指定代理池名稱--token用的是個(gè)人訪問(wèn)令牌PAT需要在 Azure DevOps 用戶設(shè)置里生成至少要有 Agent Pools 的讀和管理權(quán)限。--unattended表示非交互模式--acceptteeeula接受許可協(xié)議。最后./run.sh啟動(dòng)代理。代理啟動(dòng)后回到 Azure DevOps 頁(yè)面在 Project Settings - Agent Pools - Default 里應(yīng)該能看到這臺(tái)代理顯示為在線狀態(tài)。此時(shí) YAML 里把pool改成pool: name: default不再寫vmImage管道就會(huì)跑到這臺(tái)自托管代理上。提示自托管代理的run.sh在終端退出后就停了。生產(chǎn)環(huán)境要用systemd或者注冊(cè)成 Windows 服務(wù)讓代理常駐后臺(tái)。我一般用 systemd service 管理同時(shí)加上Restartalways代理進(jìn)程掛了能自動(dòng)拉起來(lái)。公司網(wǎng)絡(luò)有代理的需要在.env文件里加VSTS_HTTP_PROXY環(huán)境變量指向代理地址否則代理連不上 Azure DevOps。自托管代理的代價(jià)是你要為環(huán)境負(fù)責(zé)。托管代理上自帶幾百個(gè)預(yù)裝軟件自托管代理默認(rèn)只有基礎(chǔ)環(huán)境缺什么裝什么。我一般會(huì)在配置腳本里寫好apt-get install的依賴清單新代理從空白系統(tǒng)開始跑一遍腳本就能達(dá)到可用狀態(tài)。5. 避坑與排查五條最常見的翻車記錄5.1 YAML 解析失敗報(bào)錯(cuò)卻看不出來(lái)原因現(xiàn)象管道啟動(dòng)后立刻失敗錯(cuò)誤信息是YAML schematic validation failed或者Expecting a mapping不告訴具體哪一行有問(wèn)題。原因絕大多數(shù)情況是縮進(jìn)錯(cuò)誤。YAML 對(duì)縮進(jìn)極其敏感steps下的每一項(xiàng)必須保持層級(jí)一致script: |多行內(nèi)容里的命令如果比父級(jí)多縮進(jìn)或少縮進(jìn)解析器會(huì)直接報(bào)錯(cuò)。另一種常見原因是冒號(hào)后面忘了加空格。解決先把 YAML 丟到本地編輯器里開 YAML 語(yǔ)法檢查配合 Azure DevOps 頁(yè)面上的編輯校驗(yàn)提醒一邊改一邊看。我的習(xí)慣是本地用 VS Code 寫實(shí)時(shí)標(biāo)錯(cuò)比到網(wǎng)頁(yè)上試錯(cuò)快得多。另外 YAML 里不要用 Tab 鍵縮進(jìn)統(tǒng)一兩個(gè)空格這基本是行業(yè)共識(shí)了。5.2 私有項(xiàng)目每月 1800 分鐘額度不夠用現(xiàn)象月中管道就開始排隊(duì)運(yùn)行記錄里顯示No hosted parallelism has been purchased or granted。原因私有項(xiàng)目默認(rèn)每月只送 1800 分鐘30 小時(shí)多個(gè)并行作業(yè)或者長(zhǎng)構(gòu)建任務(wù)很快就會(huì)把額度吃完。公共項(xiàng)目可以免費(fèi)無(wú)限時(shí)跑管道但如果代碼是私有的這個(gè)方案不可行。解決短期做法是把不必要的 PR 觸發(fā)器關(guān)掉減少自動(dòng)運(yùn)行次數(shù)。長(zhǎng)期做法是搭自托管代理因?yàn)樽酝泄艽聿徽纪泄茴~度。我遇到一個(gè)跑 Android 構(gòu)建的團(tuán)隊(duì)每次構(gòu)建要 25 分鐘一個(gè)月 60 多次構(gòu)建托管額度完全不夠換了自托管代理后成本直降為零。5.3 自托管代理連不上一直顯示離線現(xiàn)象Agent Pools 里代理狀態(tài)是 offlinerun.sh啟動(dòng)時(shí)卡在Connecting to server。原因最常見的是公司網(wǎng)絡(luò)出口有防火墻限制代理需要訪問(wèn)https://dev.azure.com以外的多個(gè)域名比如*.vlabs.servicebus.windows.net、*.visualstudio.com這些域名被攔了代理就永遠(yuǎn)處于連接中狀態(tài)。另一個(gè)原因是代理版本太老和 Azure DevOps Server 版本不兼容。解決先確認(rèn)網(wǎng)絡(luò)策略可以在代理機(jī)上執(zhí)行curl -I https://dev.azure.com驗(yàn)證能不能通。代理程序的日志在_diag目錄下排查時(shí)先看這個(gè)文件。網(wǎng)絡(luò)沒(méi)問(wèn)題的話升級(jí)代理到最新版再重新config一次。5.4 Azure 資源管理器服務(wù)連接失敗現(xiàn)象AzureCLI2或AzureResourceManagerTemplateDeployment3任務(wù)報(bào)錯(cuò)提示Failed to get Azure Resource Manager service connection或者直接 401。原因服務(wù)連接Service Connection的認(rèn)證憑證過(guò)期了或者創(chuàng)建服務(wù)連接時(shí)只剩選了資源組級(jí)別的權(quán)限。常見的是把服務(wù)連接創(chuàng)建成了「服務(wù)主體認(rèn)證」模式但服務(wù)主體密鑰過(guò)期沒(méi)有及時(shí)輪轉(zhuǎn)。解決檢查項(xiàng)目設(shè)置里的 Service Connections選中對(duì)應(yīng)的連接點(diǎn)「Edit」走一遍重新授權(quán)流程。如果是 Azure AD 服務(wù)主體模式去 Azure AD 重新生成客戶端密鑰再回 DevOps 更新連接信息。排查時(shí)先確認(rèn)連接權(quán)限范圍給資源組權(quán)限的就別去嘗試列訂閱級(jí)別的資源否則會(huì)看到權(quán)限不足的假報(bào)錯(cuò)。5.5 經(jīng)典發(fā)布管道和 YAML 管道功能不對(duì)稱現(xiàn)象照著文檔寫 YAML 部署發(fā)現(xiàn)在 YAML 里沒(méi)有 Gates 功能。看老項(xiàng)目里有任務(wù)組的封裝自己在 YAML 里找不到對(duì)應(yīng)概念。原因Azure Pipelines 的兩套體系功能并集才是完整能力YAML 管道的某些能力比如環(huán)境部署作業(yè)是經(jīng)典發(fā)布管道沒(méi)有的反之經(jīng)典的 Gates 和任務(wù)組也是 YAML 沒(méi)有的。解決新管道優(yōu)先 YAML但碰到老項(xiàng)目或者特定需求比如必須用發(fā)布 Gates就別硬在 YAML 里折騰組件。正確做法是 YAML 管道負(fù)責(zé)構(gòu)建和測(cè)試經(jīng)典發(fā)布管道負(fù)責(zé)需要 Gates 的部署環(huán)節(jié)兩者通過(guò)工件銜接。架構(gòu)上多一層但每個(gè)環(huán)節(jié)用最合適的方式比強(qiáng)行統(tǒng)一省心。6. 裝完怎么驗(yàn)證一條流水線從提交到上線的完整檢查管道配置完最后的驗(yàn)證不只是看頁(yè)面上的綠色對(duì)勾。我從實(shí)踐中沉淀了一套檢查清單照著走一遍能確認(rèn)整條鏈路是真的通了而不是表面綠了。第一項(xiàng)檢查提交觸發(fā)。改動(dòng)代碼推到 main 分支去 Pipelines 頁(yè)面的運(yùn)行列表里看有沒(méi)有新增記錄。如果沒(méi)觸發(fā)先看 trigger 配置里的分支名對(duì)不對(duì)再看倉(cāng)庫(kù)的默認(rèn)分支是不是 main。很多從 GitHub 導(dǎo)入的倉(cāng)庫(kù)默認(rèn)分支是 masterYAML 里寫的 main 自然不會(huì)觸發(fā)。第二項(xiàng)檢查代理狀態(tài)。跑到自托管代理上的管道在運(yùn)行詳情里看 Agent 名稱是不是你的那臺(tái)機(jī)器。如果顯示的是 Microsoft-hosted agent說(shuō)明 YAML 里pool還是默認(rèn)的azure-pipelines沒(méi)切到自托管池。第三項(xiàng)檢查測(cè)試報(bào)告。管道步驟里加了PublishTestResults2的去運(yùn)行頁(yè)面的 Tests 標(biāo)簽看統(tǒng)計(jì)總共多少用例、多少失敗、失敗的具體函數(shù)名。這里能看出測(cè)試數(shù)據(jù)有沒(méi)有成功采集比終端日志里的輸出更結(jié)構(gòu)化。第四項(xiàng)檢查工件。進(jìn)入運(yùn)行詳情找 Artifacts 區(qū)域確認(rèn)構(gòu)建產(chǎn)物發(fā)布了多少 MB、文件列表對(duì)不對(duì)。再點(diǎn)下載解壓后抽查一個(gè)文件內(nèi)容是不是最新代碼編譯出來(lái)的。這個(gè)驗(yàn)證能提前發(fā)現(xiàn)工件沒(méi)更新這種坑人的問(wèn)題。第五項(xiàng)檢查部署結(jié)果。如果是多環(huán)境管道走到最后確認(rèn)deploy_prod階段的日志里有明確的成功輸出。我會(huì)在部署腳本最后加一條驗(yàn)證請(qǐng)求比如curl -I打到部署后的服務(wù)地址確認(rèn) HTTP 狀態(tài)碼是 200 才返回成功否則讓管道報(bào)失敗。最后的技巧是在管道末尾加一個(gè)自檢任務(wù)把構(gòu)建時(shí)間、提交 SHA、部署目標(biāo)環(huán)境打出來(lái)然后跑一次完整的 PR 驗(yàn)證。我的習(xí)慣是改完azure-pipelines.yml先在本地把 YAML 縮進(jìn)校驗(yàn)一遍再往一個(gè) topic 分支推一次觸發(fā)完整流程確認(rèn)每個(gè)階段都過(guò)了一遍才敢把變更移到 main 上。這套儀式感看似笨但已經(jīng)幫我擋過(guò)多次回歸事故了——最危險(xiǎn)的不是管道失敗而是管道綠了但產(chǎn)品是壞的。希望這些經(jīng)驗(yàn)?zāi)軒偷侥闵僮邘状螐澛贰1疚倪€有配套的精品資源點(diǎn)擊獲取