Как я устранил болевые точки, которые у меня были на протяжении многих лет, с помощью Serverless Framework для моих приложений Typescript, развернутых на AWS.

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

В этой статье я расскажу вам, как я структурировал свое последнее приложение, чтобы улучшить качество моей инфраструктуры как кода. Более конкретно, я расскажу о том, как

  • Создавайте повторно используемые блоки инфраструктуры, чтобы уменьшить объем кода, который вам нужно написать, и повысить удобство сопровождения вашего приложения.
  • Скройте неприятные функции CloudFormation (такие как Ref, Fn::GetAtt и другие), чтобы улучшить читабельность.
  • Структурируйте свои ресурсы в логические блоки кода

Весь код, найденный в этой статье, взят из этого репозитория и может эффективно использоваться в качестве шаблона для помощи в запуске любого бессерверного приложения, написанного с помощью Typescript и развернутого на AWS.

Базовая настройка

Самое первое, что вы должны сделать, чтобы правильно начать работу с Serverless Framework, — это использовать Typescript для своей конфигурации. Я уже говорил это раньше и буду говорить. Почему-то он не получил такой широкой огласки по сравнению с YAML, но это внесло огромное улучшение в мой опыт работы с фреймворком. Это даст вам гораздо лучшую среду для написания вашего IaC, а также послужит основой для всех остальных вещей, которые я покажу вам в этой статье. Вы даже можете получить типы, которые помогут отловить некоторые ошибки при написании вашего IaC прямо в вашей конфигурации. Взгляните на все доступные типы здесь.

Наряду с этим, я настоятельно рекомендую использовать плагин serverless-bundle. Установка очень проста, и это устранит много когнитивной нагрузки, связанной с правильной упаковкой функций Typescript Lambda. Больше не нужно думать о настройке Webpack, Babel, ESLint и т. д. Плагин предоставит вам несколько разумных значений по умолчанию, и вы получите минимизированные, молниеносно быстрые функции, которые содержат только то, что им нужно (т. е. правильное встряхивание дерева).

Наконец, я бы также рекомендовал вам использовать MiddyJS (я не буду вдаваться в подробности того, как это работает, вы можете прочитать это на их веб-сайте) для ваших функций Typescript. Этот модуль предоставляет набор промежуточного программного обеспечения для расширения функций Lambda такими вещами, как правильные заголовки для CORS, проверка схем ввода и вывода для ваших конечных точек HTTP, автоматическая десериализация JSON и другого типа контента, правильные ошибки HTTP и многое другое.

Повторно используемые блоки инфраструктуры

Основная вещь, которую вы будете добавлять в свое приложение снова и снова, — это, вероятно, лямбда-функции, поэтому здесь мы сосредоточимся на ней. Хотя этот принцип можно применить к любому другому блоку ресурсов или конфигурации, которые будут реплицированы, например к ролям IAM, очередям SQS и т. д. С помощью этого шаблона вы сможете создать раздел функций, который выглядит следующим образом:

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

Покопавшись в функции postMessage, мы получили следующее:

Дополнительная информация о функции postMessage, например. путь конечной точки, политики и переменные среды.

Кроме того, общий createFunctionHandler содержит всю конфигурацию, общую для всех функций Lambda.

Всего 3 уровня абстракции, на каждом из которых только те детали, которые вам нужны. Функция createFunctionHandler — это единственный код, который вам потребуется изменить, чтобы внести изменения, которые должны повлиять на все ваши функции Lambda. Если вам нужны конкретные сведения о лямбда-функции postMessage, вы можете найти ее в отдельном файле. Затем вы можете четко увидеть зависимости ваших функций Lambda, описанные в файле serverless.ts.

Скрыть неприятные функции CloudFormation

Поскольку Serverless Framework поддерживает шаблоны CloudFormation, обычно это нужно делать, если вы хотите создавать ресурсы помимо тех, которые фреймворк обрабатывает за вас.

Поддержка CloudFormation великолепна, так как вы можете использовать всю документацию, которую AWS предоставляет для API каждого из своих сервисов, и легко добавлять новые ресурсы в свои приложения. Но если вам нужны связи между вашими ресурсами, вам нужно использовать такие функции CloudFormation, как Ref и Fn::GetAtt. И я всегда обнаруживал, что функции CloudFormation представляют собой большие, непрозрачные блоки кода, что затрудняет понимание того, что происходит.

Вот почему мне нравится прятать их подальше от ресурсов, которым они принадлежат. Как вы видели выше, зависимости моих лямбда-функций — это красивые, хорошо названные переменные. Если бы вместо этого мне пришлось использовать функции CF, это выглядело бы так:

На мой взгляд менее читабельно. В моем serverless.ts я хочу быстро увидеть зависимости между моими различными ресурсами, а функции CF усложняют эту задачу.

Однако они имеют гораздо больше смысла рядом с ресурсами, которым они принадлежат. Вот почему я поместил их в функцию, определяющую ресурсы. Вот как выглядит функция, которая создает мою таблицу DynamoDB:

Во внутренностях моей табличной функции DynamoDB я вижу, что arn извлекается с помощью функции Fn::GetAtt, но тогда внешнему миру не нужно заботиться об этом. Они просто используют переменную arn после вызова функции.

Если вам интересно, что такое функция varToString, она просто выведет имя моей переменной в виде строки (например, “mainTable” в приведенном выше коде). См. код здесь, если интересно. Поскольку мне нужно использовать имена переменных в функциях CF, эта функция устраняет возможность внесения ошибки, переименовывая мою переменную, но забывая изменить строку в функциях CF. Используя varToString, компилятор уведомит меня об ошибке.

Структурируйте свои ресурсы в логические блоки кода

Последний шаблон, который я вам покажу, позволит сохранить читаемость файла serverless.ts по мере роста вашего приложения. Как правило, с течением времени раздел Ресурсы вашей конфигурации становится настолько большим, что у вас может развиться тендинит, когда вам приходится его прокручивать. Это часто происходит потому, что когда вам нужно добавить что-то одно, скажем, пул пользователей Cognito для вашего приложения, вы создаете не его, а тонну ресурсов AWS.

Опять же, мне нравится содержать мой serverless.ts в чистоте, чтобы он был местом, где я могу легко видеть связи между своими ресурсами. Затем я спрячу внутренности своих блоков ресурсов в отдельные функции.

Вот как выглядит функция, создающая мой пользовательский пул Cognito (полностью посмотреть здесь):

Там определены все необходимые ресурсы, и всякий раз, когда мне нужно узнать о специфике пула пользователей, я нахожу их там. Следует обратить внимание на то, что я возвращаю объект resources, содержащий все ресурсы, которые нужно создать. Сделав это, я смогу использовать замечательную функцию деструктуризации объектов JavaScript в моем serverless.ts.

Аааа, так приятно глазу! Одна строка, чтобы узнать, что у меня есть пул пользователей Cognito в этом приложении. Не нужно прокручивать сотни строк определений ресурсов CloudFormation или пытаться выяснить, к чему относится каждый отдельный ресурс!

Заключение

Вот оно! Шаблон для ваших бессерверных функций Typescript, развернутых на AWS! Не стесняйтесь просматривать репозиторий Github, так как там есть несколько других мелких вещей, которые облегчают мою жизнь при программировании.

Если у вас есть какие-либо вопросы, задавайте! И если у вас есть собственный набор практик, которые помогут вам оставаться продуктивными с Serverless Framework, поделитесь ими в комментариях! Я всегда ищу способы стать лучше!

Удачного кодирования!