Pular para o conteúdo
, , , ,

Firecracker em produção: systemd, restart automático e a diferença entre demo e serviço de verdade [pt.5]

Hoje a gente transforma o demo em serviço de verdade. Vamos usar o systemd, o gerenciador de serviços que já vem no seu Linux

Avatar de DK
DKTrabalha com Linux e Unix a mais de 23 anos e possui as certificações LPI 3, RHCE, AIX e VIO.

10 dez, 2025
19 min de leitura

Série Firecracker:


Nos artigos anteriores, a gente construiu um nano-Lambda funcional. Sobe microVM, executa função, derruba microVM. Com rede. Com snapshots pra performance. Tudo lindo.

Mas tem um problema: você tá rodando tudo na mão. sudo python3 nano-lambda.py toda vez. Se o servidor reiniciar às 3h da manhã? A microVM sumiu. Se o processo morrer por falta de memória? Ninguém vai saber. Se você precisar rodar 5 VMs diferentes? Abre 5 terminais.

Isso não é produção. Isso é um demo.

Hoje a gente transforma o demo em serviço de verdade. Vamos usar o systemd, o gerenciador de serviços que já vem no seu Linux, pra fazer as microVMs:

  • Iniciarem automaticamente no boot do servidor
  • Reiniciarem se morrerem
  • Terem rede configurada automaticamente
  • Registrarem logs consultáveis
  • Rodarem com isolamento de segurança

Por que systemd?

Se você já trabalhou com servidores Linux, provavelmente já usou systemd sem perceber. systemctl start nginx, systemctl enable postgresql… tudo isso é systemd.

O systemd é o “init system” da maioria das distros modernas. Ele é o primeiro processo que roda quando o Linux inicia (PID 1), e é responsável por subir todos os outros serviços na ordem certa.

Pra nossa microVM, o systemd resolve vários problemas de uma vez:

Problema Solução systemd
“Esqueci de iniciar a VM” systemctl enable, inicia no boot
“A VM morreu e ninguém viu” Restart=on-failure, reinicia automático
“Cadê os logs?” journalctl -u, logs centralizados
“A rede não subiu antes da VM” After=network.target, dependências
“Preciso de 5 VMs iguais” Templates com @, uma unit, N instâncias

O que vamos construir

Vamos pegar o nano-Lambda do artigo 03 (o validador de URLs com rede) e transformar ele em serviço. O resultado final:

# Inicia a microVM como serviço
sudo systemctl start nano-lambda

# Verifica status
sudo systemctl status nano-lambda

# Vê os logs
sudo journalctl -u nano-lambda -f

# Habilita pra iniciar no boot
sudo systemctl enable nano-lambda

A rede (interface TAP, NAT, regras de firewall) vai ser configurada automaticamente quando o serviço iniciar, e limpa quando parar.

Pré-requisito: Se você não leu o artigo 03 sobre redes, leia antes de continuar. Lá a gente explica por que a configuração de rede funciona assim. Aqui a gente só vai automatizar o que você já aprendeu.

Anatomia de uma unit file

Antes de meter a mão na massa, um glossário rápido. O systemd usa arquivos .service (chamados “unit files”) pra definir como um serviço funciona. A estrutura básica:

[Unit]
Description=Minha MicroVM
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/firecracker --api-sock /run/firecracker/vm.socket
Restart=on-failure

[Install]
WantedBy=multi-user.target

Três seções:

  • [Unit]: Metadados e dependências. “Quem sou eu” e “de quem dependo”
  • [Service]: Como rodar. O executável, variáveis de ambiente, política de restart
  • [Install]: Como instalar. Em qual “target” (modo de operação) esse serviço deve rodar

Os campos mais importantes pra gente:

Campo O que faz
After= Espera outros serviços iniciarem primeiro
Type=simple O processo principal é o próprio ExecStart
ExecStartPre= Comandos pra rodar ANTES do serviço principal
ExecStopPost= Comandos pra rodar DEPOIS do serviço parar
Restart=on-failure Reinicia se o processo morrer com erro
RuntimeDirectory= Cria diretório em /run/ com permissões corretas

Passo 1: O script de setup de rede

