查看源文件

Azure DevOps Self-Hosted Agents


Self-Hosted Agents Overview

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/agents?view=azure-devops&tabs=yaml%2Cbrowser#self-hosted-agents

Info

  • Azure DevOps在Azure China不支持MS managed agents,只能使用self-hosted agents,可能会存在一些cache warm-up和拉取commit到local repo的准备时间。
  • self-hosted agents提供capability,意思是上面装了什么软件。pipeline的demand会定义需要哪些软件。
  • 如果pipeline定义的demands,没有agent具备capability能满足,那么job就会失败。

  • 如果是没有空闲的agent能满足demands,job就会等待。

  • azure devops - pipelines - edit - triggers - YAML - Default agent pool for YAML:可以选择默认用Azure-hosted还是private(self hosted)。yaml文件里面没定义agent pool的话,就用这个默认的。


Agent 与 Pipeline 通信

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/agents?view=azure-devops&tabs=yaml%2Cbrowser#communication

  • agent向pipeline注册的时候会拿到一个listener OAuth Token。
  • agent监听pipeline service是否有job要执行,有job的话,会下载一个per-job的OAuth Token,用来访问pipeline的资源,用完即弃。
  • agent和pipeline之间走的是非对称加密,agent的公钥在注册的时候提供给了devops service。

Agent 版本

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/v3-agent?view=azure-devops

  • agent版本每隔几周会更新,major version.minor version的格式。
  • 对于self-hosted agent,小版本可以自动升级,大版本需要手动升级。
  • 检查现有agent的version:在agent页面的system capabilities里面。检查最新发布的版本:https://github.com/Microsoft/azure-pipelines-agent/releases

Agent 手动注册

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/linux-agent?view=azure-devops


Agent 认证

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/personal-access-token-agent-registration?view=azure-devops

  • 对于self-hosted agent,当agent向pipeline注册的时候,需要agent做认证。有三种认证方式:PAT、device code、Service Principal

  • PAT:在自己account的setting里面创建PAT,复制下来,agent注册时会用

Tip

注意这个PAT仅用于agent首次向pipeline注册时使用。后续的agent-pipeline通信用的是OAuth Token。


Agent Custom Image

Self-hosted Agent 通常需要预装 Pipeline 依赖。可先制作自定义 VM 镜像,再用于创建 Agent 实例。

Packer

Packer 可自动构建 OS 镜像,并将 Azure 镜像版本发布到 Azure Compute Gallery。

Packer@1

是Packer提供的Azure devops pipeline task。在实际使用中,遇到认证问题:

一开始打算用service connection做认证,但是一直报错:##[error]Error: Endpoint auth data not present: <id>。这个id就是service connection的id。这表明service connection在这里没起到认证作用。经过调查原因如下:

  1. 首先,Packer@1 task的参数,传递的值是azure devops service connection的name。
connectedServiceAzure: ${{ parameters.serviceConnection }}
connectedServiceType: 'azure'
  1. 去翻了Packer的源码:https://github.com/riezebosch/vsts-tasks-packer/blob/9611850a1b2571bde9c7c7e9459eb5d00cd42039/Packer/PackerV1/src/packer.ts#L69,他是要去找这个service connection对应的service principal的client id和client secert。

Warning

  1. 也就是说,service connection需要是通过secret的方式注册的,Packer@1才能顺利拿到client secret完成认证。workload identity的方式注册的他拿不到federated secret。Packer@1版本根本不支持Azure devops service connection和EntraID service principal的workload identity的认证方式。
  2. 去service connection界面尝试创建一个secret类型的service connection,结果发现根本选不了subscription,他默认会去global endpoint去找,不会识别到china,因为azure devops service连不到Azure China的tenant。也就是说我们用的global azure devops,根本无法创建secret类型的service connection。也就是说我们无法在china使用Packer@1这个task了。

PackerBuild@1

Note

PackerBuild@1 到2025年依然不支持hcl格式的config文件:https://github.com/microsoft/azure-pipelines-tasks/issues/16753,所以hcl的template用不了。改成json template可用。

Packer Template 注意事项

Important

  • build_resource_group_name - 要求这个rg必须已经提前存在。要去image_gallery.tf文件里面预先创建出来
  • shared_image_gallery_destination.image_name - 这个变量是指VM Image Definition,也需要提前创建好。Packer会在里面创建image version
  • virtual_network_name 和 virtual_network_subnet_name - 要求虚拟网络和子网也要提前存在。要去terraform中预先创建出来

Azure Image Builder

是azure推荐的,azure托管Packer来做镜像:

根据文档示例尝试建一个image:

az cloud set --name AzureChinaCloud
az login -t <tenant ID>
az account set --subscription <Sub ID>
 
# Resource group name
sigResourceGroup=""
# Datacenter location
location=chinanorth3
# Additional region to replicate the image to
additionalregion=chinanorth2
# Name of the Azure Compute Gallery
sigName=""
# Name of the image definition to be created
imageDefName=AIB_Image_test
# Reference name in the image distribution metadata
runOutputName=aibImageTest
 
# 
subscriptionID=<Sub ID>
# Create user-assigned identity for VM Image Builder to access the storage account where the script is stored
identityName=""
# Get the identity ID
imgBuilderCliId=""
# Get the user identity URI that's needed for the template
imgBuilderId=""
 
# Create VM Definition
az sig image-definition create \
    -g $sigResourceGroup \
    --gallery-name $sigName \
    --gallery-image-definition $imageDefName \
    --publisher myIbPublisher \
    --offer myOffer \
    --sku 20_04-lts-gen2 \
    --os-type Linux \
    --hyper-v-generation V2
 
curl https://raw.githubusercontent.com/Azure/azvmimagebuilder/master/quickquickstarts/1_Creating_a_Custom_Linux_Shared_Image_Gallery_Image/helloImageTemplateforSIG.json -o helloImageTemplateforSIG.json
 
sed -i -e "s/<subscriptionID>/$subscriptionID/g" helloImageTemplateforSIG.json
sed -i -e "s/<rgName>/$sigResourceGroup/g" helloImageTemplateforSIG.json
sed -i -e "s/<imageDefName>/$imageDefName/g" helloImageTemplateforSIG.json
sed -i -e "s/<sharedImageGalName>/$sigName/g" helloImageTemplateforSIG.json
sed -i -e "s/<region1>/$location/g" helloImageTemplateforSIG.json
sed -i -e "s/<region2>/$additionalregion/g" helloImageTemplateforSIG.json
sed -i -e "s/<runOutputName>/$runOutputName/g" helloImageTemplateforSIG.json
sed -i -e "s%<imgBuilderId>%$imgBuilderId%g" helloImageTemplateforSIG.json
 
az resource create \
    --resource-group $sigResourceGroup \
    --properties @helloImageTemplateforSIG.json \
    --is-full-object \
    --resource-type Microsoft.VirtualMachineImages/imageTemplates \
    -n AIBImageTemplateforSIG01
 
az resource invoke-action \
    --resource-group $sigResourceGroup \
    --resource-type  Microsoft.VirtualMachineImages/imageTemplates \
    -n AIBImageTemplateforSIG01 \
    --action Run

VMSS Agent

Summary

手动创建 Agent Pool

  1. 从 Image Gallery Definition 中的自定义镜像创建 VM,并参考 VMSS 的配置。

  2. 进入VM,手动执行一下repo中的cloud init中的脚本、注册脚本:

    # cloud init 脚本
    sudo groupadd docker
    sudo usermod -aG docker lxadmin
    sudo systemctl enable docker
    sudo systemctl start docker
    sudo chmod 666 /var/run/docker.sock
    #给lxadmin添加无密码sudo
    sudo usermod -aG sudo lxadmin
    visudo
    # User privilege specification 下面添加一行:
    lxadmin ALL=(ALL) NOPASSWD: ALL
     
    # ctrl+O保存,ctrl+X退出
    # 执行注册脚本install.sh,注意要在root下执行
    sudo su
    # keyvault{1} secret{2} pool{3} agent_name{4} username{5}
    ./install.sh "kv name" "pat secret name" "agent pool name" "agent name" "linux user name"
  3. 等待注册脚本执行完成,去azure devops agent pool中查看是否有新agent加进来,状态是否为online。

手动管理 Agent 进程

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/linux-agent?view=azure-devops&tabs=IP-V4#run-interactively

  1. 一次性启动agent进程
cd /devopsagent
./run.sh
  1. 注册成为systemd进程

这就是安装脚本install.sh里面用的方法。

https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/linux-agent?view=azure-devops&tabs=IP-V4#run-as-a-systemd-service

su ${5} -c "$DEVOPS_AGENT/config.sh --unattended  --url https://dev.azure.com/<org name> --auth pat --token ${pat} --pool ${3} --agent ${random} --acceptTeeEula"
./svc.sh install ${5}
./svc.sh start
./svc.sh status

Agent Pool Authentication

PAT

我们现在用的就是PAT模式,PAT存到keyvault里面,VMSS用uai从kayvault读取,读取出来用agent的注册脚本传进去完成认证和注册。

微软正在考虑逐步减少PAT的使用:https://devblogs.microsoft.com/devops/reducing-pat-usage-across-azure-devops/

Service Principal

想法是把Azure China的service principal或者managed identity注册到Azure devops service,然后用它进行注册。

Warning

但是仅支持从azure devops org连接到的EntraID tenant里面加载sp或mi。从Azure China加载不支持。

https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity?view=azure-devops

You can only add a managed identity or service principal for the tenant your organization is connected to.

可以看下这里的开发指南:https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity?view=azure-devops

从sp或mi直接拿access token也不行,还是要求必须是连接到AAD的tenant:

Make sure the subscription is associated with the tenant connected to the Azure DevOps organization you’re trying to access.