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

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

Обзор архитектуры разработки

Типичная архитектура разрабатываемого приложения Django выглядит так:

Таким образом, мы можем выделить два основных компонента:

  • Приложение Django: обычно обслуживается runserver, который представляет собой сервер разработки, упакованный с Django.
  • База данных: Django поддерживает несколько баз данных, например, PostgreSQL, MySQL и SQLite.

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

Это не масштабируется

Runserver не оптимизирован и не готов обрабатывать большое количество запросов, и даже если вы замените его каким-либо сервером рабочих приложений, таким как gunicorn, количество запросов, которые может обработать один сервер, будет ограничено его аппаратными ресурсами. Поэтому, как только вы начнете получать все больше и больше запросов, система в конечном итоге перестанет отвечать на запросы, и время ожидания запросов истечет.

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

Это неэффективно

Runserver не оптимизирован для производительности. Кроме того, статические файлы и мультимедиа обслуживаются одним и тем же процессом, обслуживающим запросы, что задерживает обработку запросов и потребляет больше ресурсов. Кроме того, если вы используете базу данных SQLite по умолчанию, она позволяет выполнять только одну операцию записи в любой момент времени, что значительно ограничивает пропускную способность. Я не рекомендую использовать SQLite в продакшене.

Уязвимости безопасности

Runserver работает по протоколу HTTP вместо HTTPS, что подходит для локальной разработки, но не подходит для производства. Кроме того, если база данных работает на том же хосте, что и приложение Django, и хост подключен к Интернету, если кто-то взломает сервер, он теперь также может атаковать вашу базу данных.

Техническое обслуживание серверов — это болезненно

Вам необходимо вручную отслеживать и управлять своими серверами и ресурсами. Если сервер переходит в неработоспособное состояние или требуется больше ресурсов, это потребует ручного вмешательства и может привести к простою.

Проектирование готовой к производству архитектуры в AWS

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

Предварительные требования: Докеризируйте свое приложение Django. Выбранная архитектура требует контейнерного приложения.

Давайте теперь посмотрим на целевую архитектуру:

Балансировщик нагрузки приложений

Во-первых, мы добавляем Application Load Balancer (ALB), чтобы включить горизонтальное масштабирование и проверку работоспособности. Теперь запросы можно распределять между несколькими экземплярами, а неработоспособные экземпляры можно обнаруживать и заменять. ALB также поддерживает переадресацию портов, поэтому вам не понадобятся промежуточные прокси-серверы, такие как nginx, поскольку запросы могут быть напрямую перенаправлены в приложение через порт 8000.

ALB также поддерживает сертификаты HTTPS и SSL/TLS, которые можно добавить с помощью Менеджера сертификатов AWS. В этом случае сеанс TLS завершается на балансировщике нагрузки, а затем трафик перенаправляется через HTTP в приложение, но через вашу частную облачную сеть в приложение.

Вы также заметите, что ALB развернут в двух общедоступных подсетях в двух разных зонах доступности (AZ). Нахождение в общедоступной подсети позволяет ему получать запросы из Интернета, а использование двух AZ гарантирует, что он будет работать, даже если одна AZ выйдет из строя.

Служба эластичных контейнеров (ECS) и бессерверные контейнеры

ECS позволяет запускать докер-контейнеры в облаке. Мы используем его для запуска экземпляров нашего приложения Django. ECS поддерживает два способа запуска контейнеров: вы можете запускать контейнеры внутри сервера (виртуальная машина EC2 или локально) или запускать контейнеры в бессерверном режиме с помощью Fargate.

Мы используем Fargate, так как не хотим управлять серверами. Для этого необходимо создать Кластер ECS и Службу ECS с типом запуска Fargate, а затем добавить Определение задачи, указывающее контейнеры, которые будут работать как часть службы. В этом случае нам нужно определить одну задачу/контейнер для приложения Django. Образ контейнера берется из реестра докеров.

Обратите внимание, что контейнеры не должны иметь состояния, чтобы их можно было уничтожить и воссоздать в любое время. Это обеспечивает одну из лучших функций ECS Services (и AWS в целом): автоматическое масштабирование. Мы устанавливаем минимальное, желаемое и максимальное количество задач и используем показатели CloudWatch, такие как среднее использование ЦП (более высокая загрузка ЦП = более высокий трафик), для автоматического масштабирования (добавление дополнительных экземпляров) или масштабирования (удаление экземпляров). по мере необходимости.

Наличие как минимум двух экземпляров гарантирует, что система продолжит работу, если один из них выйдет из строя, а проверки работоспособности позволят автоматически обнаруживать неисправные экземпляры и заменять их новыми и работоспособными. Кроме того, размещение этих двух экземпляров в двух разных зонах доступности (AZ) разделит риск в случае выхода из строя одного центра обработки данных AWS.

Наконец, не забываем о безопасности. Мы размещаем наши задачи ECS в частных подсетях, чтобы они не были доступны в Интернете и могли получать запросы только от балансировщика нагрузки.

Aurora serverless в качестве базы данных

База данных по своей природе имеет состояние, потому что в ней хранятся данные, которые должны быть постоянными. Поэтому он не может работать как контейнер без сохранения состояния в ECS/Fargate. Но мы не хотим управлять сервером или виртуальной машиной для базы данных.

К счастью, у AWS есть управляемый сервис под названием Aurora Serverles. Aurora Serverless — это полностью управляемая служба для реляционных баз данных, поддерживающая PostgreSQL. Бессерверность означает, что он автоматически масштабируется и не требует выделения ресурсов или управления ими.

Как и в случае с ECS Services, мы устанавливаем минимальную и максимальную емкость, и она автоматически масштабируется по требованию. Чтобы сократить некоторые расходы, вы также можете включить функцию автоматической паузы, чтобы временно уменьшить емкость до нуля после простоя (без подключений) в течение N минут. Это означает, что БД отключается до тех пор, пока не получит новый запрос на подключение. Это особенно полезно для сред pre-prod/staging, но не очень подходит для продакшена, так как время пробуждения может занимать до одной-двух минут.

Aurora DB также поддерживает автоматическое резервное копирование в качестве стратегии аварийного восстановления. Кроме того, общая производительность Aurora DB в три раза выше, чем у обычного экземпляра PostgreSQL.

Обратите также внимание, что мы размещаем базу данных в тех же частных подсетях, где работает служба ECS, поэтому база данных доступна из нашего приложения Django и недоступна для Интернета.

CodePipeline и конвейеры CI/CD

CodePipeline — это сервис непрерывной интеграции/непрерывной доставки, позволяющий автоматизировать процесс выпуска программного обеспечения. Конвейер состоит из этапов, которые можно настроить таким образом, чтобы его можно было адаптировать к любой модели ветвления, которую вы используете, такой как магистральная, GitHub Flow, GitFlow или другим настраиваемым моделям ветвления.

Например, это будут этапы, использующие классическую модель ветвления GitHub Flow.

  • Источник: конвейер запускается, когда PR объединяется с основной веткой (это обнаруживается с помощью веб-перехватчиков через соединение с AWS CodeStar).
  • Сборка и [тестирование]: образ докера создается из исходного кода и сохраняется в репозитории ECR, который представляет собой реестр образов докеров в AWS. На этом этапе могут запускаться автоматические тесты, если они не запускались перед слиянием или просто в качестве второй проверки. На этом этапе можно использовать другие службы, такие как CodeBuild или корзины S3, для создания и хранения артефактов.
  • Стадия: новый образ развертывается в некоторых предварительных средах для дополнительного контроля качества (например, интеграционные тесты, сквозные тесты, тесты пользовательского интерфейса). После ручного или автоматического утверждения он переходит на этап производства.
  • Производство: новый образ развертывается, обновляя приложение в ECS. Последовательные обновления можно использовать, чтобы избежать простоев.

Route 53 в качестве DNS и (необязательно) в качестве регистратора домена

Route53 — это DNS, в который мы добавляем записи, чтобы указать наш домен и поддомены на ресурсы AWS. Например, мы добавляем запись, чтобы указать наш основной домен на наш ALB. Сам домен также может быть зарегистрирован Route53 или у какого-либо стороннего регистратора доменов, такого как NameCheap или GoDaddy. Если вы используете стороннего регистратора, вам придется изменить записи сервера имен (NS) на сделать Route 53 вашим DNS.

Обслуживание статических ресурсов с помощью CloudFront

Статические ресурсы, такие как файлы .js или .css, хранятся в корзине S3 и обслуживаются через сеть распространения контента (CDN) с использованием CloudFront. Это позволяет кэшировать файлы рядом с клиентом, оптимизируя производительность и одновременно разгружая сервер приложений. Вы должны убедиться, что ваша корзина S3 является частной, и использовать удостоверение доступа Origin, чтобы разрешить доступ только через вашу базу раздачи CloudFront.

Защитите свои секреты с помощью AWS Secrets Manager

Вы не хотите передавать ключи API, учетные данные базы данных или любую конфиденциальную информацию в свой репозиторий кода. Вы можете задавать переменные среды непосредственно в определениях задач, но затем кто-то может прочитать их значения с помощью API AWS. С помощью AWS Secrets Manager данные хранятся в облаке в зашифрованном виде и вводятся во время выполнения. Вы можете хранить данные в виде простого текста или пар ключ-значение в формате JSON. Мы используем его для хранения таких вещей, как Django Secret и учетные данные базы данных.

Удержание трафика внутри вашей сети с помощью конечных точек VPC

