Skip to content

IaC β€” Ansible e Pulumi

IaC β€” Ansible Roles + Pulumi

Visao Geral

A infraestrutura do CPPS usa 3 ferramentas complementares, cada uma com dominio bem definido:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Git (fonte unica de verdade) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚
β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Pulumi β”‚ β”‚ Ansible β”‚ β”‚ Argo CD β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ Provisiona β”‚ β”‚ Configura β”‚ β”‚ Deploya β”‚
β”‚ o que EXISTE β”‚ β”‚ o que RODA β”‚ β”‚ workloads β”‚
β”‚ no Proxmox β”‚ β”‚ DENTRO β”‚ β”‚ no K3s β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ VMs, LXCs, β”‚ β”‚ Pacotes, β”‚ β”‚ Helm charts,β”‚
β”‚ discos, rede,β”‚ β”‚ servicos, β”‚ β”‚ manifests, β”‚
β”‚ cloud-init β”‚ β”‚ hardening, β”‚ β”‚ CRDs β”‚
β”‚ β”‚ β”‚ LINSTOR, β”‚ β”‚ β”‚
β”‚ Estado: β”‚ β”‚ drivers β”‚ β”‚ Estado: β”‚
β”‚ Pulumi state β”‚ β”‚ β”‚ β”‚ Git + etcd β”‚
β”‚ (S3) β”‚ β”‚ Estado: β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ idempotente β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚
β”‚ output: IP β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Pulumi cria VM β†’ Ansible configura

Fronteira clara β€” nenhuma ferramenta invade o dominio da outra

PerguntaFerramenta
”Preciso de uma VM com 8 cores, 32G RAM, disco em LINSTOR”Pulumi
”A VM precisa ter Docker, NVIDIA driver, NTP, hardening SSH”Ansible
”Quero rodar Airflow no K3s com Helm chart”Argo CD
”Preciso limpar /boot dos nodes Proxmox”Ansible
”Quero criar uma rede/VLAN no Proxmox”Pulumi
”Preciso instalar LINSTOR nos nodes GPU”Ansible

Ansible β€” Roles e Playbooks

O que sao Roles

Uma role Ansible e um pacote reutilizavel de tarefas. Em vez de repetir os mesmos comandos em cada playbook, encapsula em uma role e reutiliza.

Analogia: uma role e como uma funcao. O playbook e o main() que chama as funcoes.

config/roles/
β”œβ”€β”€ common/ ← base para qualquer servidor
β”œβ”€β”€ proxmox-cleanup-boot/ ← limpeza de kernels antigos
β”œβ”€β”€ proxmox-upgrade/ ← upgrade PVE 8β†’9 completo
β”œβ”€β”€ nvidia-driver/ ← driver GPU condicional
└── linstor/ ← cluster storage distribuido

Estrutura de cada Role

config/roles/minha-role/
β”œβ”€β”€ defaults/main.yaml ← variaveis com valores padrao (SEMPRE documentar)
β”œβ”€β”€ tasks/main.yaml ← lista de tarefas (o que fazer)
β”œβ”€β”€ handlers/main.yaml ← acoes reativas (ex: reiniciar servico se config mudou)
└── templates/ ← arquivos Jinja2 (config files dinamicos)
  • defaults/: valores que podem ser sobrescritos no inventory ou via -e
  • tasks/: lista ordenada de acoes. Cada task e idempotente (rodar 2x = mesmo resultado)
  • handlers/: executam so quando notificados por uma task que mudou algo
  • templates/: arquivos com variaveis {{ }} que Ansible renderiza

Roles existentes

common

Baseline para qualquer servidor. Instala pacotes utilitarios, configura DNS fallback e timezone.

# Uso em playbook:
roles:
- common

Variaveis: common_packages, common_dns_fallback, common_ntp_servers

proxmox-cleanup-boot

Limpa kernels antigos do /boot. O Proxmox acumula kernels a cada atualizacao e quando /boot enche (~920 MB tipico), novos upgrades falham.

Usa o script config/scripts/cleanup-boot.sh β€” copia para o node, roda dry-run, depois executa.

Terminal window
make pve-cleanup-boot TARGET=proxmox_sp # todos os nodes SP
make pve-cleanup-boot TARGET=pve-ippri-31 # node especifico

proxmox-upgrade

Upgrade completo de PVE (ex: 8β†’9). Sequencia:

  1. Verifica versao e pre-requisitos (pve8to9 --full)
  2. Garante DNS fallback (evitar problema de resolucao pos-reboot)
  3. Atualiza repositorios (bookworm β†’ trixie)
  4. apt full-upgrade (async, ate 1h de timeout)
  5. Reboot
  6. Valida: versao PVE, storage, rede
Terminal window
make pve-upgrade TARGET=pve-ippri-33 # upgrade 1 node

Importante: o playbook usa serial: 1 no orquestrador (config/proxmox/upgrade.yaml), garantindo que faz 1 node por vez (nao derruba o cluster inteiro).

Variaveis: pve_target_suite, pve_current_suite, pve_reboot_timeout, pve_dns_fallback

nvidia-driver

Instala driver NVIDIA. So executa em nodes que tem gpu_model definido no inventory. Detecta automaticamente se a GPU esta presente e se o driver ja esta instalado.

Terminal window
make pve-nvidia TARGET=pve-labri-31 # node com GPU
make pve-nvidia TARGET=proxmox_franca # todos os de Franca (so instala onde tem GPU)

Variaveis: nvidia_driver_package, nvidia_pin_kernel, nvidia_pinned_kernel_version

linstor

Instala e configura cluster LINSTOR + DRBD. Documentacao completa em linstor-sp.md.

5 fases: repositorio β†’ storage LVM β†’ servicos β†’ cluster β†’ integracao Proxmox.

Terminal window
make pve-linstor TARGET=linstor_sp # cluster SP inteiro

Playbooks (orquestradores)

Os playbooks ficam em config/proxmox/ e orquestram as roles:

config/proxmox/
β”œβ”€β”€ upgrade.yaml ← cleanup-boot + upgrade + nvidia (serial: 1)
β”œβ”€β”€ cleanup-boot.yaml ← so limpeza
β”œβ”€β”€ nvidia.yaml ← so driver GPU
└── linstor.yaml ← instala LINSTOR

Cada playbook aceita a variavel target para filtrar hosts:

Terminal window
# Um node
uv run ansible-playbook config/proxmox/upgrade.yaml -e target=pve-ippri-33
# Um grupo
uv run ansible-playbook config/proxmox/upgrade.yaml -e target=proxmox_sp
# Via Makefile (mais simples)
make pve-upgrade TARGET=pve-ippri-33

Inventory

O inventory define TODOS os hosts e suas variaveis:

inventory/hosts.yaml
β”œβ”€β”€ routers (vyos)
β”œβ”€β”€ servers (debian-proxy, vm-cpps-02, vm-cpps-03)
β”œβ”€β”€ proxmox
β”‚ β”œβ”€β”€ proxmox_franca (labri-21..33, com linstor_role e gpu_model)
β”‚ └── proxmox_sp (ippri-11..34, com gpu_model, linstor_ip, linstor_nvme_devices)
└── linstor_sp (ippri-31, 33, 34 β€” vars LINSTOR especificas)

Variaveis por host:

  • ansible_host: IP de conexao
  • gpu_model: modelo da GPU (condiciona role nvidia-driver)
  • linstor_role: papel LINSTOR (satellite, controller, combined, controller_primary)
  • linstor_ip: IP na rede dedicada LINSTOR
  • linstor_interface: interface de rede para LINSTOR
  • linstor_nvme_devices: lista de NVMe para o pool LINSTOR

Pulumi β€” Provisionamento de VMs (backlog)

Estado atual

Pulumi ainda nao esta implementado. Toda criacao de VM e feita manualmente na UI do Proxmox. O plano e importar as VMs existentes e depois criar novas via codigo.

Por que Pulumi (e nao Terraform)

  • Python nativo: sem aprender HCL (linguagem do Terraform). O time ja conhece Python.
  • Type safety: IDE autocomplete, erros de tipo antes de executar
  • Logica real: loops, condicionais, funcoes β€” sem hacks de DSL
  • State management: backend S3 (SeaweedFS que ja temos) ou Pulumi Cloud

Estrutura planejada

infra/
└── proxmox/
β”œβ”€β”€ Pulumi.yaml ← config do projeto
β”œβ”€β”€ Pulumi.dev.yaml ← config do stack (dev/prod)
β”œβ”€β”€ __main__.py ← entry point
β”œβ”€β”€ config.py ← defaults (CPU, RAM, rede, cloud-init)
└── vms/
β”œβ”€β”€ vyos.py ← definicao da VM VyOS
β”œβ”€β”€ debian_proxy.py ← definicao do debian-proxy
β”œβ”€β”€ vm_cpps_02.py ← definicao do vm-cpps-02 (K3s)
└── vm_cpps_03.py ← definicao do vm-cpps-03

Exemplo de como ficaria

infra/proxmox/vms/vm_cpps_02.py
import pulumi
from pulumi_proxmoxve import vm
vm_cpps_02 = vm.VirtualMachine("vm-cpps-02",
node_name="pve-labri-21",
vm_id=103,
name="vm-cpps-02",
cpu=vm.VirtualMachineCpuArgs(cores=12),
memory=vm.VirtualMachineMemoryArgs(dedicated=32768), # 32G
disks=[vm.VirtualMachineDiskArgs(
interface="scsi0",
size=170,
datastore_id="linstor", # LINSTOR storage
)],
network_devices=[vm.VirtualMachineNetworkDeviceArgs(
bridge="vmbr1",
)],
initialization=vm.VirtualMachineInitializationArgs(
type="nocloud",
ip_configs=[vm.VirtualMachineInitializationIpConfigArgs(
ipv4=vm.VirtualMachineInitializationIpConfigIpv4Args(
address="192.168.0.51/23",
gateway="192.168.0.1",
),
)],
),
)

Fases de adocao

  1. Import: pulumi import das VMs que ja existem no Proxmox (nao recria nada)
  2. Codificar: escrever definicao Python de cada VM baseado no estado importado
  3. Validar: make plan (pulumi preview) mostra diferencas entre codigo e real
  4. Criar novas VMs: a partir daqui, toda VM nova e criada via make apply

Fluxo Pulumi β†’ Ansible

1. Pulumi cria VM (CPU, RAM, disco, rede, cloud-init)
↓ output: IP da VM
2. IP e adicionado ao inventory/hosts.yaml (manual ou via Pulumi stack output)
↓
3. Ansible configura a VM (pacotes, servicos, hardening)
↓
4. VM pronta para uso (ou para receber workloads K3s via ArgoCD)

No futuro, o passo 2 pode ser automatizado: Pulumi gera o inventory dinamicamente a partir do state. Mas por agora, manter manual e mais simples e auditavel.

Decisoes pendentes

DecisaoOpcoesStatus
State backendSeaweedFS S3 (ja temos), Pulumi Cloud (gratis tier), localDecidir antes do import
Provider Proxmoxpulumi-proxmoxve (community)Unica opcao viavel
Primeiro importComeca por qual VM?Sugestao: vm-cpps-03 (simples, sem dependencias)

Makefile β€” Interface unificada

Terminal window
# === VM Configuration (Ansible) ===
make configure # aplica configs VyOS + Traefik
make verify # dry-run (nao muda nada)
make status # ping em todos os hosts
# === Proxmox Operations (Ansible) ===
make pve-upgrade TARGET=<host|grupo> # upgrade PVE 8β†’9
make pve-cleanup-boot TARGET=<host|grupo> # limpar /boot
make pve-nvidia TARGET=<host|grupo> # driver GPU
make pve-linstor TARGET=<host|grupo> # instalar LINSTOR
# === VM Provisioning (Pulumi β€” futuro) ===
make plan # pulumi preview
make apply # pulumi up

TARGET pode ser:

  • Um host: pve-ippri-33
  • Um grupo: proxmox_sp, proxmox_franca, linstor_sp
  • Todos: proxmox

Estrutura completa do repositorio

devops/
β”œβ”€β”€ apps/ ← workloads K3s (ArgoCD)
β”‚ β”œβ”€β”€ airflow/
β”‚ β”œβ”€β”€ authentik/
β”‚ β”œβ”€β”€ argocd/ ← 17 Application manifests
β”‚ └── ...
β”‚
β”œβ”€β”€ config/ ← configuracao (Ansible)
β”‚ β”œβ”€β”€ roles/ ← roles reutilizaveis
β”‚ β”‚ β”œβ”€β”€ common/
β”‚ β”‚ β”œβ”€β”€ proxmox-cleanup-boot/
β”‚ β”‚ β”œβ”€β”€ proxmox-upgrade/
β”‚ β”‚ β”œβ”€β”€ nvidia-driver/
β”‚ β”‚ └── linstor/
β”‚ β”œβ”€β”€ proxmox/ ← playbooks Proxmox
β”‚ β”‚ β”œβ”€β”€ upgrade.yaml
β”‚ β”‚ β”œβ”€β”€ cleanup-boot.yaml
β”‚ β”‚ β”œβ”€β”€ nvidia.yaml
β”‚ β”‚ └── linstor.yaml
β”‚ β”œβ”€β”€ vyos/playbook.yaml ← firewall/router
β”‚ β”œβ”€β”€ traefik/playbook.yaml ← reverse proxy
β”‚ └── scripts/cleanup-boot.sh
β”‚
β”œβ”€β”€ infra/ ← provisionamento (Pulumi β€” futuro)
β”‚ └── proxmox/
β”‚
β”œβ”€β”€ inventory/hosts.yaml ← todos os hosts e variaveis
β”œβ”€β”€ ansible.cfg
β”œβ”€β”€ Makefile ← interface unificada
β”‚
β”œβ”€β”€ docs/
β”‚ β”œβ”€β”€ infraestrutura.md
β”‚ β”œβ”€β”€ proxmox-inventario.md
β”‚ β”œβ”€β”€ linstor-sp.md
β”‚ β”œβ”€β”€ iac-ansible-pulumi.md ← este documento
β”‚ └── ...
β”‚
└── roadmap/
β”œβ”€β”€ iac.md
β”œβ”€β”€ gpu-platform.md
β”œβ”€β”€ data-platform.md
└── execucao.md