Como Criar um Self-Hosted Agent do Azure DevOps

Como Criar um Self-Hosted Agent do Azure DevOps

Compartilho como criei um Self-Hosted Agent do Azure DevOps

Introdução

Em um cenário de desenvolvimento ágil e integração contínua, a flexibilidade é importante. É nesse contexto que os Self-Hosted Agents do Azure DevOps emergem como uma solução poderosa, oferecendo controle total sobre seu ambiente de build e deploy.

Neste artigo, exploraremos passo a passo como criar seu próprio Agent Auto Hospedado do Azure DevOps. Essa abordagem não apenas amplia a capacidade de personalização do seu pipeline, mas também proporciona maior eficiência ao permitir a execução de builds em um ambiente familiar e sob seu controle direto.


Tipos de Agents do Azure DevOps

O Azure DevOps oferece flexibilidade na execução de pipelines de CI/CD através de três tipos de Agents:

  • Microsoft-Hosted Agents

  • Self-Hosted Agents

  • Azure Virtual Machine Scale Set Agents

Microsoft-Hosted Agents

  • Servidores Gerenciados pela Microsoft: Os Microsoft-Hosted Agents são servidores de build e deploy totalmente gerenciados e atualizados pela Microsoft e estão pronto para uso sem qualquer configuração adicional.

  • Limite Gratuito de Minutos por Mês: no momento da criação deste artigo, existe um limite de 1.800 minutos gratuitos por mês para o uso, oferecendo uma opção econômica para projetos menores.

  • Solicitação de Liberação de Minutos: A Microsoft começou a exigir a solicitação através de um formulário para liberar os 1.800 minutos gratuitos para evitar abusos, se você não o fizer receberá um erro parecido com:

No hosted parallelism has been purchased or granted

Essa mensagem de erro acontece porque por padrão as novas contas criadas recebem 0Microsoft-Hosted :

E para resolver isso, você pode clicar no link do próprio erro para preencher o formulário de liberação

Uma vez preenchido, você receberá por email um retorno dentro de 2 a 3 dias úteis

E isso será refletido em sua conta


Self-Hosted Agents

  • Controle Total: Você precisa hospedar ou gerenciar o servidor. O self-hosted oferece controle total ao permitir que você configure e mantenha seus próprios agentes de build.

  • Adaptabilidade ao Ambiente: Permite a execução de builds em um ambiente que você controla, com a flexibilidade de adaptar recursos conforme necessário.

  • Adequado para Projetos Complexos: Ideal para projetos maiores, equipes que requerem ambientes específicos ou aqueles que precisam integrar com recursos internos.

  • Cobrança: Você tem a cobrança do seu servidor, mas seus minutos são ilimitados e você não será cobrado pelo limite e preço do Azure DevOps.


Azure Virtual Machine Scale Set Agents

  • Elasticidade com Azure Virtual Machine Scale Set: Os Azure Virtual Machine Scale Set Agents oferecem elasticidade, permitindo a escala automática de máquinas virtuais conforme a demanda de build e deploy.

  • Integração com Azure DevOps: Projetados para integração direta com o Azure DevOps, esses agentes fornecem um ambiente escalável e gerenciável para pipelines de CI/CD.

  • Configuração Sob Demanda: Permitem a configuração sob demanda de máquinas virtuais, garantindo recursos adequados para lidar com a carga de trabalho, com a flexibilidade de ajustar conforme necessário.


Neste artigo, nos concentraremos no Self-Hosted Agent, ainda há vários tipos de instalação como por exemplo em Windows, Linux e Docker, nos concentraremos também na instalação em Docker para proporcionar maior isolamento e portabilidade.

Benefícios do Self-Hosted Agents

  • Configurações de build personalizadas: os agents permitem que você configure seu hardware para diferentes tipos de build. Por exemplo, se seu build tiver que consumir muitos recursos, usar hardware com mais memória pode melhorar o tempo de execução.

  • Acesse aplicativos ou bancos de dados internos: ao executar pipelines na infraestrutura do Azure DevOps com Microsoft-Hosted Agents, dificulta o trabalho para poder acessar sistemas internos como banco de dados por exemplo. Se você precisa executar testes de integração em seus bancos de dados ou aplicativos internos, você poderá fazer isso com agents auto hospedados na mesma rede fornecendo acesso necessário aos serviços internos.

  • Fluxos de trabalho híbridos: você pode otimizar seus recursos usando agents auto hospedados com configurações personalizadas para builds que exigem isso e usar a infraestrutura do Azure DevOps com Microsoft-Hosted Agents para outros trabalhos.


