Представляем новый бессерверный LAMP-стек

Оригинал статьи: ссылка (Benjamin Smith, Senior Developer Advocate)

Это первая статья из серии, предназначенной для PHP-разработчиков. В этой серии мы расскажем о том, как использовать бессерверные технологии вместе с PHP. Мы рассмотрим доступные инструменты, фреймворки и стратегии для создания бессерверных приложений, а также объясним, почему сейчас самое подходящее время, чтобы начать использовать бессерверные архитектуры.

В следующих постах мы продемонстрируем, как использовать AWS Lambda для запуска веб-приложений, созданных с использованием таких фреймворков, как Laravel and Symfony. Мы также покажем, как перейти от использования Lambda в качестве обычной замены для веб-хостинга к децентрализованной архитектуре, основанной на событиях. Кроме того, мы рассмотрим, как объединить несколько Lambda-функций, имеющих ограниченные зоны ответственности, с другими бессерверными технологиями для создания высокопроизводительных масштабируемых микросервисов.

В этой статье вы узнаете, как использовать PHP в Lambda-фукциях с помощью собственной среды выполнения кода (custom runtime API). Исходный код примеров вы можете найти в GitHub-репозитории.

Бессерверный LAMP-стек

Проблемы с традиционными приложениями на PHP

Масштабируемость является неизбежной проблемой традиционного LAMP-стека. Под масштабируемым приложением мы понимаем такое приложение, которое может обрабатывать сильно меняющиеся уровни пользовательского трафика. Часто PHP-приложения масштабируются горизонтально путём добавления веб-серверов по мере необходимости. Управление трафиком происходит с помощью балансировщика нагрузки, который перенаправляет запросы на разные серверы. Каждый новый сервер приводит к дополнительным накладным расходам, связанным с сетью, администрированием, объёмом хранилища, системами резервного копирования и восстановления, а также необходимостью добавления каждого сервера в систему по управлению активами (asset management). Кроме того, каждый новый сервер работает независимо от других, что может привести к проблемам с синхронизацией конфигурации между ними.

Горизонтальное масштабирование приложений в традиционном LAMP-стеке

Дополнительные проблемы возникают с хранением данных, так как каждый сервер использует свои диски и файловую систему, что часто требует от разработчиков добавления специального механизма для обработки пользовательских сессий. При использовании бессерверных технологий разработчику нет необходимости самостоятельно управлять всеми аспектами масштабируемости.

При всплеске трафика сервисы автоматически масштабируются для удовлетворения спроса без необходимости установки новых серверов. Это позволяет приложениям быстро проходить путь от прототипа к выпуску в производственную среду.

Бессерверная архитектура LAMP

Традиционное веб-приложение можно разделить на две части:

Статические ресурсы (медиа-файлы, CSS, JS)

Динамическое приложение (PHP, MySQL)

Бессерверный подход к работе с этими частями изображён ниже:

Бессерверный LAMP-стек

Все запросы на динамические ресурсы (то есть, всё, кроме пути /assets/*) перенаправляются в Amazon API Gateway. Это полностью управляемый сервис для создания, публикации и защиты API любого масштаба. Он действует как «входная дверь» в PHP-приложение, направляя запросы к соответствующим Lambda-функциям. В свою очередь, Lambda-функции содержат бизнес-логику приложения и осуществляют работу с базой данных MySQL. В качестве входных данных в Lamda-функцию вы можете передавать любые комбинации заголовков запросов, переменных пути, параметров строки запроса и тела запроса.

Важные возможности AWS для PHP-разработчиков

Amazon Aurora Serverless

Во время re:Invent 2017 компания AWS анонсировала Aurora Serverless, бессерверную реляционную базу данных с оплатой по факту использования. Этот сервис управляет созданием базы данных, а также её масштабированием.

Слои Lambda и собственные среды выполнения кода

Во время re:Invent 2018 компания AWS анонсировала две новые возможности Lambda. Они позволяют разработчикам создавать свои среды выполнения кода, а также обмениваться и управлять исходным кодом, который используется несколькими функциями.

Улучшенная работа Lambda-функций внутри VPC

В сентябре 2019 года компания AWS анонсировала значительные улучшения во времени холодного запуска Lambda-функций внутри VPC. Это приводит к более быстрому запуску функций и более эффективному использованию эластичных сетевых интерфейсов, снижая время холодного запуска внутри VPC.

Amazon RDS Proxy

Во время re:Invent 2019 компания AWS анонсировала запуск нового сервиса под названием Amazon RDS Proxy. Это полностью управляемый прокси, который находится между вашим приложением и реляционной базой данных. Он объединяет подключения к базе данных в пулы и позволяет использовать их совместно для повышения масштабируемости вашего приложения.

Важные моменты на временной шкале бессерверного LAMP-стека

Путём комбинации этих сервисов можно создать безопасные, производительные и масштабируемые бессерверные приложения с использованием PHP и реляционных баз данных.

Собственные среды выполнения кода

Собственные среды выполнения кода предлагают простой интерфейс, который позволяет запускать Lambda-функции с любой версией любого языка программирования. Для использования собственных сред выполнения кода требуется создать запускаемый текстовый файл, называющийся bootstrap. Этот файл отвечает за взаимодействие между вашим исходным кодом и средой Lambda.

Важные моменты на временной шкале бессерверного LAMP-стека

Также для создания среды выполнения вам необходимо собрать требуемую версию PHP под операционную систему Amazon Linux совместимой с Lambda версии. Чтобы это сделать, следуйте пошаговым инструкциям в GitHub.

Файл bootstrap

Ниже представлен пример простого файла boostrap для PHP. Он предназначен только для объяснения принципа работы и не покрывает обработку ошибок или дополнительные абстракции. Чтобы удостовериться, что вы корректно обрабатываете исключения, во время создания своей среды выполнения обратитесь к документации по API.

#!/opt/bin/php
<?PHP

// This invokes Composer's autoloader so that we'll be able to use Guzzle and any other 3rd party libraries we need.
require __DIR__ . '/vendor/autoload.php;

// This is the request processing loop. Barring unrecoverable failure, this loop runs until the environment shuts down.
do {
// Ask the runtime API for a request to handle.
$request = getNextRequest();

// Obtain the function name from the _HANDLER environment variable and ensure the function's code is available.
$handlerFunction = array_slice(explode('.', $_ENV['_HANDLER']), -1)[0];
require_once $_ENV['LAMBDA_TASK_ROOT'] . '/src/' . $handlerFunction . '.php;

// Execute the desired function and obtain the response.
$response = $handlerFunction($request['payload']);

// Submit the response back to the runtime API.
sendResponse($request['invocationId'], $response);
} while (true);

function getNextRequest()
{
$client = new \GuzzleHttp\Client();
$response = $client->get('http://' . $_ENV['AWS_LAMBDA_RUNTIME_API'] . '/2018-06-01/runtime/invocation/next');

return [
'invocationId' => $response->getHeader('Lambda-Runtime-Aws-Request-Id')[0],
'payload' => json_decode((string) $response->getBody(), true)
];
}

function sendResponse($invocationId, $response)
{
$client = new \GuzzleHttp\Client();
$client->post(
'http://' . $_ENV['AWS_LAMBDA_RUNTIME_API'] . '/2018-06-01/runtime/invocation/' . $invocationId . '/response',
['body' => $response]
);
}

Декларация #!/opt/bin/php сообщает загрузчику о необходимости использовать бинарный файл PHP, скомпилированный для Amazon Linux.

Файл bootstrap в рабочем цикле производит следующие действия:

Получает следующий запрос.

Выполняет исходный код для обработки запроса.

Возвращает ответ.

Следуйте шагам по ссылке для упаковки файла bootstrap и скомпилированного бинарного файла PHP в архив с названием `runtime.zip`.

Библиотеки и зависимости

В файле bootstrap используется локальный интерфейс на основе HTTP. С помощью него можно получить данные о событии, которое было передано в Lambda-функцию, а также вернуть ответ, полученный от неё. В нашем случае для выполнения запросов к указанному API-интерфейсу используется Guzzle, популярный HTTP-клиент для PHP. Пакет Guzzle устанавливается с помощью менеджера пакетов Composer. Такой способ установки позволяет простым образом добавлять новые библиотеки и зависимости по мере развития приложения.

Следуйте указанным шагам для создания и упаковки зависимостей в файл `vendors.zip`.

Слои Lambda предоставляют механизм для централизованного управления исходным кодом и данными, которые используются несколькими Lambda-функциями. Когда Lambda-функция настроена с использованием слоя, его содержимое загружается в директорию /opt среды выполнения. Вы можете включить собственную среду выполнения кода либо в установочный пакет вашей функции, либо в отдельный слой. Lambda выполняет файл bootstrap, если он есть в вашем пакете. Если же нет, то Lambda будет искать среду выполнения в настроенных в функции слоях. В настоящее время есть несколько слоёв для запуска PHP с открытым исходным кодом, в первую очередь:

Bref -Serverless PHP on AWS Lambda

Stackery – PHP Lambda layer

Шаги ниже показывают, как опубликовать созданные ранее файлы `runtime.zip` и `vendor.zip` в слои Lambda, а также как их потом использовать для создания Lambda-функции на языке PHP:

Используйте интерфейс командной строки AWS (AWS CLI) для публикации слоёв на основе ранее созданных файлов
aws lambda publish-layer-version \
--layer-name PHP-example-runtime \
--zip-file fileb://runtime.zip \
--region eu-west-1

aws lambda publish-layer-version \
--layer-name PHP-example-vendor \
--zip-file fileb://vendors.zip \
--region eu-west-1

Запомните значение поля LayerVersionArn в выводе каждой команды (например, arn:aws:lambda:eu-west-1:XXXXXXXXXXXX:layer:PHP-example-runtime:1), он вам понадобится через несколько шагов.

Создание Lambda-функции на PHP

Вы можете создать Lambda-функцию с помощью AWS CLI, AWS Serverless Application Model (SAM) или напрямую в Консоли управления AWS. В случае использования консоли, следуйте указанным ниже шагам:

Перейдите в секцию Lambda в консоли AWS и нажмите Create function.

Введите “PHPHello” в поле Function name и выберите Provide your own bootstrap в поле Runtime. Затем нажмите Create function.

Нажмите правой кнопкой мыши по файлу bootstrap.sample и выберите Delete.

Нажмите на иконку Layers, а затем на кнопку Add a layer.

Выберите опцию Provide a layer version ARN, затем введите в поле Layer version ARN значение ARN слоя среды выполнения кода, который был создан в предыдущем разделе статьи.

Повторите шаг 5 для ARN второго слоя (PHP-example-vendor).

В секции Function Code создайте директорию с названием src, а внутри неё файл index.php.

Вставьте следующий код в файл index.php:
//index function
function index($data)
{
return "Hello, ". $data['name'];
}

В поле Handler введите “index”. Таким образом вы указываете Lambda, что во время вызова необходимо запускать функцию index.

Нажмите Save в правом верхнем углу страницы.

Нажмите кнопку Test в правом верхнем углу страницы, затем введите “PHPTest” в поле Event name. Введите следующий текст в основное поле для параметров теста и нажмите Create: {"name": "world"}

Нажмите Test, затем раскройте выпадающий список Details рядом с заголовком execution result.

Вы можете увидеть, что значение параметра “name” используется для вывода “Hello, world”. Само значение берётся из параметра $data['name'], который передаётся в Lambda-функцию. Вывод в секции Log output содержит информацию о фактической продолжительности запуска, оплачиваемой продолжительности и объёме оперативной памяти, который потребовался для запуска кода.

Заключение

В этой статье мы рассмотрели создание и запуск Lambda-функции со средой выполнения PHP с использованием слоёв Lambda и собственных сред выполнения кода. Мы также показали архитектуру бессерверного LAMP-стека, который масштабируется в зависимости от объёма трафика к приложению.

Lambda позволяет функциям, использующим разные среды выполнения, взаимодействовать друг с другом. Теперь PHP-разработчики могут присоединиться к другим командам, использующим бессерверные технологии при разработке. Это позволяет фокусироваться на исходном коде и не думать об управлении веб-серверами, их масштабировании и хостинге.