Primeiro, vamos criar um script que configura toda a rede necessária pra microVM. Isso inclui:

  1. Criar a interface TAP
  2. Configurar IP
  3. Habilitar IP forwarding
  4. Configurar NAT/masquerading
  5. Bloquear acesso à rede local (segurança)

Salve como /usr/local/bin/firecracker-network-setup.sh:

#!/bin/bash
# firecracker-network-setup.sh
# Configura rede para microVM Firecracker

set -e

TAP_DEV="${TAP_DEV:-tap0}"
TAP_IP="${TAP_IP:-172.16.0.1}"
TAP_CIDR="${TAP_CIDR:-24}"

ACTION="${1:-up}"

setup_network() {
    echo "Configurando rede para microVM..."

    # Verifica se já existe
    if ip link show "$TAP_DEV" &>/dev/null; then
        echo "Interface $TAP_DEV já existe, pulando criação"
        return 0
    fi

    # Cria interface TAP
    ip tuntap add dev "$TAP_DEV" mode tap
    ip addr add "${TAP_IP}/${TAP_CIDR}" dev "$TAP_DEV"
    ip link set "$TAP_DEV" up

    echo "Interface $TAP_DEV criada com IP $TAP_IP/$TAP_CIDR"

    # Habilita IP forwarding
    sysctl -w net.ipv4.ip_forward=1 > /dev/null

    # Configura firewall (detecta firewalld ou iptables)
    if command -v firewall-cmd &>/dev/null && systemctl is-active firewalld &>/dev/null; then
        setup_firewalld
    else
        setup_iptables
    fi

    echo "Rede configurada com sucesso"
}

setup_firewalld() {
    echo "Configurando firewalld..."

    firewall-cmd --zone=trusted --add-interface="$TAP_DEV" 2>/dev/null || true
    firewall-cmd --add-masquerade 2>/dev/null || true

    # Bloqueia acesso a redes privadas (seguranca)
    firewall-cmd --zone=trusted --add-rich-rule="rule family=ipv4 source address=172.16.0.0/24 destination address=10.0.0.0/8 drop" 2>/dev/null || true
    firewall-cmd --zone=trusted --add-rich-rule="rule family=ipv4 source address=172.16.0.0/24 destination address=192.168.0.0/16 drop" 2>/dev/null || true
}

setup_iptables() {
    echo "Configurando iptables..."

    # Detecta interface de saida
    DEFAULT_IFACE=$(ip route | grep default | awk '{print $5}' | head -1)

    # NAT/Masquerading
    iptables -t nat -C POSTROUTING -o "$DEFAULT_IFACE" -j MASQUERADE 2>/dev/null || \
        iptables -t nat -A POSTROUTING -o "$DEFAULT_IFACE" -j MASQUERADE

    # Forward
    iptables -C FORWARD -i "$TAP_DEV" -o "$DEFAULT_IFACE" -j ACCEPT 2>/dev/null || \
        iptables -A FORWARD -i "$TAP_DEV" -o "$DEFAULT_IFACE" -j ACCEPT

    iptables -C FORWARD -i "$DEFAULT_IFACE" -o "$TAP_DEV" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || \
        iptables -A FORWARD -i "$DEFAULT_IFACE" -o "$TAP_DEV" -m state --state RELATED,ESTABLISHED -j ACCEPT

    # Bloqueia acesso a redes privadas (seguranca)
    iptables -C FORWARD -i "$TAP_DEV" -d 10.0.0.0/8 -j DROP 2>/dev/null || \
        iptables -I FORWARD -i "$TAP_DEV" -d 10.0.0.0/8 -j DROP

    iptables -C FORWARD -i "$TAP_DEV" -d 192.168.0.0/16 -j DROP 2>/dev/null || \
        iptables -I FORWARD -i "$TAP_DEV" -d 192.168.0.0/16 -j DROP
}

teardown_network() {
    echo "Removendo configuracao de rede..."

    if ip link show "$TAP_DEV" &>/dev/null; then
        ip link set "$TAP_DEV" down
        ip tuntap del dev "$TAP_DEV" mode tap
        echo "Interface $TAP_DEV removida"
    fi

    # Nao removemos regras de firewall automaticamente
    # pois podem estar sendo usadas por outras VMs

    echo "Limpeza concluida"
}

