DevOps & CI/CD

Como Usar Azure para DevOps: AKS, Azure Pipelines e Resource Groups em Produção

16 min de leitura

Como Usar Azure para DevOps: AKS, Azure Pipelines e Resource Groups em Produção

Azure Resource Groups: Fundação da Organização em Nuvem Um Resource Group (Grupo de Recursos) no Azure é um container lógico que agrupa todos os recursos necessários para uma solução. Diferentemente de outras plataformas de nuvem que distribuem recursos por regiões ou projetos sem uma organização clara, o Azure força uma abordagem estruturada e explícita. Você não pode criar um recurso sem designá-lo a um grupo específico; isso garante rastreabilidade, controle de acesso e faturamento consolidado. A importância de uma estratégia bem definida de Resource Groups vai além da organização visual. Quando você implanta um grupo inteiro, pode destruir todos os seus recursos de uma vez, o que é essencial para ambientes de teste e desenvolvimento. Além disso, você pode aplicar políticas (Azure Policy), controle de acesso baseado em função (RBAC) e tags a todos os recursos dentro de um grupo, economizando configurações repetitivas e reduzindo erros humanos. Criando e Gerenciando Resource Groups A forma mais direta de criar um Resource

<h2>Azure Resource Groups: Fundação da Organização em Nuvem</h2>

<p>Um Resource Group (Grupo de Recursos) no Azure é um container lógico que agrupa todos os recursos necessários para uma solução. Diferentemente de outras plataformas de nuvem que distribuem recursos por regiões ou projetos sem uma organização clara, o Azure força uma abordagem estruturada e explícita. Você não pode criar um recurso sem designá-lo a um grupo específico; isso garante rastreabilidade, controle de acesso e faturamento consolidado.</p>

<p>A importância de uma estratégia bem definida de Resource Groups vai além da organização visual. Quando você implanta um grupo inteiro, pode destruir todos os seus recursos de uma vez, o que é essencial para ambientes de teste e desenvolvimento. Além disso, você pode aplicar políticas (Azure Policy), controle de acesso baseado em função (RBAC) e tags a todos os recursos dentro de um grupo, economizando configurações repetitivas e reduzindo erros humanos.</p>

<h3>Criando e Gerenciando Resource Groups</h3>

<p>A forma mais direta de criar um Resource Group é pela CLI do Azure. Vou mostrar um exemplo prático e funcional:</p>

<pre><code class="language-bash"># Instalar Azure CLI (se ainda não tiver)

No macOS: brew install azure-cli

No Windows: choco install azure-cli

# No Linux: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Login no Azure

az login

Criar um Resource Group

az group create \

--name rg-produção-aks \

--location eastus

Listar todos os grupos de recursos

az group list --output table

Deletar um Resource Group (cuidado: deleta tudo dentro dele)

az group delete --name rg-produção-aks --yes</code></pre>

<p>A flag <code>--location</code> é crítica: ela define a região onde os recursos serão hospedados. Escolha regiões próximas aos seus usuários finais para reduzir latência. A nomeação também importa: use prefixos que indiquem propósito (rg-), ambiente (produção, dev) e contexto (aks, database), tornando gerenciamento futuro mais fácil.</p>

<h3>Aplicando Tags e Políticas</h3>

<p>Tags são metadados que você aplica a recursos para rastreamento de custos, propriedade e ciclo de vida. Quando você tiver centenas de recursos, tags corretas são a diferença entre saber exatamente o que você está pagando e ter uma fatura misteriosa no final do mês.</p>

<pre><code class="language-bash"># Adicionar tags a um Resource Group existente

az group update \

--name rg-produção-aks \

--tags ambiente=produção proprietario=devops custo-centro=eng-001

Consultar grupos por tag

