Alternar tema

Como implantar MySQL com Docker: persistência e replicação

Easton editorial illustration: orchestration hub with branches

O erro no terminal era impossível de ignorar: os dados de teste de duas semanas tinham sumido. Eu havia reiniciado o contêiner do MySQL naquela tarde, pensando que seria uma operação comum, mas os dados desapareceram junto com ele.

Implantar MySQL com Docker parece simples: basta executar docker run -e MYSQL_ROOT_PASSWORD=123456 mysql. Na prática, há armadilhas que só ficam claras depois de enfrentá-las: dados que somem, montagem de configuração que não funciona, aplicações locais sem conexão e replicação difícil de ajustar em produção. Este texto vai da persistência à replicação e explica cada configuração.

Fundamentos de uma implantação do MySQL com Docker

A forma mais simples de iniciar — e por que não a recomendo

Vamos começar pelo erro mais comum. Na primeira instalação do MySQL com Docker, muita gente executa diretamente:

docker run --name mysql-test -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0

O contêiner inicia, o estado aparece como normal e o banco pode ser acessado. Tudo parece perfeito, mas há uma bomba-relógio nessa configuração.

O motivo é simples: contêineres são temporários. Quando você remove o contêiner — ou reinicia tudo sem perceber como os dados estão armazenados — pode perder o conteúdo. Por padrão, o MySQL grava os dados em /var/lib/mysql dentro do contêiner. Se essa camada for removida, o diretório desaparece com ela.

Na primeira vez que caí nessa armadilha, achei que fosse um bug do MySQL. Depois entendi que o problema não estava no banco: eu simplesmente não havia configurado a persistência.

Persistência de dados: a forma correta de montar volumes

Em termos práticos, persistir os dados significa mapear o diretório do MySQL para um armazenamento fora da camada descartável do contêiner. Assim, mesmo que o contêiner deixe de existir, os dados continuam disponíveis.

O Docker oferece três tipos de montagem:

  1. bind mount: mapeia diretamente um diretório do host, como /home/mysql/data
  2. named volume: o Docker gerencia o volume, sem exigir que você conheça sua localização física
  3. tmpfs: armazena em memória e perde tudo na reinicialização; quase não se usa para esse caso

Em 2024, a recomendação oficial do Docker era usar named volumes, cuja diferença de desempenho em relação a bind mounts já era pequena. Eu uso ambos conforme o cenário: named volume no desenvolvimento, pela praticidade, e bind mount em produção, para facilitar os backups.

Este é o comando completo:

docker run --name mysql-persistent \
  -e MYSQL_ROOT_PASSWORD=rootpwd123 \
  -p 3306:3306 \
  -v mysql-data:/var/lib/mysql \
  -d mysql:8.0

O ponto-chave é -v mysql-data:/var/lib/mysql. mysql-data é o nome do volume — o Docker o cria automaticamente — e /var/lib/mysql é o diretório de dados dentro do contêiner do MySQL.

Agora verifique se a persistência realmente funciona:

# Crie um banco de dados dentro do contêiner
docker exec -it mysql-persistent mysql -uroot -prootpwd123 -e "CREATE DATABASE testdb;"

# Pare e remova o contêiner
docker stop mysql-persistent
docker rm mysql-persistent

# Inicie outro contêiner com o mesmo volume
docker run --name mysql-persistent \
  -e MYSQL_ROOT_PASSWORD=rootpwd123 \
  -p 3306:3306 \
  -v mysql-data:/var/lib/mysql \
  -d mysql:8.0

# Consulte os bancos; testdb deve continuar disponível
docker exec -it mysql-persistent mysql -uroot -prootpwd123 -e "SHOW DATABASES;"

Se testdb ainda estiver lá, os dados estão realmente protegidos. É o tipo de confirmação que traz tranquilidade.

Montagem do arquivo de configuração: parâmetros personalizados do MySQL

Com a persistência resolvida, surge a próxima pergunta: como alterar a configuração do MySQL?

Talvez você queira usar utf8mb4 como conjunto de caracteres ou aumentar o número máximo de conexões. Sem montar um arquivo de configuração, seria preciso entrar no contêiner e alterar tudo manualmente; ao recriá-lo, o trabalho teria de ser repetido.

O MySQL lê arquivos de configuração em /etc/mysql/conf.d/. Basta montar o seu my.cnf nesse diretório.

Primeiro, crie o arquivo de configuração no host:

mkdir -p /home/mysql/conf
cat > /home/mysql/conf/my.cnf << 'EOF'
[mysqld]
# Configuração do conjunto de caracteres
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

# Configuração do número de conexões
max_connections=1000

# Plugin de autenticação para compatibilidade com alguns clientes
default_authentication_plugin=mysql_native_password

[client]
default-character-set=utf8mb4
EOF

Depois, monte o arquivo ao iniciar o contêiner:

docker run --name mysql-custom \
  -e MYSQL_ROOT_PASSWORD=rootpwd123 \
  -p 3306:3306 \
  -v /home/mysql/conf:/etc/mysql/conf.d \
  -v /home/mysql/data:/var/lib/mysql \
  -d mysql:8.0

Observe que dois diretórios são montados: um para a configuração e outro para os dados.

Verifique se a configuração entrou em vigor:

docker exec -it mysql-custom mysql -uroot -prootpwd123 -e "SHOW VARIABLES LIKE 'character%';"

Se character_set_server aparecer como utf8mb4, a configuração foi aplicada.

Como resolver problemas de conexão externa

O contêiner está em execução, os dados são persistentes e a configuração foi montada. Agora você tenta conectar uma aplicação local ao MySQL e recebe:

ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (Connection refused)

ou:

ERROR 1045 (28000): Access denied for user 'root'@'172.17.0.1'

Também enfrentei esses dois erros e levei algum tempo para identificar a causa.

Problema 1: Connection refused

Na maioria das vezes, a porta não foi mapeada corretamente. Inclua -p 3306:3306 ao iniciar o contêiner para mapear a porta 3306 do contêiner para a mesma porta no host.

Se a porta 3306 do host já estiver ocupada — por exemplo, por uma instalação local do MySQL — use outra porta externa:

-p 3307:3306  # Acesse pela porta 3307 do host; dentro do contêiner, continua sendo 3306

Outra possibilidade é o bloqueio pelo firewall. Em um servidor Linux, confira as regras:

# CentOS/RHEL
sudo firewall-cmd --zone=public --add-port=3306/tcp --permanent
sudo firewall-cmd --reload

# Ubuntu
sudo ufw allow 3306/tcp

Problema 2: Access denied

Esse é um problema de permissão. Em algumas configurações, o usuário root do MySQL só pode se conectar a partir de localhost, e conexões vindas de outro IP são recusadas.

Uma solução é alterar o host do usuário root para %, permitindo conexões de qualquer IP:

# Entre no contêiner
docker exec -it mysql-custom mysql -uroot -prootpwd123

# Execute o SQL abaixo
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'rootpwd123';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

Há dois cuidados importantes:

  1. O plugin mysql_native_password tem compatibilidade melhor com alguns clientes MySQL antigos
  2. Nunca use essa abertura irrestrita em produção; crie um usuário específico e limite os IPs permitidos

Depois da alteração, a aplicação externa deve conseguir se conectar.

Isso encerra a primeira parte. Com esses pontos resolvidos, uma implantação básica do MySQL com Docker em uma única instância fica bem mais previsível.

Gerenciamento com Docker Compose

Por que usar Docker Compose

Para testes locais, os comandos docker run anteriores podem ser suficientes. No trabalho diário, porém, alguns problemas aparecem rapidamente:

  1. O comando é longo, e você precisa procurar no histórico toda vez
  2. É fácil errar um parâmetro, como o caminho do volume
  3. Em equipe, cada pessoa inicia o serviço de uma maneira, o que gera configurações inconsistentes
  4. Quando vários contêineres precisam trabalhar juntos — MySQL, Redis e Nginx, por exemplo — iniciá-los um por um dá trabalho

É aí que o Docker Compose ajuda.

Em resumo, ele coloca aquele comando extenso do docker run em um arquivo YAML. Depois, docker-compose up -d inicia os serviços, docker-compose down os interrompe, e o arquivo pode ser versionado no Git.

Quando uma pessoa nova na equipe perguntar como iniciar o MySQL, basta entregar o docker-compose.yml. Ela faz o git clone e inicia tudo com um comando, sem precisar reconstruir a configuração.

Configuração do MySQL em uma única instância com Docker Compose

A configuração completa está abaixo; em seguida, explico cada parte:

version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: mysql-standalone
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpwd123
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppwd123
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./conf/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./logs:/var/log/mysql
    networks:
      - mysql-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpwd123"]
      interval: 10s
      timeout: 5s
      retries: 3

volumes:
  mysql-data:

networks:
  mysql-network:
    driver: bridge

Veja o papel das principais opções:

Seção environment:

  • MYSQL_ROOT_PASSWORD: senha do root; obrigatória
  • MYSQL_DATABASE: banco criado automaticamente quando o contêiner inicia
  • MYSQL_USER e MYSQL_PASSWORD: usuário comum criado automaticamente, mais seguro do que usar root

Seção volumes:

  • mysql-data:/var/lib/mysql: persistência com named volume
  • ./conf/my.cnf:/etc/mysql/conf.d/my.cnf: montagem do arquivo de configuração por caminho relativo
  • ./logs:/var/log/mysql: montagem do diretório de logs, útil para investigar problemas

restart: always: reinicia o contêiner automaticamente após uma falha e também inicia o serviço depois que o servidor é reiniciado

healthcheck: envia pings periódicos ao MySQL para verificar sua integridade

networks: define uma rede personalizada; outros contêineres que precisam se comunicar podem entrar na mesma rede

Siga estes passos:

  1. Crie a estrutura de diretórios:
mkdir -p mysql-docker/{conf,logs}
cd mysql-docker
  1. Crie o arquivo de configuração conf/my.cnf:
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=1000
default_authentication_plugin=mysql_native_password

[client]
default-character-set=utf8mb4
  1. Crie o docker-compose.yml com a configuração completa mostrada acima.

  2. Inicie os serviços:

docker-compose up -d
  1. Consulte o estado:
docker-compose ps

A saída será semelhante a esta:

       Name                     Command                  State                 Ports
------------------------------------------------------------------------------------------------
mysql-standalone      docker-entrypoint.sh mysqld      Up (healthy)    0.0.0.0:3306->3306/tcp

O estado (healthy) indica que o healthcheck foi aprovado.

  1. Consulte os logs:
docker-compose logs -f mysql

O parâmetro -f acompanha os logs em tempo real, como tail -f. Se houver uma falha na inicialização, normalmente a causa aparecerá ali.

  1. Pare e remova os contêineres:
docker-compose down

Esse comando remove os contêineres, mas não o volume; portanto, os dados permanecem. Para remover também os dados, seria necessário usar:

docker-compose down -v  # Cuidado: remove todos os dados

Hoje uso Docker Compose na maior parte do desenvolvimento local. A configuração é escrita uma vez, e depois basta um comando para iniciar ou parar tudo.

Replicação para produção

Como funciona a replicação do MySQL

Antes da configuração, vale entender por que usar replicação.

Uma única instância do MySQL pode bastar em projetos pequenos, mas se torna um gargalo quando a carga aumenta. A replicação atende principalmente a duas necessidades:

  1. Separação de leitura e escrita: o servidor principal recebe INSERT, UPDATE e DELETE, enquanto as réplicas atendem SELECT. Como muitas aplicações têm mais leituras do que escritas, distribuir as consultas entre réplicas melhora o desempenho
  2. Backup e alta disponibilidade: se o servidor principal falhar, a réplica ainda pode atender leituras e ajudar na recuperação do serviço

O princípio é simples:

  • O servidor principal (Master) ativa o binlog, que registra todas as alterações nos dados
  • A réplica (Slave) conecta-se ao servidor principal e lê o binlog
  • Dois threads trabalham na réplica: o thread de IO baixa o binlog e o salva no relay log; o thread SQL executa as instruções desse relay log
  • Assim, as alterações feitas no servidor principal são reproduzidas na réplica

No Docker, a configuração se resume a três pontos:

  1. Cada contêiner precisa ter um server-id diferente
  2. O servidor principal precisa ter o binlog ativado
  3. A réplica precisa se conectar ao servidor principal e iniciar a replicação

Parece complexo à primeira vista, mas, depois de configurar o Docker Compose, basta iniciar os serviços.

Configuração do nó Master

O servidor principal precisa de três ajustes: ativar o binlog, definir o server-id e criar um usuário de replicação.

  1. Crie o arquivo conf/master.cnf:
[mysqld]
# ID exclusivo do servidor; principal e réplica não podem usar o mesmo valor
server-id=1

# Ativa o log binário
log-bin=mysql-bin

# O formato ROW registra alterações linha por linha e é mais seguro
binlog-format=ROW

# Conjunto de caracteres
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

# Opcional: banco que será replicado; sem esta opção, todos são replicados
# binlog-do-db=myapp

# Opcional: bancos que não serão replicados
# binlog-ignore-db=mysql
# binlog-ignore-db=information_schema
  1. Configure o servidor principal no Docker Compose:
version: '3.8'

services:
  mysql-master:
    image: mysql:8.0
    container_name: mysql-master
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpwd123
      MYSQL_DATABASE: myapp
    ports:
      - "3306:3306"
    volumes:
      - master-data:/var/lib/mysql
      - ./conf/master.cnf:/etc/mysql/conf.d/master.cnf
      - ./logs/master:/var/log/mysql
    networks:
      - mysql-replication

volumes:
  master-data:

networks:
  mysql-replication:
    driver: bridge
  1. Inicie o servidor principal:
docker-compose up -d mysql-master
  1. Crie o usuário de replicação:

Entre no contêiner do servidor principal e crie um usuário exclusivo para a replicação:

docker exec -it mysql-master mysql -uroot -prootpwd123

Execute o SQL abaixo:

-- Cria o usuário de replicação
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'replpwd123';

-- Concede a permissão de replicação
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

-- Atualiza as permissões
FLUSH PRIVILEGES;

-- Mostra o estado do servidor principal; anote File e Position
SHOW MASTER STATUS;

SHOW MASTER STATUS retorna algo semelhante a:

+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 |      156 |              |                  |
+------------------+----------+--------------+------------------+

Importante: anote os valores de File e Position; eles serão usados para configurar a réplica.

Configuração do nó Slave

A configuração da réplica é um pouco mais simples.

  1. Crie o arquivo conf/slave.cnf:
[mysqld]
# ID exclusivo do servidor, diferente do servidor principal
server-id=2

# Relay log
relay-log=relay-bin

# Mantém a réplica somente leitura para evitar escritas acidentais
read-only=1

# Conjunto de caracteres
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
  1. Atualize o docker-compose.yml e adicione a réplica:
version: '3.8'

services:
  mysql-master:
    image: mysql:8.0
    container_name: mysql-master
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpwd123
      MYSQL_DATABASE: myapp
    ports:
      - "3306:3306"
    volumes:
      - master-data:/var/lib/mysql
      - ./conf/master.cnf:/etc/mysql/conf.d/master.cnf
      - ./logs/master:/var/log/mysql
    networks:
      - mysql-replication

  mysql-slave:
    image: mysql:8.0
    container_name: mysql-slave
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpwd123
    ports:
      - "3307:3306"  # A réplica é acessada pela porta 3307 do host
    volumes:
      - slave-data:/var/lib/mysql
      - ./conf/slave.cnf:/etc/mysql/conf.d/slave.cnf
      - ./logs/slave:/var/log/mysql
    networks:
      - mysql-replication
    depends_on:
      - mysql-master

volumes:
  master-data:
  slave-data:

networks:
  mysql-replication:
    driver: bridge
  1. Inicie a réplica:
docker-compose up -d mysql-slave
  1. Configure a conexão da réplica com o servidor principal:

Entre no contêiner da réplica:

docker exec -it mysql-slave mysql -uroot -prootpwd123

Execute o SQL abaixo, substituindo File e Position pelos valores anotados anteriormente:

-- Configura a conexão com o servidor principal
CHANGE MASTER TO
  MASTER_HOST='mysql-master',           -- Nome do contêiner principal, resolvido diretamente na rede Docker
  MASTER_PORT=3306,                     -- Porta do servidor principal
  MASTER_USER='repl',                   -- Usuário de replicação
  MASTER_PASSWORD='replpwd123',         -- Senha do usuário de replicação
  MASTER_LOG_FILE='mysql-bin.000003',   -- Arquivo binlog obtido em SHOW MASTER STATUS
  MASTER_LOG_POS=156;                   -- Posição do binlog obtida em SHOW MASTER STATUS

-- Inicia a replicação
START SLAVE;

-- Mostra o estado da réplica
SHOW SLAVE STATUS\G

Como validar a sincronização

SHOW SLAVE STATUS\G exibe muitos campos. Estes são os mais importantes:

Slave_IO_Running: Yes       # O thread de IO deve estar em execução
Slave_SQL_Running: Yes      # O thread SQL deve estar em execução
Seconds_Behind_Master: 0    # Atraso da réplica em segundos; 0 indica sincronização imediata
Last_IO_Error:              # Vazio indica que não há erro de IO
Last_SQL_Error:             # Vazio indica que não há erro SQL

Se Slave_IO_Running e Slave_SQL_Running estiverem como Yes, a replicação foi configurada com sucesso.

Faça um teste de sincronização:

  1. Crie dados de teste no servidor principal:
docker exec -it mysql-master mysql -uroot -prootpwd123 -e "
USE myapp;
CREATE TABLE test_table (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO test_table VALUES (1, 'test data');
"
  1. Consulte os dados na réplica:
docker exec -it mysql-slave mysql -uroot -prootpwd123 -e "
USE myapp;
SELECT * FROM test_table;
"

Se a linha inserida aparecer, a replicação está funcionando.

É tão satisfatório quanto ver o próprio código rodando corretamente em produção.

Solução de problemas comuns

A replicação não é difícil, mas a primeira configuração costuma apresentar alguns obstáculos. Estes são os mais comuns:

Problema 1: Slave_IO_Running aparece como No

Possíveis causas:

  • Falha de rede: confira se os contêineres estão na mesma rede com docker exec -it mysql-slave ping mysql-master
  • Permissões incorretas: confirme se o usuário repl foi criado no servidor principal e recebeu a permissão adequada
  • Arquivo ou posição de binlog incorretos: execute SHOW MASTER STATUS novamente

Problema 2: Slave_SQL_Running aparece como No

Possíveis causas:

  • Erro ao executar SQL: consulte Last_SQL_Error e investigue a mensagem apresentada
  • Dados divergentes: se o servidor principal já tinha dados antes da configuração, exporte-os e importe-os na réplica antes de ativar a replicação

Problema 3: Seconds_Behind_Master permanece alto

Possíveis causas:

  • Desempenho insuficiente da réplica: verifique CPU, memória e I/O de disco
  • Escritas frequentes demais no servidor principal: considere adicionar réplicas para distribuir a carga
  • Largura de banda insuficiente: confira a latência da rede

Se o problema persistir, redefina a réplica e repita a configuração:

-- Execute na réplica
STOP SLAVE;
RESET SLAVE;

-- Depois, execute novamente CHANGE MASTER TO e START SLAVE

Otimização de desempenho e boas práticas

Como otimizar o desempenho do MySQL no Docker

Muita gente teme que o Docker reduza o desempenho do MySQL. Alguns anos atrás esse impacto era mais perceptível, mas dados de 2024 indicavam uma perda inferior a 5%, quase imperceptível em muitos cenários intensivos em I/O.

Ainda assim, alguns ajustes são úteis.

1. Escolha do tipo de volume

Named volumes e bind mounts já têm desempenho semelhante, mas atendem a necessidades operacionais diferentes:

  • named volume: gerenciado pelo Docker e associado automaticamente ao driver de armazenamento; recomendado para desenvolvimento
  • bind mount: mapeia diretamente um diretório do host; recomendado para produção, pois facilita backup e monitoramento
# Exemplo com named volume
volumes:
  - mysql-data:/var/lib/mysql

# Exemplo com bind mount
volumes:
  - /data/mysql:/var/lib/mysql

2. Escolha do modo de rede

A rede bridge padrão atende à maioria dos casos. Se cada fração de desempenho for importante, você pode testar o modo host:

services:
  mysql:
    network_mode: "host"  # Usa diretamente a rede do host para reduzir sobrecarga

No modo host, o mapeamento ports não é necessário: o contêiner escuta diretamente a porta 3306 do host.

3. Limites de recursos

Em produção, limite os recursos para impedir que o contêiner do MySQL consuma toda a capacidade do servidor:

services:
  mysql:
    image: mysql:8.0
    deploy:
      resources:
        limits:
          cpus: '2'        # No máximo dois núcleos de CPU
          memory: 2G       # No máximo 2 GB de memória
        reservations:
          memory: 1G       # Reserva de pelo menos 1 GB de memória

Essa configuração requer docker-compose --compatibility up ou o modo Docker Swarm.

4. Gerenciamento de logs

Sem limites, os logs do contêiner do MySQL podem ocupar todo o disco. Configure a rotação:

services:
  mysql:
    logging:
      driver: "json-file"
      options:
        max-size: "100m"    # Tamanho máximo de 100 MB por arquivo
        max-file: "3"       # Mantém no máximo três arquivos

Lista de boas práticas para produção

Estas recomendações vêm dos problemas que encontrei na prática e merecem atenção.

Segurança:

  1. Não use senhas padrão ou fáceis
# ❌ Não faça isto
MYSQL_ROOT_PASSWORD: 123456

# ✅ Use uma senha forte ou Docker secrets
MYSQL_ROOT_PASSWORD: "Mx8#kL9$pQ2@vN4!"
  1. Gerencie informações sensíveis com Docker secrets
services:
  mysql:
    image: mysql:8.0
    secrets:
      - mysql_root_password
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password

secrets:
  mysql_root_password:
    file: ./secrets/mysql_root_password.txt
  1. Restrinja o acesso à rede do contêiner
networks:
  mysql-network:
    driver: bridge
    internal: true  # Permite somente comunicação entre contêineres e bloqueia o acesso externo

Se o acesso externo for necessário, prefira um contêiner de aplicação como intermediário em vez de expor diretamente o contêiner do MySQL.

Operação:

  1. Faça backups periódicos do diretório de dados
# Script de backup simples
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
docker exec mysql-master mysqldump -uroot -prootpwd123 --all-databases > backup_$DATE.sql

# Ou faça backup direto do diretório de dados
tar -czf mysql-data-backup_$DATE.tar.gz /data/mysql/

Em produção, automatize um backup diário e mantenha pelo menos sete dias de histórico.

  1. Configure um healthcheck para monitorar o contêiner
healthcheck:
  test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 30s

Combine a reinicialização automática com alertas de monitoramento para detectar falhas.

  1. Monte os logs em um volume separado
volumes:
  - ./logs:/var/log/mysql

Com os logs separados, fica mais fácil investigar problemas e eles não ocupam o espaço do volume de dados.

Alta disponibilidade:

  1. Use pelo menos um servidor principal e uma réplica; quando necessário, adicione outras réplicas

Em cenários com muito mais leituras do que escritas, um servidor principal pode atender de três a cinco réplicas, distribuindo entre elas as consultas de leitura.

  1. Separe leitura e escrita com balanceamento de carga

Você pode usar MySQL Router, ProxySQL ou a própria aplicação:

  • Escritas (INSERT/UPDATE/DELETE) → servidor principal
  • Leituras (SELECT) → réplicas, com balanceamento de carga
  1. Teste periodicamente o processo de promoção da réplica

Não espere o servidor principal falhar para descobrir que a réplica não pode assumir seu lugar. Simule a troca regularmente:

-- Execute na réplica
STOP SLAVE;
RESET SLAVE ALL;
SET GLOBAL read_only=0;  -- Desativa o modo somente leitura e promove a réplica

Em produção, use uma solução madura de alta disponibilidade, como MHA ou Orchestrator, para automatizar a troca.

Conclusão

O processo completo começa com uma única instância do MySQL no Docker, passa pela persistência de dados, pela montagem do arquivo de configuração e pela solução de falhas de conexão, avança para o gerenciamento com Docker Compose e chega à replicação para produção. Esses são os cenários mais comuns de uma implantação do MySQL com Docker.

Vale reforçar alguns pontos:

  1. Persistência de dados é obrigatória: não execute o MySQL em um contêiner descartável sem volume
  2. A montagem da configuração é importante: defina antecipadamente conjunto de caracteres, limite de conexões e outros parâmetros
  3. Docker Compose simplifica o gerenciamento: centraliza a configuração e facilita o trabalho em equipe
  4. Replicação não precisa ser complicada: o essencial é acertar os arquivos e seguir a ordem das etapas
  5. Produção exige segurança e backup: evite senhas fracas e faça cópias periódicas dos dados

Todas as configurações do texto foram verificadas e podem servir como ponto de partida. Ajuste senhas e portas antes de usá-las no seu ambiente.

Se esta for sua primeira implantação do MySQL com Docker, comece por uma única instância. Entenda primeiro a persistência e a montagem da configuração; depois avance para a replicação. Fazer isso em etapas reduz bastante a chance de erro.

Quando surgir um problema, consulte os logs antes de alterar a configuração às cegas. Na maioria dos casos, a causa aparece ali. Se ainda restarem dúvidas, deixe um comentário para continuarmos a investigação.

Depois de configurado, o MySQL no Docker exige bem menos trabalho do que uma instalação tradicional. Uma configuração reproduzível pode ser executada em qualquer ambiente compatível.

Processo completo para implantar MySQL com Docker

Da persistência de dados à replicação, resolva problemas comuns como perda de dados, montagem de arquivos de configuração e falhas de conexão

⏱️ Estimated time: 1 hr

  1. 1

    Step 1: Implantação em uma instância: persistência e montagem da configuração

    Persistência de dados:
    • Monte o diretório de dados em um volume:
    docker run -d -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
    • Monte o arquivo de configuração:
    docker run -d -v mysql-data:/var/lib/mysql -v ./my.cnf:/etc/mysql/conf.d/my.cnf -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
    • Defina as variáveis de ambiente:
    MYSQL_ROOT_PASSWORD, MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD

    Valide a persistência:
    • Remova e recrie o contêiner; os dados devem continuar disponíveis
  2. 2

    Step 2: Configuração da replicação

    Configure o servidor principal:
    • Ative o binlog: defina log-bin=mysql-bin no my.cnf
    • Defina o server-id: server-id=1
    • Crie o usuário de replicação:
    CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

    Configure a réplica:
    • Informe o endereço do servidor principal:
    CHANGE MASTER TO
    MASTER_HOST='master',
    MASTER_USER='repl',
    MASTER_PASSWORD='password',
    MASTER_LOG_FILE='mysql-bin.000001',
    MASTER_LOG_POS=0;
    • Inicie a replicação: START SLAVE;
    • Verifique o estado: SHOW SLAVE STATUS\G;
  3. 3

    Step 3: Solução de problemas e implantação para produção

    Resolva os problemas mais comuns:
    • Falha de conexão: verifique o mapeamento -p 3306:3306, a rede e o firewall
    • Perda de dados: configure a persistência em volume
    • Problema de conjunto de caracteres: defina character-set-server=utf8mb4 no my.cnf
    • Problema de permissão: confira as permissões do volume com chmod 755

    Prepare a produção:
    • Gerencie vários contêineres com docker-compose
    • Configure uma verificação de integridade (healthcheck)
    • Defina limites de recursos (deploy.resources)
    • Adote uma estratégia de backup (backup periódico do volume)
    • Use volumes nomeados para facilitar o gerenciamento

FAQ

Como evitar a perda de dados ao implantar MySQL com Docker?
Use estas opções de persistência:

1) Monte o diretório de dados em um volume:
docker run -d -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

2) Monte o arquivo de configuração:
docker run -d -v mysql-data:/var/lib/mysql -v ./my.cnf:/etc/mysql/conf.d/my.cnf -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0

3) Defina as variáveis de ambiente:
• MYSQL_ROOT_PASSWORD
• MYSQL_DATABASE
• MYSQL_USER
• MYSQL_PASSWORD

Para validar, remova e recrie o contêiner. Os dados devem continuar disponíveis.

A causa do problema é que, por padrão, o Docker grava os dados na camada do contêiner. Quando o contêiner é removido, essa camada desaparece; por isso, é necessário configurar a persistência.
Como configurar a replicação do MySQL?
Configure o servidor principal:
1) Ative o binlog (defina log-bin=mysql-bin no my.cnf)
2) Defina o server-id (server-id=1)
3) Crie o usuário de replicação:
CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

Configure a réplica:
1) Informe o servidor principal:
CHANGE MASTER TO
MASTER_HOST='master',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=0;
2) Inicie a replicação: START SLAVE;
3) Verifique o estado: SHOW SLAVE STATUS\G;
Como resolver uma falha de conexão com o MySQL no Docker?
Verifique os problemas mais comuns:
• Falha de conexão: confira o mapeamento -p 3306:3306, a rede e o firewall
• Perda de dados: configure a persistência em volume
• Problema de conjunto de caracteres: defina character-set-server=utf8mb4 no my.cnf
• Problema de permissão: confira as permissões do volume com chmod 755

Siga estes passos:
1) Confirme que o contêiner está em execução (docker ps)
2) Verifique o mapeamento de portas (docker port container-name)
3) Verifique a configuração de rede (docker network inspect network-name)
4) Consulte os logs do contêiner (docker logs container-name)

17 min de leitura · Publicado em: 18 dez 2025 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog