Cluster MariaDB Galera com mariabackup no Ubuntu

Mascote do LinuxPro conectando o cabo de um nó do cluster MariaDB Galera, com o anel de replicação entre os três racks e o cão caramelo sentado ao lado

Três Ubuntu, um Galera, SST com mariabackup. Não é réplica assíncrona: todo nó aceita escrita e a maioria decide quem continua no ar. Dois nós não bastam — cai um e o outro fica sozinho, sem maioria. Abaixo, o cluster de três do pacote ao galera_new_cluster, no Ubuntu 24.04 e no 26.04, com backup a quente e a recuperação de quando tudo cai.

Três nós, um quorum

O Galera, da finlandesa Codership, entra no MariaDB como a biblioteca libgalera_smm.so (pacote galera-4, API wsrep 26.4.x). Cada commit vira um writeset: os outros nós o recebem e certificam antes de o commit retornar ao cliente, e aplicam logo em seguida. Só InnoDB; MyISAM no cluster é dor.

Quando um nó entra ou volta, ele precisa dos dados. Se ficou pouco tempo fora, recebe só o que perdeu (IST, a partir do cache gcache de um vizinho). Se é novo ou ficou muito tempo fora, recebe a base inteira (SST). O SST por rsync trava o doador durante a cópia; o mysqldump é lento. O mariabackup copia a quente, manda o fluxo por socat na porta 4444 e o doador continua atendendo. Por isso três servidores e esse SST.

Diagrama do cluster MariaDB Galera: aplicação acessando db1, db2 e db3 pela porta 3306, replicação entre os nós na 4567, SST na 4444 e IST na 4568

Os IPs deste howto — troque pelos seus:

db1  10.0.0.11
db2  10.0.0.12
db3  10.0.0.13

Mesma versão de MariaDB nos três, mesmo wsrep_cluster_name, relógio sincronizado (chrony) e disco local — datadir em NFS, não.

O mapa em setembro de 2026

  • Ubuntu 24.04 (noble): MariaDB 10.11.14 em noble-updates e galera-4 26.4.16 no universe. Em maio de 2026 a MariaDB soltou correções de segurança do Galera (10.6.27, 10.11.18, 11.4.12 e 11.8.8). Até o fechamento deste post a atualização do noble para a 10.11.18 ainda não tinha saído (LP #2158958). Para cluster em produção no 24.04, use o repositório da MariaDB.
  • Ubuntu 26.04 (resolute): MariaDB 11.8.6 e galera-4 26.4.25, os dois no main. O pacote 11.8.6-5ubuntu0.1 já traz as correções de maio. A 11.8 é LTS, com suporte da comunidade até junho de 2028.
  • O repositório da MariaDB também publica pacotes para o 26.04, mas só da 11.8 em diante; o script de instalação recusa a 10.11 no resolute.
  • Binário de backup: mariadb-backup. O valor de wsrep_sst_method continua mariabackup — o nome antigo. Não misture.

Quem vai colocar PHP na frente do banco: Nginx e várias versões de PHP. O GLPI é um dos clientes típicos deste cluster.

Pacotes

Nos três nós. Ubuntu 26.04, direto da distro:

sudo apt update
sudo apt install -y mariadb-server mariadb-backup galera-4 socat

Ubuntu 24.04 com a 11.8 do repositório da MariaDB (recomendado, pelo patch de segurança):

sudo apt install -y curl ca-certificates
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup \
  | sudo bash -s -- --mariadb-server-version="11.8"
sudo apt update
sudo apt install -y mariadb-server mariadb-backup galera-4 socat

A biblioteca fica em /usr/lib/libgalera_smm.so, com um link em /usr/lib/galera/libgalera_smm.so, tanto no pacote do Ubuntu quanto no da MariaDB. Qualquer um dos dois serve no wsrep_provider. Confira na sua máquina:

dpkg -L galera-4 | grep libgalera_smm.so

A instalação já deixou um MariaDB sozinho no ar. Pare antes de configurar:

sudo systemctl stop mariadb
sudo systemctl enable mariadb

O datadir mudou no 26.04: instalação nova usa /var/lib/mariadb; quem atualizou de uma versão anterior continua em /var/lib/mysql (via 99-legacy-datadir.cnf). No 24.04 é /var/lib/mysql. Confira com mariadbd --help --verbose | grep ^datadir.

Rede, firewall e AppArmor

O Galera usa quatro portas. Abra só entre os três IPs, nunca para o mundo:

  • 3306/tcp — cliente SQL
  • 4567/tcp — replicação (e UDP, só se usar multicast)
  • 4568/tcp — IST
  • 4444/tcp — SST do mariabackup/socat
NET=10.0.0.0/24
for p in 3306 4567 4568 4444; do
  sudo ufw allow proto tcp from "$NET" to any port "$p"
done
sudo ufw reload

No /etc/hosts dos três:

10.0.0.11  db1
10.0.0.12  db2
10.0.0.13  db3

AppArmor no 26.04. O pacote passou a trazer o perfil /etc/apparmor.d/mariadbd em modo enforce, e ele não tem regras para os scripts de SST (wsrep_sst_mariabackup, socat, mariadb-backup). Se o joiner falhar e o dmesg mostrar apparmor="DENIED", ponha o perfil em modo de reclamação:

sudo apt install -y apparmor-utils
sudo aa-complain /etc/apparmor.d/mariadbd
sudo dmesg -T | grep -i apparmor | tail

No 24.04 o perfil do pacote vem vazio, sem confinamento; nada a fazer.

O timeout do systemd não atrapalha: o mariadb.service já traz TimeoutStartSec=900, e durante o SST o próprio servidor avisa o systemd a cada 60 s para estender o prazo. Não precisa de drop-in.

60-galera.cnf

O Ubuntu já traz /etc/mysql/mariadb.conf.d/60-galera.cnf, todo comentado. Substitua o conteúdo por este. Entre os nós só mudam wsrep_node_name e wsrep_node_address.

Em db1:

[galera]
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
wsrep_cluster_name="linuxpro"
wsrep_cluster_address="gcomm://10.0.0.11,10.0.0.12,10.0.0.13"
wsrep_node_name=db1
wsrep_node_address=10.0.0.11
wsrep_sst_method=mariabackup
wsrep_sst_auth=mariabackup:troque-esta-senha
wsrep_provider_options="gcache.size=1G;gcache.recover=yes"

binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0

Em db2 e db3, copie o arquivo e troque só o nome e o IP. O gcomm:// lista os três em todos — o Galera ignora o próprio endereço. Nunca deixe gcomm:// vazio no arquivo: isso cria um cluster novo a cada start.

O gcache.size define quanto histórico cada nó guarda para IST. Quanto maior, mais tempo um nó pode ficar fora e ainda voltar só com o que perdeu, em vez de um SST completo.

No 50-server.cnf, comente o bind-address = 127.0.0.1. O 60 é lido depois e ganha, mas não dependa da ordem.

Usuário do SST

O doador roda o mariadb-backup localmente e precisa de um usuário com estes privilégios:

CREATE USER 'mariabackup'@'localhost' IDENTIFIED BY 'troque-esta-senha';
GRANT RELOAD, PROCESS, LOCK TABLES, BINLOG MONITOR ON *.* TO 'mariabackup'@'localhost';

BINLOG MONITOR é o nome desde a 10.5; REPLICATION CLIENT continua valendo como apelido. O usuário só conecta em localhost — não o abra na rede.

Na 11.8 (26.04, ou 24.04 com o repositório da MariaDB) há um atalho: sem wsrep_sst_auth no cnf, o servidor cria e apaga sozinho um usuário temporário para cada SST (MDEV-31809, desde a 11.6). Nesse caso, apague a linha wsrep_sst_auth dos três arquivos e pule o CREATE USER. Na 10.11 o usuário é obrigatório.

Bootstrap e os dois joiners

Os três estão parados, com o cnf no disco. Só o primeiro nó cria o cluster:

# em db1
sudo galera_new_cluster

É um wrapper que sobe o mariadbd com --wsrep-new-cluster. Confira:

SHOW GLOBAL STATUS LIKE 'wsrep_cluster_size';
SHOW GLOBAL STATUS LIKE 'wsrep_local_state_comment';
SHOW GLOBAL STATUS LIKE 'wsrep_ready';

Esperado: size 1, Synced, ON. Crie agora o usuário do SST no db1 (seção anterior) — ele replica para os outros quando entrarem. Depois:

# em db2; quando ele estiver Synced, em db3
sudo systemctl start mariadb

Mascote LinuxPro ajoelhado conectando o novo nó (joiner) ao doador durante o SST do MariaDB Galera, com blocos de dados passando por um tubo e o cão caramelo deitado ao lado

O joiner pede o SST ao doador. Acompanhe com journalctl -u mariadb -f. Durante a transferência, o datadir recebe mariabackup.backup.log (no doador) e mariabackup.prepare.log / mariabackup.move.log (no joiner) — é ali que está o motivo quando o SST falha. Os arquivos mantêm o nome antigo, embora o binário seja mariadb-backup, e os de transferências anteriores vão compactados para /tmp/sst_log_archive. No laboratório deste post, com a base pequena, cada joiner ficou Synced em cerca de 20 segundos.

Com os três dentro:

SHOW GLOBAL STATUS LIKE 'wsrep_cluster_size';      -- 3
SHOW GLOBAL STATUS LIKE 'wsrep_cluster_status';    -- Primary
SHOW GLOBAL STATUS LIKE 'wsrep_incoming_addresses';

Teste de verdade: CREATE DATABASE teste_galera; no db1 e SHOW DATABASES; no db3. Se apareceu, o writeset passou.

HAProxy na frente do cluster

Com três nós, a aplicação precisa de um endereço só, que continue funcionando quando um nó cai. O HAProxy faz isso em TCP. E há um motivo para não espalhar as escritas: o Galera aceita escrita em qualquer nó, mas duas transações mexendo na mesma linha em nós diferentes geram conflito de certificação, e uma delas volta com erro de deadlock no COMMIT. O arranjo mais seguro é escrever em um nó por vez e ler dos três.

Diagrama do MariaDB Galera com HAProxy: aplicações no IP virtual do keepalived, dois HAProxy, escrita na porta 3306 para um nó com os outros dois de reserva e leitura na 3307 distribuída entre os três

O HAProxy expõe duas portas: 3306 para escrita, que manda tudo para o db1 e usa db2 e db3 só se ele cair, e 3307 para leitura, distribuída entre os três. Se a aplicação não separa leitura de escrita, use só a 3306.

Health check: TCP não basta

Um nó em Donor/Desynced, ainda sincronizando ou fora do quorum aceita conexão TCP normalmente — só não serve dados confiáveis. O check precisa perguntar ao Galera. Este script responde HTTP 200 quando wsrep_local_state é 4 (Synced) e 503 em qualquer outro caso. Nos três nós:

sudo tee /usr/local/bin/galera-check >/dev/null <<'EOF'
#!/bin/bash
# Responde HTTP 200 só quando o nó está Synced (wsrep_local_state = 4).
# Lê o pedido até a linha em branco; fechar sem ler faz o kernel mandar RST.
while read -r -t 2 line; do [ -z "${line%$'\r'}" ] && break; done
state=$(mariadb -N -s -e "SHOW GLOBAL STATUS LIKE 'wsrep_local_state'" 2>/dev/null | cut -f2)
if [ "$state" = "4" ]; then
  printf 'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nSynced\r\n'
else
  printf 'HTTP/1.1 503 Service Unavailable\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nNot synced (%s)\r\n' "${state:-down}"
fi
EOF
sudo chmod 755 /usr/local/bin/galera-check
sudo useradd -r -s /usr/sbin/nologin galera-check

O script conecta sem senha: o usuário do MariaDB autentica pelo usuário do sistema (unix_socket) e não precisa de privilégio nenhum para ler o status. Crie uma vez, em qualquer nó — ele replica:

CREATE USER 'galera-check'@'localhost' IDENTIFIED VIA unix_socket;

O systemd escuta a porta 9200 e roda o script a cada conexão. Nos três nós, /etc/systemd/system/galera-check.socket:

[Unit]
Description=Health check HTTP do Galera (porta 9200)

[Socket]
ListenStream=9200
Accept=yes

[Install]
WantedBy=sockets.target

e /etc/systemd/system/galera-check@.service:

[Unit]
Description=Health check HTTP do Galera

[Service]
User=galera-check
ExecStart=/usr/local/bin/galera-check
StandardInput=socket
StandardOutput=socket
StandardError=journal
sudo systemctl daemon-reload
sudo systemctl enable --now galera-check.socket
curl -s http://127.0.0.1:9200/      # Synced

Libere a 9200 só para os IPs dos HAProxy (sudo ufw allow proto tcp from 10.0.0.21 to any port 9200, e o mesmo para o .22).

O haproxy.cfg

Nas máquinas do HAProxy (fora dos nós do banco):

sudo apt install -y haproxy

Acrescente ao /etc/haproxy/haproxy.cfg, mantendo o bloco global do pacote:

defaults galera
    mode    tcp
    log     global
    option  tcplog
    timeout connect 5s
    timeout client  8h
    timeout server  8h

# Escrita: um nó só por vez; os outros dois assumem se ele cair
frontend galera_escrita from galera
    bind *:3306
    default_backend galera_escrita

backend galera_escrita from galera
    option httpchk GET /
    http-check expect status 200
    default-server port 9200 inter 2s fall 3 rise 2 on-marked-down shutdown-sessions
    server db1 10.0.0.11:3306 check
    server db2 10.0.0.12:3306 check backup
    server db3 10.0.0.13:3306 check backup

# Leitura: os três, quem tiver menos conexões
frontend galera_leitura from galera
    bind *:3307
    default_backend galera_leitura

backend galera_leitura from galera
    balance leastconn
    option httpchk GET /
    http-check expect status 200
    default-server port 9200 inter 2s fall 3 rise 2 on-marked-down shutdown-sessions
    server db1 10.0.0.11:3306 check
    server db2 10.0.0.12:3306 check
    server db3 10.0.0.13:3306 check

listen estatisticas from galera
    mode http
    bind 127.0.0.1:8404
    stats enable
    stats uri /
    stats refresh 5s
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy

Os detalhes que importam:

  • fall 3 com inter 2s: um nó sai do pool depois de três checks ruins, uns 6 segundos. rise 2 exige dois bons para voltar.
  • on-marked-down shutdown-sessions derruba as conexões abertas no nó que caiu, para a aplicação reconectar no próximo em vez de ficar pendurada.
  • timeout client/server 8h acompanha o wait_timeout padrão do MariaDB (28800 s). Com timeout menor, o HAProxy corta conexões ociosas de pool antes do banco.
  • Os usuários da aplicação passam a chegar com o IP do HAProxy: crie-os como 'app'@'10.0.0.%', não com o IP da aplicação.

Teste pelo balanceador:

mariadb -h 10.0.0.21 -P 3306 -u app -p -e 'SELECT @@hostname'   # sempre db1
mariadb -h 10.0.0.21 -P 3307 -u app -p -e 'SELECT @@hostname'   # alterna entre os três

No laboratório deste post, com o db1 derrubado, a escrita passou para o db2 em menos de 10 segundos e um INSERT pela porta 3306 entrou normalmente. Quando o db1 voltou (por IST, só com as transações que perdeu), o HAProxy devolveu a escrita para ele. Para ver o estado dos backends: ssh -L 8404:127.0.0.1:8404 na máquina do HAProxy e abra http://127.0.0.1:8404/.

Um HAProxy sozinho vira o novo ponto único de falha. Em produção, rode dois, com a mesma configuração, e o keepalived segurando um IP virtual (VRRP) entre eles, como no diagrama; a aplicação aponta para esse IP.

Backup a quente

Backup do cluster é backup de um nó. De preferência tire esse nó do balanceador, porque ele fica mais lento durante a cópia:

sudo mkdir -p /backup    # o mariadb-backup não cria o diretório pai
sudo mariadb-backup --backup --galera-info \
  --target-dir=/backup/full-$(date +%F) \
  --user=mariabackup --password='troque-esta-senha'
sudo mariadb-backup --prepare --target-dir=/backup/full-$(date +%F)

Não precisa de SET GLOBAL wsrep_desync=ON: no Galera, o mariadb-backup já dessincroniza e pausa o nó sozinho na fase de bloqueio. O nó continua no cluster, recebe os writesets numa fila e a esvazia depois — não há IST. O --galera-info grava a posição do cluster no arquivo xtrabackup_galera_info, que é o que permite usar esse backup para reconstruir um nó.

O --prepare deixa o backup consistente. Sem ele, o diretório não serve para restaurar.

Quando o cluster inteiro cai

Se os três caírem juntos, o cluster acabou — os dados não. Em cada nó, olhe o grastate.dat no datadir:

sudo cat /var/lib/mysql/grastate.dat    # ou /var/lib/mariadb no 26.04

Desligamento limpo: o nó com safe_to_bootstrap: 1 (o último a sair) é quem sobe primeiro. Queda brusca: todos ficam com seqno: -1 e safe_to_bootstrap: 0. Descubra a posição real em cada um:

sudo galera_recovery

O nó com o maior número depois de : é o mais avançado. Só nele, edite o grastate.dat para safe_to_bootstrap: 1 e:

sudo galera_new_cluster          # no nó mais avançado
sudo systemctl start mariadb     # nos outros dois, um de cada vez

Nunca rode galera_new_cluster com algum nó ainda no ar: nasce um cluster novo com outro UUID e você tem dois bancos divergindo (split-brain).

Atualizando a versão maior

Quem está na 10.11 do 24.04 e vai para a 11.8: o SST por mariabackup não funciona entre algumas versões maiores, porque o formato do redo log mudou na 10.5 e na 10.8 (MDEV-27437). Durante a migração use wsrep_sst_method=rsync, que atravessa versões, ou pare os três, atualize todos e faça o bootstrap de novo.

O que costuma quebrar

  • wsrep_sst_method=mariadb-backup — nome errado. É mariabackup; não existe script wsrep_sst_mariadb-backup.
  • AppArmor no 26.04 negando o SST; o log do MariaDB só diz “SST failed”. Veja o dmesg.
  • socat ausente no doador ou no joiner.
  • Porta 4444 fechada no firewall: o joiner espera e o SST morre por timeout.
  • Dois nós: um cai e o outro vira Non-Primary e para de aceitar escrita. Use três, ou dois mais um árbitro garbd.
  • bind-address=127.0.0.1 ainda ativo: os outros não conectam.
  • Tabela MyISAM ou sem chave primária: replica mal ou nem replica. Converta para InnoDB e crie a PK.

Referências: código do provider em github.com/codership/galera · Getting Started · SST com mariadb-backup · Configuring MariaDB Galera Cluster · pacotes DEB.