Fala, Guerreiros e Guerreiras!
Neste dia, iniciando minhas férias, ao som de Pink Floyd, com o álbum Animals tocando e chegando à metade da faixa Pigs (Three Different Ones), só tenho uma coisa a dizer: Pink Floyd é foda demais!
E, para aproveitar esse momento de descanso, nada melhor do que compartilhar um pouco do conhecimento.
1. Introdução
Manter um servidor atualizado é uma atividade essencial para a segurança, a estabilidade e a disponibilidade das aplicações. Entretanto, atualizar um sistema em produção não significa simplesmente executar apt upgrade e reiniciar a máquina.
Um servidor pode hospedar containers Docker, bancos de dados nativos, filas de processamento, serviços de monitoramento e aplicações que dependem umas das outras. A interrupção de qualquer um desses componentes pode comprometer o funcionamento de todo o ambiente.
Por isso, uma manutenção deve seguir uma sequência controlada:
- Inventariar os serviços.
- Verificar o estado do sistema operacional.
- Auditar as políticas de reinicialização dos containers.
- Identificar bancos de dados e serviços fora do Docker.
- Realizar e validar os backups.
- Atualizar os pacotes do sistema.
- Atualizar o Docker quando necessário.
- Reiniciar o servidor, se necessário e autorizado.
- Validar as aplicações e registrar os resultados.
Regra fundamental: não iniciar uma atualização de produção sem conhecer os serviços afetados, possuir backups recuperáveis e ter um plano de retorno.
2. Identificação do ambiente
Antes de qualquer alteração, registre a versão do sistema operacional, o kernel em execução, o espaço disponível e as versões dos componentes principais.
lsb_release -a
uname -r
df -h
free -h
uptime
docker --version
docker compose version
Esses comandos permitem identificar a distribuição, o kernel ativo, a utilização de disco e memória e as versões instaladas do Docker e do Compose.
Em seguida, verifique se existem atualizações pendentes:
sudo apt-get update
apt list --upgradable
cat /var/run/reboot-required 2>/dev/null || echo "Sem reboot pendente"
O arquivo /var/run/reboot-required, quando presente, indica que uma reinicialização pode ser necessária para aplicar determinadas atualizações. Sua ausência não substitui a análise dos serviços e dos pacotes atualizados.
3. Inventário completo dos containers
O primeiro levantamento deve incluir containers em execução e containers parados.
docker ps -a \
--format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
Não utilize apenas docker ps para a auditoria inicial. Esse comando apresenta somente os containers em execução e pode ocultar uma dependência que já estava parada antes da manutenção.
Para registrar o inventário, identifique:
- Nome e imagem de cada container.
- Estado atual e indicação de saúde, quando houver.
- Aplicação ou serviço ao qual pertence.
- Dependências, como Redis, PostgreSQL e filas.
- Containers de execução única, que podem terminar normalmente.
- Responsável pelo serviço e procedimento de recuperação.
Um container com estado Exited (0) não está necessariamente com problema. Pode ser uma tarefa de inicialização executada uma única vez. Confirme sua finalidade antes de alterá-lo.
4. Auditoria das políticas de reinicialização
Um dos pontos mais importantes da manutenção é verificar se os containers voltarão a funcionar após a reinicialização do Docker ou do próprio servidor.
Execute:
docker inspect \
--format '{{.Name}} {{.HostConfig.RestartPolicy.Name}}' \
$(docker ps -aq)
O comando consulta todos os containers conhecidos pelo daemon, inclusive os que estão parados.
4.1. Entendendo as políticas
| Política | Comportamento geral |
|---|---|
no |
Não reinicia automaticamente. |
on-failure |
Reinicia quando o processo termina com erro, conforme os limites configurados. |
unless-stopped |
Reinicia automaticamente, exceto quando foi explicitamente parado e essa condição é preservada. |
always |
Procura manter o container em execução, inclusive após reinicializações do daemon ou do host. |
Para serviços persistentes, como aplicações web, filas e bancos de dados containerizados, uma política adequada costuma ser necessária.
A escolha depende da arquitetura. Não aplique a mesma política indiscriminadamente a tarefas de execução única.
4.2. Corrigindo uma política
Para um container persistente cuja política foi verificada e precisa ser corrigida:
docker update --restart unless-stopped NOME_DO_CONTAINER
Para vários containers, execute o comando com os nomes explicitamente identificados.
Atenção: docker update altera a configuração do container existente, mas não corrige automaticamente o arquivo docker-compose.yml. Se o serviço for recriado em um deploy futuro, a configuração declarada no Compose poderá prevalecer.
No arquivo Compose, configure a política no serviço correspondente:
services:
web:
restart: unless-stopped
Repita a configuração somente para os serviços persistentes que realmente precisam dela. Valide o arquivo antes de recriar qualquer container.
5. Identificação dos serviços nativos
Nem todos os serviços de um servidor estão dentro do Docker. Bancos de dados instalados diretamente no Ubuntu podem ser reiniciados durante uma atualização de pacotes.
Consulte os serviços ativos:
systemctl list-units --type=service --state=running
Verifique também os serviços instalados e relevantes:
systemctl status postgresql --no-pager
systemctl status mysql --no-pager
Os nomes exatos variam conforme a instalação. No PostgreSQL, por exemplo, podem existir unidades específicas para uma versão e um cluster.
O inventário deve incluir bancos de dados nativos, serviços de filas, aplicações executadas via systemd e quaisquer componentes externos aos containers que sejam importantes para o funcionamento do sistema.
6. Backup antes da atualização
O backup deve ser realizado antes de modificar pacotes, reiniciar serviços ou atualizar o motor Docker.
Antes de iniciar, confirme:
- Qual banco pertence à aplicação.
- Qual usuário tem permissão para executar o backup.
- Onde o arquivo será armazenado.
- Se há espaço suficiente.
- Se o backup poderá ser recuperado.
- Se existe uma cópia fora do servidor.
6.1. PostgreSQL em container
Primeiro, identifique o container correto e consulte apenas os nomes das variáveis necessárias, sem exibir senhas:
docker exec NOME_DO_CONTAINER \
env | grep -o '^POSTGRES_USER='
docker exec NOME_DO_CONTAINER \
env | grep -o '^POSTGRES_DB='
O comando acima confirma a existência das variáveis, mas não revela seus valores. Consulte a configuração protegida da aplicação para obter o usuário e o banco corretos.
Para exportar todos os bancos de um container PostgreSQL, quando as permissões e o usuário forem adequados:
docker exec NOME_DO_CONTAINER \
pg_dumpall -U USUARIO_DO_BANCO \
> backup_postgres_$(date +%Y%m%d).sql
O redirecionamento grava a saída no host que executa o comando. O arquivo resultante deve ser inspecionado e protegido conforme a política de segurança da organização.
6.2. PostgreSQL nativo
Em instalações que utilizam o usuário administrativo local postgres:
sudo -u postgres pg_dumpall \
> backup_postgres_nativo_$(date +%Y%m%d).sql
Confirme que o comando terminou com sucesso e que o arquivo foi criado.
6.3. MySQL nativo
Utilize o mecanismo de autenticação autorizado no ambiente. Em instalações Debian/Ubuntu que disponibilizam credenciais administrativas locais, um exemplo é:
sudo mysqldump \
--defaults-file=/etc/mysql/debian.cnf \
--databases NOME_DO_BANCO \
> backup_mysql_$(date +%Y%m%d).sql
O arquivo de credenciais só deve ser utilizado se existir e estiver configurado para esse propósito. Não presuma que todos os servidores possuem esse mecanismo.
6.4. Validando os arquivos
ls -lh backup_*.sql
Verifique o tamanho, o resultado de execução e os registros de erro. Para bancos críticos, faça um teste de restauração em ambiente separado. Um arquivo existente não comprova, por si só, que o backup é recuperável.
7. Atualização dos pacotes do Ubuntu
Depois de concluir o inventário e validar os backups, atualize os índices dos repositórios:
sudo apt-get update
Em seguida, avalie a lista de pacotes pendentes e execute a atualização planejada:
sudo apt-get upgrade
Em produção, evite confirmar automaticamente todas as alterações sem analisar a transação proposta. Leia a lista de pacotes que serão atualizados, instalados ou mantidos.
Não use dist-upgrade ou full-upgrade indiscriminadamente: esses comandos podem resolver dependências por meio da instalação ou remoção de pacotes. Qualquer mudança dessa natureza precisa ser avaliada previamente.
8. Atualização do Docker Engine
Antes de atualizar o Docker, consulte a versão atual:
docker --version
Se o Docker foi instalado a partir do repositório oficial da Docker, verifique quais versões estão disponíveis e quais pacotes serão alterados.
Em instalações compatíveis com os pacotes abaixo, a atualização direcionada pode ser feita com:
sudo apt-get update
sudo apt-get install --only-upgrade \
docker-ce docker-ce-cli containerd.io
Os nomes dos pacotes variam de acordo com a forma de instalação. Em sistemas que utilizam os pacotes fornecidos pela distribuição, confirme os nomes corretos antes de executar o comando.
Impacto operacional: a atualização do daemon Docker pode interromper ou recriar containers, dependendo da configuração e do procedimento de atualização. Planeje uma janela de manutenção e valide os serviços após a operação.
Não presuma que uma fase separada de atualização do Docker será necessária: o apt-get upgrade pode ter atualizado pacotes relacionados ao Docker durante a etapa anterior.
9. Diagnóstico após a atualização
Depois da atualização do sistema ou do Docker, verifique todos os containers:
docker ps -a \
--format 'table {{.Names}}\t{{.Status}}'
Consulte os logs de um container com problemas:
docker logs --tail 100 NOME_DO_CONTAINER
Verifique a saúde de um container que tenha health check configurado:
docker inspect NOME_DO_CONTAINER \
--format '{{.State.Health.Status}}'
O estado healthy depende da existência e do funcionamento correto de um health check. Containers sem essa configuração podem não apresentar um estado de saúde equivalente.
9.1. Exemplo: aplicação não encontra o Redis
Se uma aplicação apresentar um erro semelhante a Name does not resolve, verifique primeiro se o Redis está ativo:
docker ps -a --format 'table {{.Names}}\t{{.Status}}' \
| grep -i redis
Se o container correto estiver parado, investigue por que ele parou e confirme a identidade do serviço antes de iniciá-lo.
docker start NOME_DO_REDIS
Em seguida, examine os logs da aplicação dependente. Se o serviço continuar apresentando falhas, investigue a rede Docker, os nomes dos serviços, as configurações de conexão e a saúde do Redis.
Não atribua todo erro de resolução de nome a um problema de DNS. A causa também pode ser um serviço indisponível ou uma configuração incorreta.
9.2. Aplicação continua indisponível
Antes de reiniciar a aplicação, confirme se suas dependências estão saudáveis.
docker inspect NOME_DA_APLICACAO \
--format '{{.State.Status}}'
docker logs --tail 100 NOME_DA_APLICACAO
Se houver evidências de que um processo de longa duração precisa restabelecer suas conexões, um reinício controlado pode ser apropriado:
docker restart NOME_DA_APLICACAO
Depois, valide os logs, o health check e o funcionamento da aplicação pelo endereço utilizado pelos usuários.
O reinício não substitui a investigação da causa raiz.
10. Kernel e reinicialização do servidor
Uma reinicialização completa deve ocorrer somente depois que os backups, as atualizações e as verificações intermediárias tiverem sido concluídos.
Confira se existe indicação de reinicialização pendente:
cat /var/run/reboot-required 2>/dev/null \
|| echo "Sem indicação de reboot pendente"
Verifique o kernel atualmente em execução:
uname -r
Se uma atualização de kernel estiver planejada e autorizada, instale os pacotes adequados à distribuição e ao ambiente. Não force a atualização de um nome de pacote específico sem confirmar a configuração do sistema.
Antes do reboot, confira:
- Estado dos bancos de dados nativos.
- Estado dos containers e suas políticas de reinicialização.
- Backups e acesso aos arquivos de recuperação.
- Disponibilidade de console ou acesso alternativo ao servidor.
- Janela de manutenção e plano de retorno.
Quando autorizado:
sudo reboot
Após a inicialização, confirme o acesso SSH, a versão do kernel, o estado dos serviços nativos, os containers e a disponibilidade das aplicações.
11. Validação final
A manutenção só deve ser considerada concluída após a verificação funcional dos serviços.
uptime
free -h
df -h
docker ps -a
systemctl --failed
Além das verificações técnicas, acesse cada aplicação pelo domínio ou endereço utilizado normalmente pelos usuários.
Uma resposta HTTP isolada não comprova que todo o sistema está operacional. Uma aplicação pode responder com erro de autorização, host não permitido ou falha de uma dependência, mesmo que o servidor web esteja ativo.
Registre os seguintes dados:
- Data e duração da manutenção.
- Pacotes e componentes atualizados.
- Serviços interrompidos e reiniciados.
- Backups gerados e local de armazenamento.
- Testes realizados e seus resultados.
- Incidentes encontrados e causas identificadas.
- Pendências, responsáveis e prazo para resolução.
12. Checklist operacional
Antes de atualizar:
- [ ] Registrar o estado inicial do servidor.
- [ ] Inventariar todos os containers, inclusive os parados.
- [ ] Identificar containers de execução única.
- [ ] Auditar as políticas de reinicialização.
- [ ] Inventariar bancos e serviços nativos.
- [ ] Validar backups e acesso à recuperação.
- [ ] Confirmar a janela de manutenção.
Durante a atualização:
- [ ] Analisar a transação do APT antes de confirmá-la.
- [ ] Atualizar os pacotes planejados.
- [ ] Verificar se o Docker também foi atualizado.
- [ ] Examinar os logs e os estados dos serviços.
- [ ] Registrar incidentes e suas causas.
Depois da atualização:
- [ ] Validar os containers e suas dependências.
- [ ] Confirmar o estado dos bancos de dados nativos.
- [ ] Reiniciar o host somente quando necessário e autorizado.
- [ ] Confirmar o retorno dos serviços após o reboot.
- [ ] Testar as aplicações pelos endereços reais.
- [ ] Registrar as evidências e fechar as pendências.
Conclusão
A manutenção de servidores exige planejamento, inventário, backups, atualização controlada e validação funcional. A experiência operacional demonstra que os riscos nem sempre estão no pacote atualizado: podem estar em containers esquecidos, serviços fora do Docker, dependências indisponíveis ou configurações que não persistem após a recriação dos serviços.
Um procedimento documentado transforma incidentes em melhorias de processo e reduz a probabilidade de repetir as mesmas falhas em manutenções futuras.