ADR-002 — Cilium em VM como padrão; LXC apenas em hosts Tier 3
Status
Vigente — 2026-05-02. Versão final após mapeamento detalhado de hardware.
Histórico
Esta decisão passou por várias iterações ao longo da mesma data conforme o contexto de hardware foi sendo precisado:
- v1 (manhã): Cilium em VM como padrão, LXC requerendo POC
- v2: Cilium em LXC como caminho CPPS (motivado por integração Proxmox + assumindo que muitos hosts seriam workstations com NVIDIA pra display)
- v3 (esta versão): Cilium em VM é o padrão, LXC privileged é exceção apenas pra Tier 3 (hosts onde NVIDIA é única GPU + usada pra display)
A v3 emerge do mapeamento real do hardware (ver Tiers de hardware): a maioria dos hosts CPPS são Tier 1 (server rack) ou Tier 2 (com iGPU), onde Cilium em VM funciona limpo. Apenas 2 hosts (pve-ippri-31, pve-ippri-33) são Tier 3 com NVIDIA disputada, e merecem tratamento especial.
Contexto
K3s em CPPS roda dentro de LXC privileged em ambos os clusters previstos:
gpu-sp-01: já em produção, K3s standalone + flannel default + A5000- K3s SP “principal”: a ser provisionado, com Cilium
Cilium oferece features que CPPS quer:
- Hubble — observability L7 unificada de todas apps (Tutor, Invenio, OJS, etc.)
- NetworkPolicy avançada — defense-in-depth multi-app
- mTLS automático — entre services sensíveis sem cada app implementar
- kubeProxyReplacement — performance em escala futura
Alternativas (Linkerd, Calico, flannel default) foram avaliadas e descartadas por:
- Linkerd: sidecar overhead em cada pod (~10-20MB RAM por sidecar × ~50-100 pods); apps stateful pesados (Mongo, Postgres) ficariam fora do mesh sem ganho
- Calico: cobre NetworkPolicy mas não dá Hubble equivalente nem service mesh
- flannel default: sem Hubble, sem L7 NetworkPolicy, sem mTLS automático
Decisão
Cilium roda em VMs por padrão. LXC privileged é exceção apenas pra hosts Tier 3.
A decisão é diferenciada por tier de hardware (ver Tiers de hardware):
| Tier | Hosts | CNI |
|---|---|---|
| Tier 1 (server rack: ippri-11/12) | 2 | Cilium em VM |
| Tier 2A (workstation com iGPU + NVIDIA: ippri-34, labri-31/32/33) | 4 | Cilium em VM + NVIDIA passthrough |
| Tier 2B (workstation com GPU básica: ippri-32, labri-21/22) | 3 | Cilium em VM (sem workload GPU) |
| Tier 3 (NVIDIA disputada display+workload: ippri-31, ippri-33) | 2 | flannel em LXC privileged (sem Cilium) |
Implicações:
- K3s SP “principal” roda em VMs nos hosts Tier 1/2A/2B
- K3s GPU SP (
gpu-sp-01em ippri-31, futurogpu-sp-02em ippri-33) permanece em LXC sem Cilium — Tier 3 obriga - K3s GPU Franca (futuro, em labri-31/32/33) roda em VM com Cilium — Tier 2A permite
- Cilium em LXC NÃO é caminho — território não-suportado upstream, evitar exceto se forçado por hardware
Por que Tier 3 fica em LXC (e não migra pra VM)
Hosts Tier 3 (pve-ippri-31, pve-ippri-33) têm:
- Sem iGPU (Ryzen sem placa de vídeo integrada)
- NVIDIA dedicada (A5000) é a única opção de display e de workload
Se NVIDIA for passada via PCIe Passthrough pra VM:
- VM ganha A5000 (Cilium + workload OK)
- Mas Proxmox host fica sem display — tela apaga no boot da VM
- Workstation deixa de ser usável como bancada
Pra Tier 3, manter LXC privileged + flannel default é a única opção que preserva display e workload GPU. Cilium em LXC permanece não-suportado, então cluster GPU desses hosts opera com flannel (cluster K3s separado, ver ADR-005).
Consolidação operacional (justificativa mais forte)
A decisão por Cilium não é só sobre features individuais — é sobre reduzir inventário operacional. Pra cobrir o que Cilium entrega num único stack, precisaria de 5-7 componentes separados:
| Feature | Cilium entrega | Sem Cilium precisaria |
|---|---|---|
| CNI | ✅ | flannel ou Calico |
| kube-proxy replacement | ✅ (eBPF, escalável) | kube-proxy default (iptables) |
| NetworkPolicy L3/L4 | ✅ | K8s built-in ou Calico Felix |
| NetworkPolicy L7 | ✅ | Istio + Envoy sidecars |
| mTLS automático | ✅ | Linkerd OU cert-manager manual |
| Identity-based policies | ✅ | SPIFFE/SPIRE |
| Cross-cluster discovery | ✅ ClusterMesh | Submariner OU Skupper |
| Cross-cluster encryption | ✅ (WireGuard nativo) | Submariner IPSec OU WireGuard manual |
| Egress gateway | ✅ | Egress operator dedicado |
| L7 observability | ✅ Hubble | Pixie OU Kiali OU Inspektor Gadget |
| Bandwidth manager | ✅ | tc rules manuais |
Inventário comparado
Sem Cilium, pra cobertura equivalente: flannel + Calico + Linkerd + Pixie + Submariner (5 componentes), cada um com config, upgrade path, documentação, modo de falha próprios.
Com Cilium: 1 componente entrega 5-7 funcionalidades coerentes.
Por que isso importa pra time pequeno
| Aspecto | Cilium consolidado | Stack fragmentado (5+) |
|---|---|---|
| Componentes pra operar | 1 | 5-7 |
| Pontos de falha distintos | 1 | 5-7 |
| Curvas de aprendizado | 1 (eBPF + Cilium) | 5-7 (cada projeto tem seus conceitos) |
| Compatibility matrix | Coerência interna | Versões entre N projetos |
| Debugging multi-camada | Tudo no mesmo data plane | Atravessa N stacks |
| Documentação | 1 source of truth | N sources |
Quantas dessas features CPPS vai precisar?
| Feature | CPPS vai precisar? | Quando |
|---|---|---|
| CNI | ✅ obrigatório | Sempre |
| NetworkPolicy L3/L4 | ✅ defense-in-depth | MVP |
| mTLS entre services sensíveis | ✅ Tutor↔Mongo, Authentik↔Postgres | Curto prazo |
| Hubble observability | ✅ debugging multi-app | Médio prazo |
| Cross-cluster discovery | ⚠️ provável | Médio-longo prazo |
| Identity-based policies | ⚠️ se multi-tenant | Longo prazo |
4 das 6 features são “vai precisar” no horizonte 1-2 anos. Operar 4 stacks separadas pra cobrir cobertura equivalente é claramente pior que Cilium consolidado.
Esse é o argumento operacional que reforça a decisão de Cilium — não como “feature elegante”, mas como redução de complexidade operacional ao longo do tempo.
Alternativas rejeitadas
- VM com Cilium (versão anterior do ADR): rejeitada porque
- Perde integração com PBS/LINSTOR/snapshots Proxmox
- Perde performance (overhead nested virt + memory reservation)
gpu-sp-01teria que migrar (downtime + perda de performance GPU)- Time absorve custo de operar 2 modelos (LXC + VM) simultaneamente
- flannel default: rejeitada por perder Hubble + mTLS + L7 features
- Linkerd em flannel: rejeitada por sidecar overhead em apps stateful
- Calico: rejeitada por não cobrir Hubble equivalente
Consequências
Positivas:
- Integração Proxmox preservada (PBS, LINSTOR, snapshots, dashboard)
- Performance GPU/CPU ~zero overhead vs VM
- Hubble + L7 NetworkPolicy + mTLS disponíveis quando necessário
- Setup consistente entre clusters (todos LXC)
gpu-sp-01migra “naturalmente” pra mesma arquitetura se quisermos
Negativas (aceitas):
- LXC privileged compromete isolamento de segurança
- Setup não-suportado upstream pelo Cilium → debugging fica com o time
- Cilium upgrades podem quebrar setup → exige testes em staging primeiro
- Documentação CPPS tem que cobrir o “por que LXC” pra novos integrantes
- Riscos de kernel panic em workloads exóticas
Critério de revisão
Revisitar essa decisão se:
- Kernel panic real afetar Proxmox host originado de Cilium em LXC
- Cilium upgrade quebrar setup repetidamente (custo de manutenção alto)
- Multi-tenant estrito (várias equipes compartilhando) virar requisito — privileged não é aceitável
- Compliance institucional exigir isolamento de kernel formal
- Time crescer pra absorver overhead de VMs (mais admins disponíveis)
Notas operacionais (POC config)
LXC config (Pulumi ContainerLegacy)
"k3s-sp-principal": { "vmid": 1011, "node": "pve-ippri-12", "cores": 8, "memory": 32768, "disk_size": 100, "os_template": "local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst", "ip": "192.168.10.20/23", "gateway": GATEWAY, "start_on_boot": True, "unprivileged": False, # privileged "nesting": True, "keyctl": True,}LXC config raw (Proxmox)
# /etc/pve/lxc/<vmid>.conffeatures: nesting=1,keyctl=1unprivileged: 0lxc.apparmor.profile: unconfinedlxc.cap.drop:lxc.cgroup2.devices.allow: alxc.mount.auto: proc:rw sys:rw cgroup-full:rw:forcelxc.mount.entry: /sys/fs/bpf sys/fs/bpf none bind,create=dir,optional 0 0Host Proxmox
# Kernel modules carregados no hostecho -e "bpf\nbpf_jit\nvxlan\ngeneve\nbr_netfilter\noverlay" > /etc/modules-load.d/cilium.conf
# sysctl pra Cilium em LXCcat > /etc/sysctl.d/99-cilium-lxc.conf <<EOFnet.ipv4.conf.lxc*.rp_filter = 0net.bridge.bridge-nf-call-iptables = 1net.bridge.bridge-nf-call-ip6tables = 1EOF
sysctl -p /etc/sysctl.d/99-cilium-lxc.confK3s install dentro do LXC
curl -sfL https://get.k3s.io | sh -s - \ --flannel-backend=none \ --disable-network-policy \ --disable=traefik \ --disable=servicelb \ --node-name=k3s-sp-principalCilium install (Helm, modo conservador)
helm install cilium cilium/cilium \ --version 1.16.x \ --namespace kube-system \ --set kubeProxyReplacement=false \ --set tunnel=vxlan \ --set bpf.masquerade=false \ --set hubble.enabled=true \ --set hubble.relay.enabled=true \ --set hubble.ui.enabled=trueNota: começar com
kubeProxyReplacement=false(modo legacy iptables) por ser mais compatível em LXC. Avaliar mover prapartialoutrueapós validação.
Validação POC
- Pods sobem e fazem ping cross-namespace
- DNS interno (
<service>.<ns>.svc.cluster.local) resolve - Cilium status sem erros
- Hubble Relay coleta flows
- NetworkPolicy básica bloqueia tráfego entre namespaces
- Reboot do LXC: K3s + Cilium voltam funcionais
- Reboot do Proxmox host: idem
- Cilium upgrade (1.16.x → 1.16.y patch): não quebra
- App de teste (Tutor staging) acessível externamente via Traefik
Plano de rollback
- Snapshot LINSTOR do LXC antes do POC
- Se Cilium quebrar: voltar pro snapshot
- Se persistir problema: K3s
--flannel-backend=vxlan(default) recupera funcional, perde Cilium