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

17 dez, 2025
31 min de leitura

Do Ping ao Banco de Dados: Osso Linux com MariaDB

Transforme um Linux mínimo em um appliance MariaDB. Domine PID 1, sinais de sistema e shutdown seguro nesta Parte 02 da série Osso 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.

17 dez, 2025
31 min de leitura
, ,

Série Osso Linux:


No artigo anterior, construímos o Osso Linux, um sistema operacional mínimo de ~3MB capaz de dar ping na internet. Agora vamos transformar esse esqueleto em algo útil: um appliance de banco de dados rodando MariaDB.

O objetivo não é criar um servidor de produção (isso fica para o próximo artigo), mas entender os conceitos fundamentais que fazem um banco de dados rodar em um ambiente mínimo. Vamos explorar por que o MariaDB não pode simplesmente ser o primeiro processo do sistema, como o Linux lida com sinais de desligamento, e o que acontece quando você puxa o cabo de força de um servidor.

O que é um appliance (e por que isso importa)

Um appliance é uma máquina, física ou virtual, construída para executar uma única função. Em vez de um sistema operacional genérico onde você instala vários programas, um appliance vem pronto para fazer uma coisa só, e fazer bem feito.

Pense em um roteador doméstico. Você não instala aplicativos nele, não navega na internet a partir dele. Ele faz uma coisa: rotear pacotes. Isso é um appliance de hardware.

No mundo de VMs e containers, o conceito é o mesmo. Um appliance de banco de dados existe para servir queries. Um appliance de cache existe para armazenar dados temporários. Um appliance de proxy existe para intermediar conexões. Nada mais, nada menos.

Por que construir appliances mínimos?

  • Superfície de ataque reduzida: menos software significa menos vulnerabilidades
  • Recursos otimizados: sem processos competindo por CPU e memória
  • Boot instantâneo: segundos em vez de minutos
  • Previsibilidade: comportamento consistente, sem surpresas de atualização

As técnicas deste artigo servem para qualquer appliance

Embora o foco seja MariaDB, tudo que você vai aprender aqui, o papel do init como PID 1, gerenciamento de sinais, shutdown graceful, persistência de dados, aplica-se a qualquer serviço que você queira transformar em appliance:

  • Valkey ou Memcached para cache
  • Nginx ou HAProxy para proxy reverso
  • PostgreSQL para outro banco relacional
  • Mosquitto ou RabbitMQ para mensageria
  • Prometheus para métricas

O processo é sempre o mesmo: identificar o que o serviço precisa do kernel, criar um init que gerencie seu ciclo de vida, e montar um rootfs mínimo com as dependências. Muda o serviço, mas a estrutura permanece.

Por que MariaDB como exemplo?

Escolhi MariaDB justamente por ser um serviço complexo que exercita várias técnicas de uma só vez:

  • Persistência de dados: precisa de armazenamento confiável, não pode perder dados no reboot
  • Shutdown graceful obrigatório: terminar abruptamente pode corromper o banco
  • Threading intensivo: o InnoDB usa múltiplas threads, exigindo suporte adequado do kernel (futex, etc.)
  • Sockets de rede e Unix: aceita conexões TCP e locais simultaneamente
  • Autenticação e NSS: precisa resolver usuários e hosts, o que complica compilação estática
  • Consumo de memória configurável: permite demonstrar tuning para ambientes limitados

Um servidor web estático seria mais simples, mas não ensinaria tanto. Com MariaDB, você enfrenta problemas reais que aparecem em qualquer appliance de dados, e as soluções que vamos construir servem de base para cenários igualmente complexos.

O que vamos construir

No final deste artigo, teremos:

  • Um kernel Linux expandido com suporte a recursos que o MariaDB precisa
  • O MariaDB 10.11 LTS compilado e funcionando
  • Um script init que gerencia o ciclo de vida do banco
  • Duas opções de persistência de dados
  • Um ambiente para desenvolvimento e testes

Tamanho final estimado: ~115MB comprimido / ~250MB descomprimido (kernel + userland + MariaDB + libs).

Todos os arquivos de configuração e scripts utilizados neste artigo estão disponíveis no repositório: github.com/dklima/osso-linux-mariadb

Por que precisamos de um wrapper init?

Essa é a parte mais importante do artigo. Se você entender isso, o resto faz sentido.

O papel especial do PID 1

Quando o Linux termina de inicializar o kernel, ele precisa executar o primeiro processo do userspace. Esse processo recebe o PID (Process ID) número 1 e tem responsabilidades únicas:

  1. É o ancestral de todos os processos – todo processo no sistema é filho ou descendente do PID 1
  2. Recebe processos órfãos – quando um processo pai morre, seus filhos são “adotados” pelo PID 1
  3. Recebe sinais do kernel no shutdown – quando você desliga o sistema, o kernel avisa o PID 1
  4. Se morrer, o sistema entra em kernel panic – não existe Linux sem PID 1

SIGTERM e SIGKILL: os sinais de término

O Linux usa sinais para comunicação entre processos. Dois sinais são especialmente importantes para entender shutdown:

SIGTERM (sinal 15) é um pedido educado para o processo terminar. Quando uma aplicação recebe SIGTERM, ela pode:

  • Interceptar o sinal e decidir o que fazer
  • Terminar transações em andamento
  • Fechar conexões de rede corretamente
  • Sincronizar dados em memória para o disco
  • Fazer qualquer limpeza necessária antes de sair

O MariaDB, ao receber SIGTERM, inicia um shutdown graceful: termina queries em execução, faz flush dos buffers do InnoDB para disco, fecha os arquivos de dados corretamente, e só então termina. Isso pode levar alguns segundos (ou minutos, dependendo do que está acontecendo).

SIGKILL (sinal 9) é uma execução sumária. Quando o kernel envia SIGKILL:

  • O processo é removido da memória imediatamente
  • Não há chance de interceptar ou ignorar
  • Nenhum cleanup acontece
  • Arquivos abertos são fechados pelo kernel (sem flush de buffers da aplicação)

Para o MariaDB, um SIGKILL significa: transações em memória perdidas, possível inconsistência nos dados, e a necessidade de crash recovery no próximo boot. O InnoDB é projetado para sobreviver a isso (ele tem write-ahead logging), mas não é a forma ideal de parar um banco de dados.

Por que o MariaDB não pode ser PID 1

Você poderia pensar: “por que não colocar o MariaDB direto como PID 1 e simplificar tudo?”

Três motivos:

  1. Propagação de sinais: Quando você executa shutdown ou poweroff, o kernel envia SIGTERM para o PID 1. O MariaDB até entende SIGTERM, mas ele não foi projetado para propagar esse sinal para processos filhos ou coordenar o desligamento do sistema.

  2. Reaping de processos órfãos: Quando um processo morre, alguém precisa “coletar” seu status de saída (chamando wait()). Se o pai do processo morreu, essa responsabilidade vai para o PID 1. O MariaDB não faz isso, então processos zumbis se acumulariam.

  3. Se o MariaDB crashar, adeus sistema: Se qualquer coisa fizer o MariaDB terminar inesperadamente, o kernel entra em panic. Com um wrapper, podemos pelo menos logar o que aconteceu ou tentar reiniciar.

O fluxo correto de shutdown

diagram_mariadb_poweroff

O diagrama mostra a cadeia de sinais: o kernel envia SIGTERM para o init (PID 1), que repassa para o MariaDB, aguarda o shutdown graceful, e só então desmonta os filesystems. Observe como cada camada tem a chance de fazer cleanup antes de terminar.

Se você simplesmente desligar a VM ou matar o processo com kill -9, o MariaDB não tem chance de fazer nada disso. Os dados em memória são perdidos e o InnoDB precisa fazer recovery no próximo boot.

Decisões de arquitetura

Antes de colocar a mão na massa, algumas decisões importantes.

Por que compilação híbrida (e não estática)

No artigo anterior, compilamos o Toybox estaticamente com musl libc. Resultado: um binário único sem dependências. Por que não fazer o mesmo com o MariaDB?

O problema está no NSS (Name Service Switch). NSS é o subsistema do Linux responsável por resolver nomes: usuários, grupos, hosts, serviços. Quando você faz getpwnam("root") ou gethostbyname("localhost"), o NSS entra em ação.

A glibc implementa NSS de forma modular, carregando bibliotecas dinamicamente conforme a configuração em /etc/nsswitch.conf. O musl libc tem uma implementação simplificada que funciona para casos básicos, mas pode dar problemas com:

  • Resolução de nomes de usuário (MariaDB usa para autenticação)
  • Threading complexo (InnoDB usa muitas threads)
  • Alguns recursos de rede

Compilar o MariaDB estaticamente com musl é possível, mas é uma armadilha: funciona nos testes básicos e vai falhar misteriosamente em produção.

Nossa abordagem híbrida:

  1. Compilar MariaDB dinamicamente (linkando com glibc)
  2. Identificar as bibliotecas necessárias com ldd
  3. Copiar apenas essas bibliotecas para o rootfs

Isso nos dá compatibilidade total com um overhead aceitável (~15-20MB de libs).

Por que MariaDB 10.11 LTS

Escolhemos a versão 10.11 LTS (Long Term Support) por alguns motivos:

  • Estabilidade: LTS significa que essa versão recebe apenas correções de bugs e segurança, sem mudanças que possam quebrar compatibilidade
  • Suporte prolongado: Receberá atualizações até 2028
  • Documentação madura: Muita gente já usou, muitos problemas já foram documentados
  • Tamanho razoável: Versões mais novas tendem a crescer

A versão 11.x tem features interessantes, mas para um artigo educacional, preferimos previsibilidade a novidade.

Preparando o ambiente de trabalho

Antes de começar, vamos criar uma estrutura de diretórios organizada. Isso facilita acompanhar o artigo e saber onde cada coisa está.

mkdir -p ~/osso-mariadb/{kernel,mariadb,rootfs,output}
cd ~/osso-mariadb

Estrutura:

~/osso-mariadb/
  \_ kernel/         # Código fonte do Linux
  \_ mariadb/        # Código fonte do MariaDB
  \_ rootfs/         # Sistema de arquivos que vamos montar
  \_ output/         # bzImage e initramfs finais

Baixe os fontes nos diretórios apropriados:

# Kernel (mesmo do artigo anterior)
cd ~/osso-mariadb/kernel
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.1.tar.xz
tar xf linux-6.18.1.tar.xz

# MariaDB
cd ~/osso-mariadb/mariadb
wget https://downloads.mariadb.org/rest-api/mariadb/10.11.15/mariadb-10.11.15.tar.gz
tar xzf mariadb-10.11.15.tar.gz

A partir de agora, os comandos assumem que você está no diretório ~/osso-mariadb ou em um dos subdiretórios.

Sobre os comandos deste artigo:

  • Testado em Fedora 43, mas funciona em qualquer distro com as dependências instaladas
  • Dependências para Fedora/RHEL e Debian/Ubuntu são listadas nas seções relevantes
  • Os IPs 10.0.2.x no init são do QEMU user-mode network (padrão quando você usa -netdev user): a VM recebe 10.0.2.15, o gateway/host é 10.0.2.2

Preparando o kernel

Nosso kernel do artigo anterior já tem o básico: boot, rede TCP/IP, serial console, EXT4. Para o MariaDB, precisamos adicionar alguns recursos.

Baixando a configuração base

Você tem duas opções:

Opção A: Configuração pronta (recomendado para quem quer ir direto ao ponto)

cd ~/osso-mariadb/kernel/linux-6.18.1
wget https://raw.githubusercontent.com/dklima/osso-linux-mariadb/main/.config
make -j$(nproc) bzImage

A configuração completa já inclui todas as opções do osso-linux-minimo mais as adições para MariaDB. Pule para a seção “Compilando” se usar esta opção.

Opção B: Configuração manual (recomendado para aprendizado)

Baixe a configuração base e adicione as opções manualmente:

cd ~/osso-mariadb/kernel/linux-6.18.1
wget https://raw.githubusercontent.com/dklima/osso-linux-minimo/refs/heads/main/.config
make olddefconfig  # Atualiza config para a versão atual do kernel

O make olddefconfig resolve dependências e adiciona valores padrão para opções novas. Sempre rode esse comando ao reutilizar um .config de outra versão do kernel.

Mudanças necessárias

Entre no menuconfig e habilite as seguintes opções:

make menuconfig

Pegadinha clássica: A opção mais esquecida é CONFIG_FILE_LOCKING (item 7 abaixo). Sem ela, o MariaDB falha com “Can’t lock aria control file” e você vai perder horas debugando (eu perdi horas pra que você não precise). Se estiver com pressa, verifique essa primeiro.

1. Unix Domain Sockets (obrigatório)

O MariaDB usa sockets Unix para conexões locais. Sem isso, nem o cliente mysql consegue conectar no servidor.

Networking support --->
  Networking options --->
    <*> Unix domain sockets

Isso habilita CONFIG_UNIX=y.

2. PTY (Pseudo-Terminal) (necessário para scripts)

