Блог Amazon Web Services

Представляем новый бессерверный LAMP-стек, часть 3: Заменяем веб-сервер

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

Из этой статьи вы узнаете, как создавать приложения на PHP без необходимости использования веб-сервера.

Также в этой статье Matthieu Napoli, создатель Bref и Serverless Visually Explained, расскажет о том, как FastCGI Process Manager в Lambda помогает с этой задачей. Bref – это среда выполнения PHP в Lambda с открытым исходным кодом.

Я покажу, как настроить Amazon CloudFront, чтобы безопасно обслуживать запросы пользователей и кэшировать статические файлы из приватного бакета Amazon S3. Динамические запросы направляются в Amazon API Gateway, а из него – в функцию AWS Lambda.

Комбинация этих сервисов используется для замены традиционного веб-сервера в приложениях на PHP.

Пример кода вы можете найти в GitHub-репозитории.

Архитектура бессерверного LAMP-стека

Мы начали рассмотрение такой архитектуры для LAMP-стека в этой статье. Веб-приложение разделено на две части (статические файлы и бэкенд-приложение, которое генерирует динамический контент). Lambda-функция содержит бизнес-логику приложения и отвечает за работу с базой данных MySQL. Каждый результат её выполнения синхронно возвращается через API Gateway.

Обработка запросов с помощью API Gateway

Бессерверный LAMP-стек не использует HTTP-сервер. Вместо этого API Gateway заменяет механизм обработки запросов, реализованный в Apache или NGINX. AWS Serverless Application Model (AWS SAM) используется для настройки правил перенаправления.

      Events:
        DynamicRequestsRoot:
          Type: HttpApi
          Properties:
            Path: /
            Method: ANY
        DynamicRequestsProxy:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY

Шаблон AWS SAM для перенаправления всех входящих запросов из HTTP API в одну Lambda-функцию.

Шаблон выше создаёт HTTP API с правилом, которое срабатывает для всех входящих запросов. Контекст запроса передаётся дальше в Lambda-функцию. Такое поведение похоже на работу MVC фреймворка в PHP –  он перенаправляет все запросы в файл index.php, который их обрабатывает. Ниже показано, как это настраивается в традиционном LAMP-стеке с использованием веб-сервера и файла конфигурации .htaccess:

Alias /yourdir /var/www/html/yourdir/public/ 
<Directory “/var/www/html/yourdir/public”> 
AllowOverride All 
Order allow,deny 
Allow from all 
</Directory>

Конфигурация apache2.conf

<IfModule mod_rewrite.c> 
RewriteEngine On 
RewriteBase / 
RewriteRule ^index\.php$ - [L] 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteRule . /index.php [L] 
</IfModule>

Конфигурация .htaccess

Использование Bref для хостинга традиционных фреймворков на PHP

Bref является средой выполнения PHP в Lambda с открытым исходным кодом. Используя слой bref-fpm, можно создавать приложения с использованием стандартных фреймворков на PHP, таких как Symfony и Laravel. Весь фреймворк находится внутри одной Lambda-функции и вызывается с помощью архитектуры и правил перенаправления, описанных выше. Это возможно благодаря имплементации FastCGI Process Manager в Bref. Ниже приведено объяснение того, как это работает, от создателя Bref, Matthieu Napoli.

«Среда выполнения FPM» в Bref (Bref FPM runtime) запускает бинарный файл php-fpm. PHP-FPM – это сервер, разрабатываемый основной командой PHP, который реализует протокол FastCGI. Обычно он используется с HTTP-серверами, например Apache или NGINX.

Имплементация PHP-FPM в Bref позволяет PHP-приложениям работать в знакомой среде с помощью:

  • Запуска каждого HTTP-запроса в новом процессе, что является одной из основ архитектуры «shared-nothing» в PHP (то есть, без совместного использования ресурсов);
  • Заполнения глобальных переменных ($_GET, $_POST…), которые используются для доступа к данным HTTP-запроса;
  • Предоставления PHP-скриптам способа возврата HTTP-ответа (функция header(), stdout…);
  • Возможности оптимизации производительности, например, OPcache(кэширование байт-кода), APCu (кэширование данных в общей памяти) или переиспользуемые постоянные соединения к базе данных.

Большинство PHP-фреймворков построено на этих возможностях PHP-FPM, что делает рассматриваемую среду выполнения отличным способом перехода от «серверного хостинга» к бессерверному.

Ниже представлен обзор того, как Bref FPM runtime работает:

Цикл работы Bref-fpm

Цикл работы Bref-fpm

Запуск

При первом («холодном») вызове в новой среде Lambda выполняется bootstrap-скрипт Bref, который запускает процесс php-fpm в фоновом режиме. Этот сервер PHP-FPM ждёт подключений по протоколу FastCGI.

Цикл запроса-ответа

Когда в приложение приходит HTTP-запрос, происходит следующее:

  1. APIGateway получает HTTP-запрос и вызывает AWSLambda.
  2. Среда выполнения Lambda-функции запускает bootstrap-скрипт Bref.
  3. Bref конвертирует HTTP-запрос из формата API Gateway в формат FastCGI.
  4. Bref вызывает PHP-FPM через протокол FastCGI.
  5. PHP-FPM вызывает код обработчика Lambda-функции на PHP и возвращает его ответ.
  6. Bref конвертирует ответ FastCGI в формат API Gateway.
  7. Bref возвращает ответ в APIGateway, который, в свою очередь, возвращает HTTP-ответ клиенту.

Хотя здесь и присутствуют несколько процессов, всё происходит достаточно быстро.

Трассировка AWS X-Ray

В трассировке AWS X-Ray видно, что Lambda-функция выполняется за 9мс.

Среда выполнения Bref выполняет работу, похожую на Apache или NGINX (перенаправляет HTTP запрос через протокол FastCGI), а PHP-FPM оптимизировался десятилетиями. Между запросами PHP-FPM не убивает и не пересоздаёт процессы PHP. Он использует один и тот же процесс, но сбрасывает его память (сохраняя при этом кэш в памяти, например OPcache и APCu).

Настройка PHP в Lambda

Bref оптимизирует конфигурацию PHP-FPM в соответствии с особенностями Lambda:

  • PHP-FPM запускается с одним рабочим процессом, потому что каждый экземпляр функции в Lambda обрабатывает только один HTTP-запрос за раз;
  • Стандартный вывод ошибок PHP-FPM перенаправляется в CloudWatch. Благодаря этому для создания записи в логе достаточно просто из PHP отправить вывод в stderr;
  • Все ошибки (errors), предупреждения (warnings) и уведомления (notices) регистрируются вне HTTP-ответа и отправляются в CloudWatch по умолчанию;
  • OPcache настроен на избежание чтения с диска, так как исходный код Lambda-функции подключён в режиме «только для чтения» и не может изменяться в процессе запуска.

Кроме того, Bref имеет функциональность, которая помогает облегчить миграцию из Apache/NGINX в API Gateway и Lambda:

  • Загруженные файлы передаются в механизм загрузки файлов PHP-FPM;
  • HTTP-запросы с бинарными данными автоматически декодируются из формата Base64, в котором их присылает API Gateway;
  • Бинарные данные в HTTP-ответах могут также автоматически кодироваться в Base64;
  • Файлы cookie адаптируются для работы с механизмами PHP-FPM.

Bref поддерживает формат данных (payload formats) API Gateway версии v1 и v2.

Обработка запросов на статический контент и кэширование с помощью Amazon CloudFront

В модели ценообразования Lambda оплата взымается за количество запросов, и за длительность каждого запроса, в зависимости от количества выделенной оперативной памяти. Это делает Lambda идеальной для обработки динамических запросов.

Amazon CloudFront обрабатывает запросы на статический контент более эффективно, чем обычный сервер. Он представляет собой крупномасштабную глобальную сеть доставки контента (Content Delivery Network, CDN), которая обеспечивает безопасную и масштабируемую доставку данных. Это достигается путём их кэширования в точках присутствия по всему миру. Благодаря этому снижается нагрузка на изначальное приложение, а также улучшается опыт пользователей за счёт доставки локальной копии контента.

База раздачи (web distribution) в CloudFront может отдавать разные типы данных из нескольких источников. В этом шаблоне происходит настройка CloudFront, при которой запросы на статические файлы отправляются напрямую в бакет S3. Все остальные запросы направляются в API Gateway.

Origins:
   -   Id: Website  
   DomainName: !Join ['.', [!Ref ServerlessHttpApi, 'execute-api', !Ref AWS::Region, 'amazonaws.com']]
   # This is the stage
   OriginPath: "/dev"
   CustomOriginConfig:
   	   OriginProtocolPolicy: 'https-only' # API Gateway only supports HTTPS
   # The assets (S3)
   -   Id: Assets
   DomainName: !GetAtt Assets.RegionalDomainName
   S3OriginConfig: {}

Как мы показывали ранее, API Gateway настроен с использованием HTTP API на перенаправление всех запросов в единственную Lambda-функцию.

Ограничение доступа к файлам в Amazon S3 с использованием Origin Access Identity (OAI)

Одной из лучших практик в безопасности является конфигурация минимальных прав доступа для каждого ресурса. Это помогает снизить риски безопасности и последствия от ошибок или злонамеренных действий. В соответствии с этой практикой необходимо применить ограничения на бакет S3: он не должен быть публично доступен, при этом объекты в нём должны быть доступны только для базы раздачи CloudFront.

Это достигается с помощью Origin Access Identity (OAI). OAI задаётся в шаблоне CloudFormation следующим образом:

  S3OriginIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: Cloudfront OAI

Затем OAI используется в поле Principal политики безопасности бакета S3.

AssetsBucketPolicy: 
    Type: AWS::S3::BucketPolicy
    Properties: 
      Bucket:
        Ref: Assets # References the bucket we defined above
      PolicyDocument: 
        Statement:
          Effect: Allow  
          Action: s3:GetObject # to read
          Principal: 
            CanonicalUser: 
              Fn::GetAtt: S3OriginIdentity.S3CanonicalUserId
          Resource: # things in the bucket 'arn:aws:s3:::<bucket-name>/*'
            Fn::Join: 
                - ""
                - 
                  - "arn:aws:s3:::"
                  - 
                    Ref: Assets
                  - "/*"

Развёртывание инфраструктуры

В этом репозитории GitHub находится шаблон AWS SAM с инструкциями по развёртыванию инфраструктуры. В нём содержится одна Lambda-функция (index.php), которая использует слой со средой запуска php-73-fpm:25:

Layers:
        - 'arn:aws:lambda:us-east-1:209497400698:layer:php-73-fpm:25'

В директории /vendor находятся зависимости среды выполнения Bref. Обработчик запросов в виде файла index.php возвращает HTML-контент в ответ на запросы API Gateway. В нём присутствуют ссылки на статическое изображение и статический файл CSS:

<link href="/assets/style.css" rel="stylesheet">
…
<img src="/assets/serverless-lamp-stack.png">

Как видно выше, указан относительный путь к файлам (а не абсолютный), потому что они обрабатываются в том же домене CloudFront, что и динамическая часть веб-сайта. При переходе на домен CloudFront, отображается динамическая веб-страница, содержащая статическое изображение. FastCGI Process Manager позволяет Lambda-функции использовать глобальную переменную $_GET.

Пример бессерверного сайта на PHP

Заменив файл index.php на исходный код с использованием необходимого вам фреймворка, вы сможете развернуть многофункциональные бессерверные веб-приложения с использованием PHP. В документации Bref можно найти больше информации об использовании популярных фреймворков на PHP со средой выполнения bref-fpm.

Заключение

В этой статье мы рассмотрели, как создать PHP-приложение с помощью Lambda и API Gateway вместо HTTP-серверов, таких как Apache и NGINX. Мы показали, как разделить обработку статических и динамических запросов к вашему приложению. Все динамические HTTP-запросы направляются в единственную Lambda-функцию, которая использует среду выполнения Bref FPM. Реализация FastCGI Process Manager в Bref FPM позволяет создавать PHP-приложения с использованием стандартных фреймворков.

Отказ от HTTP-сервера освобождает разработчиков от необходимости его обслуживания, конфигурации, синхронизации и масштабирования. Команды разработки на PHP могут сосредоточиться на выпуске кода, не меняя подход к его созданию.

Начинайте создавать бессерверные приложения с PHP прямо сейчас!