Давайте разместим PHP-приложение прямо в бессерверном облаке

AWS Lambda и бессерверный подход могут дать широкий спектр преимуществ. К сожалению, Lambda не имеет встроенной поддержки PHP, и запускать здесь PHP-приложения не так-то просто. Одним из крупных анонсов AWS re: Invent 2020 стала поддержка образов контейнера Lambda. Это создает огромные возможности, и с этого момента мы легко перемещаем любой технологический стек, который пожелаем, прямо в бессерверное облако.

Предположим, у нас есть PHP REST API с относительно низким средним использованием и высокими пиками в непредсказуемые промежутки времени. Перемещение его на лямбда-выражение обеспечит одновременное выполнение и позволит масштабировать по мере необходимости с чрезвычайно низкими затратами. Приложение основано на PHP 8 и использует Nginx в качестве веб-сервера. Ключевые драйверы миграции - выпустить его как можно быстрее и обеспечить среду выполнения, аналогичную устаревшей, с небольшими изменениями исходного кода или без них. Благодаря поддержке образов контейнера мы можем обернуть все в образ Docker и развернуть его непосредственно в Lambda. Посмотрим, как это можно сделать.

Полный исходный код доступен здесь:



AWS предоставляет базовые образы для всех поддерживаемых языков (например, Python, GO и Python), а также некоторые для использования настраиваемой среды выполнения. Мы также могли бы использовать наш собственный образ на основе Linux, но это потребует некоторой дополнительной работы, и мы хотим быть готовыми к работе как можно быстрее. Мы решили использовать provided:al2, то есть Amazon Linux с некоторыми встроенными функциями, чтобы упростить разработку.

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

Нам нужно предоставить файл с именем bootstrap, помещенный в каталог var/runtime, который запустит наше приложение. Этот скрипт извлечет и обработает последнее событие, отправленное в Lambda с помощью Lambda Runtime API. Каждое событие будет перенаправлено как HTTP-запрос нашему REST PHP API, работающему локально.

Мы используем приложение Lumen с двумя конечными точками: /get и /post. Каждый из них возвращает значения HTTP GET и POST из запроса.

Теперь все будет работать без сбоев на нашей локальной машине, но поскольку мы работаем с Lambda, файловая система в производственной среде будет доступна только для чтения, и мы сможем писать только в каталог /tmp. Это не лучшее место для хранения журналов, поскольку /tmp будет очищаться после каждого вызова. Мы перенаправим их на STDOUT и STDERR, чтобы все журналы автоматически отображались в CloudWatch и упростили отладку.

Теперь мы можем протестировать все локально и эмулировать вызов Lambda перед выпуском.

// build the image
docker build -t php-lambda-dev .
// start the container
docker run --rm -p 127.0.0.1:9000:8080 --name php-lambda-dev php-lambda-dev
// send test events (we prepared two sample ones)
curl -XPOST "http://127.0.0.1:9000/2015-03-31/functions/function/invocations" -d @events/APIGatewayGET.json
curl -XPOST "http://127.0.0.1:9000/2015-03-31/functions/function/invocations" -d @events/APIGatewayPOST.json

Теперь нам нужно сохранить наше изображение в репозитории ECR. Это можно сделать с помощью нескольких простых команд:

// create repository (provide any name you like)
aws ecr create-repository --repository-name REPOSITORY_NAME --image-scanning-configuration scanOnPush=true
// tag image (REPOSITORY_URL is return by previous command as "repositoryUri")
docker tag php-lambda-dev:latest REPOSITORY_URL
// log into ECR
aws ecr get-login-password --region eu-west-1 | docker login --
username AWS --password-stdin REPOSITORY_URL
// push the image
docker push REPOSITORY_URL:latest

Теперь мы готовы развернуть приложение и поставить его за API Gateway. Мы будем использовать простой скрипт Cloudformation для автоматизации процесса.

Теперь у нас есть только одна команда для тестирования всего этого в облаке.

// use created ECR repository url
aws cloudformation create-stack --stack-name "SOME_STACK_NAME" --template-body file://infrastructure/stack.yml --capabilities CAPABILITY_IAM --parameters ParameterKey=lambdaImageUrl,ParameterValue="REPOSITORY_URL:latest"

Создание стека может занять несколько минут, и мы сможем увидеть, как он работает. Недавно созданный URL-адрес шлюза API можно найти в выходных данных стека.

curl API_GATEWAY_URL/get?foo=bar
{
   "version":"Lumen (8.2.1) (Laravel Components ^8.0)",
   "POST":[
   ],
   "GET":{
      "foo":"bar"
   }
}
curl -XPOST API_GATEWAY_URL/post -d '{"foo":"bar"}'
{
   "version":"Lumen (8.2.1) (Laravel Components ^8.0)",
   "POST":{
      "foo":"bar"
   },
   "GET":[
      
   ]
}

Здорово! Мы только что переместили наше PHP-приложение на Lambda и можем воспользоваться всеми его преимуществами. Для PHP есть еще несколько решений промышленного уровня. Также можно использовать сервис под названием Vapor для приложений Laravel или более универсальных приложений, таких как Bref.

Код был написан для демонстрации и обучения и не предназначен для производственного использования.

Ресурсы: