Skip to content

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:

  1. v1 (manhã): Cilium em VM como padrão, LXC requerendo POC
  2. v2: Cilium em LXC como caminho CPPS (motivado por integração Proxmox + assumindo que muitos hosts seriam workstations com NVIDIA pra display)
  3. 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):

TierHostsCNI
Tier 1 (server rack: ippri-11/12)2Cilium em VM
Tier 2A (workstation com iGPU + NVIDIA: ippri-34, labri-31/32/33)4Cilium em VM + NVIDIA passthrough
Tier 2B (workstation com GPU básica: ippri-32, labri-21/22)3Cilium em VM (sem workload GPU)
Tier 3 (NVIDIA disputada display+workload: ippri-31, ippri-33)2flannel em LXC privileged (sem Cilium)

Implicações:

  1. K3s SP “principal” roda em VMs nos hosts Tier 1/2A/2B
  2. K3s GPU SP (gpu-sp-01 em ippri-31, futuro gpu-sp-02 em ippri-33) permanece em LXC sem Cilium — Tier 3 obriga
  3. K3s GPU Franca (futuro, em labri-31/32/33) roda em VM com Cilium — Tier 2A permite
  4. 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:

FeatureCilium entregaSem Cilium precisaria
CNIflannel ou Calico
kube-proxy replacement✅ (eBPF, escalável)kube-proxy default (iptables)
NetworkPolicy L3/L4K8s built-in ou Calico Felix
NetworkPolicy L7Istio + Envoy sidecars
mTLS automáticoLinkerd OU cert-manager manual
Identity-based policiesSPIFFE/SPIRE
Cross-cluster discovery✅ ClusterMeshSubmariner OU Skupper
Cross-cluster encryption✅ (WireGuard nativo)Submariner IPSec OU WireGuard manual
Egress gatewayEgress operator dedicado
L7 observability✅ HubblePixie OU Kiali OU Inspektor Gadget
Bandwidth managertc 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

AspectoCilium consolidadoStack fragmentado (5+)
Componentes pra operar15-7
Pontos de falha distintos15-7
Curvas de aprendizado1 (eBPF + Cilium)5-7 (cada projeto tem seus conceitos)
Compatibility matrixCoerência internaVersões entre N projetos
Debugging multi-camadaTudo no mesmo data planeAtravessa N stacks
Documentação1 source of truthN sources

Quantas dessas features CPPS vai precisar?

FeatureCPPS vai precisar?Quando
CNI✅ obrigatórioSempre
NetworkPolicy L3/L4✅ defense-in-depthMVP
mTLS entre services sensíveis✅ Tutor↔Mongo, Authentik↔PostgresCurto prazo
Hubble observability✅ debugging multi-appMédio prazo
Cross-cluster discovery⚠️ provávelMédio-longo prazo
Identity-based policies⚠️ se multi-tenantLongo 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-01 teria 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-01 migra “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:

  1. Kernel panic real afetar Proxmox host originado de Cilium em LXC
  2. Cilium upgrade quebrar setup repetidamente (custo de manutenção alto)
  3. Multi-tenant estrito (várias equipes compartilhando) virar requisito — privileged não é aceitável
  4. Compliance institucional exigir isolamento de kernel formal
  5. 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>.conf
features: nesting=1,keyctl=1
unprivileged: 0
lxc.apparmor.profile: unconfined
lxc.cap.drop:
lxc.cgroup2.devices.allow: a
lxc.mount.auto: proc:rw sys:rw cgroup-full:rw:force
lxc.mount.entry: /sys/fs/bpf sys/fs/bpf none bind,create=dir,optional 0 0

Host Proxmox

Terminal window
# Kernel modules carregados no host
echo -e "bpf\nbpf_jit\nvxlan\ngeneve\nbr_netfilter\noverlay" > /etc/modules-load.d/cilium.conf
# sysctl pra Cilium em LXC
cat > /etc/sysctl.d/99-cilium-lxc.conf <<EOF
net.ipv4.conf.lxc*.rp_filter = 0
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl -p /etc/sysctl.d/99-cilium-lxc.conf

K3s install dentro do LXC

Terminal window
curl -sfL https://get.k3s.io | sh -s - \
--flannel-backend=none \
--disable-network-policy \
--disable=traefik \
--disable=servicelb \
--node-name=k3s-sp-principal

Cilium install (Helm, modo conservador)

Terminal window
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=true

Nota: começar com kubeProxyReplacement=false (modo legacy iptables) por ser mais compatível em LXC. Avaliar mover pra partial ou true apó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