az group list --query &quot;[?tags.ambiente==&#039;produção&#039;]&quot; --output table

Criar um grupo com tags já incluídas (mais eficiente)

az group create \

--name rg-dev-aks \

--location eastus \

--tags ambiente=desenvolvimento proprietario=devops</code></pre>

<h2>Azure Kubernetes Service (AKS): Orquestração de Contêineres Simplificada</h2>

<p>Azure Kubernetes Service é a oferta gerenciada do Azure para Kubernetes. Diferentemente de gerenciar um cluster Kubernetes do zero, o AKS abstrai a complexidade da camada de controle (control plane), permitindo que você foque em aplicações, não em infraestrutura. A Microsoft gerencia atualizações, patches de segurança e alta disponibilidade do control plane automaticamente.</p>

<p>Um cluster AKS reside dentro de um Resource Group e depende de uma rede virtual (VNet), mas para começar você não precisa se preocupar com essas complexidades—o AKS cria muitos desses recursos automaticamente. O que você realmente controla são os node pools (grupos de nós onde seus contêineres executam), versão do Kubernetes, scaling e acesso à rede.</p>

<h3>Criando um Cluster AKS</h3>

<pre><code class="language-bash"># Pré-requisito: ter um Resource Group criado (feito na seção anterior)

Criar um cluster AKS básico

az aks create \

--resource-group rg-produção-aks \

--name cluster-produção \

--node-count 3 \

--vm-set-type VirtualMachineScaleSets \

--load-balancer-sku standard \

--enable-managed-identity \

--network-plugin azure \

--docker-bridge-address 172.17.0.1/16 \

--service-cidr 10.0.0.0/16 \

--dns-service-ip 10.0.0.10 \

--generate-ssh-keys \

--tier free

Este comando leva cerca de 5-10 minutos. Enquanto isso, entenda os parâmetros:

--node-count: quantidade inicial de nós

--vm-set-type VirtualMachineScaleSets: permite scaling automático (recomendado)

--enable-managed-identity: segurança—deixe o Azure gerenciar credenciais

--network-plugin azure: usa CNI do Azure (mais flexível que kubenet)</code></pre>

<p>Após a criação, você precisa configurar suas credenciais locais para interagir com o cluster:</p>

<pre><code class="language-bash"># Obter credenciais do cluster

az aks get-credentials \

--resource-group rg-produção-aks \

--name cluster-produção \

--overwrite-existing

Verificar que kubectl está apontando para o cluster correto

kubectl cluster-info

kubectl get nodes</code></pre>

<p>O output do <code>kubectl get nodes</code> mostrará algo assim:</p>

<pre><code>NAME STATUS ROLES AGE VERSION

aks-nodepool1-12345678-vmss000000 Ready agent 2m34s v1.27.3

aks-nodepool1-12345678-vmss000001 Ready agent 2m28s v1.27.3

aks-nodepool1-12345678-vmss000002 Ready agent 2m22s v1.27.3</code></pre>

<h3>Escalabilidade e Node Pools</h3>

<p>Um dos maiores benefícios do AKS é a capacidade de ter múltiplos node pools com diferentes configurações. Imagine que você tem aplicações web que precisam de CPU e aplicações de processamento que precisam de GPU. Em vez de um cluster homogêneo, você cria pools diferentes:</p>

<pre><code class="language-bash"># Criar um node pool adicional com máquinas mais poderosas

az aks nodepool add \

--resource-group rg-produção-aks \

--cluster-name cluster-produção \

--name gpupool \

--node-count 2 \

--vm-set-type VirtualMachineScaleSets \

--node-vm-size Standard_NC6 \

--labels workload=gpu \

--taints gpu=true:NoSchedule

Listar todos os node pools

az aks nodepool list \

--resource-group rg-produção-aks \

--cluster-name cluster-produção \

--output table

Configurar auto-scaling para um pool

az aks nodepool update \

--resource-group rg-produção-aks \

--cluster-name cluster-produção \

--name nodepool1 \

--enable-cluster-autoscaler \

--min-count 2 \

--max-count 10</code></pre>

<p>A taint <code>gpu=true:NoSchedule</code> garante que apenas pods que tolerem essa taint sejam agendados nesse pool. Em seu YAML de deployment, você declararia:</p>

<pre><code class="language-yaml">apiVersion: apps/v1

kind: Deployment

metadata:

name: ml-training-job

spec:

replicas: 1

selector:

matchLabels:

app: ml-training

template:

metadata:

labels:

app: ml-training

spec:

nodeSelector:

workload: gpu

tolerations:

  • key: gpu

operator: Equal

value: &quot;true&quot;

effect: NoSchedule

containers:

  • name: training

image: seu-registro.azurecr.io/ml-training:latest

resources:

limits:

nvidia.com/gpu: 1</code></pre>

<h2>Azure Pipelines: Automação de Build, Teste e Deploy</h2>

<p>Azure Pipelines é o serviço de CI/CD (Integração Contínua / Entrega Contínua) do Azure. Ele automatiza o processo de pegar código do repositório, compilar, testar e fazer deploy para produção—ou qualquer outro ambiente. Sem pipelines, um desenvolvedor teria que fazer esses passos manualmente toda vez, o que é lento, propenso a erros e não escala com times crescentes.</p>

<p>A beleza do Azure Pipelines é que ele integra perfeitamente com o Azure DevOps, GitHub, GitLab e Bitbucket. Você define seus passos em um arquivo YAML (chamado <code>azure-pipelines.yml</code>) no repositório, e o sistema executa automaticamente quando você faz push de código. Você também pode usar a interface visual, mas YAML é mais versionado e colaborativo.</p>

<h3>Estrutura de um Pipeline Básico</h3>

<pre><code class="language-yaml"># azure-pipelines.yml

Este arquivo deve estar na raiz do seu repositório

trigger:

  • main # Executa quando houver push na branch main

pool:

vmImage: &#039;ubuntu-latest&#039; # Ambiente onde o pipeline executa

variables:

dockerRegistryServiceConnection: &#039;seu-registro-acr&#039;

imageRepository: &#039;seu-app&#039;

containerRegistry: &#039;seuregistry.azurecr.io&#039;

dockerfilePath: &#039;$(Build.SourcesDirectory)/Dockerfile&#039;

tag: &#039;$(Build.BuildId)&#039;

stages:

  • stage: Build

displayName: Build e Push da Imagem Docker

jobs:

  • job: BuildImage

displayName: Build Image

steps:

  • task: Docker@2

displayName: Build e Push Imagem

inputs:

command: buildAndPush

repository: $(imageRepository)

dockerfile: $(dockerfilePath)

containerRegistry: $(dockerRegistryServiceConnection)

tags: |

$(tag)

latest

  • stage: Deploy

displayName: Deploy para AKS

dependsOn: Build

condition: succeeded()

jobs:

  • deployment: DeployToAKS

displayName: Deploy para Produção

environment: &#039;production&#039;

strategy:

runOnce:

deploy:

steps:

  • task: KubernetesManifest@0

displayName: Deploy

inputs:

action: &#039;deploy&#039;

kubernetesServiceConnection: &#039;cluster-produção&#039;

namespace: &#039;default&#039;

manifests: |

$(Pipeline.Workspace)/manifests/deployment.yml

$(Pipeline.Workspace)/manifests/service.yml</code></pre>

<p>Este pipeline tem dois estágios: primeiro faz build da imagem Docker e envia para um registro de container (Azure Container Registry), depois realiza o deploy dessa imagem no AKS. O <code>dependsOn: Build</code> garante que o Deploy só aconteça se o Build tiver sucesso.</p>

<h3>Conectando Azure Pipelines ao Azure Container Registry (ACR)</h3>

<p>Você precisa de um registro privado para armazenar suas imagens Docker. Azure Container Registry é gerenciado, integrado e seguro:</p>

<pre><code class="language-bash"># Criar um registro de container

az acr create \

--resource-group rg-produção-aks \

--name seuregistry \

--sku Basic \

--admin-enabled false

Autenticar o AKS para puxar imagens do ACR

az aks update \

--resource-group rg-produção-aks \

--name cluster-produção \

--attach-acr seuregistry

Listar imagens no registro

az acr repository list --name seuregistry --output table</code></pre>

<p>A integração com <code>az aks update --attach-acr</code> é essencial: ela cria uma identidade gerenciada que permite que o AKS puxe imagens sem você precisar de senhas ou tokens.</p>

<h3>Estrutura Recomendada de Manifests Kubernetes</h3>

<p>Seus manifests (arquivos YAML) devem estar em um diretório chamado <code>manifests</code> no repositório:</p>

<pre><code class="language-yaml"># manifests/deployment.yml

apiVersion: apps/v1

kind: Deployment

metadata:

name: minha-aplicacao

namespace: default

spec:

replicas: 3

selector:

matchLabels:

app: minha-aplicacao

template:

metadata:

labels:

app: minha-aplicacao

spec:

containers:

  • name: app

image: seuregistry.azurecr.io/seu-app:latest

ports:

  • containerPort: 8080

env:

  • name: ENVIRONMENT

value: &quot;production&quot;

resources:

requests:

memory: &quot;256Mi&quot;

cpu: &quot;100m&quot;

limits:

memory: &quot;512Mi&quot;

cpu: &quot;500m&quot;

livenessProbe:

httpGet:

path: /health

port: 8080

initialDelaySeconds: 30

periodSeconds: 10

readinessProbe:

httpGet:

path: /ready

port: 8080

initialDelaySeconds: 5

periodSeconds: 5

imagePullSecrets:

  • name: acr-secret</code></pre>

<pre><code class="language-yaml"># manifests/service.yml

apiVersion: v1

kind: Service

metadata:

name: minha-aplicacao-service

namespace: default

spec:

type: LoadBalancer

selector:

app: minha-aplicacao

ports:

  • protocol: TCP

port: 80

targetPort: 8080</code></pre>

<h3>Variáveis e Secrets no Pipeline</h3>

<p>Nunca coloque senhas ou chaves direto no arquivo YAML. Use variáveis de pipeline:</p>

<pre><code class="language-yaml"># azure-pipelines.yml (continuação)

variables:

dockerRegistryServiceConnection: &#039;seu-registro-acr&#039;

imageRepository: &#039;seu-app&#039;

containerRegistry: &#039;seuregistry.azurecr.io&#039;

tag: &#039;$(Build.BuildId)&#039;

Sensitive variables não devem ser declaradas aqui,

mas sim em Library &gt; Secure Files ou Variable Groups

steps:

  • task: Docker@2

displayName: Build e Push Imagem

inputs:

command: buildAndPush

repository: $(imageRepository)

dockerfile: $(dockerfilePath)

containerRegistry: $(dockerRegistryServiceConnection)

tags: |

$(tag)

latest</code></pre>

<p>No Azure DevOps, você cria uma &quot;Variable Group&quot; no Pipeline &gt; Library com suas variáveis sensíveis (senhas, tokens), e referencia:</p>

<pre><code class="language-yaml">variables:

  • group: &#039;production-secrets&#039; # Criada na UI</code></pre>

<h2>Integrando AKS, Pipelines e Resource Groups: Um Fluxo Completo</h2>

<p>Até aqui você entendeu cada peça isoladamente. Agora vamos ver como elas se conectam em um fluxo real de DevOps. Você tem um repositório com código, um pipeline que builda uma imagem Docker, um registro de container que armazena a imagem, um cluster AKS que roda a aplicação, e tudo organizado dentro de um Resource Group no Azure.</p>

<p>O fluxo típico é: você faz <code>git push</code> do seu código → Azure Pipelines detecta a mudança → compila e testa o código → builda uma imagem Docker → envia para ACR → executa testes de integração → faz deploy automático no AKS. Se algo falhar em qualquer etapa, o pipeline para e você é notificado. Se tudo passar, sua aplicação atualizada está rodando em produção em minutos.</p>

<h3>Exemplo de Pipeline End-to-End</h3>

<pre><code class="language-yaml"># azure-pipelines.yml completo com teste e deploy

trigger:

  • main

pool:

vmImage: &#039;ubuntu-latest&#039;

variables:

dockerRegistryServiceConnection: &#039;seu-acr-connection&#039;

imageRepository: &#039;minha-app&#039;

containerRegistry: &#039;seuregistry.azurecr.io&#039;

dockerfilePath: &#039;$(Build.SourcesDirectory)/Dockerfile&#039;

tag: &#039;$(Build.BuildId)&#039;

kubernetesServiceConnection: &#039;cluster-produção&#039;

resourceGroupName: &#039;rg-produção-aks&#039;

clusterName: &#039;cluster-produção&#039;

stages:

  • stage: Build

displayName: Build e Testes

jobs:

  • job: TestAndBuild

displayName: Executar Testes e Build

steps:

Step 1: Checkout do código (automático, mas explícito é melhor)

  • checkout: self

Step 2: Setup Node.js (ou qualquer linguagem que você use)

  • task: NodeTool@0

inputs:

versionSpec: &#039;18.x&#039;

Step 3: Instalar dependências e rodar testes

- script: |

npm install

npm run test

npm run lint

displayName: &#039;Instalar dependências e rodar testes&#039;

Step 4: Build da aplicação

  • script: npm run build

displayName: &#039;Build da aplicação&#039;

Step 5: Build e Push da imagem Docker

  • task: Docker@2

displayName: &#039;Build e Push da imagem Docker&#039;

inputs:

command: buildAndPush

repository: $(imageRepository)

dockerfile: $(dockerfilePath)

containerRegistry: $(dockerRegistryServiceConnection)

tags: |

$(tag)

latest

  • stage: Deploy

displayName: Deploy para AKS

dependsOn: Build

condition: succeeded()

jobs:

  • deployment: DeployToProduction

displayName: Deploy em Produção

environment: &#039;production&#039;

strategy:

runOnce:

deploy:

steps:

Step 1: Baixar manifests publicados no build anterior

  • download: current

artifact: manifests

Step 2: Substituir tag da imagem nos manifests

  • task: KubernetesManifest@0

displayName: &#039;Bake manifests&#039;

name: bake

inputs:

action: &#039;bake&#039;

renderingEngine: &#039;kustomize&#039;

kustomizationPath: &#039;$(Pipeline.Workspace)/manifests/kustomize&#039;

Step 3: Deploy no AKS

  • task: KubernetesManifest@0

displayName: &#039;Deploy no AKS&#039;

inputs:

action: &#039;deploy&#039;

kubernetesServiceConnection: $(kubernetesServiceConnection)

namespace: &#039;default&#039;

manifests: $(bake.manifestsBundle)

Step 4: Verificar status do deployment

- script: |

kubectl rollout status deployment/minha-aplicacao -n default --timeout=5m

displayName: &#039;Aguardar Deployment estar pronto&#039;</code></pre>

<h3>Monitorando e Troubleshooting</h3>

<p>Depois que seu pipeline deploya, você precisa saber se a aplicação está saudável. Use esses comandos para investigar:</p>

<pre><code class="language-bash"># Ver status dos pods

kubectl get pods -n default

kubectl describe pod &lt;pod-name&gt; -n default

Ver logs da aplicação

kubectl logs deployment/minha-aplicacao -n default --tail=100

kubectl logs deployment/minha-aplicacao -n default -f # -f para seguir em tempo real

Verificar eventos recentes

kubectl get events -n default --sort-by=&#039;.lastTimestamp&#039;

Acessar um pod interativamente (útil para debug)

kubectl exec -it &lt;pod-name&gt; -n default -- /bin/sh

Ver métricas de recursos

kubectl top nodes

kubectl top pods -n default

Verificar status do deployment

kubectl rollout status deployment/minha-aplicacao -n default

kubectl rollout history deployment/minha-aplicacao -n default</code></pre>

<h2>Conclusão</h2>

<p>Você aprendeu que <strong>Resource Groups são a fundação organizacional</strong>—eles agrupam logicamente todos os seus recursos Azure, permitem controle de acesso centralizado, e facilitam limpeza e rastreamento de custos. Uma boa estratégia de nomeação e tags em Resource Groups economiza horas de troubleshooting depois.</p>

<p><strong>AKS abstrai complexidade de Kubernetes</strong> mantendo você focado em aplicações, não em infraestrutura. Node pools permitem customização de cargas de trabalho (GPU, memória, CPU) sem precisar de múltiplos clusters. Auto-scaling garante que você não desperdice recursos nem fique sem capacidade.</p>

<p><strong>Azure Pipelines orquestra o fluxo inteiro</strong>—de código para produção—removendo trabalho manual e tornando deploys repetíveis, rastreáveis e seguro. Combinados, esses três serviços formam uma plataforma de DevOps robusta que cresce com sua organização, desde startups até enterprises.</p>

<p>O próximo passo é praticar: crie um cluster AKS, configure um pipeline simples, e observe como uma mudança no seu código percorre automaticamente todo o caminho até produção. Erros acontecem, e esse é o valor real—quando você consegue iterar rápido e com segurança.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://learn.microsoft.com/pt-br/azure/aks/" target="_blank" rel="noopener noreferrer">Documentação oficial do Azure Kubernetes Service (AKS)</a></li>

<li><a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/" target="_blank" rel="noopener noreferrer">Azure Pipelines Documentation</a></li>

<li><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal" target="_blank" rel="noopener noreferrer">Azure Resource Groups - Best Practices</a></li>

<li><a href="https://learn.microsoft.com/en-us/azure/aks/best-practices" target="_blank" rel="noopener noreferrer">Kubernetes Best Practices on Azure</a></li>

<li><a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/ecosystems/kubernetes/deploy" target="_blank" rel="noopener noreferrer">CI/CD com Azure DevOps e Kubernetes</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em DevOps & CI/CD

O que Todo Dev Deve Saber sobre Terraform Avançado: Módulos, Workspaces e Remote State
O que Todo Dev Deve Saber sobre Terraform Avançado: Módulos, Workspaces e Remote State

Entendendo Módulos no Terraform Um módulo no Terraform é um diretório contend...

Dominando Chaos Engineering: Princípios, Chaos Monkey e LitmusChaos em Kubernetes em Projetos Reais
Dominando Chaos Engineering: Princípios, Chaos Monkey e LitmusChaos em Kubernetes em Projetos Reais

Entendendo Chaos Engineering: Fundamentos e Filosofia Chaos Engineering é uma...

Como Usar Segurança em Containers Docker: Rootless, Capabilities e Scanning em Produção
Como Usar Segurança em Containers Docker: Rootless, Capabilities e Scanning em Produção

Segurança em Containers Docker: Rootless, Capabilities e Scanning Quando fala...