Laboratórios

Sessões curtas, do zero a um resultado que se verifica. Cada uma inclui a limpeza — nenhuma deixa nada para trás.

A verificação no fim de cada lab não é decorativa: é o que separa «copiei comandos» de «sei o que isto faz». Onde um passo pode falhar em silêncio, o lab diz exactamente o que olhar.

Primeiro serviço, em 60 segundos iniciante

Objectivo: perceber o ciclo run → ps → logs → exec → rm e ver que não há daemon nenhum por baixo.

# 1. Um serviço web, em segundo plano, publicado na porta 8080 do host
delonix container run -d --name web -p 8080:80 nginx:alpine

# 2. Confirmar — o STATUS diz há quanto tempo está de pé
delonix ps

# 3. A prova de que não há daemon: NENHUM processo residente do motor
pgrep -a delonix || echo "sem daemon — o container é filho do init do host"

# 4. Falar com ele
curl -s localhost:8080 | head -3

# 5. Entrar lá dentro
delonix exec -it web sh -c 'hostname; id; ls /etc/nginx'

# 6. Ver o que ele escreveu
delonix logs web | tail -5

# 7. Limpar
delonix rm -f web

Verificação: o passo 3 não imprime processo nenhum do motor. Um container a correr sem supervisor residente é a diferença de fundo para o Docker, e vê-se aqui em duas linhas.

Limites de recursos que pegam mesmo iniciante

Objectivo: descobrir se este host aplica limites — e o que fazer quando não aplica. É o erro nº1 de quem começa em rootless.

# 1. Perguntar ANTES de assumir
delonix system setup

# 2. Se disser INERT ou PARTIAL, a 1.ª correcção não precisa de root nenhum
systemd-run --user --scope -p Delegate=yes -- delonix system setup
#   … e é dentro desse scope que corres os passos seguintes.
#   Só se ELE ainda disser que falta o `cpu`:
#     sudo delonix system setup --delegate   (+ logout / login)

# 3. Um container com tecto de memória
delonix container run -d --name limitado -m 128M alpine sleep 300

# 4. A prova: ler o cgroup REAL, não o que o comando diz
PID=$(delonix container inspect limitado | jq -r .pid)
cat /sys/fs/cgroup$(awk -F: '/^0::/{print $3}' /proc/$PID/cgroup)/memory.max

delonix rm -f limitado

Verificação: o passo 4 imprime 134217728 (128 MiB), não max. Se imprimir max, a delegação não está feita — e o container corre sem limite nenhum, em silêncio. É por isso que o passo 1 existe.

Nota: cpuset e io aparecem quase sempre como absent, e está certo — num Ubuntu de fábrica o user.slice (da root) só passa cpu memory pids para baixo. Nada aqui precisa deles.

Duas aplicações que se falam por nome intermédio

Objectivo: rede de utilizador, DNS interno, e isolamento — sem configurar DNS nenhum.

# 1. Uma rede própria
delonix network create loja

# 2. Base de dados e app, ambas nela
delonix container run -d --name db  --net loja -e POSTGRES_PASSWORD=x postgres:16-alpine
delonix container run -d --name app --net loja -p 8080:80 nginx:alpine

# 3. A app resolve a db PELO NOME, sem /etc/hosts nem variáveis
delonix exec app sh -c 'getent hosts db; nc -z db 5432 && echo "porta aberta"'

# 4. Fechar a db a tudo menos à app (alcançabilidade DIRIGIDA)
cat > dep.yaml <<'EOF'
apiVersion: delonix.io/v1
kind: Dependency
metadata:
  name: app-conhece-db
spec:
  from: app
  to: db
  ports: ["5432"]
EOF
delonix stack apply -f dep.yaml

# 5. Provar: um terceiro container na MESMA rede já não alcança a db
delonix container run --rm --net loja alpine sh -c 'nc -z -w2 db 5432 || echo BLOQUEADO'

delonix rm -f db app; delonix network rm loja; rm dep.yaml

Verificação: o passo 3 diz "porta aberta" e o passo 5 diz "BLOQUEADO". A mesma rede, dois resultados — é isso que kind: Dependency faz e uma rede sozinha não faz.

Do Dockerfile à imagem, sem daemon intermédio