case "$ACTION" in
    up|start)
        setup_network
        ;;
    down|stop)
        teardown_network
        ;;
    *)
        echo "Uso: $0 {up|down}"
        exit 1
        ;;
esac

Torne executável:

sudo chmod +x /usr/local/bin/firecracker-network-setup.sh

O script detecta automaticamente se você usa firewalld (Fedora, RHEL) ou iptables (Ubuntu, Debian) e configura apropriadamente.

Passo 2: O script de inicialização da microVM

Agora precisamos de um script que inicia o Firecracker e configura a microVM via API. Esse script vai ser chamado pelo systemd.

Salve como /usr/local/bin/firecracker-vm-start.sh:

#!/bin/bash
# firecracker-vm-start.sh
# Inicia uma microVM Firecracker

set -e

# Configuracoes (podem ser sobrescritas por variaveis de ambiente)
VM_NAME="${VM_NAME:-default}"
SOCKET_PATH="${SOCKET_PATH:-/run/firecracker/${VM_NAME}.socket}"
KERNEL_PATH="${KERNEL_PATH:-/var/lib/firecracker/vmlinux.bin}"
ROOTFS_PATH="${ROOTFS_PATH:-/var/lib/firecracker/rootfs-${VM_NAME}.ext4}"
VCPU_COUNT="${VCPU_COUNT:-1}"
MEM_SIZE_MIB="${MEM_SIZE_MIB:-256}"
TAP_DEV="${TAP_DEV:-tap0}"
GUEST_MAC="${GUEST_MAC:-AA:FC:00:00:00:01}"

FIRECRACKER_BIN="${FIRECRACKER_BIN:-/usr/local/bin/firecracker}"

# Garante que o diretorio do socket existe
mkdir -p "$(dirname "$SOCKET_PATH")"

# Remove socket antigo se existir
rm -f "$SOCKET_PATH"

echo "Iniciando Firecracker para VM: $VM_NAME"
echo "  Socket: $SOCKET_PATH"
echo "  Kernel: $KERNEL_PATH"
echo "  Rootfs: $ROOTFS_PATH"

# Inicia Firecracker em background e salva PID
$FIRECRACKER_BIN --api-sock "$SOCKET_PATH" &
FC_PID=$!

# Aguarda socket ficar disponivel
for i in $(seq 1 50); do
    if [ -S "$SOCKET_PATH" ]; then
        break
    fi
    sleep 0.1
done

if [ ! -S "$SOCKET_PATH" ]; then
    echo "ERRO: Timeout esperando socket do Firecracker"
    exit 1
fi

sleep 0.2

echo "Configurando VM via API..."

# Configura kernel
curl --unix-socket "$SOCKET_PATH" -s -X PUT \
    "http://localhost/boot-source" \
    -H "Content-Type: application/json" \
    -d "{
        \"kernel_image_path\": \"$KERNEL_PATH\",
        \"boot_args\": \"console=ttyS0 reboot=k panic=1 pci=off quiet\"
    }"

# Configura rootfs
curl --unix-socket "$SOCKET_PATH" -s -X PUT \
    "http://localhost/drives/rootfs" \
    -H "Content-Type: application/json" \
    -d "{
        \"drive_id\": \"rootfs\",
        \"path_on_host\": \"$ROOTFS_PATH\",
        \"is_root_device\": true,
        \"is_read_only\": false
    }"

# Configura recursos
curl --unix-socket "$SOCKET_PATH" -s -X PUT \
    "http://localhost/machine-config" \
    -H "Content-Type: application/json" \
    -d "{
        \"vcpu_count\": $VCPU_COUNT,
        \"mem_size_mib\": $MEM_SIZE_MIB
    }"

# Configura rede
curl --unix-socket "$SOCKET_PATH" -s -X PUT \
    "http://localhost/network-interfaces/eth0" \
    -H "Content-Type: application/json" \
    -d "{
        \"iface_id\": \"eth0\",
        \"guest_mac\": \"$GUEST_MAC\",
        \"host_dev_name\": \"$TAP_DEV\"
    }"

echo "Iniciando microVM..."

# Inicia a VM
curl --unix-socket "$SOCKET_PATH" -s -X PUT \
    "http://localhost/actions" \
    -H "Content-Type: application/json" \
    -d '{"action_type": "InstanceStart"}'

