Pull to refresh
822.32
OTUS
Цифровые навыки от ведущих экспертов

Бессерверный PHP на AWS Lambda

Reading time 5 min
Views 7.5K
Original author: Rob Allen's
Всем привет. Уже в понедельник состоится первое занятие в новой группе курса «Backend разработчик на PHP». В связи с этим мы продолжаем публиковать полезный материал по теме. Начнем.



Как и Саймон Вордли, я считаю, что бессерверные вычисления – это крайне интересная область, в первую очередь из-за гранулированной системы оплаты (платите только тогда, когда выполняется ваш код), и вам не нужно беспокоиться об облуживании и подготовке серверов и контейнеров. Настолько, что я работаю с открытым PHP Runtime для Apache OpenWhisk, коммерческая версия которого доступна как одна из функций IBM Cloud.

Есть и другие бессерверные провайдеры, и AWS Lambda является лидером рынка, однако до недавнего времени поддержка PHP в нем была крайне громоздкой и неказистой. Это изменилось в конце 2018 года с новым runtime API от Lambda и поддержкой слоев.

Давайте посмотрим на практические аспекты бессерверного PHP на Lambda с Serverless Framework.

TL;DR


Исходный код простого Hello World находится в моем lambda-php репозитории на Github. Перейдите в раздел Notes, и мы можем продолжать.

PHP Runtime


API среды выполнения позволяет использовать любой runtime с Lambda. В некотором смысле это похоже на работу OpenWhisk, поскольку между бессерверной платформой и рантаймом есть HTTP API. Есть одно большое отличие, которое заключается в том, что с Lambda рантайм отправляет запрос платформе, чтобы получить данные вызова, тогда как OpenWhisk вызывает конечную точку, которую должен обеспечивать рантайм. Более подробная информация содержится в статье Майкла Муссы в блоге AWS, которая и вдохновила меня на эту работу.

Чтобы приступить к работе, нам понадобится среда выполнения PHP для Lambda. Она будет состоять из исполняемого файла PHP, кода на PHP для вызова бессерверной функции и файла bootstrap, как того требует платформа. Из этих трех вещей мы собираем слой. Слои могут переиспользоваться в разных аккаунтах, поэтому я удивлен, что AWS не предоставляет нам аккаунт PHP. Невероятно, но факт, они не используют PHP 7.3, поэтому нам придется построить свой собственный.
Все файлы мы положим в директорию layer/php в нашем проекте.

Построение исполняемого файла PHP


Нам нужен исполняемый файл PHP, который будет запускаться внутри контейнеров Lambda. Самый простой способ это сделать – это скомпилировать его на той же платформе, что и Lambda, поэтому мы будем использовать ЕС2. Статья Майкла поясняет как это сделать, и эти команды я обернул в скрипт compile_php.sh, чтобы потом скопировать его в экземпляр ЕС2, запустить и скопировать исполняемый файл обратно на свой компьютер:

$ export AWS_IP=ec2-user@{ipaddress}
$ export SSH_KEY_FILE=~/.ssh/aws-key.rsa
 
$ scp -i $SSH_KEY_FILE compile_php.sh $AWS_IP:doc/compile_php.sh
$ ssh -i $SSH_KEY_FILE -t $AWS_IP "chmod a+x compile_php.sh && ./compile_php.sh 7.3.0"
$ scp -i $SSH_KEY_FILE $AWS_IP:php-7-bin/bin/php layer/php/php


Этот подход сделает его хорошо воспроизводимым, и, надеюсь, его будет просто обновлять до новых версий PHP.

Bootstrapping


Поскольку мы используем runtime API, нам понадобится bootstrap файл. Такое имя задавать файла требует сама Lambda, он отвечает для вызов функции, соответствующим образов вызывая API в цикле.

По сути нам нужно находиться в цикле и вызывать конечную точку /next, чтобы понимать, что вызывать следующим, вызвать ее, а затем отправить ответ в конечную точку /response.
AWS предоставляет пример в BASH с использованием curl:

while true
do
  HEADERS="$(mktemp)"
  # Get an event
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
 
  # Execute the handler function from the script
  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
 
  # Send the response
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
done


Мы хотим сделать то же самое в PHP, и хотя я мог бы написать это самостоятельно, но Парикшит Агнихотри уже опередил меня в PHP-Lambda-Runtime/runtime.php, поэтому мы просто скопируем это в layer/php/runtime.php. В своей версии я сделал несколько изменений, добавил json_encoding и улучшил обработчик ошибок.
Файл layer/php/bootstrap очень простой, и все что от него требуется, это запускать исполняемый файл PHP с этим файлом:

#!/bin/sh
cd $LAMBDA_TASK_ROOT
/opt/php /opt/runtime.php


Вот и все. Теперь у нас есть три файла в layer/php:

  • php – исполняемый файл PHP;
  • runtime.php – Рабочий файл runtime API;
  • bootstrap – требуемый Lambda, файл.


В итоге все это станет PHP Layer (слоем) в нашем Lambda приложении.

Настройка Serverless Framework


Serverless Framework обеспечивает повторяемую конфигурацию и разворачивание бессерверного приложения. Я поклонник этой концепции и мне хочется использовать больше таких инструментов. Мы будем использовать Serverless Framework для нашего PHP Hello World.
Поскольку удобного шаблона для приложений на PHP в Serverless Framework нет, мы просто создадим файл serverless.yml в каталоге с нашим проектом.
Для начала, самое базовое:

service: php-hello-world
provider:
  name: aws
  runtime: provided
  region: eu-west-2
  memorySize: 128


Мы назовем наше приложение php-hello-world и используем AWS в качестве провайдера. Поскольку я в Великобритании, я установил регион Лондон. Нам не нужно много памяти, поэтому 128 МБ будет достаточно.
Runtime обычно является языком, на котором вы хотите, чтобы ваша функция выполнялась. Чтобы использовать runtime API, который будет выполнять наш bootstrap файл, вы установите это поле в provided.
А еще вам понадобится .gitignore файл, содержащий:

.serverless


Поскольку в git нам эта директория не нужна.
Дальше давайте добавим наш слой к serverless.yml, добавив:

layers:
  php:
    path: layer/php


Это создаст AWS слой и даст ему имя PhpLambdaLayer, на которое мы сможем ссылаться в нашей функции.

Напишем функцию Hello World
Теперь мы можем написать нашу бессерверную PHP-функцию. Это необходимо вписать в файл handler.php : 

<?php
function hello($eventData) : array
{
    return ["msg" => "hello from PHP " . PHP_VERSION];
}


Функция берет информацию о событии и возвращает ассоциативный массив.
Чтобы сообщить Serverless Framework о развертывании нашей функции, нужно добавить следующее в файл serverless.yml:

functions:
  hello:
    handler: handler.hello
    layers:
      - {Ref: PhpLambdaLayer}


Serverless Framework поддерживает несколько функций для одного приложения. Каждая из них имеет имя , в данном случае hello , и, в нашем случае, обработчик, который представляет собой имя файла без расширения, за которым следует точка, а затем имя функции в этом файле. Таким образом, обработчик handler.hello означает, что мы запустим функцию hello() в handler.php .
Наконец, мы также сообщаем функции о нашем слое PHP, чтобы он мог выполнять код на PHP.

Развертывание в Lambda


Чтобы развернуть нашу функцию с её слоем, мы запускаем следующую команду:

$ sls deploy


При возможно продолжительном выполнении команды будет получен вывод, похожий на этот:



Выполнение нашей функции


После развертывания, мы можем вызвать функцию используя команду:

$ sls invoke -f hello -l




И все готово!

Подведем итоги


Благодаря новым слоям и API среды выполнения теперь можно легко запускать бессерверные функции PHP в Lambda. Это хорошая новость для PHP разработчиков, привязанных к AWS.

Ждем ваши комментарии, друзья!
Tags:
Hubs:
+12
Comments 12
Comments Comments 12

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS