Развертывание в production
Профессиональное руководство по развертыванию Localzet Server в production окружении.
Подготовка к развертыванию
Системные требования
Минимальные требования:
- PHP >= 8.1
 - Расширения: pcntl, posix, sockets, json
 - Операционная система: Linux (рекомендуется), macOS, Windows
 - RAM: минимум 512MB, рекомендуется 2GB+
 - CPU: минимум 2 ядра, рекомендуется 4+
 
Рекомендуемые расширения:
ext-uvилиext-evилиext-event- для оптимальной производительностиext-redis- для сессий и кешированияopcache- для оптимизации PHP
Установка зависимостей
# Обновление системы
apt-get update && apt-get upgrade -y
# Установка PHP и расширений
apt-get install -y \
    php8.1-cli \
    php8.1-common \
    php8.1-pcntl \
    php8.1-posix \
    php8.1-sockets \
    php8.1-json \
    php8.1-opcache
# Установка рекомендуемых расширений
apt-get install -y \
    php8.1-redis \
    php8.1-mongodb
# Установка libuv для оптимальной производительности
apt-get install -y libuv1-dev
pecl install uv
Структура проекта
Рекомендуемая структура каталогов
/var/www/myapp/
├── app/                    # Код приложения
│   ├── Handlers/           # Обработчики запросов
│   ├── Services/           # Бизнес-логика
│   └── Models/             # Модели данных
├── config/                 # Конфигурация
│   ├── server.php          # Настройки сервера
│   └── database.php        # Настройки БД
├── storage/                # Хранилище
│   ├── logs/               # Логи
│   ├── sessions/           # Сессии (если используется файловое хранилище)
│   └── cache/              # Кеш
├── public/                 # Публичные файлы (если используется Nginx)
├── vendor/                 # Composer зависимости
├── start.php              # Точка входа
└── composer.json
Файл запуска (start.php)
<?php
declare(strict_types=1);
use localzet\Server;
use localzet\ServerAbstract;
use localzet\Server\Connection\TcpConnection;
use localzet\Server\Protocols\Http\Request;
require_once __DIR__ . '/vendor/autoload.php';
// Загрузка конфигурации
$config = require __DIR__ . '/config/server.php';
// Создание сервера
$server = new Server($config['listen'], $config['context'] ?? []);
$server->name = $config['name'] ?? 'MyApp';
$server->count = $config['count'] ?? cpu_count();
$server->user = $config['user'] ?? '';
$server->group = $config['group'] ?? '';
$server->reloadable = $config['reloadable'] ?? true;
// Настройка логирования
Server::$logFile = $config['log_file'] ?? __DIR__ . '/storage/logs/server.log';
Server::$stdoutFile = $config['stdout_file'] ?? __DIR__ . '/storage/logs/stdout.log';
Server::$pidFile = $config['pid_file'] ?? __DIR__ . '/storage/server.pid';
Server::$statusFile = $config['status_file'] ?? __DIR__ . '/storage/status';
// Создание обработчика
$handler = new AppHandler();
// Привязка обработчиков
localzet_bind($server, $handler);
// Запуск
Server::runAll();
Конфигурация
Конфигурационный файл
// config/server.php
return [
    'listen' => 'http://0.0.0.0:8080',
    
    'context' => [
        // SSL настройки (если используется HTTPS)
        'ssl' => [
            'local_cert' => '/etc/ssl/certs/server.pem',
            'local_pk' => '/etc/ssl/private/server.key',
            'verify_peer' => false,
            'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER,
        ],
    ],
    
    'name' => 'MyApp Server',
    'count' => cpu_count(),
    
    // Запуск от непривилегированного пользователя
    'user' => 'www-data',
    'group' => 'www-data',
    
    'reloadable' => true,
    'reusePort' => true, // Для лучшего распределения нагрузки
    
    // Файлы
    'log_file' => __DIR__ . '/../storage/logs/server.log',
    'stdout_file' => __DIR__ . '/../storage/logs/stdout.log',
    'pid_file' => __DIR__ . '/../storage/server.pid',
    'status_file' => __DIR__ . '/../storage/status',
];
Process Management
Systemd Service
Создание systemd unit файла:
# /etc/systemd/system/localzet.service
[Unit]
Description=Localzet Server
After=network.target
[Service]
Type=forking
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php /var/www/myapp/start.php start -d
ExecReload=/bin/kill -USR1 $MAINPID
ExecStop=/bin/kill -SIGINT $MAINPID
Restart=always
RestartSec=5
# Лимиты
LimitNOFILE=65535
LimitNPROC=4096
# Безопасность
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/www/myapp/storage
[Install]
WantedBy=multi-user.target
Управление сервисом:
# Загрузка конфигурации
systemctl daemon-reload
# Запуск
systemctl start localzet
# Остановка
systemctl stop localzet
# Перезапуск
systemctl restart localzet
# Перезагрузка кода (graceful)
systemctl reload localzet
# Статус
systemctl status localzet
# Автозапуск при загрузке
systemctl enable localzet
# Логи
journalctl -u localzet -f
Supervisor
Альтернатива systemd для управления процессами:
# /etc/supervisor/conf.d/localzet.conf
[program:localzet]
command=/usr/bin/php /var/www/myapp/start.php start -d
directory=/var/www/myapp
user=www-data
autostart=true
autorestart=true
startretries=3
stopwaitsecs=10
stdout_logfile=/var/www/myapp/storage/logs/supervisor.log
stderr_logfile=/var/www/myapp/storage/logs/supervisor_error.log
environment=HOME="/var/www/myapp"
Управление:
# Перезагрузка конфигурации
supervisorctl reread
supervisorctl update
# Управление
supervisorctl start localzet
supervisorctl stop localzet
supervisorctl restart localzet
supervisorctl status localzet
Reverse Proxy Setup
Nginx конфигурация
# /etc/nginx/sites-available/myapp
upstream localzet_backend {
    least_conn;
    server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:8081 max_fails=3 fail_timeout=30s backup;
    keepalive 32;
}
# HTTP сервер
server {
    listen 80;
    server_name example.com www.example.com;
    
    # Редирект на HTTPS
    return 301 https://$server_name$request_uri;
}
# HTTPS сервер
server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    
    # SSL сертификаты
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # Оптимизация
    client_max_body_size 10M;
    client_body_buffer_size 128k;
    
    # Проксирование на Localzet Server
    location / {
        proxy_pass http://localzet_backend;
        proxy_http_version 1.1;
        
        # Заголовки
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebSocket поддержка
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # Таймауты
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        
        # Буферизация
        proxy_buffering off;
        proxy_request_buffering off;
    }
    
    # Статические файлы (опционально)
    location /static/ {
        alias /var/www/myapp/public/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}
HAProxy конфигурация
# /etc/haproxy/haproxy.cfg
global
    daemon
    maxconn 4096
defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
frontend http_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/example.com.pem
    redirect scheme https if !{ ssl_fc }
    
    default_backend localzet_backend
backend localzet_backend
    balance leastconn
    option httpchk GET /health
    http-check expect status 200
    
    server server1 127.0.0.1:8080 check inter 2s
    server server2 127.0.0.1:8081 check inter 2s backup
Мониторинг
Health Check Endpoint
$server->onMessage = function(TcpConnection $connection, Request $request) {
    if ($request->path() === '/health') {
        $health = [
            'status' => 'ok',
            'timestamp' => time(),
            'uptime' => time() - $_SERVER['SERVER_START_TIME'],
            'connections' => count($connection->server->connections),
            'memory' => [
                'used' => memory_get_usage(true),
                'peak' => memory_get_peak_usage(true),
            ],
        ];
        
        $connection->send(new Response(200, [
            'Content-Type' => 'application/json'
        ], json_encode($health)));
        return;
    }
    
    // Обработка других запросов...
};
Интеграция с Prometheus
class PrometheusExporter {
    public static function export(): string {
        $stats = ConnectionInterface::$statistics;
        
        $metrics = [];
        
        // Метрики соединений
        $metrics[] = '# HELP localzet_connections_total Total number of connections';
        $metrics[] = '# TYPE localzet_connections_total counter';
        $metrics[] = "localzet_connections_total {$stats['connection_count']}";
        
        // Метрики запросов
        $metrics[] = '# HELP localzet_requests_total Total number of requests';
        $metrics[] = '# TYPE localzet_requests_total counter';
        $metrics[] = "localzet_requests_total {$stats['total_request']}";
        
        // Метрики ошибок
        $metrics[] = '# HELP localzet_errors_total Total number of errors';
        $metrics[] = '# TYPE localzet_errors_total counter';
        $metrics[] = "localzet_errors_total {$stats['throw_exception']}";
        
        return implode("\n", $metrics) . "\n";
    }
}
// Endpoint для Prometheus
$server->onMessage = function($conn, $req) {
    if ($req->path() === '/metrics') {
        $conn->send(PrometheusExporter::export());
    }
};
Логирование
// Настройка ротации логов
class LogRotator {
    public static function rotate(string $logFile): void {
        if (!file_exists($logFile)) {
            return;
        }
        
        $size = filesize($logFile);
        
        // Ротация при достижении 100MB
        if ($size > 100 * 1024 * 1024) {
            $backup = $logFile . '.' . date('Y-m-d');
            rename($logFile, $backup);
            
            // Сжатие старого лога
            if (function_exists('gzencode')) {
                file_put_contents($backup . '.gz', gzencode(file_get_contents($backup)));
                unlink($backup);
            }
            
            // Удаление логов старше 30 дней
            $files = glob($logFile . '.*');
            foreach ($files as $file) {
                if (filemtime($file) < time() - 30 * 86400) {
                    unlink($file);
                }
            }
        }
    }
}
// Периодическая ротация
Timer::repeat(3600.0, function() {
    LogRotator::rotate(Server::$logFile);
});
Безопасность в production
Ограничение доступа
// Whitelist IP адресов
$allowedIPs = [
    '192.168.1.0/24',
    '10.0.0.0/8'
];
$server->onConnect = function(TcpConnection $connection) {
    $ip = $connection->getRemoteIp();
    
    $allowed = false;
    foreach ($allowedIPs as $range) {
        if (ipInRange($ip, $range)) {
            $allowed = true;
            break;
        }
    }
    
    if (!$allowed) {
        Server::log("Blocked connection from: $ip");
        $connection->close();
    }
};
Rate Limiting
// Глобальный rate limiter
class GlobalRateLimiter {
    private static array $clients = [];
    private const MAX_REQUESTS = 100;
    private const WINDOW = 1;
    
    public static function check(string $ip): bool {
        $now = time();
        $window = floor($now / self::WINDOW);
        $key = "$ip:$window";
        
        $count = self::$clients[$key] ?? 0;
        
        if (++$count > self::MAX_REQUESTS) {
            return false;
        }
        
        self::$clients[$key] = $count;
        
        // Очистка старых записей
        foreach (self::$clients as $k => $v) {
            $w = (int)explode(':', $k)[1];
            if ($w < $window - 10) {
                unset(self::$clients[$k]);
            }
        }
        
        return true;
    }
}
$server->onMessage = function($conn, $req) {
    if (!GlobalRateLimiter::check($conn->getRemoteIp())) {
        $conn->close(new Response(429, [], 'Too Many Requests'));
        return;
    }
    
    // Обработка запроса...
};
Защита от DDoS
// Простая защита от DDoS
class DDoSProtection {
    private static array $connections = [];
    private const MAX_CONNECTIONS_PER_IP = 10;
    
    public static function check(string $ip): bool {
        $count = self::$connections[$ip] ?? 0;
        
        if ($count >= self::MAX_CONNECTIONS_PER_IP) {
            return false;
        }
        
        self::$connections[$ip] = $count + 1;
        return true;
    }
    
    public static function release(string $ip): void {
        if (isset(self::$connections[$ip])) {
            self::$connections[$ip]--;
            if (self::$connections[$ip] <= 0) {
                unset(self::$connections[$ip]);
            }
        }
    }
}
$server->onConnect = function($conn) {
    if (!DDoSProtection::check($conn->getRemoteIp())) {
        $conn->close();
        return;
    }
    
    $conn->context->ip = $conn->getRemoteIp();
};
$server->onClose = function($conn) {
    if (isset($conn->context->ip)) {
        DDoSProtection::release($conn->context->ip);
    }
};
Backup и восстановление
Backup конфигурации
#!/bin/bash
# backup.sh
BACKUP_DIR="/var/backups/localzet"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup конфигурации
tar -czf $BACKUP_DIR/config_$DATE.tar.gz \
    /var/www/myapp/config \
    /var/www/myapp/start.php
# Backup логов (опционально)
tar -czf $BACKUP_DIR/logs_$DATE.tar.gz \
    /var/www/myapp/storage/logs
# Удаление старых бэкапов (старше 30 дней)
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
Автоматический backup
# Добавление в crontab
0 2 * * * /var/www/myapp/scripts/backup.sh
Обновление в production
Процедура обновления
#!/bin/bash
# deploy.sh
set -e
echo "Starting deployment..."
# 1. Backup текущей версии
echo "Creating backup..."
./scripts/backup.sh
# 2. Получение обновлений
echo "Pulling updates..."
git pull origin main
# 3. Обновление зависимостей
echo "Updating dependencies..."
composer install --no-dev --optimize-autoloader
# 4. Применение миграций (если есть)
# php artisan migrate
# 5. Очистка кеша
echo "Clearing cache..."
rm -rf storage/cache/*
# 6. Graceful reload
echo "Reloading server..."
php start.php reload -g
echo "Deployment completed!"
Zero-downtime deployment
# Плавное обновление без простоя
php start.php reload -g
# Или через systemd
systemctl reload localzet
Оптимизация для production
PHP настройки
; php.ini оптимизации для production
; Отключение отладочной информации
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/www/myapp/storage/logs/php_errors.log
; Оптимизация OPcache
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0 ; Для production
opcache.revalidate_freq = 0
; Увеличение лимитов
memory_limit = 256M
max_execution_time = 0 ; Без ограничений для long-running
Оптимизация Localzet Server
// Оптимальные настройки для production
TcpConnection::$defaultMaxSendBufferSize = 2097152; // 2MB
TcpConnection::$defaultMaxPackageSize = 10485760;  // 10MB
// Оптимизация кешей
// Автоматически управляется в протоколах
// Выбор оптимального Event Loop
// Автоматически при наличии расширений
Чеклист развертывания
- Системные требования выполнены
 - Все зависимости установлены
 - Конфигурация настроена
 - Логирование настроено
 - Мониторинг настроен
 - Backup процедуры настроены
 - Reverse proxy настроен
 - SSL сертификаты установлены
 - Firewall настроен
 - Rate limiting настроен
 - Process manager настроен (systemd/supervisor)
 - Health checks работают
 - Протестировано под нагрузкой
 - Документация обновлена
 