echo "MicroVM iniciada com sucesso (PID: $FC_PID)"

# Aguarda o processo Firecracker (isso mantem o servico "rodando")
wait $FC_PID

Torne executável:

sudo chmod +x /usr/local/bin/firecracker-vm-start.sh

Passo 3: A unit file do systemd

Agora o coração da coisa: a unit file que junta tudo.

Salve como /etc/systemd/system/nano-lambda.service:

[Unit]
Description=Nano-Lambda MicroVM (Firecracker)
Documentation=https://github.com/firecracker-microvm/firecracker
After=network.target
Wants=network.target

[Service]
Type=simple

# Variaveis de ambiente para os scripts
Environment=VM_NAME=nano-lambda
Environment=SOCKET_PATH=/run/firecracker/nano-lambda.socket
Environment=KERNEL_PATH=/var/lib/firecracker/vmlinux.bin
Environment=ROOTFS_PATH=/var/lib/firecracker/rootfs-network.ext4
Environment=VCPU_COUNT=1
Environment=MEM_SIZE_MIB=256
Environment=TAP_DEV=tap0
Environment=TAP_IP=172.16.0.1
Environment=GUEST_MAC=AA:FC:00:00:00:01

# Cria diretorio em /run para o socket
RuntimeDirectory=firecracker
RuntimeDirectoryMode=0755

# Antes de iniciar: configura rede
ExecStartPre=/usr/local/bin/firecracker-network-setup.sh up

# Comando principal
ExecStart=/usr/local/bin/firecracker-vm-start.sh

# Depois de parar: limpa rede
ExecStopPost=/usr/local/bin/firecracker-network-setup.sh down

# Politica de restart
Restart=on-failure
RestartSec=5
StartLimitBurst=3
StartLimitIntervalSec=60

# Timeout
TimeoutStartSec=30
TimeoutStopSec=10

# Logs
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nano-lambda

[Install]
WantedBy=multi-user.target

Vamos entender os campos importantes:

Ciclo de vida:

  • ExecStartPre=, Roda ANTES do serviço principal. Aqui a gente configura a rede.
  • ExecStart=, O serviço principal. O Firecracker rodando.
  • ExecStopPost=, Roda DEPOIS do serviço parar. Limpeza da rede.

Diretórios:

  • RuntimeDirectory=firecracker, Cria /run/firecracker/ automaticamente com permissões certas. Esse diretório é limpo no reboot (é um tmpfs), então o systemd recria toda vez.

Restart:

  • Restart=on-failure, Reinicia se o processo morrer com código de erro (não se for parado manualmente)
  • RestartSec=5, Espera 5 segundos antes de reiniciar
  • StartLimitBurst=3 e StartLimitIntervalSec=60, No máximo 3 restarts em 60 segundos. Se falhar mais que isso, desiste (evita loop infinito de crash).

Logs:

  • StandardOutput=journal, Manda stdout pro journald
  • SyslogIdentifier=nano-lambda, Nome que aparece nos logs

Passo 4: Preparando os arquivos

Antes de testar, precisamos colocar os arquivos nos lugares certos.

# Cria diretorio para arquivos do Firecracker
sudo mkdir -p /var/lib/firecracker

# Copia o binario do Firecracker
sudo cp ./firecracker /usr/local/bin/firecracker

# Copia o kernel
sudo cp ./vmlinux.bin /var/lib/firecracker/vmlinux.bin

# Copia o rootfs com rede (do artigo 03 - https://fogonacaixadagua.com.br/2025/12/redes-no-firecracker-configurando-tap-nat-e-internet-para-seu-nano-lambda/)
sudo cp ./rootfs-network.ext4 /var/lib/firecracker/rootfs-network.ext4

Importante: O rootfs precisa ter a rede configurada internamente:

  • IP estático: 172.16.0.2/24 (ou outro IP da subnet)
  • Gateway: 172.16.0.1 (o IP do host na TAP)
  • DNS: configurado em /etc/resolv.conf

Sem isso, a VM não terá conectividade. Se você não fez essa configuração, volte ao artigo 03 e siga os passos de configuração do rootfs.

Passo 5: Testando

