Как стать автором
Обновить
347.58
Рейтинг
OTUS
Цифровые навыки от ведущих экспертов

Развертывание приложения Symfony в AWS Lambda

Блог компании OTUSПрограммированиеSymfony
Перевод
Автор оригинала: Smaine Milianni

Сначала давайте разберемся, что такое бессерверная архитектура и когда она нужна.

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

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

Наш сценарий использования

Предоставить доступ к API, созданному с помощью Symfony, который публикует сообщения в LinkedIn. Процесс разработки будет включать этапы от написания до развертывания кода.

Пишем код

В Symfony 5-й версии появился новый компонент под названием Notifier, который дает возможность отправлять уведомления через разные сервисы (Slack, Twitter, Twilio и др.).

В Symfony нет встроенной поддержки LinkedIn, поэтому несколько месяцев назад я создал поверх Notifier шлюз для публикации контента в этой социальной сети. Исходный код шлюза можно посмотреть по ссылке, его же мы будем использовать и в этой демонстрации.

Приступаем

$ symfony new --full aws-lambda-linkedin-notifier
$ cd aws-lambda-linkedin-notifier
$ composer require eniams/linkedin-notifier

Включаем шлюз (см. документацию)

<?php
// config/bundles.php
return [
// others bundles,
Eniams\Notifier\LinkedIn\LinkedInNotifierBundle::class => ['all' => true]];
// .env
LINKEDIN_DSN=

Логика публикации контента


<?php

class PostContentController
{
    /**
     * @Route("/contents", name="post_content", methods="POST")
     */
    public function __invoke(NotifierInterface $notifier, Request $request)
    {
        if(null !== $message = (\json_decode($request->getContent(), true)['message'] ?? null)) {
            $notifier->send(new Notification($message, ['chat/linkedin']));
            return new JsonResponse('message posted with success', 201);
        }

        throw new BadRequestException('Missing "message" in body');
    }
}

Логика проста: мы предоставляем доступ к API через маршрут /contents, который принимает запрос POST с сообщением message в его теле.

В 11-й строке мы отправляем публикуемое сообщение в LinkedIn — благодаря Symfony и шлюзу это делается очень просто!

Будучи профессиональными разработчиками, покроем этот код автотестами:


<?php
class PostContentControllerTest extends WebTestCase
    /**
     * @dataProvider methodProvider
     */
    public function testANoPostRequestShouldReturnA405(string $method)
    {
        $client = static::createClient();

        $client->request($method, '/contents');

        self::assertEquals(405, $client->getResponse()->getStatusCode());
    }

    public function testAPostRequestWithoutAMessageInBodyShouldReturnA400()
    {
        $client = static::createClient();

        $client->request('POST', '/contents');

        self::assertEquals(400, $client->getResponse()->getStatusCode());
    }

    public function testAPostRequestWithAMessageInBodyShouldReturnA201()
    {
        $request = new Request([],[],[],[],[],[], json_encode(['message' => 'Hello World']));

        $notifier = new class implements NotifierInterface {
            public function send(Notification $notification, Recipient ...$recipients): void
            {
            }
        };

        $controller = new PostContentController();
        $response = $controller->__invoke($notifier, $request);

        self::assertEquals(201, $response->getStatusCode());
    }

    public function methodProvider()
    {
        return [
            ['GET'],
            ['PUT'],
            ['DELETE'],
        ];
    }
view rawTestPostContentController.php hosted with ​ by GitHub

Код готов! Пришло время его развернуть

Стоп! Что?! AWS Lambda не поддерживает PHP!

Именно так: AWS Lambda поддерживает не все языки программирования, а только некоторые, в том числе Go, Java, Python, Ruby, NodeJS и .NET.

Теперь надо учить новый язык и переписывать код? Надеюсь, нет!

Ничего переписывать не придется благодаря Матье Напполи (Matthieu Nappoli), создателю Bref.sh, и замечательной команде, помогающей ему поддерживать этот проект с открытым исходным кодом.

Bref позволяет развертывать PHP-приложения в AWS и запускать их на AWS Lambda.

Конфигурация развертывания

Добавим в kernel.php обработку логов:

<?php
    // Kernel.php
    public function getLogDir(): string
    {
        if (getenv('LAMBDA_TASK_ROOT') !== false) {
            return '/tmp/log/';
        }

        return parent::getLogDir();
    }

    public function getCacheDir()
    {
        if (getenv('LAMBDA_TASK_ROOT') !== false) {
            return '/tmp/cache/'.$this->environment;
        }

        return parent::getCacheDir();
    }

Подготовим фреймворк Serverless (см. документацию):

$ npm install -g serverless
$ serverless config credentials --provider aws --key  --secret
$ composer require bref/bref

Зададим конфигурацию в файле serverless.yaml:

service: notifier-linkedin-api

provider:
    name: aws
    region: eu-west-3
    runtime: provided
    environment: # env vars
        APP_ENV: prod
        LINKEDIN_DSN: YOUR_DSN


plugins:
    - ./vendor/bref/bref

functions:
    website:
        handler: public/index.php # bootstrap 
        layers:
            - ${bref:layer.php-73-fpm} # https://bref.sh/docs/runtimes/index.html#usage 
        timeout: 28 # Timeout to stop the compute time
        events:
            - http: 'POST /contents' # Only POST to /contents are allowed

package:
    exclude:
        - 'tests/**'
view rawserverless.yaml hosted with ​ by GitHub

Развертываем!

$ serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service notifier-linkedin-api.zip file to S3 (10.05 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................
Serverless: Stack update finished...
Service Information
service: notifier-linkedin-api
stage: dev
region: eu-west-3
stack: notifier-linkedin-api-dev
resources: 15
api keys:
  None
endpoints:
  POST - https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents
functions:
  website: notifier-linkedin-api-dev-website
layers:
  None
Serverless: Removing old service artifacts from S3...
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.

Теперь наш код развернут в AWS Lambda, а API доступен по адресу https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents.

Попробуем:


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

Теги:symfony frameworkaws lambdaserverlesssolution architectphp
Хабы: Блог компании OTUS Программирование Symfony
Всего голосов 7: ↑6 и ↓1+5
Просмотры2.5K

Похожие публикации

Лучшие публикации за сутки