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



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.

BASH
# 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

100%
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.

BASH
# 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.

BASH
# 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


  1. Implantar o contêiner MySQL

▶ Exemplo: Iniciando um contêiner MySQL (Dificuldade: ⭐⭐)

BASH
# 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
💡 Dica: Inicie o MySQL primeiro e aguarde a conclusão da inicialização (cerca de 30 segundos) antes de iniciar outros contêineres. Você pode usar 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

BASH
# 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.

BASH
# 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: ⭐⭐)

BASH
# 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
📌 Ponto-chave: -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.

BASH
# 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
📌 Ponto-chave: 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: ⭐⭐)

BASH
# 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: ⭐)

BASH
# 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
💻 Saída:

TEXT 📖 Somente leitura
# curl http://localhost:8080/db-test.php
Connected to MySQL successfully!
MySQL version: 8.0.35
✅ Ponto de Verificação: Se você vir "Connected to MySQL successfully!", significa que os três contêineres estão colaborando com sucesso: Nginx → PHP-FPM → MySQL.

(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: ⭐⭐⭐)

BASH
# 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;"
💻 Saída:

TEXT 📖 Somente leitura
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
|  2 | Bob   |
+----+-------+
✅ Ponto de Verificação: Os dados permanecem após o contêiner ser excluído e recriado — a persistência do volume nomeado está funcionando.



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)
💡 Dica: Esta aula usa uma sequência de inicialização manual. Na Fase 3, você aprenderá sobre 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

BASH
# ============================================
# 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
💻 Saída (trecho):

TEXT 📖 Somente leitura
# 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:9000 aponta 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 -a Verifique 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 logs cada contêiner individualmente; ② docker logs -f abrir várias janelas de terminal para monitorá-los simultaneamente; ③ redirecionar os logs para um arquivo e mesclá-los para visualização.


📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Implante a stack LEMP do zero e depois visite http://localhost:8080/index.php para verificar se os três contêineres estão colaborando corretamente.
  2. 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.
  3. 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; visite http://localhost:8081 e faça login com appuser/apppass.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%