Recarregue o systemd e inicie o serviço:

# Recarrega as unit files
sudo systemctl daemon-reload

# Inicia o servico
sudo systemctl start nano-lambda

# Verifica status
sudo systemctl status nano-lambda

Se tudo der certo, você verá algo assim:

? nano-lambda.service - Nano-Lambda MicroVM (Firecracker)
     Loaded: loaded (/etc/systemd/system/nano-lambda.service; disabled; preset: disabled)
     Active: active (running) since Mon 2024-12-09 10:30:00 UTC; 5s ago
       Docs: https://github.com/firecracker-microvm/firecracker
    Process: 1234 ExecStartPre=/usr/local/bin/firecracker-network-setup.sh up (code=exited, status=0/SUCCESS)
   Main PID: 1235 (firecracker-vm-)
      Tasks: 5 (limit: 4915)
     Memory: 105.0M
        CPU: 1.234s
     CGroup: /system.slice/nano-lambda.service
             ??1235 /bin/bash /usr/local/bin/firecracker-vm-start.sh
             ??1242 /usr/local/bin/firecracker --api-sock /run/firecracker/nano-lambda.socket

Pra ver os logs em tempo real:

sudo journalctl -u nano-lambda -f

Pra habilitar no boot:

sudo systemctl enable nano-lambda

Passo 6: Verificando a rede

Pra confirmar que a rede está funcionando:

# Verifica se a interface TAP existe
ip addr show tap0

# Verifica se o NAT está configurado
sudo iptables -t nat -L -n | grep MASQUERADE
# ou
sudo firewall-cmd --query-masquerade

Se você tiver outro terminal aberto, pode verificar que a VM consegue acessar a internet usando o console serial via journalctl.

A saída do console serial da VM (console=ttyS0) vai pro stdout do Firecracker, que o systemd redireciona pro journald. Então você pode ver a saída do boot e interagir (de forma limitada) pelos logs:

# Ver a saida do console em tempo real
sudo journalctl -u nano-lambda -f

Pra testar a conectividade de dentro da VM, o rootfs precisa ter um script de inicialização que faça isso automaticamente, ou você pode usar SSH se tiver configurado no rootfs.

Uma alternativa é rodar o Firecracker manualmente (fora do systemd) pra ter acesso interativo ao console:

# Para o servico
sudo systemctl stop nano-lambda

# Roda manualmente com console interativo
sudo /usr/local/bin/firecracker --api-sock /tmp/fc-test.socket &
# ... configure via API ou use o nano-lambda.py do artigo 02 ...

De dentro da VM (se tiver acesso), teste a conectividade:

# Testa acesso a internet
ping -c 3 8.8.8.8

# Testa resolucao DNS
ping -c 3 google.com

Logging e observabilidade

Com o systemd, os logs vão todos pro journald. Isso significa que você pode:

# Ver todos os logs do servico
sudo journalctl -u nano-lambda

# Logs em tempo real
sudo journalctl -u nano-lambda -f

# Logs desde o ultimo boot
sudo journalctl -u nano-lambda -b

# Logs das ultimas 2 horas
sudo journalctl -u nano-lambda --since "2 hours ago"

# Logs com prioridade de erro ou acima
sudo journalctl -u nano-lambda -p err

Isso é muito melhor do que caçar arquivos de log espalhados pelo sistema.

Restart automático e health checks

A configuração que fizemos já reinicia automaticamente em caso de falha. Mas você pode ajustar o comportamento:

# Reinicia sempre (inclusive se parar normalmente)
Restart=always

# Reinicia só em falha (codigo de saida != 0)
Restart=on-failure

# Nunca reinicia
Restart=no

O RestartSec=5 evita que o systemd fique tentando reiniciar freneticamente. Se a VM morrer, espera 5 segundos antes de tentar de novo.

Os limites StartLimitBurst=3 e StartLimitIntervalSec=60 previnem loops infinitos: se a VM crashar 3 vezes em menos de 1 minuto, o systemd desiste e marca o serviço como “failed”. Isso evita consumir CPU tentando subir algo que claramente está quebrado.

Hardening: segurança via systemd

Até aqui, o Firecracker roda como root. Funciona, mas não é ideal. O systemd oferece várias diretivas pra isolar o processo e reduzir a superfície de ataque.