Tutorial Prático - Pré-Requisitos

Para a instalação de um Agent auto hospedado do Azure DevOps, como o nome sugere você precisará de uma máquina, isso pode ser desde seu próprio computador até qualquer VPS de sua preferência.

Para o tutorial deste artigo, usarei uma instância do AWS Lightsail, pois seu pacote de instância + tráfego de rede é ofertado a um preço mais acessível. Mas novamente você pode escolher de acordo com sua preferência como AWS EC2, Google Compute Engine, Digital Ocean, Vultr, Linode, etc.
💡
Se você já é mais experiente, ou se não deseja usar o Lightsail, você pode ignorar as etapas abaixo de criação de instância, criação de IP fixo e conexão SSH, não importa qual VPS você criou. Você pode pular diretamente para etapa de configuração dentro da instância e encurtar seu caminho deste artigo, mas se você é mais iniciante ou se está um pouco confuso nesse momento, recomendo seguir todos os passos.

Criando uma instância Lightsail

1- Acesse o console AWS → Lightsail → Create Instance

2- Escolha a região

Em nosso exemplo, escolhi a região Norte da Virgínia e a Zona de Disponibilidade A

3- Escolha o sistema operacional

Nem todos os sistemas operacionais são suportados, então escolhi a opção Ubuntu 22.04 que é a mais recente no momento da criação deste artigo

4- Não adicione um script de inicialização, não altere sua chave key-pair e não habilite snapshots automáticos, as opções padrões parecem boas aqui.

5- Escolha Dual Stack, pois ainda há limitações para uso do IPv6 e precisamos que nossa instância tenha IPv4 também.

6- Selecione o tamanho da instância

Lembre-se que essa decisão é muito importante, quanto menor a instância você escolher, mais lentidão e problemas seu pipeline pode ter por conta de falta de memória ou cpu. Para nosso artigo escolhi a opção small de USD 10 com 2GB de memória, 2 vCPU e 3TB de transferência de dados.
Em minhas experiências, as instâncias abaixo de 1GB de memória não chegam a funcionar corretamente, e tenho usado instâncias acima de 4GB de memória em ambientes de produção . Mas para efeitos de tutorial essa parece ser uma escolha boa.

7- Defina um nome, opcionalmente você pode inserir tags e clique em criar instância

8- Aguarde até o status Pending mudar para Running

Pronto! Nesse momento, você já tem uma instância criada com sucesso.

Criando um IP Fixo

Se olharmos bem, a instância Lightsail recebeu um IPv4 e IPv6 público dinâmicos, isso aconteceu porque selecionamos a opção Dual Stack quando criamos a instância. O dinâmico significa que quando desligarmos essa instância e ligarmos novamente, esses endereços IPs irão mudar, isso pode ser um pouco chato e frustrante de se lembrar, principalmente quando você está tentando se conectar a ela.

Então vamos resolver isso criando um IP Fixo.

1- No console AWS Lightsail → Networking

2- Clique em Create Static IP

3- Mantenha a mesma região e zona de disponibilidade da sua instância → escolha a instância recém criada

Em nosso exemplo Norte da Virgínia e Zona de Disponibilidade A

4- Defina um nome → Create

Pronto! Agora já temos um IP Fixo anexado a nossa instância Lightsail

Conectando via SSH na instância Lightsail

Você pode fazer isso de muitas formas, com qualquer cliente SSH de sua preferência.

Se você não quiser usar um cliente SSH, pode fazer isso diretamente no console da AWS clicando nesse botão:

E isso irá abrir uma nova janela com seu acesso com sucesso:

Se você estiver usando uma distribuição linux, como por exemplo ubuntu, poderia acessar também através do seu terminal com o comando:

ssh -i <LightsailDefaultKey.pem> <user>@<public-ip>
Lembre-se que LightsailDefaultKey.pem é sua chave de acesso baixada do console da AWS, então especifique certinho o caminho da pasta para onde você fez download em sua máquina, também lembre-se que usamos o ubuntu, então mude user para ubuntu e <public-ip> para seu ip fixo criado.
Para nosso exemplo isso seria:
# Example
ssh -i LightsailDefaultKey.pem ubuntu@54.160.152.44

Porém como estou usando em meu computador o sistema operacional Windows, eu poderia fazer isso através do WSL2 com Prompt de Comando , Powershell ou Terminal do Windows, o Putty e muitas outras opções de clientes SSH disponíveis.

Mas prefiro usar o cliente gratuito chamado Termius, você pode obtê-lo em sua página de download e talvez seja necessário criar uma conta gratuita, há planos pagos disponíveis, mas o gratuito aqui é suficiente.

Importante ressaltar que a escolha do seu cliente SSH é totalmente opcional.

Também há opções disponíveis para Mac e Linux no final da página;

Não importa qual ferramenta SSH você usará, vimos agora que há muitas opções, tudo que você precisa ter em mente aqui é que você precisa se conectar na instância de alguma forma para seguirmos o tutorial.


Como acabamos de ver, vou utilizar o cliente SSH Termius, então nossos prints irão se basear nisso, mas uma vez que você estiver conectado, os comandos serão todos iguais independente de qual ferramenta usou.

1- Acesse o console AWS Lightsail → Account → SSH Keys

2- Procure por Default Keys → download

Lembre-se que estou usando Norte da Virgínia, por isso faço download da chave padrão dessa região.

3- Abra o cliente SSH (em nosso exemplo Termius) → clique no ícone de engrenagem

4- Defina um nome de sua preferência → mova o arquivo da sua chave keypair que baixou do console AWS

5- Clique em New Host

6- Preencha os campos com:

  • Address: o IP da instância

  • Label: nome de sua preferência

  • User: ubuntu

  • Selecione Key

Clique em Connect

Clique em Continue

Pronto! Você está conectado.

Instalando o Docker e Docker Compose

Uma vez conectado na instância, você precisará instalar o Docker e o Docker Compose, por favor copie o código de instalação que criamos em outro artigo disponível aqui:

Cole em seu terminal e pressione Enter.

Aguarde a instalação e ao final você deverá ver algo parecido com:

Agora feche e abra seu terminal novamente para que os comandos docker sejam atualizados.
Essa instalação é muito importante, pois precisaremos dela mais a frente para a instalação do self-hosted, além disso muito provavelmente seu agent precisará do docker para baixar e construir imagens em seu pipeline através do docker in doker (dind). Então certifique-se de concluir essa instalação antes de seguir aos próximos passos.
Pronto! Você está com todos os pré-requisitos deste tutorial concluídos.

Criando um Personal Access Token (PAT)

1- Faça login no console Azure DevOps → ícone de engrenagem → Personal Access Token

2- Clique em New Token

3- Defina:

  • Um nome de sua preferência, por exemplo self-hosted-agent-token

  • Sua organização

  • Altere o tempo de expiração de acordo com sua preferência, por exemplo gosto de definir o tempo máximo de um ano através da seleção no calendário, mas totalmente opcional

  • Em scope, selecione Custom definiedShow all scopes

4- Em Agent Pools → selecione Read & manageCreate

5- Copie e salve seu token em um local seguro agora

Isso não será acessível depois


Instalando o Self-Hosted Agent

1- Abra novamente seu terminal → crie o diretório chamado azp-agent-in-docker com o comando:

mkdir ~/azp-agent-in-docker \
&& cd ~/azp-agent-in-docker

Nós criaremos os arquivos chamados Dockerfile e start.sh nesta pasta nos próximos passos, certifique-se que ambos os arquivos estejam na mesma pasta.

2- Crie o arquivo Dockerfile com o comando:

touch Dockerfile

3- Insira o seguinte conteúdo em seu Dockerfile:

Você pode fazer através de qualquer editor de texto de sua preferência como por exemplo, VSCode, vim, nano, etc. Para nosso exemplo estamos usando o nano para editar o arquivo.
sudo nano Dockerfile

E então eu copio o código abaixo:
# Use Ubuntu 22.04 as the base image
FROM ubuntu:22.04

# Update package information and upgrade installed packages
RUN apt update && \
    apt upgrade -y

# Install dependencies and capabilities
RUN apt install -y \
    curl \
    git \
    jq \
    libicu70

# Set the target architecture (e.g., "linux-x64", "linux-arm", "linux-arm64")
ENV TARGETARCH="linux-x64"

# Set the working directory inside the container
WORKDIR /azp/

# Copy the start script to the working directory
COPY ./start.sh ./

# Make the start script executable
RUN chmod +x ./start.sh

# Create a user 'agent' and set ownership of the working directory
RUN useradd agent && \
    chown agent ./

# Switch to the 'agent' user
USER agent

# Another option is to run the agent as root.
# ENV AGENT_ALLOW_RUNASROOT="true"

