Fundamentos
Introdução ao cloud native
Antes de leres: Fundações de Linux (namespaces, cgroups v2, descritores de ficheiro) e IaaS e cloud native (do que é responsável o motor).
O motor é uma camada fina e cuidadosa sobre funcionalidades do kernel Linux e um punhado de especificações abertas. Esta página dá-te o suficiente de cada conceito para leres o código, e diz onde vive neste repositório. Depois dela consegues pegar em qualquer mecanismo — um namespace, um limite de cgroup, uma layer de imagem, uma chain de firewall, o arranque de uma VM, o apply de um manifesto — e nomear o ficheiro e o símbolo que o implementa. Para aprofundar, segue os links oficiais — são melhores do que qualquer resumo aqui.
Os conceitos são ensinados uma vez neste manual. Os primitivos do kernel (namespaces, user
namespaces, cgroups v2) são ensinados à mão em Fundações de Linux, por
isso as secções 4.1 e 4.2 só os recapitulam e mapeiam para o código. O que cada padrão aberto
exige, e até onde o motor é conforme, está em Padrões cloud native,
mais à frente no curso. Os caminhos nomeiam crates (crates/<camada>/<crate>); as camadas são
explicadas em Arquitectura, e por agora um caminho diz apenas onde o código está.
Cada secção tem três partes: o conceito, No Delonix (ficheiros e símbolos que podes procurar com
grep), e Ler mais. Os caminhos são relativos à raiz do repositório.
Para te orientares no ecossistema mais amplo, o CNCF Landscape e o CNCF Glossary são bons mapas. As comparações com runc, crun, containerd ou Podman só aparecem onde ajudam a explicar uma escolha de desenho.
4.1 Namespaces do Linux e funcionamento rootless#
Recapitulação. Um container é um processo arrancado num conjunto novo de namespaces (mount,
PID, network, IPC, UTS, cgroup, user). O user namespace é o que o torna rootless: lá dentro, o
processo é uid 0 sobre os recursos que esse namespace possui; fora, é um utilizador comum. Um
utilizador sem privilégio só consegue mapear o seu próprio uid; um intervalo exige
newuidmap/newgidmap e /etc/subuid. Os dois são ensinados, com comandos para escreveres, em
Fundações de Linux — Namespaces e
User namespaces e mapeamento de uid.
Algumas distribuições restringem além disso os user namespaces sem privilégio através do
AppArmor — ver
Ambiente para
as consequências práticas.
No Delonix
- A
fn spawnemcrates/adapters/delonix-linux/src/lib.rsconstrói asCloneFlags(CLONE_NEWNS,CLONE_NEWPID,CLONE_NEWNET,CLONE_NEWUSER, …) e chamanix::sched::clone. A partilha de IPC/UTS entre membros de um pod é tratada porsetnsnocontainer_init. - O
write_userns_mapsno mesmo ficheiro escreve os mapas a partir do pai: um mapa de um só uid (0 <euid> 1) em rootless, ou um intervalo de subuid através denewuidmap/newgidmapquandohave_subid_helpers()diz que estão disponíveis.USERNS_UID_BASE/USERNS_RANGEdefinem o intervalo usado quando se corre como root. - O
setup_rootfsmonta a raiz do container e chamapivot_root; ocontainer_inité o código que corre dentro dos novos namespaces antes doexecvp. - As operações rootless sobre ficheiros que pertencem a subuids mapeados re-executam o binário
dentro de um user namespace mapeado:
reexec_mapped,reexec_mapped_hold,remove_tree_mapped.
Ler mais: namespaces(7),
user_namespaces(7),
newuidmap(1),
subuid(5),
pivot_root(2).
4.2 cgroups v2 e delegação#
Recapitulação. O cgroup v2 é uma árvore única em /sys/fs/cgroup cujos ficheiros
(memory.max, cpu.max, pids.max, memory.events) limitam e contabilizam um grupo de
processos. Um utilizador sem privilégio só consegue escrever numa subárvore que o systemd lhe
tenha delegado, e uma shell num scope de sessão SSH fica normalmente fora dela — por isso os
limites podem, em silêncio, não se aplicar aí. A árvore, a regra "sem processos internos" e a
delegação são ensinadas à mão em Fundações de Linux — cgroups v2;
o contrato de delegação como padrão, e a conformidade do motor, estão em
Padrões cloud native — 13.15.
O motor é só v2.
No Delonix
- O modo root coloca os containers debaixo de
delonix_compute::DELONIX_SLICE(/sys/fs/cgroup/delonix.slice). - O modo rootless encontra o cgroup do serviço do utilizador e cria folhas debaixo de
<user@uid.service>/dlx-containers— veruser_service_baseetry_delegated_baseemcrates/adapters/delonix-linux/src/lib.rs. Ocgroup_limits_applyresponde a «os limites vão aplicar-se neste host?» sem arrancar um container. - A decisão de desenho sobre o nível intermédio é o
ADR-0015; a forma como o CRI segue a hierarquia de
cgroups do kubelet é o ADR-0038, com o pai
do kubelet validado por
KubeCgroupParent::parseemcrates/contexts/delonix-compute/src/record.rs.
Ler mais: Linux kernel — Control Group v2,
systemd — Control Group APIs and Delegation,
cgroups(7).
4.3 Capabilities, seccomp, AppArmor, caminhos mascarados#
O poder do root divide-se em capabilities (CAP_NET_ADMIN, CAP_SYS_ADMIN, …). Um container
mantém um pequeno conjunto por omissão e larga o resto. O seccomp instala um filtro BPF que
permite ou recusa system calls, reduzindo a superfície de ataque do kernel. O AppArmor (e o
SELinux noutras distribuições) são Linux Security Modules que confinam um processo por perfil.
Por fim, os runtimes mascaram caminhos sensíveis de /proc e /sys (montam algo vazio por
cima) e tornam outros só-de-leitura, porque esses ficheiros vazam informação do host ou permitem
controlar o host.
Um ponto subtil que o código regista: o clone3 passa as suas flags através de um ponteiro que um
filtro seccomp não consegue inspeccionar, por isso um filtro que bloqueia
clone(CLONE_NEWUSER) tem também de fazer o clone3 falhar com ENOSYS para forçar a libc a
voltar ao clone filtrável.
No Delonix
- Capabilities:
KEPT_CAPSeresolve_cap_keepemcrates/adapters/delonix-linux/src/capabilities.rs;drop_capabilitiesemlib.rs. - seccomp:
apply_seccompemcrates/adapters/delonix-linux/src/lib.rs(construído com o crateseccompiler, incluindo o pré-filtroclone3→ENOSYS); perfis JSON personalizados são analisados e compilados emseccomp_profile.rs(parse,compile). - AppArmor:
apply_apparmoremlib.rs. - Caminhos mascarados e só-de-leitura:
DEFAULT_MASKED_PATHS,DEFAULT_READONLY_PATHS,apply_masked_paths,apply_readonly_paths,mask_proc_pathsemlib.rs. - As decisões de segurança ao nível do nó (política, admissão, eventos, score) são um crate puro à
parte:
evaluateemcrates/contexts/delonix-security-runtime/src/admission.rs(ADR-0026).
Ler mais: capabilities(7),
kernel — Seccomp BPF,
Documentação do AppArmor,
OCI runtime spec — Linux config
(os campos maskedPaths/readonlyPaths/seccomp que outros runtimes consomem).
4.4 Imagens OCI, armazenamento endereçado por conteúdo e overlayfs#
A Open Container Initiative publica três especificações:
- a image spec — uma imagem é um manifesto (JSON) que aponta para uma config e uma lista ordenada de layers (tarballs), e possivelmente um index que aponta para um manifesto por plataforma;
- a distribution spec — a API HTTP que os registos servem (
/v2/<name>/manifests/<ref>,/v2/<name>/blobs/<digest>, autenticação por token); - a runtime spec — como um runtime como o runc ou o crun recebe a instrução de correr um bundle de sistema de ficheiros.
Tudo é endereçado por conteúdo: um blob é nomeado pelo digest SHA-256 dos seus bytes, por isso
um cliente verifica o que descarregou fazendo-lhe hash. Fazer pull por digest (name@sha256:…)
só é uma garantia se o próprio manifesto for verificado contra esse digest, além de cada blob
contra o manifesto.
Em runtime, as layers são empilhadas com overlayfs: lowerdirs só-de-leitura, um upperdir
escrevível para onde as mudanças são copiadas, e um workdir. Muitos containers podem partilhar
as mesmas lower layers.
No Delonix
- Cliente de registo (distribution spec):
crates/adapters/delonix-oci/src/registry.rs— funçõespull_from_registry*, os media typesACCEPT_MANIFEST, everify_manifest_digest. Os tipos vêm do crateoci-spec. - Store de blobs endereçado por conteúdo:
Casemcrates/adapters/delonix-oci/src/cas.rs. - Escrever um arquivo de OCI image layout:
write_oci_archiveemsave.rs. - Preparação do overlay:
ImageStore::prepare_overlayemoverlay.rsescreve um marcadoroverlay-lowers(LOWERS_FILE); o mount em si acontece dentro do user e mount namespace do container emmount_overlay_if_marked(crates/adapters/delonix-linux/src/lib.rs), usando a nova API de mount — ver ADR-0016 e ADR-0037. - O motor corre os containers ele próprio em vez de entregar um bundle de runtime OCI ao runc/crun.
Ler mais: OCI image spec, OCI distribution spec, OCI runtime spec, kernel — Overlay Filesystem.
4.5 Rede de containers#
Blocos de construção da rede em Linux:
- um network namespace tem as suas próprias interfaces, rotas e firewall;
- um par veth é um cabo virtual com uma ponta em cada namespace;
- uma bridge é um switch virtual que junta várias pontas veth;
- o nftables é o filtro de pacotes e motor de NAT do kernel; o DNAT reescreve um destino
(como uma porta publicada alcança um container), e o conntrack acompanha fluxos para o
tráfego de resposta de uma ligação permitida passar (
ct state established,related); - o slirp4netns dá conectividade de saída a um network namespace sem privilégio, emulando uma pilha TCP/IP em user space, e reencaminha portas do host para dentro dele;
- o VXLAN transporta frames L2 sobre UDP entre hosts, e o WireGuard cifra um túnel.
O CNI (Container Network Interface) é uma especificação em que um runtime executa binários de
plugin (bridge, host-local, portmap, …) com comandos ADD/DEL e uma config JSON de
/etc/cni/net.d. Os runtimes do Kubernetes usam-no para a rede dos pods.
No Delonix
- A rede rootless não consegue criar interfaces no host, por isso o motor mantém um network
namespace holder de vida longa: um processo pin mínimo possui os namespaces, e um
processo control reiniciável serve um socket Unix. Ver
start_pin,start_controleensure_upemcrates/adapters/delonix-sdn/src/infra.rs. - Ligar um workload:
attach_container(IPAM + comando de controlo) edo_attach(veth para a bridge, dentro do holder). Os nomes de bridge vêm debridge_name, no crate sem dependênciascrates/foundation/delonix-net-rules/src/lib.rs. - Saída e reencaminhamento de portas:
slirp_attacheslirp_add_hostfwdemcrates/adapters/delonix-sdn/src/lib.rs(que arrancam oslirp4netns); a publicação dentro do holder empublish_port/do_publish(infra.rs). - Firewall:
table ip dlxingcom as base chainsfwguard,fwdeny,fwconte o verdict mapfwmap(FWMAP), gerado eminfra.rs(do_firewall,apply_firewall_all,ns_set_joinpara os sets de isolamento de namespace). - DNS interno (nome padrão
<name>.<namespace>.svc.delonix.internal, o antigo<name>.<namespace>.delonix.internalainda responde;service_fqdn,parse_internal_name):dns_server_main,handle_dns,dns_resolve_for,dns_resolve_multi_foreminfra.rs. - Redes overlay:
set_vxlan(infra.rs) e os helpers de WireGuard emcrates/adapters/delonix-sdn/src/wg.rs, orquestrados porrealize_overlayembins/delonix-runtime-bin/src/cmd/network.rs. - CNI:
crates/adapters/delonix-sdn/src/cni.rs—add,del,readiness,attach_named_netns. O uso em rootless é opt-in (enabled_confverificaDELONIX_CNI=1); o caminho do CRI em root usa a cadeia CNI do nó (root_cni_readinessemcrates/interfaces/delonix-cri/src/runtime_svc.rs). - Decisões de topologia: ADR-0013, ADR-0014.
Ler mais: network_namespaces(7),
veth(4),
nftables wiki,
slirp4netns,
kernel — VXLAN,
WireGuard,
CNI e a sua especificação.
4.6 Kubernetes: CRI, kubelet, kubeadm e kind#
O kubelet é o agente de nó do Kubernetes. Não corre containers ele próprio; fala com um
runtime de containers através da Container Runtime Interface, uma API gRPC (RuntimeService,
ImageService) servida num socket Unix local. O kubelet cria uma pod sandbox
(RunPodSandbox) e depois os containers dentro dela. Tem também uma definição de cgroup
driver (systemd ou cgroupfs) que tem de bater certo com a forma como o runtime gere
cgroups, senão os cgroups do pod e os do container divergem.
O kubeadm faz bootstrap de um cluster em máquinas já existentes (kubeadm init, kubeadm
join). O kind corre nós Kubernetes como containers construídos a partir da imagem
kindest/node.
No Delonix
crates/interfaces/delonix-crié um servidor CRIruntime.v1. O protobuf éproto/api.protodentro desse crate, compilado pelobuild.rscomtonic-build. O binário ésrc/bin/delonix-cri.rs.- O cgroup driver reportado ao kubelet:
engine_cgroup_driveremruntime_svc.rs; o seu doc-comment regista porque é essa a resposta e o que teria de mudar para ser a outra. - Um round-trip sobre gRPC real é testado em
crates/interfaces/delonix-cri/tests/grpc_status.rs. - Comandos de bootstrap de cluster: kubeadm por SSH em
bins/delonix-runtime-bin/src/cmd/cluster.rs(comkubeadm_config.rs,etcd.rs,lb.rs), e clusters locais ao estilo kind emkindmode.rs.
Ler mais: Kubernetes — Container Runtime Interface, repositório cri-api, Configurar um cgroup driver, kubeadm, kind.
4.7 Virtualização: KVM, virtio, Cloud Hypervisor, libvirt, cloud-init#
O KVM é o hipervisor do kernel, exposto como /dev/kvm; um VMM em user-space (QEMU,
Cloud Hypervisor) usa-o para correr convidados. O virtio é a família de dispositivos
paravirtuais (disco, rede, partilha de sistema de ficheiros 9p) que os convidados usam para I/O
eficiente. O Cloud Hypervisor é um VMM em Rust focado em workloads cloud que consegue correr
sem privilégio com acesso a /dev/kvm; arranca convidados através de um firmware (uma build UEFI
EDK2 ou rust-hypervisor-firmware) ou directamente a partir de uma imagem de kernel. O
libvirt gere domínios QEMU/KVM descritos em XML, via virsh e libvirtd.
As cloud images são genéricas; a configuração por-instância (hostname, chaves SSH, utilizadores,
rede) vem do cloud-init, que lê uma datasource. A datasource NoCloud é um pequeno ISO
etiquetado cidata com user-data, meta-data e opcionalmente network-config.
No Delonix
- A porta é
VmBackendemcrates/adapters/delonix-vm/src/lib.rs, implementada porCloudHypervisorBackendeLibvirtBackendaí e porProxmoxBackendemcrates/providers/delonix-proxmox(ADR-0008). - Ordem de procura do firmware do Cloud Hypervisor:
DEFAULT_CH_FIRMWARES(EDK2CLOUDHV.fdantes dohypervisor-fw); a linha de comandos do VMM é construída emboot_ch. - XML de domínio libvirt:
libvirt_domain_xml. - Geração de seed NoCloud:
generate_seed_isoembins/delonix-runtime-bin/src/cmd/vm.rs. - As VMs na SDN rootless ganham um lease DHCP derivado do seu MAC:
dhcp_lease_ipemcrates/adapters/delonix-sdn/src/infra.rs. - Configuração prática e armadilhas conhecidas do host: Construir microVMs.
Ler mais: kernel — KVM, especificação virtio (OASIS), Cloud Hypervisor e a sua documentação, libvirt, cloud-init NoCloud.
4.8 Reconciliação declarativa#
O Kubernetes popularizou um modelo em que os utilizadores submetem estado desejado como
objectos tipados (apiVersion, kind, metadata, spec), e controladores comparam-no
repetidamente com o estado real e agem para convergir. O kubectl apply acrescenta um
diff de três vias: guarda a última configuração aplicada no objecto, para conseguir
distinguir "tiraste este campo do teu ficheiro" (reverte-o) de "alguém pôs este campo à mão"
(deixa-o em paz).
O princípio, e o que te pede quando acrescentas um Kind, estão em IaaS e cloud native — Declarativo e convergente.
No Delonix
- O motor tem os seus próprios Kinds em grupos de API (
delonix api-resourceslista-os). Os factos sobre cada Kind (domínio, se converge, teardown, namespacing) vivem numa tabela só:KindFactsemcrates/contexts/delonix-stack/src/kinds.rs. - O planeador é puro:
plan(desired, actual, stack)emcrates/contexts/delonix-stack/src/reconcile.rs. O doc-comment do módulo tem a tabela de verdade de três vias, e o último spec aplicado é guardado no próprio recurso sob a anotaçãoLAST_APPLIED(delonix.io/last-applied) — não há ficheiro de estado à parte. - A posse é uma label no recurso; o histórico de revisões está em
revision.rs(ADR-0019). - Os manifestos são analisados em
bins/delonix-runtime-bin/src/cmd/manifest.rs;stack plan/applyvivem emcmd/stack.rs. - Não há nenhum ciclo de controlador a correr em segundo plano: a reconciliação acontece quando um comando corre (daemonless). O reconciliador de pull proposto mantém essa propriedade por ser um timer systemd que invoca o mesmo apply, não um processo residente (ADR-0021, estado Proposed).
Ler mais: Kubernetes — Objects,
Controllers,
Gestão declarativa com kubectl apply.
4.9 Observabilidade e a interface MCP#
O OpenTelemetry é um padrão CNCF para traces, métricas e logs, exportados por OTLP para
um colector. O Prometheus faz scrape de métricas a partir de um endpoint HTTP /metrics num
formato de exposição em texto. O Model Context Protocol é um protocolo aberto que deixa
clientes de IA descobrir e chamar ferramentas expostas por um servidor, normalmente sobre stdio.
No Delonix
- Logging estruturado e spans OTLP opcionais:
initemcrates/adapters/delonix-telemetry/src/telemetry.rs(os spans só são exportados quandoDELONIX_OTLP_ENDPOINTestá definida; o exportador corre na sua própria thread para a CLI síncrona não precisar de nenhum runtime assíncrono). - Registo Prometheus (prefixo
delonix) e codificação em texto:crates/adapters/delonix-telemetry/src/metrics.rs(encode). A API de gestão local serve/metricsemcrates/interfaces/delonix-mgmt/src/lib.rs. - MCP:
crates/interfaces/delonix-mcp(construído sobre ormcp, transporte stdio), com o binário embins/delonix-mcp-bin. Âmbito e limites: ADR-0025.
Ler mais: Documentação do OpenTelemetry, Especificação OTLP, Prometheus — formatos de exposição, Model Context Protocol.
4.10 Daemonless, num parágrafo#
O containerd e o Docker Engine mantêm um daemon residente que possui o estado dos containers; o
Podman mostrou que um runtime pode ser, em vez disso, um comando que sai, com processos auxiliares
por-container e o systemd para tudo o que tem de persistir. O Delonix segue o segundo modelo: a
CLI faz o trabalho e sai, o estado são ficheiros debaixo do state root guardados por flock (ver
Introdução ao Rust §3.8), existe um processo
supervisor por container destacado, o holder de rede só existe enquanto algo precisa dele, e a
persistência no arranque são units systemd
(bins/delonix-runtime-bin/src/cmd/boot.rs). As consequências — boas e más — são discutidas em
Arquitectura e System Design Interview. O
princípio em si, e a regra de que um daemon novo precisa de um ADR, estão em
IaaS e cloud native — Daemonless.