Adicione essas linhas na seção [Service] da unit file:

[Service]
# ... configuracoes anteriores ...

# Isolamento de filesystem
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/firecracker /run/firecracker

# Restricoes de processo
NoNewPrivileges=yes
PrivateDevices=yes
DeviceAllow=/dev/kvm rw
DeviceAllow=/dev/net/tun rw

# Capabilities minimas
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW

# Limites de recursos
MemoryMax=512M
TasksMax=10

O que cada um faz:

Filesystem:

  • ProtectSystem=strict, Filesystem do host é read-only (exceto /dev, /proc, /sys)
  • ProtectHome=yes, Sem acesso ao /home
  • PrivateTmp=yes, /tmp isolado (cada serviço vê um /tmp diferente)
  • ReadWritePaths=, Exceções: onde o Firecracker precisa escrever

Processo:

  • NoNewPrivileges=yes, Previne escalação de privilégios via setuid
  • PrivateDevices=yes, Sem acesso a /dev (exceto o que liberarmos)
  • DeviceAllow=, Whitelist: só /dev/kvm (virtualização) e /dev/net/tun (rede)

Capabilities:

  • CapabilityBoundingSet=, Dropa todas as capabilities exceto as listadas
  • CAP_NET_ADMIN, Necessário pra criar interface TAP
  • CAP_NET_RAW, Necessário pra alguns tipos de operação de rede

Recursos:

  • MemoryMax=512M, Limite de memória pro processo (além da RAM da VM)
  • TasksMax=10, Limite de threads/processos filhos

Com essas ~10 linhas, você reduz drasticamente o que um possível atacante poderia fazer mesmo se conseguisse escapar da microVM.

Nota sobre User=: O ideal seria rodar o Firecracker como usuário não-privilegiado usando User=firecracker na unit file. O problema: os scripts de rede (criar TAP, configurar NAT) precisam de root. Uma solução seria separar os comandos de rede em scripts com sudo ou usar ExecStartPre=+/script (o + força execução como root). Para este artigo, mantemos a simplicidade. Se você for pra produção de verdade, vale investigar essa separação.

Templates: múltiplas VMs com uma unit file

E se você precisar rodar 5 microVMs diferentes? Criar 5 unit files quase iguais seria tedioso.

O systemd tem um recurso chamado “templates” (ou “instanced units”). Você cria uma unit file com @ no nome, e ela pode ser instanciada várias vezes com parâmetros diferentes.

Salve como /etc/systemd/system/[email protected]:

[Unit]
Description=Firecracker MicroVM (%i)
Documentation=https://github.com/firecracker-microvm/firecracker
After=network.target

[Service]
Type=simple

# %i é substituido pelo nome da instancia
Environment=VM_NAME=%i
Environment=SOCKET_PATH=/run/firecracker/%i.socket
Environment=KERNEL_PATH=/var/lib/firecracker/vmlinux.bin
Environment=ROOTFS_PATH=/var/lib/firecracker/rootfs-%i.ext4

# Cada VM precisa de TAP e MAC diferentes
# Usamos um arquivo de configuracao por VM
# O hifen (-) significa: nao falha se o arquivo nao existir
EnvironmentFile=-/etc/firecracker/%i.conf

RuntimeDirectory=firecracker
RuntimeDirectoryMode=0755

ExecStartPre=/usr/local/bin/firecracker-network-setup.sh up
ExecStart=/usr/local/bin/firecracker-vm-start.sh
ExecStopPost=/usr/local/bin/firecracker-network-setup.sh down

Restart=on-failure
RestartSec=5

StandardOutput=journal
StandardError=journal
SyslogIdentifier=firecracker-%i

[Install]
WantedBy=multi-user.target

O %i é substituído pelo nome da instância. O hífen em EnvironmentFile=- significa que o serviço não falha se o arquivo de configuração não existir, usa os defaults definidos nas linhas Environment=.

Agora crie arquivos de configuração:

# /etc/firecracker/web.conf
TAP_DEV=tap0
TAP_IP=172.16.0.1
GUEST_MAC=AA:FC:00:00:00:01
VCPU_COUNT=2
MEM_SIZE_MIB=512