# Define the entry point for the container to execute the start script
ENTRYPOINT ./start.sh
E colo no terminal com o botão direito do mouse

Depois pressiono Ctrl + X + Y (para sim) + Enter para salvar
💡
E podemos ver o arquivo criado com o comando:
cat Dockerfile

4- Agora crio o arquivo chamado start.sh com o comando:

touch start.sh

5- Insira o seguinte conteúdo em seu start.sh:

Você pode fazer através de qualquer editor de texto de sua preferência como por exemplo, VSCode, vim, nano, etc. Para nosso exemplo estamos usando o nano para editar o arquivo.
sudo nano start.sh

E então eu copio o código abaixo:
#!/bin/bash
set -e

if [ -z "${AZP_URL}" ]; then
  echo 1>&2 "error: missing AZP_URL environment variable"
  exit 1
fi

if [ -z "${AZP_TOKEN_FILE}" ]; then
  if [ -z "${AZP_TOKEN}" ]; then
    echo 1>&2 "error: missing AZP_TOKEN environment variable"
    exit 1
  fi

  AZP_TOKEN_FILE="/azp/.token"
  echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}"
fi

unset AZP_TOKEN

if [ -n "${AZP_WORK}" ]; then
  mkdir -p "${AZP_WORK}"
fi

cleanup() {
  trap "" EXIT

  if [ -e ./config.sh ]; then
    print_header "Cleanup. Removing Azure Pipelines agent..."

    # If the agent has some running jobs, the configuration removal process will fail.
    # So, give it some time to finish the job.
    while true; do
      ./config.sh remove --unattended --auth "PAT" --token $(cat "${AZP_TOKEN_FILE}") && break

      echo "Retrying in 30 seconds..."
      sleep 30
    done
  fi
}

print_header() {
  lightcyan="\033[1;36m"
  nocolor="\033[0m"
  echo -e "\n${lightcyan}$1${nocolor}\n"
}

# Let the agent ignore the token env variables
export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE"

print_header "1. Determining matching Azure Pipelines agent..."

AZP_AGENT_PACKAGES=$(curl -LsS \
    -u user:$(cat "${AZP_TOKEN_FILE}") \
    -H "Accept:application/json;" \
    "${AZP_URL}/_apis/distributedtask/packages/agent?platform=${TARGETARCH}&top=1")

AZP_AGENT_PACKAGE_LATEST_URL=$(echo "${AZP_AGENT_PACKAGES}" | jq -r ".value[0].downloadUrl")

if [ -z "${AZP_AGENT_PACKAGE_LATEST_URL}" -o "${AZP_AGENT_PACKAGE_LATEST_URL}" == "null" ]; then
  echo 1>&2 "error: could not determine a matching Azure Pipelines agent"
  echo 1>&2 "check that account "${AZP_URL}" is correct and the token is valid for that account"
  exit 1
fi

print_header "2. Downloading and extracting Azure Pipelines agent..."

curl -LsS "${AZP_AGENT_PACKAGE_LATEST_URL}" | tar -xz & wait $!

source ./env.sh

trap "cleanup; exit 0" EXIT
trap "cleanup; exit 130" INT
trap "cleanup; exit 143" TERM

print_header "3. Configuring Azure Pipelines agent..."

./config.sh --unattended \
  --agent "${AZP_AGENT_NAME:-$(hostname)}" \
  --url "${AZP_URL}" \
  --auth "PAT" \
  --token $(cat "${AZP_TOKEN_FILE}") \
  --pool "${AZP_POOL:-Default}" \
  --work "${AZP_WORK:-_work}" \
  --replace \
  --acceptTeeEula & wait $!

print_header "4. Running Azure Pipelines agent..."

chmod +x ./run.sh

# To be aware of TERM and INT signals call ./run.sh
# Running it with the --once flag at the end will shut down the agent after the build is executed
./run.sh "$@" & wait $!
E colo no terminal com o botão direito do mouse

Depois pressiono Ctrl + X + Y (para sim) + Enter para salvar
💡
E podemos ver o arquivo criado com o comando:
cat start.sh

💡
Agora podemos ver a criação dos dois arquivos Dockerfile e start.sh criados na mesma pasta azp-agent-in-docker com o comando:
ls -1

6- Agora construo a imagem com o comando:

docker build -t "azp-agent:linux" -f Dockerfile .

E podemos confirmar a imagem docker criada com sucesso com o comando:
docker image ls --format "table {{.ID}}\t{{.Repository}}"

