Pular para o conteúdo principal

Desafio 21: Infraestrutura de runners e agentes

Plataforma: comparação

Este desafio compara runners do GitHub Actions e agentes do Azure DevOps lado a lado.

Habilidades do exame

  • Projetar e implementar uma infraestrutura de runner do GitHub ou agente do Azure DevOps, incluindo custo, seleção de ferramentas, licenças, conectividade e manutenibilidade

Cenário

A Contoso Ltd possui requisitos de build diversificados em suas equipes de engenharia:

  • A equipe mobile faz build de apps iOS que requerem macOS com Xcode
  • A equipe de engenharia de dados executa testes de integração contra um SQL Server on-premises atrás de um firewall
  • A equipe de plataforma faz build de imagens Docker que precisam de acesso privilegiado
  • Todas as equipes precisam de builds rápidos com dependências em cache

A configuração atual usa runners hospedados pelo GitHub para tudo, resultando em builds lentos (sem cache persistente), incapacidade de alcançar recursos on-premises e custos altos para runners macOS. A Contoso precisa de uma estratégia híbrida de runners/agentes que equilibre custo, segurança e capacidade.

Tarefa 1: Comparar runners hospedados versus self-hosted

FatorRunners hospedados pelo GitHubRunners self-hosted
CustoMinutos incluídos (2.000 para Team, 3.000 para Enterprise), depois cobrança por minutoApenas custo de infraestrutura (VM, manutenção)
Taxa macOSMultiplicador de 10x sobre minutos LinuxHardware próprio a custo fixo
ManutençãoGerenciado pelo GitHub (atualizado automaticamente)Autogerenciado (patches de SO, atualizações de ferramentas)
Ambiente limpoVM nova a cada jobPersistente (necessário gerenciar limpeza)
Acesso à redeApenas internet públicaPode acessar redes privadas
Tempo de inicialização15-45 segundos (fila + provisionamento)Quase instantâneo (já em execução)
PersonalizaçãoLimitado a ferramentas pré-instaladasControle total sobre software instalado
Cacheactions/cache (round-trip de rede)Cache em filesystem local (mais rápido)
SegurançaIsolado por designRisco de runner compartilhado se não for efêmero

Comparação com Azure DevOps:

FatorAgentes hospedados pela MicrosoftAgentes self-hosted
Custo1 job paralelo gratuito, depois $40/job paralelo/mês$15/job paralelo/mês (licenciamento) + infra
ManutençãoGerenciado pela MicrosoftAutogerenciado
Ambiente limpoVM nova a cada jobPersistente
Acesso à redeApenas internet públicaAcesso a rede privada
Tempo de inicializaçãoPode ser lento devido ao provisionamentoRápido (pré-provisionado)

Tarefa 2: Configurar um runner self-hosted do GitHub no Linux

Provisione e configure um runner em uma VM Linux do Azure:

# Create an Azure VM for the runner
az vm create \
--resource-group contoso-runners-rg \
--name contoso-runner-linux-01 \
--image Ubuntu2404 \
--size Standard_D4s_v5 \
--admin-username runneradmin \
--generate-ssh-keys \
--nsg-rule SSH \
--vnet-name contoso-runners-vnet \
--subnet runners-subnet \
--public-ip-address ""

# SSH into the VM and install the runner
ssh runneradmin@<private-ip>

# Download and configure the GitHub Actions runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.321.0.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-linux-x64-2.321.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.321.0.tar.gz

# Configure the runner (get token from repo/org settings)
./config.sh \
--url https://github.com/contoso \
--token <REGISTRATION_TOKEN> \
--name contoso-runner-linux-01 \
--labels linux,docker,on-prem \
--runnergroup contoso-internal \
--work _work \
--replace

# Install and start as a service
sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status

Instale as ferramentas de build necessárias:

# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker runneradmin

# Install .NET SDK
wget https://dot.net/v1/dotnet-install.sh
chmod +x dotnet-install.sh
./dotnet-install.sh --channel 8.0

# Install Node.js via nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 20

# Install Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Tarefa 3: Configurar runner groups e labels

Configure runner groups para controle de acesso organizacional:

# Create a runner group (requires GitHub Enterprise)
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/orgs/contoso/actions/runner-groups \
-f name="internal-network" \
-f visibility="selected" \
-F selected_repository_ids[]="<repo-id-1>" \
-F selected_repository_ids[]="<repo-id-2>" \
-F allows_public_repositories=false

# List runner groups
gh api /orgs/contoso/actions/runner-groups

# Add runner to a group (done during config.sh with --runnergroup)

Use labels nos arquivos de workflow:

jobs:
build-ios:
runs-on: [self-hosted, macOS, xcode-15]
steps:
- uses: actions/checkout@v4
- run: xcodebuild -scheme ContosoApp -sdk iphoneos

integration-tests:
runs-on: [self-hosted, linux, on-prem]
steps:
- uses: actions/checkout@v4
- run: npm run test:integration
env:
SQL_SERVER: sql-server.contoso.internal:1433

docker-build:
runs-on: [self-hosted, linux, docker]
steps:
- uses: actions/checkout@v4
- run: docker build -t contoso-api:${{ github.sha }} .

Tarefa 4: Configurar agente self-hosted do Azure DevOps

Configure um agent pool e agente do Azure DevOps:

# Create an agent pool in Azure DevOps (via REST API, as CLI only supports list/show)
az devops invoke \
--area distributedtask \
--resource pools \
--org "https://dev.azure.com/contoso" \
--http-method POST \
--in-file - <<< '{"name": "contoso-linux-pool", "autoProvision": true}'

# On the agent VM, download and configure the agent
mkdir azagent && cd azagent
curl -o vsts-agent-linux-x64-3.248.0.tar.gz -L \
https://vstsagentpackage.azureedge.net/agent/3.248.0/vsts-agent-linux-x64-3.248.0.tar.gz
tar xzf ./vsts-agent-linux-x64-3.248.0.tar.gz

# Configure the agent with a PAT
./config.sh \
--unattended \
--url https://dev.azure.com/contoso \
--auth pat \
--token <PAT_TOKEN> \
--pool "contoso-linux-pool" \
--agent contoso-agent-linux-01 \
--acceptTeeEula \
--replace

# Install and start as a service
sudo ./svc.sh install
sudo ./svc.sh start

Declare capacidades do agente e demandas no pipeline:

pool:
name: contoso-linux-pool
demands:
- docker
- Agent.OS -equals Linux
- dotnet8

# Or use vmImage for hosted
pool:
vmImage: "ubuntu-latest"

Tarefa 5: Configurar agentes de scale set para auto-scaling

Use Azure Virtual Machine Scale Sets (VMSS) para pools de agentes elásticos:

# Create a VMSS for Azure DevOps agents
az vmss create \
--resource-group contoso-agents-rg \
--name contoso-agent-vmss \
--image Ubuntu2404 \
--vm-sku Standard_D4s_v5 \
--instance-count 0 \
--upgrade-policy-mode manual \
--single-placement-group false \
--admin-username agentadmin \
--generate-ssh-keys \
--vnet-name contoso-agents-vnet \
--subnet agents-subnet \
--load-balancer "" \
--custom-data cloud-init-agent.yaml

# Create scale set pool in Azure DevOps (via UI):
# Organization Settings > Agent pools > Add pool > Azure virtual machine scale set
# Configure:
# - Minimum agents: 0
# - Maximum agents: 10
# - Idle timeout: 30 minutes
# - Desired idle agents: 2

Configuração cloud-init para provisionamento automático do agente (cloud-init-agent.yaml):

#cloud-config
package_update: true
packages:
- docker.io
- curl
- git
- jq

runcmd:
- usermod -aG docker agentadmin
- systemctl enable docker
- systemctl start docker
- |
# Install .NET SDK
wget https://dot.net/v1/dotnet-install.sh -O /opt/dotnet-install.sh
chmod +x /opt/dotnet-install.sh
/opt/dotnet-install.sh --channel 8.0 --install-dir /usr/share/dotnet
ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet
- |
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs

Para GitHub Actions, use o Actions Runner Controller (ARC) no Kubernetes:

# Install ARC using Helm
helm install arc \
--namespace arc-systems \
--create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

# Create a runner scale set
helm install contoso-runners \
--namespace arc-runners \
--create-namespace \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
--set githubConfigUrl="https://github.com/contoso" \
--set githubConfigSecret.github_token="<PAT>" \
--set minRunners=1 \
--set maxRunners=10

Use no workflow:

jobs:
build:
runs-on: arc-runner-set # Matches the scale set name
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test

Tarefa 6: Considerações de segurança de runners e agentes

Runners efêmeros versus persistentes

# GitHub: Ephemeral runner (recommended for public repos)
# Configure during setup:
./config.sh --ephemeral \
--url https://github.com/contoso \
--token <TOKEN> \
--name ephemeral-runner-01

# Azure DevOps VMSS agents: configure "tear down after each use"
# In pool settings: "Automatically tear down virtual machines after every use" = Yes

Checklist de hardening de segurança

# 1. Run agent as non-root user with minimal permissions
useradd -m -s /bin/bash agentuser
# Configure runner under agentuser, not root

# 2. Restrict network access with firewall rules
az network nsg rule create \
--resource-group contoso-runners-rg \
--nsg-name runners-nsg \
--name AllowGitHub \
--priority 100 \
--direction Outbound \
--access Allow \
--protocol Tcp \
--destination-port-ranges 443 \
--destination-address-prefixes "140.82.112.0/20" "143.55.64.0/20"

# 3. Limit runner group to specific repositories
# 4. Use short-lived registration tokens
# 5. Enable audit logging for runner activity
# 6. Use just-in-time runner provisioning (ephemeral)

Gerenciamento de credenciais do runner

# GitHub: Use OIDC for cloud authentication (no stored secrets)
jobs:
deploy:
runs-on: [self-hosted, linux]
permissions:
id-token: write
contents: read
steps:
- name: Azure login with OIDC
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

Tarefa 7: Análise de custos

Cálculo de ponto de equilíbrio

GitHub-hosted runner costs (per minute):
Linux: $0.008/min
Windows: $0.016/min
macOS: $0.08/min

Monthly usage estimate for Contoso:
Linux builds: 200 builds x 10 min = 2,000 min = $16/month
macOS iOS: 50 builds x 20 min = 1,000 min = $80/month
Total hosted: $96/month

Self-hosted alternative:
Azure VM (Standard_D4s_v5):
On-demand: ~$140/month (always on)
Spot pricing: ~$28/month (with interruption risk)
Reserved 1yr: ~$89/month
Maintenance overhead: ~$500/month (engineer time)

Break-even analysis:
- Hosted is cheaper until ~50 hours/month on macOS
- Self-hosted makes sense when:
a) You need private network access (no hosted option)
b) Build times exceed 5,000+ min/month on Linux
c) macOS builds exceed 1,000 min/month
d) You need custom hardware or persistent caches

Matriz de decisão

RequisitoRecomendaçãoJustificativa
Builds iOS (macOS)Mac Mini self-hosted ou OrkaMultiplicador de custo 10x para macOS hospedado
Acesso a SQL on-premisesSelf-hosted na rede corporativaRunners hospedados não podem alcançar redes privadas
Builds Docker com cacheSelf-hosted com cache Docker localEvita baixar imagens base novamente a cada build
CI simples (lint, teste unitário)Hospedado pelo GitHubBaixo custo, zero manutenção
Compliance (residência de dados)Self-hosted na região exigidaControle sobre onde código e artefatos residem

Exercícios de quebra e conserto

Exercício 1: Falha de conectividade do runner

Um runner self-hosted aparece como "Offline" no GitHub. Diagnostique:

# Check runner service status
sudo ./svc.sh status
# Output: active (running)

# Check runner logs
cat _diag/Runner_*.log | tail -50
# Shows: "Failed to connect. Http response code: 403"

# Root cause: Registration token expired or runner was idle too long
# Fix: Re-register the runner
./config.sh remove --token <REMOVAL_TOKEN>
./config.sh \
--url https://github.com/contoso \
--token <NEW_REGISTRATION_TOKEN> \
--name contoso-runner-linux-01 \
--labels linux,docker,on-prem \
--replace
sudo ./svc.sh start

Exercício 2: Incompatibilidade de capacidade do agente

Um pipeline do Azure DevOps falha com "No agent found in pool matching demands":

pool:
name: contoso-linux-pool
demands:
- dotnet8
- docker
- Agent.OS -equals Linux

Diagnóstico: O agente não anuncia a capacidade dotnet8.

Mostrar solução

Correção: Adicione a capacidade ao agente ou defina-a como variável de ambiente:

# On the agent machine, add the capability
# Option 1: Environment variable (auto-detected)
echo 'export dotnet8=/usr/share/dotnet' >> ~/.bashrc

# Option 2: Add via Azure DevOps UI
# Organization Settings > Agent pools > contoso-linux-pool > Agents >
# Select agent > Capabilities > Add "dotnet8" = "/usr/share/dotnet"

# Restart the agent
sudo ./svc.sh stop
sudo ./svc.sh start

Verificação de conhecimento

1. Quando uma organização deve usar runners self-hosted em vez de runners hospedados pelo GitHub?

2. Qual é o propósito da flag '--ephemeral' ao configurar um runner self-hosted do GitHub Actions?

3. No Azure DevOps, o que determina se um agente self-hosted pode executar um pipeline específico?

4. Qual é a principal vantagem de usar Azure Virtual Machine Scale Sets (VMSS) para pools de agentes do Azure DevOps?

Limpeza

# Remove self-hosted GitHub runner
cd ~/actions-runner
sudo ./svc.sh stop
sudo ./svc.sh uninstall
./config.sh remove --token <REMOVAL_TOKEN>

# Delete the Azure VM
az vm delete --resource-group contoso-runners-rg --name contoso-runner-linux-01 --yes
az network nic delete --resource-group contoso-runners-rg --name contoso-runner-linux-01VMNic
az disk delete --resource-group contoso-runners-rg --name contoso-runner-linux-01_OsDisk --yes

# Remove Azure DevOps agent pool
az pipelines pool delete --pool-id <pool-id> \
--organization "https://dev.azure.com/contoso"

# Delete VMSS
az vmss delete --resource-group contoso-agents-rg --name contoso-agent-vmss

# Delete resource group
az group delete --name contoso-runners-rg --yes --no-wait
az group delete --name contoso-agents-rg --yes --no-wait