# /etc/firecracker/worker.conf
TAP_DEV=tap1
TAP_IP=172.16.1.1
GUEST_MAC=AA:FC:00:00:00:02
VCPU_COUNT=1
MEM_SIZE_MIB=256

E use assim:

# Inicia a VM "web"
sudo systemctl start firecracker@web

# Inicia a VM "worker"
sudo systemctl start firecracker@worker

# Status de todas
sudo systemctl status 'firecracker@*'

# Logs da VM web
sudo journalctl -u firecracker@web -f

Cada instância roda independente, com sua própria rede, seus próprios logs, seu próprio ciclo de vida.

Exemplo prático: daemonizando o validador de URLs

Vamos juntar tudo e transformar o validador de URLs do artigo 03 em serviço.

Primeiro, garanta que você tem o rootfs com rede e a função configurada:

# Estrutura esperada
/var/lib/firecracker/
??? vmlinux.bin              # Kernel
??? rootfs-network.ext4      # Rootfs com Python, requests, rede configurada
??? functions/
    ??? handler.py           # Validador de URLs (opcional, se montar dinamicamente)

A unit file já está pronta (nano-lambda.service). Só precisamos garantir que o rootfs tem:

  1. Rede configurada (IP 172.16.0.2, gateway 172.16.0.1, DNS)
  2. Python e requests instalados
  3. O script /run-function.sh configurado

Se você seguiu o artigo 03, já tem tudo isso.

Agora teste:

# Inicia
sudo systemctl start nano-lambda

# Verifica que está rodando
sudo systemctl status nano-lambda

# Vê os logs
sudo journalctl -u nano-lambda -f

# Para
sudo systemctl stop nano-lambda

# Verifica que a rede foi limpa
ip link show tap0  # Deve dar erro "does not exist"

Troubleshooting

Serviço não inicia:

# Veja os logs detalhados
sudo journalctl -u nano-lambda -b --no-pager

# Verifique a sintaxe da unit file
sudo systemd-analyze verify /etc/systemd/system/nano-lambda.service

Rede não funciona:

# Verifique se a TAP existe
ip addr show tap0

# Verifique IP forwarding
sysctl net.ipv4.ip_forward

# Verifique masquerading
sudo iptables -t nat -L -n | grep MASQUERADE

Firecracker não encontra o socket:

# Verifique se o diretório existe
ls -la /run/firecracker/

# Verifique permissões
stat /run/firecracker/

VM crashando em loop:

# Veja o status detalhado
systemctl status nano-lambda

# Se atingiu o limite de restarts, resete
sudo systemctl reset-failed nano-lambda
sudo systemctl start nano-lambda

Indo além: menção ao firectl

Se você quiser ir pra produção de verdade, existe o firectl, uma ferramenta oficial que abstrai muito do que fizemos manualmente.

Com firectl, você faz:

firectl \
  --kernel=/var/lib/firecracker/vmlinux.bin \
  --root-drive=/var/lib/firecracker/rootfs.ext4 \
  --tap-device=tap0/AA:FC:00:00:00:01

E ele cuida de criar o socket, configurar via API, etc.

Mas agora você entende o que o firectl faz por baixo. Quando algo quebrar, e vai quebrar, você sabe onde olhar.

Conclusão

Pronto. Sua microVM agora é um serviço de verdade:

  • Inicia no boot
  • Reinicia se morrer
  • Tem rede configurada automaticamente
  • Logs centralizados no journald
  • Isolamento de segurança via systemd

A diferença entre “funciona no meu terminal” e “funciona em produção” é exatamente isso: automação, resiliência, observabilidade.

No próximo artigo… bom, a série pode crescer. Orquestração com múltiplas VMs? Integração com containerd? Métricas com Prometheus? Me conta o que te interessa.

Até lá!


Arquivos deste artigo:

  • firecracker-network-setup.sh, Script de setup/teardown de rede
  • firecracker-vm-start.sh, Script de inicialização da VM
  • nano-lambda.service, Unit file básica
  • [email protected], Template para múltiplas VMs

Este artigo faz parte da série “Firecracker: MicroVMs na Prática”. Todo o código está disponível em GitHub.

Avatar de DK

Comentários

Deixe um comentário

Seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Ir para