Objectivo: construir uma imagem e correr o que construíste.

mkdir -p lab-build && cd lab-build
cat > Delonixfile <<'EOF'
FROM alpine:latest
RUN apk add --no-cache curl
COPY ola.txt /ola.txt
HEALTHCHECK CMD test -f /ola.txt
CMD ["sh", "-c", "cat /ola.txt; sleep 300"]
EOF
echo "construído com delonix" > ola.txt

# `Delonixfile` é procurado antes de `Dockerfile` — sem -f nenhum
delonix build -t minha-app:1.0 .

# `--wait` bloqueia até o HEALTHCHECK passar: sem isto escreve-se
# `until ...; do sleep 1; done`, e escreve-se mal
delonix run -d --name a1 --wait --health-interval 2 minha-app:1.0
delonix ps

delonix rm -f a1; delonix image rm minha-app:1.0; cd ..; rm -rf lab-build

Verificação: o ps mostra (healthy) na coluna STATUS. O motor está a sondar o container sozinho — sem systemd, ao contrário do Podman rootless.

Uma microVM a sério intermédio

Objectivo: uma VM completa com o mesmo CLI dos containers.

# 1. Sem imagem local, o create descarrega a oficial sozinho
delonix vm create dev

# 2. Onde ficou, e com que IP
delonix vm ls
delonix vm describe dev

# 3. Entrar (voltar ao host: Ctrl+])
delonix vm console dev

# 4. Checkpoint de sistema — memória E disco, com a VM a correr
delonix vm snapshot dev limpa
delonix vm snapshots dev

# 5. Estragar alguma coisa lá dentro e voltar atrás
delonix vm restore dev limpa

delonix vm rm dev

Verificação: o passo 4 devolve sem erro com a VM A CORRER. Um snapshot de uma VM parada falha de propósito — o vm stop faz undefine do domínio para não deixar órfãos.

A tua própria imagem de VM, com VMfile avançado

Objectivo: construir uma imagem qcow2 à tua medida, como se constrói uma imagem de container.

mkdir -p lab-vm && cd lab-vm

# Scaffold que CONSTRÓI COMO ESTÁ — apaga o que não precisares
delonix vm init --vmfile --name minha-base
cat VMfile

# Precisa de libguestfs no host: sudo apt install libguestfs-tools
delonix vm build -t minha-base:1.0 .
delonix vm ls

# Um RUN com `apt-get install` precisa de rede no convidado, e pede-se:
#   delonix vm build --network -t minha-base:1.0 .

# Arrancar a partir dela
delonix vm create teste --disk-image minha-base:1.0 --ssh-key @~/.ssh/id_ed25519.pub

# …ou a partir de um qcow2 publicado por ti, sem passar pelo store
delonix vm create outra --url-img https://o-teu-bucket/imagem.qcow2

delonix vm rm teste; cd ..; rm -rf lab-vm

Verificação: o build imprime [1/1] stage-1: FROM ubuntu:24.04 e verifica o checksum da cloud image. Com --url-img, se não houver <url>.sha256 publicado, o motor diz que está a confiar só no TLS — em vez de calar. A chave que injectas vai para a conta delonix, não para a conta default da distro — o bloco de próximos passos do vm create imprime o ssh exacto.

Kubernetes sem Docker avançado

Objectivo: um cluster local cujo runtime dos nós É o Delonix.

# 1. Preflight — falha em milissegundos se faltar a delegação de `cpu`,
#    em vez de descarregar 425 MB para morrer aos 90 segundos
delonix system setup

# 2. O cluster
delonix cluster create --name lab

# 3. Falar com ele
export KUBECONFIG=$(delonix cluster ls -o json | jq -r '.[0].kubeconfig')
kubectl get nodes -o wide

# 4. Levar uma imagem TUA para dentro dos nós, sem registo nenhum
delonix build -t app:dev .
delonix cluster load app:dev --name lab
kubectl run app --image=app:dev --image-pull-policy=Never

# 5. A prova que interessa
kubectl get pod app -w

delonix cluster delete --name lab

Verificação: o passo 5 chega a Running. Nem o ctr images import nem o crictl images provam isto — os dois já reportaram sucesso sobre uma imagem que o kubelet depois não resolvia.