O mysqld_safe e outros scripts precisam de PTY para funcionar.

Device Drivers --->
  Character devices --->
    <*> Unix98 PTY support

Habilita CONFIG_UNIX98_PTYS=y.

3. /dev/urandom (já habilitado por padrão)

MariaDB precisa de números aleatórios para criptografia e geração de IDs. O /dev/urandom é provido pelo kernel automaticamente via CONFIG_RANDOM=y (opção sempre habilitada, não aparece no menuconfig).

Troubleshooting: Se /dev/urandom não existir na VM, o problema provavelmente é o mdev não populando /dev. Verifique se mdev -s está sendo chamado no init. A opção “Hardware Random Number Generator Core support” (CONFIG_HW_RANDOM) é para hardware RNG físico e não é necessária para /dev/urandom.

4. FUTEX (importante para threading)

O InnoDB usa futexes para sincronização de threads. Sem isso, performance degrada muito.

General setup --->
  Configure standard kernel features (expert users) --->
    [*] Enable futex support

Habilita CONFIG_FUTEX=y. Essa opção só aparece porque CONFIG_EXPERT=y já está habilitado no .config original.

5. EPOLL (importante para I/O)

Mecanismo eficiente de I/O multiplexado. MariaDB usa para gerenciar múltiplas conexões.

General setup --->
  Configure standard kernel features (expert users) --->
    [*] Enable eventpoll support

Habilita CONFIG_EPOLL=y.

6. SysFS (necessário para algumas operações)

File systems --->
  Pseudo filesystems --->
    <*> sysfs file system support

Habilita CONFIG_SYSFS=y.

7. File Locking (obrigatório para MariaDB)

O MariaDB usa file locking para controlar acesso aos arquivos de dados. Sem isso, o servidor não consegue iniciar e exibe erros como “Can’t lock aria control file”.

File systems --->
  [*] Enable POSIX file locking API

Habilita CONFIG_FILE_LOCKING=y. Esta opção é crítica e frequentemente esquecida em kernels mínimos.

8. DEVTMPFS (essencial para /dev funcionar)

O kernel pode montar /dev automaticamente com devtmpfs, evitando dependência do mdev para criar dispositivos básicos.

Device Drivers --->
  Generic Driver Options --->
    [*] Maintain a devtmpfs filesystem to mount at /dev
    [*]   Automount devtmpfs at /dev, after the kernel mounted the rootfs

Habilita CONFIG_DEVTMPFS=y e CONFIG_DEVTMPFS_MOUNT=y.

9. Block Devices (necessário para disco virtual)

Antes de habilitar VIRTIO_BLK, é preciso habilitar a seção de block devices:

Device Drivers --->
  [*] Block devices --->

Habilita CONFIG_BLK_DEV=y.

10. Suporte a disco virtual (para persistência)

Para a opção de disco separado:

Device Drivers --->
  Block devices --->
    <*> Virtio block driver

Habilita CONFIG_VIRTIO_BLK=y. O suporte base ao Virtio (CONFIG_VIRTIO=y e CONFIG_VIRTIO_PCI=y) já está habilitado no .config original pois é usado pela rede.

11. Suporte a 9P filesystem (para dev)

Para compartilhar pasta com o host:

Networking support --->
  [*] Plan 9 Resource Sharing Support (9P2000) --->
    <*> 9P Virtio Transport

File systems --->
  Network File Systems --->
    <*> Plan 9 Resource Sharing Support (9P2000)
        <*> 9P POSIX Access Control Lists

Compilando

make -j$(nproc) bzImage

O kernel resultante estará em arch/x86/boot/bzImage:

$ ls -lh arch/x86/boot/bzImage
-rw-r--r--. 1 dklima dklima 2.9M Dec 17 12:20 arch/x86/boot/bzImage

Praticamente o mesmo tamanho do original (~2.7MB), as opções adicionais não aumentaram significativamente.

Compilando o MariaDB

Aqui começa a parte mais trabalhosa. Vamos compilar o MariaDB no sistema host e depois extrair o que precisamos.

Requisito de espaço: A compilação do MariaDB consome bastante disco. Tenha pelo menos 10GB livres antes de começar. Com 5GB a compilação falha por falta de espaço.

Dependências de compilação

Fedora/RHEL:

sudo dnf install -y \
  cmake gcc gcc-c++ make \
  ncurses-devel openssl-devel \
  libaio-devel systemd-devel \
  bison perl

Debian/Ubuntu:

sudo apt install -y \
  cmake gcc g++ make \
  libncurses-dev libssl-dev \
  libaio-dev libsystemd-dev \
  bison perl

Configurando a compilação

O código fonte já foi baixado na preparação do ambiente. Entre no diretório e configure:

cd ~/osso-mariadb/mariadb/mariadb-10.11.15
mkdir build && cd build

Vamos compilar uma versão mínima, sem features que não precisamos:

cmake .. \
  -DCMAKE_INSTALL_PREFIX=/usr \
  -DMYSQL_DATADIR=/var/lib/mysql \
  -DWITH_SSL=system \
  -DWITH_ZLIB=bundled \
  -DWITH_JEMALLOC=no \
  -DWITH_UNIT_TESTS=no \
  -DWITH_EMBEDDED_SERVER=no \
  -DWITH_TOKUDB=no \
  -DWITH_MROONGA=no \
  -DWITH_SPIDER=no \
  -DWITH_OQGRAPH=no \
  -DWITH_CONNECT=no \
  -DWITH_ROCKSDB=no \
  -DWITH_WSREP=no \
  -DPLUGIN_ARCHIVE=NO \
  -DPLUGIN_BLACKHOLE=NO \
  -DPLUGIN_FEDERATED=NO \
  -DPLUGIN_FEDERATEDX=NO

Explicando as flags importantes:

  • WITH_SSL=system: usa OpenSSL do sistema (vamos copiar as libs)
  • WITH_*=no: desabilita engines e plugins que não precisamos
  • PLUGIN_*=NO: desabilita mais plugins

Compilando

make -j$(nproc)

Isso leva uns 10-20 minutos dependendo da máquina. Bom momento para um café.

Instalando em diretório temporário

make DESTDIR=~/osso-mariadb/mariadb-install install

Identificando as dependências

Agora vem a parte divertida: descobrir de quais libs o MariaDB precisa.

ldd ~/osso-mariadb/mariadb-install/usr/bin/mariadbd

Saída típica (simplificada):

linux-vdso.so.1
libpthread.so.0 => /lib64/libpthread.so.0
libdl.so.2 => /lib64/libdl.so.2
librt.so.1 => /lib64/librt.so.1
libssl.so.3 => /lib64/libssl.so.3
libcrypto.so.3 => /lib64/libcrypto.so.3
libaio.so.1 => /lib64/libaio.so.1
libm.so.6 => /lib64/libm.so.6
libc.so.6 => /lib64/libc.so.6
libz.so.1 => /lib64/libz.so.1

Vamos precisar copiar todas essas libs (exceto linux-vdso que é virtual).

Script para copiar dependências

Identificar e copiar manualmente cada biblioteca é tedioso. O script copy-deps.sh automatiza essa tarefa, analisando o binário com ldd e copiando tudo que ele precisa.

O script usa ferramentas básicas do Linux (ldd, awk, grep) que normalmente já vêm instaladas. Se você estiver compilando em um container minimalista, certifique-se de que essas ferramentas estão disponíveis.

O script está disponível no repositório do projeto. Baixe e use assim:

# Clone o repositório (se ainda não fez)
git clone https://github.com/dklima/osso-linux-mariadb.git /tmp/osso-repo

# Copie o script para o diretório de trabalho
cp /tmp/osso-repo/copy-deps.sh ~/osso-mariadb/
chmod +x ~/osso-mariadb/copy-deps.sh

copy-deps.sh:


#!/bin/bash
# copy-deps.sh - Copia um binário e todas suas dependências dinâmicas
#
# Uso: ./copy-deps.sh <binario> <diretorio-destino>
#
# O script irá:
#   1. Copiar o binário para o destino mantendo a estrutura
#   2. Identificar todas as libs com ldd
#   3. Copiar as libs para <destino>/lib64/
#   4. Copiar o linker dinâmico

set -e

BINARY=$1
DEST=$2

if [ -z "$BINARY" ] || [ -z "$DEST" ]; then
    echo "Uso: $0 <binario> <destino>"
    echo ""
    echo "Exemplo: $0 /usr/bin/mariadbd ./rootfs"
    exit 1
fi

if [ ! -f "$BINARY" ]; then
    echo "Erro: Binario '$BINARY' nao encontrado"
    exit 1
fi

echo "Copiando $BINARY e dependencias para $DEST"

mkdir -p "$DEST/lib64"
mkdir -p "$DEST/$(dirname $BINARY)"

echo "[1/3] Copiando binario..."
cp -v "$BINARY" "$DEST/$BINARY"

echo "[2/3] Copiando bibliotecas..."
ldd "$BINARY" 2>/dev/null | grep "=>" | awk '{print $3}' | sort -u | while read lib; do
    if [ -f "$lib" ]; then
        LIB_NAME=$(basename "$lib")
        if [ ! -f "$DEST/lib64/$LIB_NAME" ]; then
            cp -vL "$lib" "$DEST/lib64/"
        fi
    fi
done

echo "[3/3] Copiando linker dinamico..."
LINKER=$(ldd "$BINARY" 2>/dev/null | grep -E "ld-linux|ld\.so" | awk '{print $1}')
if [ -f "$LINKER" ]; then
    LINKER_NAME=$(basename "$LINKER")
    if [ ! -f "$DEST/lib64/$LINKER_NAME" ]; then
        cp -vL "$LINKER" "$DEST/lib64/"
    fi
fi

echo ""
echo "Concluido! Bibliotecas copiadas:"
ls -la "$DEST/lib64/"

Como usar:


# Sintaxe: ./copy-deps.sh <binário> <dir-destino>

# Copiar mariadbd e suas dependências para o rootfs
~/osso-mariadb/copy-deps.sh ~/osso-mariadb/mariadb-install/usr/bin/mariadbd ~/osso-mariadb/rootfs

# Copiar o cliente mysql
~/osso-mariadb/copy-deps.sh ~/osso-mariadb/mariadb-install/usr/bin/mariadb ~/osso-mariadb/rootfs

O script cria a estrutura de diretórios necessária e copia o binário junto com todas as bibliotecas que ele precisa para lib64/. Ele também verifica se a lib já existe antes de copiar, evitando duplicação.

Nota sobre compatibilidade: As bibliotecas copiadas são da sua máquina host, então o appliance fica atrelado a essa versão de glibc. Se você compilar em um Fedora 43 e tentar usar o rootfs em outra distro com glibc diferente, pode ter problemas. Para um appliance autocontido isso não é problema, mas é bom saber.

Limitação do script: O copy-deps.sh “achata” todas as libs em lib64/, independente do caminho original. Isso funciona em Fedora/RHEL onde libs ficam em /lib64, mas pode precisar de ajustes em distros que usam /usr/lib64, /lib/x86_64-linux-gnu ou outros layouts. Se precisar de portabilidade, considere preservar o caminho absoluto original.

Montando o Rootfs

Agora vamos juntar tudo em um filesystem.

Estrutura de diretórios

cd ~/osso-mariadb/rootfs
mkdir -p {bin,sbin,lib64,usr/{bin,sbin,share/mysql},etc,var/{lib/mysql,run,log},proc,sys,dev,tmp,data}
chmod 1777 tmp

Copiando o Toybox

No artigo anterior, compilamos o Toybox estaticamente com musl libc. Se você já tem esse binário, pode usá-lo diretamente. Não há conflito entre o Toybox estático e o MariaDB dinâmico, são binários independentes.

Se você não tem o Toybox compilado, aqui está um resumo rápido:

cd ~/osso-mariadb

# Baixar Toybox
wget https://landley.net/toybox/downloads/toybox-0.8.13.tar.gz
tar xzf toybox-0.8.13.tar.gz
cd toybox-0.8.13

Opção A: Configuração pronta (recomendado)

O defconfig do Toybox NÃO inclui vários comandos que precisamos: sh (shell), mdev (gerenciador de devices), route (rotas de rede) e pidof (identificação de processos). O repositório inclui um .config pronto com tudo habilitado:

# Baixar configuração pronta
wget https://raw.githubusercontent.com/dklima/osso-linux-mariadb/main/toybox.config -O .config

# Compilar estaticamente
LDFLAGS="-static" make toybox

Opção B: Configuração manual

Se preferir configurar manualmente:

# Gerar config padrão
make defconfig

# Habilitar comandos necessários
sed -i 's/# CONFIG_SH is not set/CONFIG_SH=y/' .config
sed -i 's/# CONFIG_MDEV is not set/CONFIG_MDEV=y/' .config
sed -i 's/# CONFIG_ROUTE is not set/CONFIG_ROUTE=y/' .config
sed -i 's/# CONFIG_PIDOF is not set/CONFIG_PIDOF=y/' .config

# Compilar estaticamente
LDFLAGS="-static" make toybox

