Serverless функции на PHP: AWS Lambda — от теории к продвинутой практике
Serverless-архитектура давно перестала быть экзотикой, а PHP, благодаря таким инструментам, как Bref и Laravel Vapor, уверенно занял свою нишу в мире бессерверных вычислений. В этой статье мы разберем продвинутые техники работы с AWS Lambda на PHP: как победить холодный старт, настроить работу внутри VPC и эффективно обрабатывать события от S3 и SQS. Материал рассчитан на разработчиков, уже знакомых с основами деплоя PHP-приложений на Lambda.
1. Оптимизация холодного старта: от 2 секунд к 200 миллисекундам
Главный враг serverless приложений на PHP — холодный старт. В отличие от Node.js или Python, PHP требует загрузки интерпретатора и фреймворка. Вот как минимизировать задержки.
1.1. Использование Bref и предварительно скомпилированных слоев
Bref — это де-факто стандарт для запуска PHP на Lambda. Он предоставляет готовые слои (Layers) с PHP-интерпретатором. Для ускорения используйте bref/extra-php-extensions и подключайте только необходимые расширения.
Пример базовой конфигурации serverless.yml:
service: my-php-app
provider:
name: aws
runtime: provided.al2
region: eu-central-1
plugins:
- ./vendor/bref/bref
functions:
api:
handler: public/index.php
layers:
- ${bref:layer.php-81}
events:
- httpApi: '*'
1.2. Ленивая загрузка и кэширование конфигураций
Не загружайте весь фреймворк (Laravel, Symfony) при каждом запросе. Используйте Laravel Octane с Swoole или RoadRunner, но для Lambda лучше подойдет Bref Laravel Bridge, который кэширует конфиги и маршруты.
Код для кэширования конфигурации в bootstrap.php:
// public/index.php (точка входа для Lambda)
require_once __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
// Кэшируем конфигурацию при первом запуске
if (! file_exists(__DIR__ . '/../bootstrap/cache/config.php')) {
$app->make('config')->get('app');
// Сохраняем скомпилированный конфиг
file_put_contents(
__DIR__ . '/../bootstrap/cache/config.php',
'<?php return ' . var_export($app->make('config')->all(), true) . ';'
);
} else {
$config = require __DIR__ . '/../bootstrap/cache/config.php';
$app->instance('config', $config);
}
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
Совет: Установите memorySize в 1024 MB и timeout в 30 секунд для баланса скорости и стоимости. Больше памяти — быстрее процессор.
2. Работа с VPC: подключение к RDS и ElastiCache
Lambda-функции по умолчанию работают вне вашей VPC. Для доступа к базам данных (RDS, Aurora) или кэшу (ElastiCache) необходимо разместить функцию внутри VPC. Это добавляет сложность и увеличивает холодный старт на 5-10 секунд.
2.1. Настройка VPC в serverless.yml
functions:
processUser:
handler: src/handlers/userHandler.php
layers:
- ${bref:layer.php-81}
vpc:
securityGroupIds:
- sg-0123456789abcdef0
subnetIds:
- subnet-abc123
- subnet-def456
events:
- sqs:
arn: arn:aws:sqs:eu-central-1:123456789012:user-queue
2.2. Управление подключениями через PDO
Используйте пул соединений RDS Proxy или ElastiCache для многократного использования соединений внутри одного контейнера Lambda.
Пример класса для работы с БД с повторным использованием соединения:
class DatabaseConnection
{
private static ?\PDO $instance = null;
public static function getConnection(): \PDO
{
if (self::$instance === null) {
$host = getenv('DB_HOST');
$dbname = getenv('DB_NAME');
$username = getenv('DB_USER');
$password = getenv('DB_PASS');
self::$instance = new \PDO(
"mysql:host={$host};dbname={$dbname};charset=utf8mb4",
$username,
$password,
[
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
]
);
}
return self::$instance;
}
}
Важно: В VPC Lambda не имеет доступа к интернету по умолчанию. Если нужен внешний API — настройте NAT Gateway или используйте VPC Endpoints для S3 и DynamoDB.
3. Обработка событий S3 и SQS: асинхронные пайплайны
Serverless-функции идеальны для событийно-ориентированных архитектур. Рассмотрим два частых сценария.
3.1. Триггер на загрузку файла в S3
Допустим, нужно создавать превью изображений при загрузке в бакет.
Код обработчика:
// src/handlers/imageResizer.php
require_once __DIR__ . '/../../vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Lambda\LambdaClient;
function handler(array $event): void
{
$s3 = new S3Client([
'region' => getenv('AWS_REGION'),
'version' => 'latest',
]);
foreach ($event['Records'] as $record) {
$bucket = $record['s3']['bucket']['name'];
$key = rawurldecode($record['s3']['object']['key']);
// Получаем оригинал