Pronto! Já temos nossa imagem pronta para uso.

7- Agora vamos precisar de três informações para poder iniciar nosso container:

  • AZP_URL:

    Você pode obtê-la olhando para a barra de endereço do seu navegador:

    🔗
    dev.azure.com<your-organization>

  • AZP_POOL:

    Você precisa criar um novo em Organization Settings → Agent Pools → Add Pool

    Selecionar o tipo Self-Hosted

    Definir um nome de sua preferência, por exemplo AWS-Lightsail-Self-Hosted-Linux-Docker → manter a opção Auto-provision this agent pool in all projects marcado → Create

  • AZP_TOKEN:

    Este é o Personal Access Token (PAT) que criamos e copiamos para um local seguro antes.


8- Com essas três informações em mãos, vamos iniciar o container com o seguinte comando:

Lembre-se de substituir os valores para seus dados reais
docker run \
  -e AZP_URL="<Your Azure DevOps instance>" \
  -e AZP_TOKEN="<Your Personal Access Token>" \
  -e AZP_POOL="<Your Agent Pool Name>" \
  -e AZP_AGENT_NAME="AWS Lightsail Docker Agent 1" \
  --name "azp-agent-1" \
  azp-agent:linux

Pronto! Podemos ver o Agent pronto para uso

💡
E podemos verificar os capabilities com as ferramentas úteis que foram listadas no Dockerfile

💡
Por padrão, o container não irá iniciar automaticamente se sua instância for desligada ou reiniciada. Então vamos garantir que o container sempre se inicie com o restart policy deste container do agent

9- Digite o comando abaixo para atualizar o restart policy deste container:

Lembre-se de alterar <your-container-id> para o ID de seu container obtido com o comando docker ps -a por exemplo
docker update --restart always <your-container-id>

10- Verifique a inspeção de container com o novo restart policy definido:

docker inspect -f '{{ json .HostConfig.RestartPolicy }}' <your-container-id>
Pronto! Seu agent auto hospedado já está online e pronto para uso.

Usando o Self-Hosted Agent

Se você estiver usando o arquivo azure-pipelines.yml você pode definir seu Self-Hosted Agent no trecho pool, por exemplo:

trigger:
- main

pool:
  # Name of your agent pool
  name: 'AWS-Lightsail-Self-Hosted-Linux-Docker'  

jobs:
- job: RunOnSelfHostedAgent
  displayName: 'Run on AWS Lightsail Docker Agent'
  steps:
  - script: echo "Hi, I'm running on AWS Lightsail Docker Agent 1"
    displayName: 'Print Message'

E executando o pipeline, podemos visualizar em Initialize Job o agente self-hosted agent que criamos sendo usado.

Se voltarmos para nosso terminal, podemos ver exatamente o nome do container igual ao apresentado acima com o comando:
docker ps --format "table {{.ID}}\t{{.Names}}" --all


Se você estiver usando o editor classic , poderá fazer isso selecionando o pool


Bônus - Atualização dos Agents

A qualquer momento, você pode atualizar seus agents em Organization Settings → Agent Pools → More Options (três pontinhos) → Update Agent


Conclusão

Em meio ao dinâmico cenário da automação e integração contínua, o Self-Hosted Agent do Azure DevOps emerge como uma ferramenta poderosa para capacitar equipes de desenvolvimento. Neste artigo, exploramos os diferentes tipos de agents disponíveis, destacando a versatilidade e flexibilidade do Self-Hosted Agent.

Ao conduzir um passo a passo prático, mergulhamos na criação de um Self-Hosted Agent utilizando o Docker. Essa abordagem não apenas oferece controle total sobre o ambiente de execução, mas também demonstra a escalabilidade e eficiência proporcionadas pelo uso de containers.

Vimos que os passos são manuais e você pode repeti-los quantas vezes for necessário. Também pode ainda pensar em uma forma de automatizar e escalar a criação de seus Self-Hosted Agents, mas para a maioria das pessoas isso será o suficiente.

Ao concluir esta jornada, espero que você esteja equipado com os conhecimentos necessários para aproveitar ao máximo o potencial do Self-Hosted Agent no Azure DevOps. A capacidade de personalizar, escalar e integrar-se perfeitamente ao seu ambiente torna esse recurso essencial para otimizar seus fluxos de trabalho de CI/CD.

Lembre-se de excluir todos os recursos ao final para não obter cobranças indesejadas e manter sua conta AWS limpa e organizada.
Espero que as informações tenham sido úteis para você!