Por que precisamos desses comandos:

  • sh: O init é um script shell
  • mdev: Popula /dev com dispositivos
  • route: Configura gateway de rede
  • pidof: Identifica PID do MariaDB (necessário porque $! não funciona no Toybox sh)

Erro comum: Se você receber o erro:

/usr/bin/ld: cannot find -lc: No such file or directory
/usr/bin/ld: have you installed the static version of the c library ?

Isso significa que a biblioteca C estática não está instalada. No Fedora, instale com:

sudo dnf install glibc-static

Depois repita o comando de compilação.

A opção com glibc é mais simples pois não requer instalar musl. O binário fica maior (~1.9MB vs ~300KB com musl), mas funciona perfeitamente para nosso propósito.

Copie para o rootfs (certifique-se de ter criado a estrutura de diretórios da seção anterior):

cd ~/osso-mariadb/rootfs
cp ~/osso-mariadb/toybox-0.8.13/toybox bin/

# Criar symlinks para os comandos
cd bin
for cmd in sh ls cat cp mv rm mkdir mount umount ps kill sleep grep echo clear mdev ifconfig route seq sync dirname basename test head tail sed find id hostname pwd chown chmod ln touch halt poweroff reboot pidof; do
    ln -sf toybox $cmd
done
cd ..

Se receber erro bin/: Not a directory, volte e execute a seção Estrutura de diretórios primeiro.

Copiando o MariaDB

cd ~/osso-mariadb/rootfs
MARIADB_INSTALL=~/osso-mariadb/mariadb-install

# Binários essenciais (nota: mariadbd fica em /usr/bin, não /usr/sbin)
cp $MARIADB_INSTALL/usr/bin/mariadbd usr/bin/
cp $MARIADB_INSTALL/usr/bin/mariadb usr/bin/
cp $MARIADB_INSTALL/usr/scripts/mariadb-install-db usr/bin/

# Scripts e binários auxiliares
cp $MARIADB_INSTALL/usr/bin/mariadbd-safe usr/bin/
cp $MARIADB_INSTALL/usr/bin/my_print_defaults usr/bin/
cp $MARIADB_INSTALL/usr/bin/resolveip usr/bin/

# Arquivos de suporte (charsets, errmsg, SQL de inicialização)
cp -r $MARIADB_INSTALL/usr/share/charsets usr/share/mysql/
cp $MARIADB_INSTALL/usr/share/english/errmsg.sys usr/share/mysql/
cp $MARIADB_INSTALL/usr/share/*.sql usr/share/mysql/

Copiando as bibliotecas

Usando o script anterior ou manualmente:

cd ~/osso-mariadb/rootfs

# Libs do sistema (lista atualizada para glibc moderno)
# Nota: libpthread, libdl e librt são integradas no libc em versões recentes
for lib in libc.so.6 libm.so.6 libdl.so.2 libpthread.so.0 librt.so.1 \
           libssl.so.3 libcrypto.so.3 libaio.so.1 libz.so.1 \
           libnss_files.so.2 libnss_dns.so.2 libresolv.so.2 \
           libcrypt.so.2 libsystemd.so.0 libstdc++.so.6 \
           libgcc_s.so.1 libcap.so.2 \
           libncurses.so.6 libtinfo.so.6; do
    cp -L /lib64/$lib lib64/ 2>/dev/null || \
    cp -L /usr/lib64/$lib lib64/ 2>/dev/null || true
done

# Linker dinâmico
cp -L /lib64/ld-linux-x86-64.so.2 lib64/

Configuração do NSS

Os arquivos de configuração estão disponíveis no repositório do projeto em etc/. Copie-os para o rootfs:

cd ~/osso-mariadb/rootfs

# Baixe do repositório ou crie manualmente

etc/nsswitch.conf – Define como o sistema resolve nomes de usuários, grupos e hosts:

passwd:     files
group:      files
hosts:      files dns

etc/passwd – Usuários do sistema (apenas root e mysql):

root:x:0:0:root:/root:/bin/sh
mysql:x:27:27:MariaDB:/var/lib/mysql:/bin/false

etc/group – Grupos do sistema:

root:x:0:
mysql:x:27:

etc/hosts – Resolução de nomes local:

127.0.0.1   localhost

Para copiar do repositório:

# Clone o repositório
git clone https://github.com/dklima/osso-linux-mariadb.git /tmp/osso-repo

# Copie os arquivos
cp -r /tmp/osso-repo/etc ~/osso-mariadb/rootfs/

Checklist NSS e acesso remoto – sem isso, o MariaDB falha misteriosamente:

Arquivo Sintoma se faltar
etc/nsswitch.conf “Unknown user” ou falha silenciosa na autenticação
etc/passwd MariaDB não encontra usuário root/mysql
etc/group Erros de permissão ao criar arquivos
etc/hosts “Can’t resolve hostname” no log de erro
etc/remote_access.sql “Host is not allowed to connect” ao tentar conectar do host
lib64/libnss_files.so.2 Segfault ou “cannot load shared object”

Se o MariaDB iniciar mas rejeitar conexões com erros de autenticação estranhos, verifique esses arquivos primeiro.

Configuração do MariaDB (my.cnf)

O arquivo my.cnf completo está disponível no repositório. Aqui está uma versão simplificada com as configurações essenciais:

etc/my.cnf:

[mysqld]
# Diretórios
datadir = /data/mysql
socket = /var/run/mysqld/mysqld.sock
pid-file = /var/run/mysqld/mysqld.pid

# Rede - aceita conexões de qualquer IP
# AVISO: 0.0.0.0 expõe o banco em todas as interfaces!
bind-address = 0.0.0.0
port = 3306

# Buffers (conservador para VM pequena)
innodb_buffer_pool_size = 128M
key_buffer_size = 16M
max_connections = 50

# Logs
log_error = /var/log/mysql/error.log

# Charset
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci

[client]
socket = /var/run/mysqld/mysqld.sock
default-character-set = utf8mb4

[mysql]
default-character-set = utf8mb4
prompt = "osso-mariadb> "

Copie do repositório:

cp /tmp/osso-repo/my.cnf ~/osso-mariadb/rootfs/etc/

O arquivo completo no repositório tem comentários explicativos adicionais sobre cada configuração e opções para tuning.

Note o prompt = "osso-mariadb> " na seção [mysql]. Esse detalhe muda o prompt do cliente MySQL para mostrar que você está no seu appliance customizado, não em um MariaDB genérico. Pequeno toque, mas faz diferença na experiência.

Aviso de segurança: A configuração bind-address = 0.0.0.0 combinada com o usuário root@'%' (que criaremos no init) e senha vazia é extremamente insegura. Isso é aceitável apenas em ambiente de desenvolvimento isolado. Nunca use essa configuração em produção ou em redes não confiáveis. O próximo artigo da série abordará hardening adequado.

O Init Wrapper

Aqui está o coração do sistema. O script init será o PID 1 e está disponível no repositório. Copie para o rootfs:

cp /tmp/osso-repo/init ~/osso-mariadb/rootfs/
chmod +x ~/osso-mariadb/rootfs/init

init (script completo):

Nota importante sobre Toybox sh: O shell do Toybox tem limitações comparado ao bash. As principais diferenças que afetam este script:

  • export PATH=... causa erro de parsing – use caminhos absolutos para todos os comandos
  • Caracteres UTF-8 em comentários podem causar erros de sintaxe – use apenas ASCII
  • $! para capturar PID de processo em background não funciona – use /bin/pidof ao invés
  • Negação em condicionais (if ! comando; then) pode falhar silenciosamente – reescreva a lógica
  • Heredocs com aspas simples dentro perdem as aspas – use arquivos externos para SQL
  • Scripts complexos como mariadb-install-db causam fork bombs – inicialize o banco no host
#!/bin/sh
# Osso Linux MariaDB - Init Script (PID 1)
# Toybox sh compatible - ASCII only, absolute paths

exec >/dev/console 2>&1

SHUTDOWN_TIMEOUT=30
FIRST_BOOT_MARKER="/data/mysql/.first_boot_done"

log() {
    echo "[init] $1"
}

get_mysql_pid() {
    /bin/pidof mariadbd 2>/dev/null
}

mount_filesystems() {
    log "Montando filesystems virtuais..."
    /bin/mount -t proc none /proc
    /bin/mount -t sysfs none /sys
    /bin/mount -t tmpfs none /tmp
    /bin/mount -t tmpfs none /var/run
    /bin/mkdir -p /var/run/mysqld
    /bin/mkdir -p /var/log/mysql
}

setup_network() {
    log "Configurando rede..."
    # IPs fixos do QEMU user-mode network:
    # 10.0.2.15 = IP padrao da VM, 10.0.2.2 = gateway/host
    /bin/ifconfig eth0 10.0.2.15 netmask 255.255.255.0 up
    /bin/route add default gw 10.0.2.2
}

mount_data() {
    log "Montando armazenamento de dados..."

    if /bin/mount -t 9p -o trans=virtio,version=9p2000.L hostdata /data 2>/dev/null; then
        log "Montado via 9p (modo dev)"
        return 0
    fi

    if [ -b /dev/vda ]; then
        /bin/mount /dev/vda /data 2>/dev/null
        log "Montado disco virtual /dev/vda"
        return 0
    fi

    log "AVISO: Usando tmpfs, dados serao perdidos no reboot!"
    /bin/sleep 3
    /bin/mount -t tmpfs none /data
}

configure_remote_access() {
    if [ -f "$FIRST_BOOT_MARKER" ]; then
        return 0
    fi
    log "Configurando acesso remoto..."
    /bin/sleep 2
    if [ -f /etc/remote_access.sql ]; then
        /usr/bin/mariadb < /etc/remote_access.sql
        RESULT=$?
        if [ $RESULT -eq 0 ]; then
            /bin/touch "$FIRST_BOOT_MARKER"
            log "Acesso remoto configurado"
        else
            log "AVISO: Falha ao configurar acesso remoto (codigo $RESULT)"
        fi
    else
        log "AVISO: Arquivo /etc/remote_access.sql nao encontrado"
    fi
}

start_mariadb() {
    log "Iniciando MariaDB..."
    # AVISO: --user=root e INSEGURO! Usado aqui por simplicidade.
    # Em producao, crie usuario 'mysql' e use --user=mysql
    /usr/bin/mariadbd --user=root --datadir=/data/mysql &

    log "Aguardando MariaDB..."
    COUNT=0
    while [ $COUNT -lt 30 ]; do
        if /usr/bin/mariadb -e "SELECT 1" >/dev/null 2>&1; then
            MYSQL_PID=$(get_mysql_pid)
            log "MariaDB iniciado com sucesso (PID: $MYSQL_PID)"
            return 0
        fi
        /bin/sleep 1
        COUNT=$((COUNT + 1))
    done

    log "ERRO: MariaDB nao iniciou em 30 segundos"
    return 1
}

stop_mariadb() {
    MYSQL_PID=$(get_mysql_pid)
    if [ -n "$MYSQL_PID" ]; then
        log "Enviando SIGTERM para MariaDB (PID: $MYSQL_PID)..."
        /bin/kill -TERM $MYSQL_PID 2>/dev/null

        log "Aguardando MariaDB terminar..."
        COUNT=0
        while [ $COUNT -lt $SHUTDOWN_TIMEOUT ]; do
            MYSQL_PID=$(get_mysql_pid)
            if [ -z "$MYSQL_PID" ]; then
                log "MariaDB terminou graciosamente"
                return 0
            fi
            /bin/sleep 1
            COUNT=$((COUNT + 1))
        done

        log "AVISO: Timeout, enviando SIGKILL..."
        MYSQL_PID=$(get_mysql_pid)
        /bin/kill -KILL $MYSQL_PID 2>/dev/null
    fi
}

handle_shutdown() {
    log "Recebido sinal de shutdown"
    stop_mariadb
    /bin/sync
    log "Desmontando filesystems..."
    /bin/umount /data 2>/dev/null
    /bin/umount /var/run 2>/dev/null
    /bin/umount /tmp 2>/dev/null
    /bin/umount /sys 2>/dev/null
    /bin/umount /proc 2>/dev/null
    log "Shutdown completo"
    exit 0
}

trap handle_shutdown TERM INT

echo ""
echo "   OSSO LINUX (MariaDB Edition)"
echo "--------------------------------"
log "Iniciando..."

mount_filesystems
/bin/mdev -s

setup_network
mount_data

log "Verificando banco de dados..."
if [ -d /data/mysql ]; then
    log "Banco de dados encontrado"
else
    log "ERRO: Banco de dados nao encontrado em /data/mysql"
    log "Inicialize o banco no host antes de iniciar a VM"
    exec /bin/sh
fi

/bin/sync

if start_mariadb; then
    configure_remote_access
    echo ""
    log "Sistema pronto!"
    log "MariaDB escutando em 0.0.0.0:3306"
    log "Conecte via: mysql -h 
<ip_host> -P 3306 -u root"
    echo ""

    while true; do
        MYSQL_PID=$(get_mysql_pid)
        if [ -z "$MYSQL_PID" ]; then
            log "ERRO: MariaDB morreu inesperadamente!"
            break
        fi
        /bin/sleep 5
    done
else
    log "Falha ao iniciar MariaDB"
fi

exec /bin/sh

Entendendo o init

Vamos analisar as partes importantes:

Captura de PID com pidof:

get_mysql_pid() {
    /bin/pidof mariadbd 2>/dev/null
}

O Toybox sh tem um bug onde $! retorna vazio após iniciar um processo em background. A solução é usar pidof para descobrir o PID do processo pelo nome do executável. Por isso habilitamos CONFIG_PIDOF=y na compilação do Toybox.

Handlers de sinais:

trap handle_shutdown TERM INT

Isso diz ao shell: “quando receber SIGTERM ou SIGINT, execute a função handle_shutdown”. É assim que interceptamos o pedido de desligamento.

Health check:

MYSQL_PID=$(get_mysql_pid)
if [ -z "$MYSQL_PID" ]; then

Verificamos se o MariaDB ainda está vivo consultando pidof. Se retornar vazio, o processo morreu.

Graceful shutdown:

/bin/kill -TERM $MYSQL_PID
COUNT=0
while [ $COUNT -lt $SHUTDOWN_TIMEOUT ]; do
    ...
    COUNT=$((COUNT + 1))
done
/bin/kill -KILL $MYSQL_PID

Primeiro pedimos educadamente (SIGTERM), esperamos até 30 segundos, e só então forçamos (SIGKILL) se necessário. Note o uso de while com contador em vez de for i in $(seq ...), pois o Toybox sh tem melhor compatibilidade com essa construção.

Configuração de acesso remoto:

configure_remote_access() {
    if [ -f /etc/remote_access.sql ]; then
        /usr/bin/mariadb < /etc/remote_access.sql
        ...
    fi
}

Por padrão, o MariaDB só aceita conexões do localhost. Para permitir acesso externo (necessário para conectar do host via port forwarding do QEMU), criamos o usuário root@'%' no primeiro boot.

Por que usar arquivo SQL ao invés de comandos inline? O Toybox sh tem problemas ao interpretar aspas simples dentro de strings. Comandos como mariadb -e "CREATE USER 'root'@'%'" perdem as aspas internas e falham. A solução é colocar os comandos SQL em um arquivo separado e usar redirecionamento de entrada.

O arquivo marker (FIRST_BOOT_MARKER) evita recriar o usuário em boots subsequentes.

Arquivo SQL para acesso remoto

Crie o arquivo etc/remote_access.sql no rootfs:

CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

Esse arquivo será lido pelo init no primeiro boot para configurar o acesso remoto.

Inicialização do banco de dados no host

Importante: O script mariadb-install-db não funciona dentro da VM com Toybox sh, ele causa fork bombs e kernel panic. A solução é inicializar o banco de dados no host antes de empacotar o initramfs ou antes de iniciar a VM (se usando 9p).

Se você estiver usando 9p para persistência (pasta compartilhada com o host):

# Crie o diretório de dados no host
mkdir -p ~/mariadb-data/mysql

# Inicialize o banco usando os binários compilados
cd ~/osso-mariadb/mariadb-install/usr
./scripts/mariadb-install-db --basedir=. --datadir=~/mariadb-data/mysql --user=$USER

Se você estiver empacotando o banco dentro do initramfs (não recomendado para dados grandes):

# Inicialize em um diretório temporário
cd ~/osso-mariadb/mariadb-install/usr
./scripts/mariadb-install-db --basedir=. --datadir=~/osso-mariadb/rootfs/data/mysql --user=$USER

# Remova arquivos de lock antes de empacotar
rm -f ~/osso-mariadb/rootfs/data/mysql/aria_log*

Persistência de dados

Opção A: 9p/virtfs (para teste/desenvolvimento)

Esta opção compartilha uma pasta do host com a VM. Ideal para desenvolvimento porque você pode inspecionar os arquivos diretamente.

No host, crie um diretório para os dados:

mkdir -p ~/mariadb-data

Inicie o QEMU com o compartilhamento:

qemu-system-x86_64 \
  -kernel bzImage \
  -initrd initramfs.cpio.gz \
  -nographic \
  -append "console=ttyS0" \
  -enable-kvm \
  -m 1024 \
  -netdev user,id=n1,hostfwd=tcp::3306-:3306 \
  -device virtio-net-pci,netdev=n1 \
  -virtfs local,path=$HOME/mariadb-data,mount_tag=hostdata,security_model=mapped-xattr

O parâmetro -virtfs cria o compartilhamento. O mount_tag=hostdata é o identificador que usamos no init para montar.

Vantagens:

  • Dados ficam no host, fácil backup
  • Pode inspecionar arquivos sem entrar na VM
  • Persiste entre reboots

Desvantagens:

  • Performance menor que disco nativo
  • Requer configuração extra no QEMU

Opção B: Disco virtual (mais realista)

Esta opção usa um disco virtual separado, simulando um cenário real de appliance.

Crie o disco:

qemu-img create -f qcow2 mariadb-data.qcow2 10G

Formate o disco (primeira vez apenas). Você tem duas opções:

Opção 1: Usando guestfish (do host, sem precisar da VM)

O guestfish é uma ferramenta do pacote libguestfs-tools que permite manipular imagens de disco sem iniciar uma VM:

# Instalar (Fedora/RHEL)
sudo dnf install -y libguestfs-tools

# Instalar (Debian/Ubuntu)
sudo apt install -y libguestfs-tools

# Formatar o disco e criar diretório do MySQL
guestfish -a mariadb-data.qcow2 <<eof
run
part-disk /dev/sda mbr
mkfs ext4 /dev/sda1
mount /dev/sda1 /
mkdir /mysql
umount /
EOF

Nota: O guestfish vê o disco como /dev/sda, mas dentro da VM com virtio ele aparece como /dev/vda. O part-disk cria uma partição única ocupando todo o disco.

Opção 2: De dentro da VM (requer boot inicial)

Se preferir formatar de dentro da VM, inicie sem o banco inicializado e use o shell de emergência:

# Na VM, quando cair no shell por falta do banco:
mkfs.ext4 /dev/vda
mkdir -p /data/mysql
mount /dev/vda /data

Depois você precisará inicializar o banco no host e reiniciar a VM.

Inicie o QEMU com o disco:

qemu-system-x86_64 \
  -kernel bzImage \
  -initrd initramfs.cpio.gz \
  -nographic \
  -append "console=ttyS0" \
  -enable-kvm \
  -m 1024 \
  -netdev user,id=n1,hostfwd=tcp::3306-:3306 \
  -device virtio-net-pci,netdev=n1 \
  -drive file=mariadb-data.qcow2,if=virtio,format=qcow2

Vantagens:

  • Performance melhor
  • Mais próximo de um cenário real
  • Backup é copiar o arquivo .qcow2

Desvantagens:

  • Precisa parar a VM para backup consistente
  • Menos conveniente para inspecionar arquivos

Empacotando o initramfs

Com tudo no lugar, empacote o rootfs:

cd ~/osso-mariadb/rootfs
find . -print0 | cpio --null -ov --format=newc | gzip -9> ../output/initramfs.cpio.gz

Atenção: O find . inclui tudo no diretório. Se você criou arquivos temporários ou de teste dentro de rootfs/, eles vão junto. Verifique se a pasta está limpa antes de empacotar.

Se você pré-inicializou o banco de dados no host: O mariadb-install-db cria arquivos de lock (aria_log*) que pertencem ao ambiente do host. Se esses arquivos forem incluídos no initramfs, o MariaDB dentro da VM não conseguirá iniciar, exibindo erro “Can’t lock aria control file”. Remova-os antes de empacotar:

rm -f ~/osso-mariadb/rootfs/data/mysql/aria_log*

Copie também o kernel para o diretório de output:

cp ~/osso-mariadb/kernel/linux-6.18.1/arch/x86/boot/bzImage ~/osso-mariadb/output/

Agora você tem tudo em ~/osso-mariadb/output/:

~/osso-mariadb/output/
??? bzImage              # Kernel
??? initramfs.cpio.gz    # Sistema de arquivos

Testando

Boot

cd ~/osso-mariadb/output
mkdir -p ~/mariadb-data  # Diretório para persistência via 9p

qemu-system-x86_64 \
  -kernel bzImage \
  -initrd initramfs.cpio.gz \
  -nographic \
  -append "console=ttyS0" \
  -enable-kvm \
  -m 1024 \
  -netdev user,id=n1,hostfwd=tcp::3306-:3306 \
  -device virtio-net-pci,netdev=n1 \
  -virtfs local,path=$HOME/mariadb-data,mount_tag=hostdata,security_model=mapped-xattr

Por que não usamos quiet: O parâmetro quiet na linha de comando do kernel suprime mensagens de boot. Embora deixe a saída mais limpa, dificulta muito o debug quando algo falha. Em ambiente de desenvolvimento, prefira ver todas as mensagens.

Você deve ver:

   OSSO LINUX (MariaDB Edition)
--------------------------------
[init] Iniciando...
[init] Montando filesystems virtuais...
[init] Configurando rede...
[init] Montando armazenamento de dados...
[init] Montado via 9p (modo dev)
[init] Verificando banco de dados...
[init] Banco de dados encontrado
[init] Iniciando MariaDB...
[init] Aguardando MariaDB...
[init] MariaDB iniciado com sucesso (PID: 32)
[init] Configurando acesso remoto...
[init] Acesso remoto configurado

[init] Sistema pronto!
[init] MariaDB escutando em 0.0.0.0:3306
[init] Conecte via: mysql -h 
<ip_host> -P 3306 -u root

Se você ver “Banco de dados nao encontrado”, significa que o banco não foi inicializado no host. Volte para a seção Inicialização do banco de dados no host e execute os comandos.

Nota sobre memória: Com a configuração deste artigo (innodb_buffer_pool_size=128M no my.cnf), o MariaDB precisa de pelo menos 512MB de RAM. Se precisar rodar com menos memória, reduza o innodb_buffer_pool_size e outros buffers, mas o desempenho será impactado.

Conectando de fora da VM

Do host:

mysql -h 127.0.0.1 -P 3306 -u root

O -h 127.0.0.1 força conexão TCP (o -h localhost tentaria socket Unix, que não existe no host).

Shutdown graceful

De dentro da VM (se tiver shell) ou enviando sinal para o QEMU:

# Opção 1: De dentro da VM
poweroff

# Opção 2: Do host, enviando SIGTERM para o QEMU
kill -TERM $(pgrep qemu)

Você deve ver:

[init] Recebido sinal de shutdown
[init] Enviando SIGTERM para MariaDB (PID: 42)...
[init] Aguardando MariaDB terminar...
[init] MariaDB terminou graciosamente
[init] Desmontando filesystems...
[init] Shutdown completo

O que acontece com kill -9

Para demonstrar a importância do shutdown graceful:

  1. Conecte no MariaDB e inicie uma transação grande
  2. Mate o QEMU com kill -9
  3. Reinicie e observe o recovery do InnoDB
InnoDB: Database was not shutdown normally!
InnoDB: Starting crash recovery.
InnoDB: Recovering transactions from log file(s)...

O InnoDB consegue se recuperar, mas leva mais tempo e há risco de perda de dados não commitados.

Conclusão

Construímos um appliance de banco de dados funcional partindo de um Linux mínimo. O sistema final tem:

  • Kernel: ~3MB (expandido para suportar recursos do MariaDB)
  • Userland (Toybox): ~2MB
  • MariaDB + libs: ~245MB (mariadbd é ~220MB + ~15MB de libs + ~10MB de suporte)
  • Total: ~250MB (descomprimido) / ~115MB (initramfs comprimido)

Mais importante que o tamanho, entendemos:

  • Por que o PID 1 é especial e suas responsabilidades
  • Como sinais funcionam e a diferença entre SIGTERM e SIGKILL
  • Por que um banco de dados não pode ser simplesmente jogado como init
  • A importância do shutdown graceful para integridade dos dados

Esse foi um artigo denso, com muitas peças se encaixando ao mesmo tempo. Você precisa de um MariaDB rodando como appliance? Provavelmente não. Mas tenho certeza que houve bastante aprendizado durante essa jornada, desde entender o papel do PID 1 até debugar incompatibilidades entre shells. São conhecimentos que se aplicam muito além deste projeto específico.

Próximos passos

No próximo artigo da série, vamos transformar isso em um appliance de produção:

  • Rodar MariaDB como usuário não-root — o “pecado mortal” que cometemos aqui por simplicidade
  • Hardening de segurança (bind específico, permissões restritivas)
  • Tuning do my.cnf para performance
  • Recovery automático de tabelas corrompidas
  • Monitoramento e health checks avançados
  • Backup automatizado

O código completo está disponível no repositório github.com/dklima/osso-linux-mariadb.


Este artigo faz parte da série “Osso Linux”, onde construímos sistemas Linux mínimos para fins específicos.

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