Azure Pipelines实战:从零搭建CI/CD管道与自托管代理

发布时间:2026/10/24 19:16:51
Azure Pipelines实战:从零搭建CI/CD管道与自托管代理

简介:Azure DevOps Pipelines 操作手册以 PDF 文件形式汇集了微软官方管道文档的中文译本,系统覆盖持续集成与持续交付从入门到实战的核心知识。资源面向运维开发工程师与技术管理者,帮助解决搭建自动化流程时文档分散、概念繁杂、上手困难等实际问题。手册依次梳理代理、条件、表达式、环境、作业、阶段、任务、模板、触发器及运行时参数等关键概念,并结合容器镜像、编程语言与云服务等主流技术栈给出具体配置方法。同时收录与主流代码仓库平台的集成方案,以及部署到数据库、容器服务和密钥管理等服务的操作指引,便于不同背景读者按需查阅。针对常见故障还附有排查思路,可有效缩短问题定位时间。压缩包内为单个 PDF 文档,容量约 106.6MB,章节完整且结构清晰,既适合团队内部分享,也可作为自学入门教材。目前已有 412 人浏览学习,实用性得到初步验证。

1. 为什么都在用 Azure Pipelines:CI/CD 工具链里的第一个选择题

某开发者最初对管道工具是抵触的,觉得构建部署用脚本就够了。直到某次上线前,发现测试环境跑的代码比主分支落后十几个提交,排查半天才发现是部署脚本漏了一行拉取命令。把团队从手动构建、手动推送、手动部署的循环里拽出来,是当时唯一确定要做的事。Azure Pipelines 是微软这套 CI/CD 方案里最容易被先拿起来用的部分,支持几乎所有主流语言,从 .NET、Java、Node.js 到 Python、Go、Android 和 iOS,公共项目免费,私有项目每月有 1800 分钟免费额度。对刚起步的团队够用,对规模化团队也有自托管代理和部署组的扩展路径。这篇文章按实际搭过的顺序,从注册组织、创建第一条管道,到配置自托管代理、排查部署失败,完整走一遍。

2. 环境准备:账号注册、版本选型和代理选择

2.1 注册 Azure DevOps:Microsoft 账户和 GitHub 账户两条路

注册是整套环境的第一步,也是不少人第一次卡住的地方。用 Microsoft 账户走的话,登录后选择“免费启动”,输入组织名称、选一个主机位置,过一遍人机验证,就能拿到https://dev.azure.com/{组织名}的访问地址。组织名会出现在所有管道和仓库的 URL 里,尽量用团队简称,后面改起来非常麻烦。

用 GitHub 账户注册是第二条路,官方现在支持直接通过 GitHub 登录。但有一个坑:如果 GitHub 邮箱关联到某个启用 Azure AD 的组织,就无法用 GitHub 账户直接登录,必须换成 Azure AD 账户。我第一次注册时因为公司邮箱开了企业认证,用 GitHub 账号登录一直跳错,最后换 Azure AD 账户才进去。注册完成后系统会引导创建项目,项目分公共和私有两类。公共项目用 Azure Pipelines 完全免费,私有项目每月送 1800 分钟,分钟数按作业时间累加,同一时间并行跑多个作业会消耗更快。

2.2 版本选型:Services、Server 还是 Express

版本这块很容易混淆,选错后面迁移成本很高。Azure DevOps Services 是云端 SaaS 版本,不需要自己维护服务器,注册即用,本文所有流程基于这个版本。Azure DevOps Server 适合数据必须留在内网的团队,可以部署在 Windows Server 上,构建、测试、发布全流程都在内网闭环。Azure DevOps Express 是 Server 的免费轻量版,支持五个或更少的活跃用户,功能上与 Server 2019 基本一致,适合个人学习或小团队内部试用。

选型的判断标准其实就两条:数据能不能出内网,团队规模多大。数据敏感就选 Server 或 Express,否则直接上 Services 最快。见过不少团队一开始选了 Server,后来发现升级补丁和维护代理服务器比写管道还累,又迁回 Services 的。

对比项ServicesServerExpress
部署形态云端 SaaS本地部署本地免费轻量版
运维成本微软维护自建维护自建维护
适用规模不限企业内网不超过 5 个活跃用户
数据位置微软数据中心完全内网完全内网
功能一致性最新功能与 Services 大部分一致与 Server 2019 基本一致

2.3 代理选择:Microsoft 托管还是自托管

代理是执行管道任务的计算资源,要尽早决定用哪种。Microsoft 托管代理是 Azure 提供的虚拟机,Windows、Linux、macOS 三种镜像都有,预装了大量 SDK 和常用工具,不需要维护。自托管代理是装在自己机器上的代理程序,可以放在公司内网,能访问内网数据库和私有包源,但环境需要自己维护,系统补丁、SDK 升级都得自己管。

对刚上手管道的团队,建议先默认用 Microsoft 托管代理,管道里写一行pool: vmImage: 'ubuntu-latest'就能用。等需要访问内网资源,或者构建时间太长想加缓存时,再上自托管。托管代理适合跑公开仓库的验证构建,自托管适合需要稳定访问内网服务的生产部署。两种代理支持的语言任务基本一致,区别主要在网络边界和资源可控性。

3. 创建第一个管道:YAML 配置从零到跑通

3.1 代码先入 Git:仓库怎么接

Azure Pipelines 要求源代码必须在版本控制系统中。自己的代码可以放在 Azure Repos,外部代码支持 GitHub、Bitbucket Cloud、Subversion、TFVC 等。用 YAML 管道关联仓库时,需要先创建服务连接(Service Connection),这个连接用 PAT 令牌或 OAuth 授权,目的是让管道能读取仓库内容。

关联完成后,推送到仓库的代码就会触发默认分支的管道。要特别注意管道文件azure-pipelines.yml必须放在仓库的根目录,或者至少放在一个固定的已知路径下,否则在创建管道的向导里选不到这个文件。第一次创建时建议先推一个空提交,确认触发正常,再开始填真正的构建逻辑。

3.2 最小可用的 azure-pipelines.yml

先给一个能跑的最小配置,然后一行行解释:

# azure-pipelines.yml trigger: - main pool: vmImage: 'ubuntu-latest' steps: - task: UseDotNet@2 inputs: packageType: 'sdk' version: '6.x' - script: | dotnet restore dotnet build --configuration Release displayName: 'Build .NET project' - script: | dotnet test --configuration Release displayName: 'Run unit tests'

trigger指定监听的分支,这里监听main分支,只要这个分支有新的推送,管道就会自动启动。pool.vmImage选择托管代理镜像,ubuntu-latest是 Linux 环境最常用的选择。steps是任务列表,按顺序执行:先装 .NET SDK,再还原依赖并编译,最后跑测试。displayName是任务在运行日志里的可读名称,排错时能一眼看到卡在哪一步。

这段配置覆盖了 CI 的核心:代码推上去自动构建、自动测试。前几次跑通后,再逐步加 CD 部署步骤。

3.3 触发器、变量和任务:三个高频配置

触发器的写法不止分支触发一种。拉取请求校验用pr,定时构建用schedules,还可以在trigger下用paths精确指定哪些路径变更才触发。

trigger: branches: include: - main - release/* paths: exclude: - docs/* pr: branches: include: - main variables: buildConfiguration: 'Release' dotnetVersion: '6.x' schedules: - cron: "0 2 * * *" displayName: Daily midnight build branches: include: - main

variables里定义的变量可以在后续任务中通过$(变量名)引用,例如在编译命令里用--configuration $(buildConfiguration)。cron调度用的是 UTC 时间,国内团队容易按本地时间填,结果本来想凌晨两点跑,实际成了早上八点跑。路径排除docs/*是个很实用的优化,文档变更不触发构建,省下大量无效的管道运行。

任务的选择上,Azure Pipelines 自带大量内置任务。编译用对应语言的官方任务,例如DotNetCoreCLI@2、NodeTool@0、PythonScript@0;通用操作直接用script或powershell写命令脚本。判断标准很简单:能明确表达意图就用内置任务,逻辑复杂就退回到脚本。

3.4 YAML 模板:几十个服务的管道不用抄来抄去

模板是 YAML 管道里最值得花时间掌握的功能。团队里往往有十几个微服务,每个服务都要构建、测试、发布,如果每个管道都复制一份 YAML,改一个公共配置就要改十几个地方。用模板可以把公共步骤抽出来。

# templates/build-template.yml parameters: - name: projectName type: string steps: - task: DotNetCoreCLI@2 displayName: Build ${{ parameters.projectName }} inputs: command: build projects: '**/${{ parameters.projectName }}.csproj'

调用模板时只需传入参数:

steps: - template: templates/build-template.yml parameters: projectName: MyApi

parameters是模板的入参声明,${{ }}是模板表达式语法,在管道编译阶段就会把参数值替换进去。模板的好处是公共逻辑只维护一份,各服务管道只保留自己的差异化配置。这个习惯越早养成,后期管道维护越轻松。

3.5 经典编辑器 vs YAML:这个选择别纠结

经典编辑器比 YAML 早出现,靠页面点选配置任务,适合不常写代码的同事维护简单流程。但经典管道一旦任务多起来,页面切换比看代码慢得多,而且管道配置不能像代码一样走评审和版本回滚。

我选 YAML 的原因很实际:管道文件随代码走,改管道和改代码走同一套评审流程;模板复用让几十个服务统一管理。经典编辑器只在一种情况下值得用——团队里有人完全不能接受代码形式,强制要求图形界面。其他情况一律建议从 YAML 开始,少走一条回头路。

4. 进阶部署:自托管代理、环境与变量组

4.1 自托管代理:内网部署的关键一步

自托管代理解决的最大问题是网络边界。托管代理跑在微软数据中心,访问不了公司内网的数据库、私有包源、内部 API。把代理程序装在内网机器上,管道任务就在内网环境里执行,网络问题少一大半。

在 Linux 上部署自托管代理,我一般这样做:

# 创建代理目录并进入 mkdir myagent && cd myagent # 从代理下载页获取 Linux x64 代理包并解压 curl -L <代理包下载地址> -o agent.tar.gz tar zxvf agent.tar.gz # 交互式配置,指定组织 URL、认证方式和代理池 ./config.sh --url https://dev.azure.com/{你的组织} --auth pat --token {PAT令牌} --pool default # 前台运行,确认能收到任务 ./run.sh

config.sh的--url指向组织地址,--pool指定注册到哪个代理池,--token使用一个有 Agent Pool 管理权限的 PAT。先跑./run.sh前台模式确认能正常连接,再配置成系统服务。Windows 和 macOS 也有对应的代理包,流程一致,只是可执行文件名不同。

部署代理的坑集中在权限。代理机器必须能访问代码仓库、包源和部署目标,这三个方向缺一个,构建就会在某个阶段报网络错误,日志看起来像依赖拉不下来,实际是代理机器本身出不去。

4.2 环境(Environments):部署目标的管理单元

环境用来组织部署目标,一个环境可以包含多台虚拟机、一个 Kubernetes 集群或一组自定义资源。YAML 管道通过deployment作业关联环境,部署记录、审批流程、资源健康状态都会归集到环境页面下。

jobs: - deployment: DeployWebApp environment: prod strategy: runOnce: deploy: steps: - task: AzureWebApp@1 inputs: azureSubscription: 'my-service-connection' appName: 'my-webapp' package: '$(System.DefaultWorkingDirectory)/**/*.zip'

environment: prod把这次部署关联到prod环境。环境可以配置审批人,生产环境的部署作业会先停在等待审批状态,审批通过后才继续执行。AzureWebApp@1的azureSubscription参数指向一个服务连接,这个连接保存了访问 Azure 订阅的授权信息。部署失败时先查服务连接是否有效,是最常见的定位路径。

环境的另一个价值是可观测。所有部署历史、当前资源状态、最近变更都能在一个页面里看,配合审批流程,生产环境不再是谁都能碰的黑匣子。

4.3 变量组和服务连接:机密别写进 YAML

YAML 文件里放普通变量没问题,但密码、令牌、连接字符串这些机密一旦提交进版本库,就等于公开了。变量组(Variable Groups)是存放共享变量的地方,可以标记为机密,管道运行时引用但日志里会打码。

variables: - group: prod-credentials - name: appName value: my-webapp

变量组prod-credentials里存了password、apiKey这类值,管道里直接用$(password)引用。服务连接(Service Connection)是另一类凭证管理方式,它定义对外部服务的访问凭据——Azure 订阅、GitHub、SSH 服务器、Kubernetes 集群都通过服务连接授权。服务连接在组织级别统一管理,可以复用到多个管道,比在 YAML 里写死连接字符串安全得多。

我自己的习惯是:所有走网络的凭据一律用服务连接,所有跨管道共享的配置用变量组,YAML 文件里只留非敏感变量和参数占位符。这样即使管道文件泄漏,也不会直接泄露任何访问凭证。

5. 常见问题排查:五条血泪踩坑记录

5.1 管道跑到错误的代理池

现象:自托管代理明明部署好了,但某个作业一直失败,点开日志发现作业被分配到了其他代理池的机器上。

原因:YAML 里没有显式指定代理池,管道默认使用了名为default的代理池,而自托管代理注册到了另外命名的池,两边根本没对上。

解决:在管道里显式声明代理池名称,确认名称完全一致,大小写也算。

pool: name: 'my-onprem-pool'

5.2 YAML 缩进错误导致管道解析失败

现象:保存管道后提示 Pipeline validation failed,错误信息指到某个行号,但肉眼完全看不出问题。

原因:Azure DevOps 的 YAML 解析对缩进极其严格,steps下的列表项多一个空格或少一个空格,整个任务就不会被识别。

解决:先在管道编辑页点 Validate 做语法检查,再把 YAML 贴到本地编辑器的格式化工具里重新排版,确认所有-对齐、冒号后都有空格。这个毛病在复制粘贴模板时最容易出现。

5.3 GitHub 授权失败,登录一直跳回原页

现象:用 GitHub 账户注册后,连接 GitHub 仓库时提示授权失败,页面反复跳转回登录页。

原因:GitHub 邮箱关联到了启用 Azure AD 的组织,GitHub 登录无法完成组织级授权;或者 GitHub 账号的 OAuth 应用授权列表里,对应授权被撤销了。

解决:确认当前登录的 GitHub 账号有权访问目标仓库;如果关联了企业 Azure AD,改用 Azure AD 账户登录。在 GitHub 账户的 Settings 里检查 OAuth 应用授权列表,把对应应用的权限重新授予一次。

5.4 部署作业卡在等待审批,审批人收不到通知

现象:deployment 作业卡在等待审批状态,环境配置里明明添加了审批人,但没有任何人收到审批请求。

原因:审批人配置的是一个群组,但群组里没有该组织下的活跃用户;或者添加的审批人是外部账户,没有组织登录权限。

解决:在环境页面检查审批人列表,确保每个审批人都有该组织的登录权限。测试环境先配置个人用户做审批人,流程稳定后再换成群组,避免一开始就排查群组成员这种玄学问题。

5.5 免费分钟数提前用完了

现象:私有项目一个月跑了很多次管道,发现分钟数提前耗尽,后续作业直接失败,日志提示没有可用的并行作业。

原因:每次作业都会消耗分钟数,无论构建成功还是失败;多个作业并行跑时,分钟数按并行数叠加消耗,比想象中快很多。

解决:设置月度额度提醒,或者在触发器里排除不必要的分支和路径,减少无效构建。

trigger: branches: include: - main paths: exclude: - docs/*

6. 装完怎么验证跑通:把调试模式当成检测管道健康的第一工具

管道写完不等于能跑,跑过一次也不等于就稳定了。我验证管道健康的固定流程是三件事:先开调试模式跑完整流程,再看测试结果和覆盖率,最后检查工件是否完整发布。

开启调试模式是排查问题的第一步。在管道定义的变量里加上System.Debug并设为true,重新运行管道,日志会输出每个任务的详细输入输出,包括内部变量的解析值。遇到“任务参数没生效”这类问题时,调试日志能直接告诉你解析后的值是什么。

测试结果在运行页的 Tests 页签里查看,每个测试用例的名称、耗时、通过失败状态都在这里。配置了代码覆盖率收集的任务后,Coverage 页签会展示行覆盖率和分支覆盖率。我一般会给关键服务设一个覆盖率下限,低于阈值构建直接失败,把这个检查写进管道。

- task: PublishCodeCoverageResults@1 inputs: codeCoverageTool: 'Cobertura' summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'

工件(Artifact)是 CI 的最终产物。检查 Artifacts 页签里的文件列表和预期的发布包是否一致,这一步能避免“构建绿了但部署时找不到包”的尴尬。部署到测试环境后,再用冒烟测试请求一遍核心接口,整个 CI/CD 闭环才算真正确认跑通。

从那以后,每次新写管道我都会强制走一遍这套流程:调试模式跑一次,测试结果看一眼,工件列表对一遍,最后部署到测试环境冒个烟。哪怕改一个变量名,也不跳过。这套习惯救过我很多次,希望帮到你。

本文还有配套的精品资源,点击获取