☰
Azure Pipelines 新手入门:从YAML流水线到CI/CD避坑
2026/10/28 12:39:34 网站建设 项目流程

简介:面向使用 Azure DevOps 进行持续集成与持续交付的开发者与运维人员,这份操作手册以 PDF 形式系统梳理 Azure Pipelines 的核心能力与落地路径。内容覆盖管道创建与克隆、YAML 架构、代理与自托管代理、条件与表达式、环境、作业、阶段、任务、模板和触发器等关键概念,并延伸到 GitHub、Azure Repos、TFVC、Bitbucket 等代码仓库集成,以及 Azure SQL、AKS、Key Vault、Slack、Teams 等部署与协作场景。包内共 1 个 PDF 文件,约 106.6MB,适合作为日常查阅与专项配置参考。已有 412 人学习,读者可从中获得 CI/CD 流程搭建思路、经典发布管道与持续测试配置要点,以及生成发布排错和包发布等实践参考。

1. 先说人话:Azure Pipelines 到底是个什么东西

“代码在我电脑上是能跑的”——这句话大概是每个刚进团队的人都说过,也有人因为这句话被叫去开会。等你接触到 azure devops pipeline 这套东西,会慢慢明白:持续集成(CI)和持续交付(CD)真正想解决的,就是把“在我电脑上是好的”这句让人不太踏实的说法,变成一条谁都能复现的自动化流水线。Azure Pipelines 是 Azure DevOps 里的一个组成部分,它做的事很朴素:你把代码推上去,它自动帮你装环境、编译、跑测试、打包,然后按你的要求送到测试环境或者线上。它不挑语言,.NET、Java、Python、Node.js、PHP、Go、Android、iOS 都能跑;也不挑平台,Linux、Windows、macOS 的机器都能用。适合谁?适合所有“改完代码还要手动折腾一遍才能给别人看”的人,尤其是刚入行、还没独立配过部署的新手。

2. 为什么值得学:CI/CD 帮你省掉的三笔账

先不急着注册账号,也不急着背概念。这一章先把“为什么”——为什么一个写业务代码的人,也要花半天时间搞懂流水线。搞不清动机,后面看 YAML 会越看越烦。

2.1 CI 到底在自动做什么

想象一个三人小组,改同一个仓库。A 同学改了一处工具函数,在本地跑通了,提交;B 同学同时在改调用这个函数的地方,也在本地跑通了,提交。两次提交单独看都没问题,合到一起就崩了。这种事不是谁水平差,而是“本地”这个环境太私人了:你的机器上装着某个旧版本的依赖、某个全局装的命令行工具、某个没写进依赖清单的库。三个人的“本地”是三套不同的环境,合在一起必然出问题。

持续集成要做的事情,就是把这套私人环境换成一个公共环境。你每次推送代码,流水线就在一台干净的机器上从头来一遍:拉代码、装依赖、编译、跑测试。跑不过,你当场就知道,而不是等到两周后联调时才发现。修复成本是随着时间指数级上涨的——刚提交时发现,改两行;两周后发现,可能要翻十几个提交去找是谁改坏的。

我把这件事的差别列成一张表,你感受一下:

环节全靠手动接入 CI 之后
装依赖每人一套,版本靠口头约定流水线每次从零装,清单写死
跑测试想起来才跑,赶进度就跳过每次推送必跑,跳不过去
发现问题联调时,责任不清提交后几分钟,定位到具体提交
合并代码提心吊胆,怕冲突冲突在合并前就被测试拦住

表格里最值钱的是最后一行。CI 给人的心理变化是很大的:以前合并代码像拆盲盒,现在合并前先看一眼流水线是不是绿的,绿了再合。这个习惯一旦养成,你会发现自己敢改代码了,因为知道有东西在兜底。

2.2 CD 不是“自动上线”,是“随时能上线”

很多新手一听持续交付,第一反应是“自动把代码发到生产环境?太危险了吧”。这是个误解。持续交付的核心不是“自动发”,而是“随时可以发”——代码始终处于一个可部署的状态,需要发的时候一键发,而不是临时抱佛脚打包到半夜。