Когда вы используете такие сервисы AWS, как S3 или SQS, по умолчанию доступ к ним осуществляется через Интернет. Если ваше приложение работает в AWS внутри виртуального частного облака (VPC) в частной сети, то для доступа к любому ресурсу в Интернете требуется Шлюз NAT. Плата за NAT GW взимается за час и за ГБ, и они могут быстро увеличить ваш счет.

Таким образом, сохраняя трафик внутри вашего VPC, вы повышаете безопасность и производительность, но также можете снизить некоторые расходы. Поскольку ваше приложение находится в AWS, есть способ получить доступ к этим сервисам AWS из вашего VPC без подключения к Интернету: конечные точки VPC (частные ссылки fka). Все, что вам нужно сделать, это включить их для каждой службы на уровне VPC, после чего любой вызов этих служб будет перенаправляться внутри сети AWS.

Обратите внимание, что использование конечных точек VPC тоже требует затрат, но цены за час и обработку данных составляют четверть от цен на GW NAT.

Шаблоны расширенной архитектуры

Развязка с очередями и работниками

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

Зачем мне это может понадобиться?

Эффективность

Экземпляр вашего приложения может обрабатывать ограниченное количество одновременных запросов. Таким образом, вы хотите обрабатывать запросы как можно быстрее, чтобы иметь возможность обрабатывать больше запросов в секунду на каждом экземпляре. Допустим, у вас есть трудоемкий код, который вы выполняете синхронно во время обработки запроса. Чем больше времени вы добавляете, тем меньше запросов в секунду вы можете обработать.

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

Лучшая масштабируемость

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

Отказоустойчивость

Допустим, вы вызываете какой-то внешний API (сторонний или другой собственный сервис). Если вы делаете это синхронно, поскольку ваше приложение напрямую связано с ним, то, как только внешний API выйдет из строя, все ваше приложение тоже выйдет из строя. Перемещение вызова API в асинхронную задачу позволяет отделить ваше приложение от внешней службы. Приложение просто поставит задачу в очередь и продолжит работу.

Рабочий подберет задачу позже и выполнит ее. В случае сбоя вызова API задачу можно повторить с другими стратегиями, пока она не завершится успешно или пока вы не сдадитесь.

Использование Celery для реализации рабочих процессов

Сельдерей в гибкой и надежной реализации распределенных очередей задач и воркеров на питоне. Он берет на себя тяжелую работу, такую ​​как доставка сообщений, выполнение рабочих операций, отключения и повторные подключения, и имеет некоторые замечательные функции, такие как повторные попытки выполнения задач с различными стратегиями. Он поддерживает несколько очередей, Amazon SQS — одна из них, и поставляется с интеграцией Django из коробки.

Использование Amazon SQS для реализации очередей

SQS — это полностью управляемый высокодоступный сервис, предоставляющий надежные очереди в облаке. Вам не нужно предоставлять ресурсы или управлять серверами любого типа. Количество сообщений не ограничено, поэтому очередь не может быть заполнена, а сообщения хранятся в нескольких избыточных зонах доступности (AZ).

Использование современных интерфейсных фреймворков и сервис-ориентированной архитектуры (SOA)

Возможно, вы захотите разделить свой интерфейс и серверную часть и использовать какой-нибудь современный интерфейсный фреймворк, такой как React или Vue. Мы адаптируем нашу архитектуру для поддержки этого.

Итак, теперь приложение Django будет реализовывать REST API, который будет интерфейсом связи с интерфейсом.

Бэкенд

Наше приложение Django теперь реализует серверную веб-службу, обслуживающую REST API. Слой шаблонов Django не используется, так как внешний рендеринг теперь перенесен на клиентскую сторону. Вы можете взглянуть на django-rest-framework, если выберете эту архитектуру.

Внешний интерфейс

Фронтенд теперь будет разрабатываться и развертываться отдельно. Опять же, мы используем CodePipeline для создания и развертывания внешнего приложения. После создания внешнего интерфейса это просто набор статических файлов (html, js, css). Таким образом, мы можем хранить его в частной корзине S3 для обслуживания через CloudFront. Типичный пайплайн для фронтенда может иметь следующие этапы:

  • Источник: конвейер запускается, когда новые коммиты отправляются в master во внешнем репозитории в GitHub.
  • Тест: автоматические тесты выполняются перед продвижением вперед.
  • Сборка: реагирующее приложение создается (сборка пряжи), а пакет (статические файлы) передается на следующий этап.
  • Стадия: новый пакет развертывается в корзине S3 в предпроизводственной среде для дополнительного контроля качества (например, сквозных тестов, тестов пользовательского интерфейса). После ручного или автоматического утверждения он переходит на этап производства.
  • Производство: новый пакет развертывается в корзине S3 в рабочей среде. Затем дистрибутив CloudFront обновляется, а кэш становится недействительным, чтобы принудительно распространять новую версию приложения.

Что дальше?

Пришло время развертывания! Узнайте, как развернуть ваши приложения Django в AWS с помощью CDK в моей следующей статье.

Спасибо за прочтение!