Deploy
Este guia cobre as melhores práticas e procedimentos para fazer deploy de aplicações PivotPHP em ambientes de produção.
Requisitos do Servidor
Antes de fazer o deploy, certifique-se de que seu servidor atende a estes requisitos:
- PHP 8.1 ou superior
- Composer 2.0 ou superior
- Servidor Web: Nginx ou Apache
- Banco de Dados: MySQL 5.7+, PostgreSQL 10+, ou SQLite 3.8.8+
- Extensões PHP:
- BCMath
- Ctype
- JSON
- Mbstring
- OpenSSL
- PDO
- Tokenizer
- XML
Otimização
Otimização do Composer
# Instalar dependências sem pacotes de desenvolvimento
composer install --optimize-autoloader --no-dev
# Ou se já estiver instalado
composer dump-autoload --optimize --no-dev
Cache de Configuração
Cache seus arquivos de configuração para melhor performance:
# Cachear configuração
php helix config:cache
# Limpar cache de configuração
php helix config:clear
Cache de Rotas
Cache suas rotas para resolução mais rápida:
# Cachear rotas
php helix route:cache
# Limpar cache de rotas
php helix route:clear
Cache de Views
Se estiver usando um motor de templates:
# Cachear views
php helix view:cache
# Limpar cache de views
php helix view:clear
Configuração do Servidor Web
Configuração do Nginx
server {
listen 80;
listen [::]:80;
server_name exemplo.com www.exemplo.com;
root /var/www/exemplo.com/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
# Cabeçalhos de segurança
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Compressão Gzip
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types application/json application/javascript text/css text/plain;
}
# Configuração SSL
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name exemplo.com www.exemplo.com;
root /var/www/exemplo.com/public;
ssl_certificate /etc/letsencrypt/live/exemplo.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/exemplo.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# ... resto da configuração igual ao acima
}
Configuração do Apache
<VirtualHost *:80>
ServerName exemplo.com
ServerAlias www.exemplo.com
DocumentRoot /var/www/exemplo.com/public
<Directory /var/www/exemplo.com/public>
AllowOverride All
Require all granted
# Habilitar .htaccess
Options -MultiViews -Indexes
RewriteEngine On
# Lidar com cabeçalho de autorização
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirecionar barras finais se não for uma pasta...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Enviar requisições para o controlador frontal...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</Directory>
# Cabeçalhos de segurança
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
ErrorLog ${APACHE_LOG_DIR}/exemplo.com-error.log
CustomLog ${APACHE_LOG_DIR}/exemplo.com-access.log combined
</VirtualHost>
Configuração do Ambiente
Arquivo .env de Produção
APP_NAME="Minha Aplicação"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://exemplo.com
# Segurança
APP_KEY=base64:sua-chave-aleatoria-segura-aqui
# Banco de Dados
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=production_db
DB_USERNAME=db_user
DB_PASSWORD=senha_segura
# Cache
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_DRIVER=redis
# Redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# Email
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=seu-usuario
MAIL_PASSWORD=sua-senha
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@exemplo.com
MAIL_FROM_NAME="${APP_NAME}"
# Logging
LOG_CHANNEL=daily
LOG_LEVEL=error
# Serviços Externos
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
Protegendo Variáveis de Ambiente
# Definir permissões adequadas
chmod 600 .env
# Garantir que .env esteja no .gitignore
echo ".env" >> .gitignore
# Usar variáveis de ambiente do servidor
# No Apache
SetEnv APP_KEY "sua-chave"
# No Nginx (na configuração do pool PHP-FPM)
env[APP_KEY] = "sua-chave"
Deploy do Banco de Dados
Executando Migrações
# Executar migrações
php helix migrate --force
# Com seeding (cuidado em produção!)
php helix migrate --seed --force
# Rollback se necessário
php helix migrate:rollback --force
Otimização do Banco de Dados
-- Exemplo de otimização MySQL
ALTER TABLE users ADD INDEX idx_email (email);
ALTER TABLE posts ADD INDEX idx_user_created (user_id, created_at);
ALTER TABLE sessions ADD INDEX idx_user_id (user_id);
-- Analisar tabelas
ANALYZE TABLE users, posts, comments;
-- Otimizar tabelas
OPTIMIZE TABLE users, posts, comments;
Deploy Sem Downtime
Script de Deploy
#!/bin/bash
# Configuração
APP_DIR="/var/www/exemplo.com"
NEW_RELEASE_DIR="${APP_DIR}/releases/$(date +%Y%m%d%H%M%S)"
SHARED_DIR="${APP_DIR}/shared"
CURRENT_DIR="${APP_DIR}/current"
# Criar diretório da nova release
mkdir -p $NEW_RELEASE_DIR
# Baixar código mais recente
cd $NEW_RELEASE_DIR
git clone https://github.com/seurepositorio/suaapp.git .
# Instalar dependências
composer install --optimize-autoloader --no-dev
# Copiar arquivo de ambiente
cp ${SHARED_DIR}/.env .env
# Criar links para diretórios compartilhados
ln -nfs ${SHARED_DIR}/storage storage
ln -nfs ${SHARED_DIR}/public/uploads public/uploads
# Executar comandos de deploy
php helix migrate --force
php helix config:cache
php helix route:cache
php helix view:cache
# Aquecer cache
php helix cache:warmup
# Trocar symlink atomicamente
ln -nfs $NEW_RELEASE_DIR $CURRENT_DIR
# Recarregar PHP-FPM
sudo service php8.1-fpm reload
# Limpar releases antigas (manter últimas 5)
cd ${APP_DIR}/releases && ls -t | tail -n +6 | xargs rm -rf
echo "Deploy concluído com sucesso!"
Usando Envoy
// Envoy.blade.php
@servers(['web' => 'usuario@exemplo.com'])
@setup
$repository = 'git@github.com:seurepositorio/suaapp.git';
$releases_dir = '/var/www/exemplo.com/releases';
$app_dir = '/var/www/exemplo.com';
$release = date('YmdHis');
$new_release_dir = $releases_dir .'/'. $release;
@endsetup
@task('deploy')
echo 'Clonando repositório'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 --branch master {{ $repository }} {{ $new_release_dir }}
echo 'Instalando dependências do composer'
cd {{ $new_release_dir }}
composer install --prefer-dist --no-dev --optimize-autoloader
echo 'Criando link do arquivo .env'
ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env
echo 'Criando link do diretório storage'
ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage
echo 'Executando migrações'
cd {{ $new_release_dir }}
php helix migrate --force
echo 'Cacheando configuração'
php helix config:cache
php helix route:cache
echo 'Criando link da release atual'
ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
echo 'Reiniciando FPM'
sudo service php8.1-fpm reload
@endtask
Executar deploy:
envoy run deploy
Deploy com Containers
Dockerfile
FROM php:8.1-fpm-alpine
# Instalar dependências
RUN apk add --no-cache \
git \
curl \
libpng-dev \
oniguruma-dev \
libxml2-dev \
zip \
unzip \
nginx \
supervisor
# Instalar extensões PHP
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Instalar Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Definir diretório de trabalho
WORKDIR /var/www
# Copiar arquivos da aplicação
COPY . /var/www
# Instalar dependências da aplicação
RUN composer install --optimize-autoloader --no-dev
# Copiar arquivos de configuração
COPY docker/nginx/nginx.conf /etc/nginx/nginx.conf
COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Definir permissões
RUN chown -R www-data:www-data /var/www
# Expor porta
EXPOSE 80
# Iniciar serviços
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
Docker Compose
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
image: pivotphp-app
container_name: pivotphp-app
restart: unless-stopped
working_dir: /var/www
volumes:
- ./:/var/www
- ./docker/php/php.ini:/usr/local/etc/php/conf.d/app.ini
networks:
- pivotphp
nginx:
image: nginx:alpine
container_name: pivotphp-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./docker/nginx:/etc/nginx/conf.d
networks:
- pivotphp
mysql:
image: mysql:8.0
container_name: pivotphp-mysql
restart: unless-stopped
environment:
MYSQL_DATABASE: pivotphp
MYSQL_ROOT_PASSWORD: secret
MYSQL_PASSWORD: secret
MYSQL_USER: pivotphp
volumes:
- dbdata:/var/lib/mysql
networks:
- pivotphp
redis:
image: redis:alpine
container_name: pivotphp-redis
restart: unless-stopped
networks:
- pivotphp
networks:
pivotphp:
driver: bridge
volumes:
dbdata:
driver: local
Monitoramento
Endpoint de Verificação de Saúde
// routes/web.php
$app->get('/health', function() {
$checks = [
'database' => $this->checkDatabase(),
'cache' => $this->checkCache(),
'queue' => $this->checkQueue(),
'storage' => $this->checkStorage(),
];
$healthy = !in_array(false, $checks);
return response()->json([
'status' => $healthy ? 'healthy' : 'unhealthy',
'checks' => $checks,
'timestamp' => now()->toIso8601String(),
], $healthy ? 200 : 503);
});
Logging
Configure logging adequado para produção:
// config/logging.php
'channels' => [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/helix.log'),
'level' => env('LOG_LEVEL', 'error'),
'days' => 14,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'PivotPHP Log',
'emoji' => ':boom:',
'level' => 'critical',
],
'papertrail' => [
'driver' => 'monolog',
'level' => 'debug',
'handler' => SyslogUdpHandler::class,
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
],
],
],
Otimização de Performance
Configuração do OPcache
; php.ini
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.save_comments=0
Ajuste do PHP-FPM
; /etc/php/8.1/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
Checklist de Segurança
- Desabilitar modo de debug (
APP_DEBUG=false
) - Definir APP_KEY segura
- Configurar HTTPS com certificado SSL válido
- Definir permissões adequadas de arquivo (755 para diretórios, 644 para arquivos)
- Desabilitar listagem de diretórios
- Configurar regras de firewall
- Configurar fail2ban para proteção contra força bruta
- Atualizações regulares de segurança
- Configurar estratégia de backup
- Monitorar logs para atividade suspeita
- Usar senhas fortes no banco de dados
- Restringir acesso do banco de dados apenas ao localhost
- Habilitar logging de consultas para auditoria
- Configurar limitação de taxa
- Configurar monitoramento e alertas