Docker: Projeto Fase 1
Última atualização: 2026-08-26
Agora vamos unir o que aprendemos nas primeiras cinco aulas — implantando uma stack LEMP completa (Linux + Nginx + MySQL + PHP) do zero. Este é o projeto final abrangente da Fase 1.
1. O Que Você Vai Aprender
- Design de uma Arquitetura de Colaboração entre Múltiplos Contêineres
- Estratégias de Persistência com Volume de Dados
- Comunicação de Rede Entre Contêineres
- Gerenciamento de Configuração com Variáveis de Ambiente
- Solução de Problemas e Reparo de Contêineres
2. Uma História Real de uma Pessoa Desenvolvedora Backend
(1) Ponto de Dor: Configurar um ambiente de desenvolvimento manualmente leva meio dia
Alice precisa configurar um ambiente de desenvolvimento para o sistema de gestão interno da empresa: o Nginx servirá conteúdo estático, o PHP-FPM tratará requisições dinâmicas e o MySQL armazenará dados. A instalação manual requer configurar hosts virtuais Nginx, instalar o PHP-FPM e suas extensões, instalar e inicializar o MySQL e configurar os caminhos de comunicação entre os três componentes. Há potenciais armadilhas em cada etapa; levou um dia inteiro para uma pessoa colega conseguir configurar tudo.
(2) Soluções para Implantações com Múltiplos Contêineres no Docker
Alice usou Docker para configurar uma stack LEMP completa em 10 minutos — três contêineres, uma rede e dois volumes de dados.
# Visão geral da arquitetura: Nginx → PHP-FPM → MySQL
docker network create lemp-net
docker volume create mysql-data
(3) Benefício: 10 minutos vs. meio dia
A implantação manual leva meio dia (e não é reutilizável), enquanto a implantação com Docker leva 10 minutos (os scripts são reutilizáveis e podem ser compartilhados com a equipe). A integração de novos membros da equipe foi reduzida de um dia inteiro de configuração de ambiente para 15 minutos.
3. Design da Arquitetura LEMP
(1) Visão Geral da Arquitetura
graph TB
USER["Navegador da Pessoa Usuária<br/>:8080"] --> NGX["Contêiner Nginx<br/>:80<br/>Arquivos estáticos + proxy PHP"]
NGX -->|"FastCGI :9000"| PHP["Contêiner PHP-FPM<br/>:9000<br/>Processamento PHP"]
PHP -->|"MySQL :3306"| DB["Contêiner MySQL<br/>:3306<br/>Banco de Dados"]
DB --> VOL["Volume Nomeado<br/>mysql-data<br/>Armazenamento persistente"]
PHP --> PHPV["Bind Mount<br/>./src → /var/www/html<br/>Código fonte PHP"]
NGX --> NGXV["Bind Mount<br/>./nginx.conf<br/>Configuração Nginx"]
(2) Comparação entre Implantação Manual vs. Implantação com Docker
| Dimensão | Implantação Manual | Implantação com Docker |
|---|---|---|
| Tempo necessário | 4–8 horas | 10 minutos |
| Reprodutibilidade | Baixa (depende da experiência da pessoa operadora) | Alta (scripts/comandos podem ser reutilizados) |
| Portabilidade | Baixa (específica do SO) | Alta (consistente entre plataformas) |
| Isolamento de Ambiente | Nenhum (ambiente de sistema compartilhado) | Totalmente isolado |
| Rollback | Difícil (desinstalar e reinstalar) | Fácil (excluir contêiner e reconstruir) |
| Compartilhamento em Equipe | Escrever documentação + seguir instruções | Compartilhar comandos/scripts |
4. Preparações: Rede e Volumes
(1) Criar uma Rede Personalizada
A comunicação entre contêineres requer uma rede bridge personalizada — a rede bridge padrão não suporta resolução DNS de nomes de contêineres.
# Criar uma rede bridge personalizada para a stack LEMP
docker network create lemp-net
# Verificar
docker network ls | grep lemp-net
(2) Criar um volume de dados
Os dados do MySQL devem ser persistentes — os dados não devem ser perdidos quando o contêiner for excluído.
# Criar um volume nomeado para dados do MySQL
docker volume create mysql-data
# Verificar
docker volume ls | grep mysql-data
(3) Comparação de Estratégias de Montagem de Volume
| Estratégia | Sintaxe | Casos de Uso | Persistência |
|---|---|---|---|
| Volume Nomeado | -v mysql-data:/var/lib/mysql |
banco de dados, armazenamento persistente | ✅ Retido após exclusão do contêiner |
| Bind Mount | -v ./src:/var/www/html |
Sincronização de Código no Ambiente de Desenvolvimento | ✅ Mapear para Diretório do Host |
| tmpfs | --tmpfs /tmp |
Dados temporários, informações sensíveis | ❌ Desaparece quando o contêiner para |
- Implantar o contêiner MySQL
▶ Exemplo: Iniciando um contêiner MySQL (Dificuldade: ⭐⭐)
# Iniciar MySQL 8.0 com armazenamento persistente
docker run -d \
--name lemp-mysql \
--network lemp-net \
-e MYSQL_ROOT_PASSWORD=rootsecret \
-e MYSQL_DATABASE=appdb \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=apppass \
-v mysql-data:/var/lib/mysql \
--restart=unless-stopped \
mysql:8.0
docker logs -f lemp-mysql para monitorar o progresso da inicialização; assim que vir "ready for connections", a inicialização está concluída.
(1) Verificar se o MySQL está em execução
# Verificar se o MySQL está em execução
docker ps --filter name=lemp-mysql
# Conectar ao MySQL para verificar
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e "SELECT 1;"
5. Implantando o Contêiner PHP-FPM
(1) Preparar o código PHP
Primeiro, crie um arquivo de teste PHP e um arquivo de teste de conexão com o banco de dados.
# Criar diretório do projeto
mkdir -p lemp-project/src
# Criar página de informações PHP
cat > lemp-project/src/info.php << 'EOF'
<?php
phpinfo();
?>
EOF
# Criar teste de conexão com banco de dados
cat > lemp-project/src/db-test.php << 'EOF'
<?php
$host = 'lemp-mysql';
$db = 'appdb';
$user = 'appuser';
$pass = 'apppass';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connected to MySQL successfully!<br>";
echo "MySQL version: " . $pdo->query("SELECT VERSION()")->fetchColumn();
} catch (PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
?>
EOF
▶ Exemplo: Iniciando um contêiner PHP-FPM (Dificuldade: ⭐⭐)
# Iniciar contêiner PHP-FPM
docker run -d \
--name lemp-php \
--network lemp-net \
-v $(pwd)/lemp-project/src:/var/www/html \
--restart=unless-stopped \
php:8.2-fpm
-v $(pwd)/lemp-project/src:/var/www/html é uma montagem bind — mapeia um diretório de código local para o contêiner, então as alterações feitas em arquivos locais têm efeito imediato dentro do contêiner, tornando-o adequado para desenvolvimento e depuração.
6. Implantar o Contêiner Nginx
(1) Preparar a configuração do Nginx
O Nginx precisa ser configurado como um proxy FastCGI para encaminhar requisições PHP ao contêiner PHP-FPM.
# Criar configuração do Nginx
cat > lemp-project/nginx.conf << 'EOF'
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass lemp-php:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
EOF
lemp-php em fastcgi_pass lemp-php:9000 é o nome do contêiner PHP-FPM. Como estão na mesma rede personalizada lemp-net, o DNS do Docker resolve automaticamente o nome do contêiner para o endereço IP do contêiner.
▶ Exemplo: Iniciando um contêiner Nginx (Dificuldade: ⭐⭐)
# Iniciar contêiner Nginx
docker run -d \
--name lemp-nginx \
--network lemp-net \
-p 8080:80 \
-v $(pwd)/lemp-project/src:/var/www/html \
-v $(pwd)/lemp-project/nginx.conf:/etc/nginx/conf.d/default.conf \
--restart=unless-stopped \
nginx:1.25-alpine
7. Validação e Testes
▶ Exemplo: Acessando uma página PHP para verificação (Dificuldade: ⭐)
# Testar página de informações PHP
curl http://localhost:8080/info.php | head -5
# Testar conexão com banco de dados
curl http://localhost:8080/db-test.php
# curl http://localhost:8080/db-test.php
Connected to MySQL successfully!
MySQL version: 8.0.35
(1) Lista de Verificação Completa
| Item de Teste | Comando | Resultado Esperado |
|---|---|---|
| Todos os três contêineres em execução | docker ps |
3 contêineres no estado "Up" |
| Página PHP exibe normalmente | curl localhost:8080/info.php |
Retorna informações de configuração PHP |
| Conexão com banco de dados normal | curl localhost:8080/db-test.php |
"Connected successfully" |
| Resolução DNS Entre Contêineres | docker exec lemp-nginx ping -c 2 lemp-php |
Pode ser pingado |
| Volume de dados existe | docker volume ls |
mysql-data está na lista |
8. Validação de Persistência de Dados
▶ Exemplo: Simulando uma falha no contêiner MySQL (Dificuldade: ⭐⭐⭐)
# 1. Criar uma tabela de teste com dados
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e \
"CREATE TABLE test_items (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100)); INSERT INTO test_items (name) VALUES ('Alice'), ('Bob');"
# 2. Verificar se os dados existem
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e \
"SELECT * FROM test_items;"
# 3. Parar e remover o contêiner MySQL
docker stop lemp-mysql && docker rm lemp-mysql
# 4. Recriar contêiner MySQL com o mesmo volume
docker run -d \
--name lemp-mysql \
--network lemp-net \
-e MYSQL_ROOT_PASSWORD=rootsecret \
-e MYSQL_DATABASE=appdb \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=apppass \
-v mysql-data:/var/lib/mysql \
--restart=unless-stopped \
mysql:8.0
# 5. Aguardar o MySQL iniciar e verificar se os dados ainda estão lá
docker exec -it lemp-mysql mysql -uappuser -papppass appdb -e \
"SELECT * FROM test_items;"
+----+-------+
| id | name |
+----+-------+
| 1 | Alice |
| 2 | Bob |
+----+-------+
9. Políticas de Ordenação e Dependência de Contêineres
| Estratégia | Método | Confiabilidade |
|---|---|---|
| Inicialização Sequencial Manual | MySQL → PHP → Nginx | Média (depende da experiência) |
| Verificação de Saúde Pendente | Aguardar MySQL pronto antes de reiniciar PHP | Alta (Automatizada) |
| Estratégia de Reinicialização | --restart=on-failure Tentar automaticamente serviços com falha |
Média (ordem não garantida) |
| Docker Compose | depends_on + Verificações de Saúde (Estudo da Fase 3) |
Alta (Declarativa) |
depends_on do Docker Compose para automatizar o gerenciamento de dependências.
10. Exemplo Completo: Script de Implantação com Um Clique para a Stack LEMP
# ============================================
# Passo a passo completo: Implantar stack LEMP do zero
# Abrange: rede, volume, MySQL, PHP, Nginx
# ============================================
# --- Configuração ---
# Criar rede e volume
docker network create lemp-net
docker volume create mysql-data
# Criar diretório do projeto e arquivos
mkdir -p lemp-project/src
cat > lemp-project/src/index.php << 'PHPEOF'
<?php
$host = 'lemp-mysql';
$db = 'appdb';
$user = 'appuser';
$pass = 'apppass';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
echo "<h1>LEMP Stack is working!</h1>";
echo "<p>MySQL version: " . $pdo->query("SELECT VERSION()")->fetchColumn() . "</p>";
} catch (PDOException $e) {
echo "<h1>Database connection failed</h1><p>" . $e->getMessage() . "</p>";
}
?>
PHPEOF
cat > lemp-project/nginx.conf << 'NGINXEOF'
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass lemp-php:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
NGINXEOF
# --- Implantar ---
# 1. Iniciar MySQL (deve ser primeiro)
docker run -d \
--name lemp-mysql \
--network lemp-net \
-e MYSQL_ROOT_PASSWORD=rootsecret \
-e MYSQL_DATABASE=appdb \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=apppass \
-v mysql-data:/var/lib/mysql \
--restart=unless-stopped \
mysql:8.0
# 2. Aguardar inicialização do MySQL (~30 segundos)
echo "Waiting for MySQL to initialize..."
sleep 30
# 3. Iniciar PHP-FPM
docker run -d \
--name lemp-php \
--network lemp-net \
-v $(pwd)/lemp-project/src:/var/www/html \
--restart=unless-stopped \
php:8.2-fpm
# 4. Iniciar Nginx
docker run -d \
--name lemp-nginx \
--network lemp-net \
-p 8080:80 \
-v $(pwd)/lemp-project/src:/var/www/html \
-v $(pwd)/lemp-project/nginx.conf:/etc/nginx/conf.d/default.conf \
--restart=unless-stopped \
nginx:1.25-alpine
# --- Verificar ---
echo "Verifying deployment..."
docker ps
curl -s http://localhost:8080/index.php
# docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
c1d2e3f4 nginx:1.25-alpine Up 5 min 0.0.0.0:8080->80/tcp lemp-nginx
b2c3d4e5 php:8.2-fpm Up 5 min 9000/tcp lemp-php
a1b2c3d4 mysql:8.0 Up 5 min 3306/tcp, 33060/tcp lemp-mysql
# curl http://localhost:8080/index.php
<h1>LEMP Stack is working!</h1><p>MySQL version: 8.0.35</p>
❓ Perguntas Frequentes
P: Por que estamos iniciando-os separadamente em vez de usar Compose? R: Este é um exercício abrangente para a Fase 1, projetado para ajudar você a entender como cada contêiner é criado e conectado de forma independente. Na Fase 3, você aprenderá sobre Docker Compose, que usa um único arquivo YAML para substituir esses comandos
docker run. Iniciar manualmente antes de passar para a automação ajuda a obter uma compreensão mais profunda.
P: Como o Nginx se conecta ao PHP-FPM? R: Através do protocolo FastCGI. Na configuração do Nginx,
fastcgi_pass lemp-php:9000aponta para o contêiner PHP-FPM.lemp-phpé o nome do contêiner; dentro da mesma rede personalizada, o DNS do Docker resolve automaticamente para o endereço IP do contêiner, então não há necessidade de codificar o endereço IP.
P: Os dados ainda estão lá após a reinicialização do contêiner? R: Dados montados usando um Volume Nomeado (
-v mysql-data:/var/lib/mysql) permanecem mesmo após a exclusão do contêiner. No entanto, o código fonte de um Bind Mount (-v ./src:/var/www/html) é mapeado para um diretório do host; enquanto o diretório do host não for excluído, o código não será perdido.
P: Como descubro qual contêiner está causando o erro? R: Método de solução de problemas em três etapas: ①
docker ps -aVerifique qual status do contêiner não é "Up"; ②docker logs <contêiner>Verifique os logs de erro; ③docker inspect <contêiner> --format='{{.State.ExitCode}}'Verifique o código de saída. Problema comum: erros de conexão PHP ocorrem quando o MySQL não está pronto; simplesmente aguarde o MySQL terminar a inicialização.
P: Como posso visualizar logs de vários contêineres ao mesmo tempo? R:
docker compose logs(Aprendido na Fase 3) permite agregar e visualizar. Atualmente, você pode: ①docker logscada contêiner individualmente; ②docker logs -fabrir várias janelas de terminal para monitorá-los simultaneamente; ③ redirecionar os logs para um arquivo e mesclá-los para visualização.
📖 Resumo
- Stack LEMP = Linux + Nginx + MySQL + PHP, uma arquitetura clássica de aplicação web
- Rede Personalizada
lemp-net: Permite comunicação entre contêineres via resolução DNS usando nomes de contêineres - Volume Nomeado para persistir dados do banco de dados; Bind Mount para sincronizar código de desenvolvimento
- Ordem de inicialização: MySQL (inicializa primeiro) → PHP-FPM → Nginx (expõe a porta por último)
fastcgi_pass lemp-php:9000Implementando Comunicação FastCGI Entre Contêineres- Verificação de persistência de dados: Após excluir o contêiner MySQL e recriá-lo, os dados no Volume Nomeado permanecem intactos.
📝 Exercícios
- Exercício Básico (Dificuldade: ⭐): Implante a stack LEMP do zero e depois visite
http://localhost:8080/index.phppara verificar se os três contêineres estão colaborando corretamente. - Exercício Avançado (Dificuldade: ⭐⭐): Simule uma falha no contêiner MySQL (
docker stop lemp-mysql && docker rm lemp-mysql) e, após reconstruí-lo, verifique se os dados do banco de dados foram persistidos. - Desafio (Dificuldade: ⭐⭐⭐): Adicione um contêiner Adminer (ferramenta de gerenciamento de banco de dados) para conectar ao MySQL e use a interface web para gerenciar o banco de dados. Dica:
docker run -d --name adminer --network lemp-net -p 8081:8080 adminer; visitehttp://localhost:8081e faça login com appuser/apppass.