Capítulo 19: Conteinerização com Laravel Sail e Deploy com Octane
Especialização em Backend com PHP & Laravel • Laravel 11 & PHP 8.3+ • Eloquent ORM, Sanctum, Filas, Horizon e APIs Corporativas
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Dev local: Laravel Sail (Docker Compose)"] --> B["PHP 8.3 + PostgreSQL + Redis"]
B --> C["Código idêntico ao de produção"]
C --> D["Deploy: Laravel Octane (Swoole/FrankenPHP)"]
D --> E["Worker mantido em memória"]
E --> F["Milhares de req/s sem reboot do framework"]
subgraph RISCO["Cuidado"]
G["Vazamento de estado entre requisições"]
end
E -.-> RISCO
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. Laravel Sail: Ambiente de Desenvolvimento Padronizado
O Laravel Sail é uma CLI leve sobre o Docker Compose oficial do Laravel: provisiona PHP, banco de dados, Redis e Mailpit sem exigir instalação local de nenhuma dessas dependências. O ganho real não é conveniência individual, mas paridade de ambiente: todo desenvolvedor do time roda exatamente a mesma versão de PHP e das extensões, eliminando o clássico “na minha máquina funciona”.
# docker-compose.yml (gerado pelo Sail)
services:
laravel.test:
image: sail-8.3/app
ports:
- '${APP_PORT:-80}:80'
environment:
WWWUSER: '${WWWUSER}'
LARAVEL_SAIL: 1
depends_on:
- pgsql
- redis
pgsql:
image: postgres:16-alpine
volumes:
- sail-pgsql:/var/lib/postgresql/data
redis:
image: redis:7-alpine
sail up -d # sobe os contêineres em background
sail artisan migrate # executa Artisan dentro do contêiner PHP
sail composer install
🚀 2. Laravel Octane: Eliminando o Custo de Bootstrap
Em PHP-FPM tradicional, cada requisição reinicia o framework do zero: autoload, resolução de todos os Service Providers, novo container de injeção de dependências. O Laravel Octane roda sobre servidores assíncronos (Swoole, RoadRunner ou FrankenPHP) que mantêm a aplicação carregada em memória entre requisições — um “worker” processa requisição após requisição sem reboot, o que pode multiplicar o throughput por 5-10x em benchmarks.
// config/octane.php
return [
'server' => env('OCTANE_SERVER', 'frankenphp'),
'workers' => env('OCTANE_WORKERS', 'auto'),
'max_requests' => 500, // recicla o worker periodicamente como precaução
];
php artisan octane:start --server=frankenphp --workers=4
⚠️ 3. O Preço da Performance: Vazamento de Estado
Como a mesma instância de classes (incluindo Singletons registrados no Container) persiste entre requisições de usuários diferentes, qualquer estado mutável guardado em propriedade estática ou em Singleton vaza de um usuário para o próximo — um bug praticamente impossível de acontecer no modelo tradicional stateless do PHP-FPM.
<?php
// app/Providers/AppServiceProvider.php
use Laravel\Octane\Events\RequestReceived;
use Illuminate\Support\Facades\Event;
Event::listen(RequestReceived::class, function () {
// Reseta qualquer estado acumulado (ex: buffers de log, cache local)
// antes de processar a próxima requisição no mesmo worker.
});
Boas práticas: preferir injeção de dependência escopada por requisição em vez de Singletons mutáveis, nunca guardar dados de usuário em propriedades estáticas, e revisar bibliotecas de terceiros que assumem uma vida útil “por requisição” (algumas mantêm cache interno em atributos de classe).
🐳 4. Imagem de Produção Multi-stage
Diferente do Sail (feito para desenvolvimento), a imagem de produção deve ser mínima, com apenas as extensões necessárias e OPcache habilitado:
# Dockerfile.prod
FROM php:8.3-fpm-alpine AS base
RUN docker-php-ext-install pdo pdo_pgsql opcache
COPY docker/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
FROM base AS app
WORKDIR /var/www/html
COPY . .
RUN composer install --no-dev --optimize-autoloader \
&& php artisan config:cache
EXPOSE 9000
CMD ["php-fpm"]