这里面有个关键概念叫“制品”(artifact)。一次构建产出的东西,比如一个压缩包、一个容器镜像、一个 Python 包,就是制品。好的做法是:构建一次,产出制品,然后同一个制品依次部署到测试环境、预发环境、生产环境。这样做的好处是,你在测试环境验过的那份东西,和你最终发上去的那份东西是同一个二进制,不是“重新编译一遍祈祷结果一样”。很多线上事故就出在“测试环境和生产环境编译出来的东西不一样”上。

部署到多个环境还有一个隐含价值:它逼着你把配置和代码分开。数据库地址、密钥、开关,这些东西不该写死在代码里,而是运行时注入。这个习惯在你只有一个环境的时候感觉不到好处,等到你有三个环境、五个客户私有部署的时候,它就是救命的。

还有一层是持续测试。流水线不只在构建时跑单元测试,还能在部署完之后跑一轮针对环境的验证,比如接口能不能通、页面能不能打开、关键流程走不走得通。这一步做完,你对“这次发布是好的”才有底气,而不是靠手点几下页面确认。

2.3 哪些人最该先学它

如果你只写前端页面,可能会想“部署是运维的事”。但实际上,现在大多数团队的流水线配置文件是开发自己写的,因为只有写代码的人知道这个项目该怎么构建、要跑哪些测试、依赖哪些环境变量。运维能提供机器和网络,但azure-pipelines.yml里的那些步骤,最终还是要开发来填。

后端开发最直接受益,因为构建、测试、打包这三步几乎天天要用。测试同学也值得学,因为把自动化测试接进流水线之后,回归测试就不用靠人肉点了。做数据、做算法方向的同样需要,训练脚本、模型评估、数据集预处理,这些活儿用一个流水线串起来,比手动敲一串命令可靠得多。至于运维和平台方向,那更不用说,代理池、服务连接、环境审批这些都是他们的主场。

反过来说,如果你的项目是一个人写、一周发一次、没有测试,那先别急着上流水线——先把测试补上,再来谈自动化。流水线是放大镜,你有好的实践它放大好的,你有坏的实践它放大坏的。这个顺序搞反了,会得到一个“每天稳定地跑失败”的流水线,然后你就再也不想碰它了。

3. 上手前的准备:一个组织、一个仓库、几个必须认识的词

这一章是动手前的最后一次“读说明书”。不用背,看完有个印象就行,后面写 YAML 的时候遇到再回来查。

3.1 免费注册这一步,别被“企业级”三个字吓到

很多人卡在第一步,觉得这东西听起来是给大公司用的,注册肯定很麻烦。其实注册过程就是填几个框。打开 Azure Pipelines 的页面,点“免费启动”,然后选登录方式:用 Microsoft 账号,或者用 GitHub 账号。两种都行,看你代码放在哪。如果你代码在 GitHub 上,直接用 GitHub 账号登进去更省事,因为后面连接仓库的时候少一次授权。

登录之后会让你填一个组织名。这个名字会变成你的访问地址https://dev.azure.com/{你的组织名},所以起个短一点、好记一点的,别用一长串拼音。主机位置那个下拉框,选一个离你团队近的区域就行。填完验证码,就进去了。

进去之后第一件事是创建项目。项目是组织下面的一层容器,一个项目里包含代码仓库、流水线、看板这些东西。可见性可以选公开或私有。这里要说清楚一件新手最容易搞混的事:公开项目的流水线有免费的托管额度,私有项目每月也有一定的免费分钟数,超出之后才需要付费。所以学习阶段完全不用担心花钱,随便建,跑坏了删掉重来。

提示:注册完之后先把项目建好,别在组织首页停着。后面所有操作都在项目里进行,找不到入口很容易让人以为出问题了。

这里还有个小建议:学习阶段专门建一个“练手项目”,别拿公司正在跑的业务仓库做实验。你会在里面反复删改 YAML、反复触发运行,弄脏了历史记录不好看,万一误操作影响到真实流水线更麻烦。练手项目跑通了,再把配置照搬到正式仓库,这是比较稳的做法。

3.2 十个概念不用背,先记住这五个

官方文档里列了一长串概念:代理、条件、表达式、环境、作业、运行时参数、阶段、任务、模板、触发器。第一次看确实容易懵。我用一个工厂的比喻把它们串起来,你记这个比喻就够了。

一条流水线就是一座工厂。工厂分几个车间,叫“阶段”(stage),比如一个车间负责造零件(构建),一个车间负责组装(部署)。每个车间里有若干个工位,叫“作业”(job),工位之间是并行的,互不干扰。每个工位上有一连串动作,叫“步骤”(step),按顺序执行。有些动作是通用的、现成的,比如“安装 Python 3.11”,这种东西叫“任务”(task),本质是一个打包好的脚本。干活得有工人和场地,那个提供机器的就是“代理”(agent),可以是云端临时给的机器,也可以是你自己准备的一台服务器。什么时候开工?门铃响了就开工,那个门铃就是“触发器”(trigger),最常见的是“有人往主分支推代码”。

先记这五个:阶段、作业、步骤、任务、代理。剩下五个等你写第二个流水线的时候自然会遇到。

概念工厂比喻一句话理解
阶段 stage车间按大流程分段,比如构建、部署
作业 job工位一段独立的执行单元,默认并行
步骤 step动作工位上的一个个具体操作
任务 task现成工具别人写好的步骤包,直接调用
代理 agent工人和场地真正跑命令的那台机器
触发器 trigger开工铃什么事件会让流水线跑起来
变量 variable参数可以在不改文件的情况下换的值
制品 artifact成品箱构建产出,供后续阶段使用
环境 environment收货地址部署的目标,可加审批
模板 template图纸复用把重复的 YAML 抽出来共用

这张表不用背下来,扫一眼有印象就行。真到写的时候,卡在哪一行就回来查哪一行,比一次性记住十个词效率高得多。

3.3 YAML 和经典编辑器,新手到底选哪个

Azure Pipelines 有两种建流水线的方式,这是新手第一个要做的选择。

一种是 YAML:流水线的定义写在一个叫azure-pipelines.yml的文件里,跟着代码一起放仓库。另一种是经典编辑器:在网页上点鼠标,拖任务、填表单,配置存在服务端,不跟代码走。

我建议新手直接用 YAML,理由有三个。第一,它能跟着代码一起做版本管理,谁改的、什么时候改的、改了什么,翻提交记录一目了然,出问题能回滚。第二,它能被复制。你在这个项目写好的 YAML,改几行就能搬到另一个项目去,而鼠标点出来的配置基本没法复用。第三,网上能搜到的教程、别人分享的示例,绝大多数是 YAML,遇到问题更容易找到答案。

对比项YAML 管道经典编辑器
配置存放仓库里的文件服务端
版本管理跟代码一起走基本没有
复用复制粘贴即可只能手动重建
学习曲线前半小时别扭前五分钟顺手
长期维护越用越顺越复杂越乱

前半小时你会觉得写 YAML 很别扭,尤其是缩进,多一个空格少一个空格都报错。但也就半小时,过了这个坎,后面全是收益。经典编辑器不是不能用,它在某些老项目里还在跑,你迟早会碰到,但不建议作为入门的第一站。

4. 第一个能跑通的例子:从空仓库到绿色的勾

铺垫完了,动手。这一章的目标很明确:让一条流水线跑起来,并且你能看懂它为什么成功、为什么失败。

4.1 建项目、传代码,把原料备好

流水线要跑,得先有代码。在项目里找到 Repos(代码库)入口,你会看到一个空仓库,页面上通常有个“从命令行推送现有仓库”或者“导入仓库”的选项。最省事的做法是在本地建一个空目录,写两个文件,然后推上去。

我们要准备的东西很少,一个 Python 文件加一个测试文件就够:

# 在本地新建一个练手目录,初始化仓库 mkdir pipeline-demo && cd pipeline-demo git init # 建一个被测的函数 cat > calc.py << 'EOF' def add(a, b): return a + b EOF # 建一个测试文件 cat > test_calc.py << 'EOF' from calc import add def test_add(): assert add(1, 2) == 3 assert add(-1, 1) == 0 EOF # 把测试依赖写进清单,流水线会照着装 echo "pytest" > requirements.txt git add . && git commit -m "初始化练手项目"

上面这几个命令做的事是:建目录、建仓库、写一个只有加法函数的calc.py、写一个用 pytest 跑的测试、把 pytest 写进requirements.txt。最后那个文件很关键,流水线是台干净的机器,上面什么都没有,它必须靠这个清单知道要装什么。很多新手漏掉这一步,本地跑得好好的,一到流水线上就报“找不到模块”。

然后把仓库地址加上,推送到远端:

git remote add origin <你的仓库地址> git push -u origin main

仓库地址在 Repos 页面上能直接复制。推上去之后刷新页面,能看到calc.py、test_calc.py、requirements.txt三个文件,准备工作就算完成了。

4.2 手写一个 azure-pipelines.yml:逐行拆开看

现在建流水线。在左侧菜单找 Pipelines,点新建,选择仓库来源(Azure Repos Git 或者 GitHub),选中刚才那个仓库。接下来它会问你从哪个模板开始,选“Starter pipeline”,它会给你一个能跑的最小示例。

先别急着用它的,我们把内容换成自己的第一个版本,短一点,看得清楚:

# azure-pipelines.yml —— 最小可运行版本 trigger: - main # 主分支有推送时自动触发 pool: vmImage: 'ubuntu-latest' # 用云端临时提供的 Linux 机器跑 steps: - script: echo "第一条流水线跑起来了" displayName: '打个招呼' # 显示在界面上的步骤名

这三段分别是什么意思?

trigger决定“什么时候跑”。写main就是主分支一有推送就跑。如果这一段整个不写,某些情况下会变成“什么都不触发”,你会盯着页面等半天以为卡住了。

pool决定“在哪跑”。vmImage: 'ubuntu-latest'意思是让服务端临时开一台 Ubuntu 机器给你用,跑完就销毁。这个机器是干净的,所以上一步强调依赖清单要写全。除了 ubuntu,还可以写windows-latest、macos-latest,看你项目需要。

steps是这个作业里按顺序执行的步骤列表。script表示执行一段命令行,displayName只是界面上显示的名字,不改执行逻辑,但强烈建议写,因为后面步骤一多,日志里全是CmdLine你会找不到北。

写完点保存并运行,几十秒后你应该能看到一个绿色的勾。这一步跑通的意义比看起来大:证明账号、权限、仓库连接、代理分配这一整条链路是通的。后面的失败都是内容问题,不是环境问题了。

4.3 把测试接进去,看懂成功与失败

最小版本跑通之后,把它改成一个真正干活的版本:

# azure-pipelines.yml —— 加入依赖安装、测试和制品发布 trigger: - main pool: vmImage: 'ubuntu-latest' variables: pythonVersion: '3.11' # 集中管理版本,换的时候只改这一行 steps: # 第一步:在代理上装好指定版本的 Python - task: UsePythonVersion@0 inputs: versionSpec: '$(pythonVersion)' displayName: '准备 Python 环境' # 第二步:装依赖并跑测试,多行命令用 | 保持为一个整体 - script: | python -m pip install --upgrade pip pip install -r requirements.txt pytest -v displayName: '安装依赖并运行测试' # 第三步:把整个工作目录打包成制品,留给后续的部署阶段用 - task: PublishPipelineArtifact@1 inputs: targetPath: '$(Build.SourcesDirectory)' artifact: 'drop' displayName: '发布制品'

这段一共三步,我挨个说。

第一步用的是task,不是script,区别在于 task 是别人写好的功能包,你只需要填参数。UsePythonVersion@0后面的@0是版本号,表示用这个任务的第 0 版,写错版本号会直接报“找不到任务”。versionSpec通过$(pythonVersion)引用上面variables里定义的变量,这就是变量最朴素的用法:同一个值在多处出现时,只在顶上改一次。

第二步是核心。script: |那个竖线表示下面几行属于同一段脚本。三行依次是升级 pip、按清单装依赖、跑测试。这里避不开一个硬规则:任何一行返回非零退出码,这一步就判定失败,后面的步骤不再执行,整条流水线标红。你要的就是这个效果——测试挂了就该停,别硬着头皮往下走。

第三步把工作目录打成制品。targetPath里的$(Build.SourcesDirectory)是系统预定义变量,指向代理上拉下来的代码目录,这种内置变量不需要你定义,直接用就行。artifact是给这份制品起的名字,后面部署阶段要用这个名字取货。

现在故意改坏一次,感受一下失败长什么样。把calc.py里的a + b改成a - b,推上去。流水线会跑到“安装依赖并运行测试”这一步标红,点进去看日志,最后几行会明确告诉你assert add(1, 2) == 3失败,期望 3 实际得到 -1。这就是 CI 的价值具象化:错误在提交后几分钟内被指出来,还带具体行号。

看完之后记得改回来,再推一次,等那个绿色的勾回来。这一红一绿的循环,你后面会经历几百次,早点熟悉它的节奏没坏处。

5. 新手避坑:五个最容易翻车的地方

这一章是血泪经验合集。下面这五个坑,基本每个刚上手的人都会踩至少两个,提前知道能省你几个小时。

5.1 坑一:YAML 缩进和中文标点

现象:保存或者运行的时候直接报解析错误,提示unexpected value、mapping values are not allowed之类,指向的行号看起来完全正常。

原因:九成是两件事。一是用了 Tab 键缩进,YAML 只认空格,而且不允许 Tab 出现;二是从别处复制示例的时候,冒号、引号被输入法换成了中文全角字符,肉眼几乎看不出来,解析器直接懵。

解决:把编辑器设成“Tab 转两个空格”,写 YAML 的时候养成两个空格一级的习惯,别用四个也别混着来。复制粘贴之后,把可疑的那几行删掉重敲一遍冒号。另外,值里面如果出现冒号加空格(比如某个 URL),整个值要用引号包起来。这种错误排查起来很磨人,建议写完先看缩进对齐的竖线,对不齐的地方八成有问题。

5.2 坑二:触发器没做路径过滤,改个说明文档也跑一遍

现象:只改了一行 README,流水线哗啦啦跑起来,还占用了额度。项目一大,一天跑几十次,额度蹭蹭掉。

原因:trigger只写了分支名,等于告诉系统“这个分支上任何文件变动都触发”。

解决:加路径过滤,让它在该跑的时候才跑:

trigger: branches: include: - main paths: include: - src/* - requirements.txt exclude: - docs/* - '*.md'

这段的意思是:主分支上,只有src目录和依赖清单变了才触发,文档目录和 Markdown 文件的变化忽略。路径匹配是区分大小写的,写的时候照着仓库里的实际目录名抄,别凭感觉。过滤器写得太宽等于没写,写得太窄又会漏跑,建议先宽一点,观察一段时间再收窄。

5.3 坑三:密钥写进了 YAML,然后提交了

现象:数据库密码、第三方接口的 token 直接写在azure-pipelines.yml里,跑是能跑,但仓库里所有人都能看到。

原因:图省事。而且删除也没用,历史提交里还在,翻一翻就能找出来。

解决:用变量组或者密钥管理服务存这些值,YAML 里只留变量名。还有个特别容易忽略的点:密钥类型的变量不会自动变成脚本里的环境变量,必须显式映射进去。

steps: - script: | echo "正在连接数据库..." python migrate.py displayName: '执行数据库迁移' env: DB_PASSWORD: $(dbPassword) # 显式把密钥变量映射成环境变量

env那一段是关键,不写的话脚本里读DB_PASSWORD会读到空值,然后报一个跟密码毫无关系的连接错误,排查方向会被带偏。至于已经提交上去的密码,正确的做法不是删文件,而是立刻去改密码,把它作废。这点后悔药是没有的。

5.4 坑四:本地能过,流水线挂

现象:本地pytest全绿,推上去就红,报错信息看着莫名其妙,比如某个包版本不兼容、某个命令找不到。

原因:环境不一致。最常见的是本地已经装过的依赖,没有写进requirements.txt;其次是本地 Python 版本和流水线上用的不一样;再就是本地有些环境变量,流水线上没有。

解决:第一步,在一个全新的虚拟环境里重装一遍依赖再跑测试,能复现问题就说明是依赖没写全。第二步,把版本号尽量钉死,别用“最新版”这种模糊说法,pytest和pytest==7.4.0是两个概念,前者今天装和下周装可能拿到不同结果。第三步,看流水线日志里打印的 Python 版本,和你本地对一下。这类问题的排查顺序建议是:先看版本,再看依赖,最后看环境变量,从最常见的地方开始找,别一上来就怀疑代理机器有问题。

5.5 坑五:托管并行额度没申请,一直排队

现象:流水线状态一直是等待,或者直接提示没有可用的托管并行能力,怎么点重试都不动。

原因:私有项目的免费托管额度需要有个开通动作,新组织不一定默认就有。另一种情况是同时在跑的作业数超了,后面的只能排队。

解决:去组织设置里找并行作业相关的选项,按提示申请免费额度,通常填个用途说明提交就行。等待期间可以先检查一下是不是自己同时触发了好几条。如果团队对等待时间敏感,可以考虑接一台自托管代理,用自己的机器跑,缺点是机器要自己维护、环境要自己装。入门阶段先用托管代理,够用了再考虑自建,别一开始就把复杂度拉满。

6. 下一步学什么:从“能跑通”到“敢改”的路线

跑通第一个例子之后,最容易犯的错是立刻去啃完整份 YAML 架构文档。那玩意儿几百个字段,看完只会更懵。我建议按“遇到问题再学”的顺序推进,每一步都对应一个真实痛点。

第一步,把条件用起来。你现在所有步骤都是必跑的,但实际项目里常有“只有主分支才部署”“只有打标签才发布”的需求。condition就是干这个的,写法不复杂,and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))这种表达式看着唬人,拆开就是“前面都成功,且当前是主分支”。学会它,你就能用一条流水线覆盖多个分支的不同行为。

第二步,学模板。当你发现第二个、第三个项目的 YAML 长得一模一样的时候,就该抽模板了。把公共步骤放进一个单独文件,主文件里用template引进来,改一处全线生效。这一步是从“会写”到“会管”的分水岭。

# templates/build-steps.yml —— 抽出来的公共步骤 steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.11' - script: pip install -r requirements.txt && pytest -v displayName: '安装依赖并测试'
# azure-pipelines.yml —— 引用模板,主文件变短 trigger: - main pool: vmImage: 'ubuntu-latest' steps: - template: templates/build-steps.yml

模板路径是相对于主文件所在目录的,放错位置会报找不到文件,这个错我犯过不止一次。

第三步,学环境和审批。给部署阶段挂一个environment,再配一条人工审批,你就有了“自动跑到生产门口停下来等人点头”的能力。这是持续交付真正落地的地方,也是让团队敢用自动化的关键。

阶段要学的东西解决的问题
入门trigger / pool / steps让流水线跑起来
进阶一condition、变量、多作业不同分支不同行为
进阶二模板、变量组多项目复用,少改重复代码
进阶三环境、审批、服务连接安全地部署到真实目标
进阶四缓存、并行、自托管代理跑得更快,成本更低

我自己的教训是:一开始贪多,把缓存、模板、审批一次性全塞进第一个 YAML,结果一个缩进错误卡了整个下午,最后全部推倒重来。从那以后我每次加新东西都只加一处,跑通了再加第二处,绿色勾确认过才继续。这个笨办法救过我很多次。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询