Я работаю в компании Initlab. Мы специализируемся на разработке и поддержке Drupal проектов. У нас есть продукт для быстрого создания Ecommerce решений, основанный на Drupal. В 2019 году мы начали решать задачу построения масштабируемой и отказоустойчивой инфраструктуры для нашего продукта. В качестве платформы для инфраструктуры был выбран Kubernetes. В этой статье разберем особенности Drupal и подходы, с помощью которых можно запускать Drupal в кластере Kubernetes.  Часть статьи является переводом статьи Джеффа Герлинга. Со своей стороны добавили более подробное описание вариантов запуска баз данных и отказоустойчивого хранилища в кластере.  Drupal и Docker могут работать вместе и хорошим примером тут служат локальные среды разработки, построенные вокруг Docker, такие как Docksal, Ddev, Lando и DrupalVM. Но локальные среды разработки, в которых кодовая база Drupal в основном монтируется, как том в контейнер Docker, сильно отличаются от рабочей среды, где цель состоит в том, чтобы «вместить» как можно большую часть кода в stateless контейнер. В Kubernetes все, или почти все приложения вынуждены работать, как 12-факторное приложение и при развертывании Drupal в Kubernetes необходимо это учитывать.  На базовом уровне 12-факторное приложение — это приложение, которое легко развертывается в масштабируемой, широкодоступной и облачной среде. Многим приложениям нужны базы данных для учетных записей пользователей, общая файловая система для загруженных или сгенерированных файлов, совместно используемых несколькими экземплярами, а также другие элементы, способные затруднить развертывание приложений масштабируемым и гибким способом. Но 12-факторное приложение — это приложение, которое облегчает развертывание и управление в масштабируемой среде: Чтобы превратить приложение или веб-сайт на Drupal в более «безликое» 12-факторное приложение, необходимо соблюсти пять главных условий: Существует несколько способов решить задачу сохранения данных при перезапуске контейнеров в Kubernetes. Эту задачу необходимо решить любому веб-сервису, использующему базу данных. Рассмотрим эти варианты. Самый простой и наиболее часто применяемый способ использование сервиса управляемых баз данных: AWS RDS Aurora или Cloud SQL в Google Cloud. В этом случае, мы можем подключить свой сайт на Drupal напрямую к управляемой базе данных. Недостатком данного подхода является то, что он может быть дороже, чем решение с самостоятельным управлением, и не всегда применим из-за особенностей инфраструктуры. Преимущество управляемых баз данных в том что это надежное решение и не требуется тратить ресурсы на администрирование. Использовать базы данных внутри кластера Kubernetes можно, организовав отдельный контейнер для данных с помощью Docker. Это решение по стоимости дешевле, сохраняет переносимость и хорошо работает с небольшими сайтами и приложениями.
Но если необходимо иметь высокодоступные базы данных, реплицируемые на запись/чтение данных внутри Kubernetes, потребуется сделать гораздо больше работы. Проект MySQL Operator предназначен для упрощения процесса размещения базы в кластере. Не работает в последних версиях kubernetes, начиная с 1.16. Кроме того, в каждой версии Kubernetes могут вноситься значительные изменения в API, из-за чего проект требуется адаптировать к новым версиям, что разработчики не всегда успевают делать. Альтернатива MySQL Operator — это решение Percona XtraDB Cluster Operator. Percona XtraDB Cluster объединяет в себе Percona Server для MySQL, работающий с механизмом хранения XtraDB, и Percona XtraBackup с библиотекой Galera, для обеспечения синхронной multi-master репликации.   Балансировка нагрузки выполняется с помощью демона ProxySQL, который принимает входящий трафик от клиентов MySQL и перенаправляет его на серверы MySQL. ProxySQL эффективно управляет рабочей нагрузкой базы данных по сравнению с другими балансировщиками нагрузки, которые не поддерживают SQL. Более простым для запуска базы данных в кластере Kubernetes может быть использование Helm chart для MariaDB, которое позволяет развернуть в Kubernetes реплицированный кластер MariaDB. В данном кластере развертывается один master, а остальные — slave контейнеры, с которых возможно чтение данных. В отличии от multi-master репликации, при отказе master контейнера будет остановке сервиса, на время запуска нового «мастера». Если рассматривать СУБД PostgreSQL, то существует проект Stolon — облачный менеджер для обеспечения высокой доступности кластера PostgreSQL. При деплое баз данных под MySQL или PostgreSQL, в Kubernetes, нужно где-то сохранять файлы с фактическими данных. Наиболее распространенный способ — подключить Kubernetes PV (Persistent Volume) к контейнеру MySQL.  Итак, придется решить проблему запуска постоянной базы данных в кластере Kubernetes, если требуется использовать полностью облачную среду. Drupal должен иметь возможность записи в файловую систему внутри кодовой базы в нескольких случаях: Существует несколько вариантов решения этих задач. Самое простое решение всех этих задач, позволяющее Drupal масштабировать и запускать более одного экземпляра Drupal — это использовать NFS для папки файлов Drupal (например sites/default/files). Однако легче — не значит лучше. У NFS есть свои особенности: Можно использовать сервис хранения типа Amazon S3 в качестве общедоступной файловой системы для своего Drupal сайта, что снизит затраты на поддержку хранилища и повысит его надежность. Для интеграции S3 хранилища с drupal можно использовать, например, модуль S3FS. Также можно использовать распределенные файловые системы, такие как Gluster или Ceph. Однако тут увеличивается сложность поддержки таких решений, которые можно решить в Kubernetes с помощью проекта rook.  Rook "превращает" распределенные системы хранения в самоуправляемые, самомасштабируемые, самовосстанавливающиеся сервисы хранения. Он автоматизирует задачи администратора хранилища: развертывание, загрузку, настройку, предоставление, масштабирование, обновление, миграцию, аварийное восстановление, мониторинг и управление ресурсами. Rook использует возможности платформы Kubernetes для предоставления своих услуг: облачное управление контейнерами, планирование и оркестрация. Rook управляет несколькими решениями для хранения, предоставляя общую платформу для всех из них. Rook гарантирует, что все они будут хорошо работать в Kubernetes:  Сделать файл settings.php доступным для записи во время установки более сложная задача, особенно если вам нужно установить Drupal, а затем развернуть новый образ контейнера ( что произойдет со всеми записанными значениями settings.php? Они будут удалены при перезапуске контейнера! ). Поэтому решение данной задачи получило отдельный пункт описания особенностей запуска Drupal в Kubernetes. Пользователи могут устанавливать Drupal через веб-интерфейс мастера установки или автоматически через Drush. В любом случае Drupal во время установки, если еще не установлены все правильные переменные в settings.php, добавляет некоторые дополнительные конфигурации к файлу sites/default/settings.php или создает этот файл, если его не существует. Следующие параметры должны быть предварительно установлены в settings.php, чтобы Drupal автоматически пропускал экран установки и не требовал записи дополнительных конфигураций в файл settings.php: В данном примере используются переменные окружения (например: DRUPAL_HASH_SALT) с функцией PHP getenv() вместо "жестко" заданных значений в файле. Это позволяет контролировать настройки, которые Drupal использует как при установки, так и для новых контейнеров, которые запускаются после установки Drupal. В файле Docker Compose, чтобы передать переменные, указываются директива environment для контейнера web/Drupal следующим образом: А в Kubernetes, когда создаете Drupal Deployment, передать переменные среды можно с помощью envFrom и ConfigMap (предпочтительно), также можно напрямую передавать переменные среды в спецификации контейнера: Приведенный выше фрагмент манифеста предполагает, что уже в kubernetes есть ConfigMap с именем drupal-config содержащей все DRUPAL_* переменные, кроме пароля к базе данных, а также секрет, названный mysql-pass, с паролем в ключе drupal-mysql-pass. На этом этапе, если создаете новую среду с помощью Docker Compose локально или в кластере Kubernetes, можете перейти к установщику пользовательского интерфейса или использовать Drush. При этом не требуется вводить учетные данные базы данных, поскольку эти и другие необходимые переменные уже предварительно настроены. Также может быть ряд других параметров конфигурации, которыми требуется управлять, используя переменные среды. Drupal еще не имеет механизма для этого, поэтому необходимо встроить такие настройки в код, используя getenv().  Чтобы отслеживать любые проблемы с cron отдельно от обычного веб-трафика и быть уверенным, что выполнение заданий cron не мешает обычным посещениям страниц следует запускать cron через внешний процесс вместо того, чтобы запускать его при посещениях интерфейсной страницы (именно так Drupal по умолчанию запускает cron). Внутри Kubernetes не нужно настраивать crontab ни на один из узлов Kubernetes, работающих с сайтами Drupal. И хотя может быть внешняя система, например Jenkins, запускающая задания cron на Drupal сайте, гораздо проще просто запускать Cron Drupal как Kubernetes CronJob, в идеале в том же пространстве имен Kubernetes, что и Drupal. Самый надежный способ запуска Drupal cron — через Drush, но запуск отдельного контейнера Drush через CronJob означает, что CronJob должен запланировать сложный контейнер, работающий как минимум на PHP и Drush. Поды CronJob должны быть максимально легкими, чтобы их можно было планировать на любом системном узле и запускать очень быстро (даже если ваш контейнер Drupal еще не был загружен на этот конкретный узел Kubernetes). Cron Drupal поддерживает запуск путем посещения URL-адреса, и это самый простой вариант для работы с Kubernetes. Пример настройки CronJob: URL drupal_cron_url зависит от сайта и можно его найти, посетив страницу /admin/config/system/cron. Убедитесь, что в настройках cron для «Run cron every» установлено значение «Never», чтобы cron запускался только через CronJob Kubernetes. Можно использовать byrnedo/alpine-curl образ докера, который является чрезвычайно легким — всего 5 или 6 МБ — так как он основан на Alpine Linux. Большинство других контейнеров, которые есть на базе Ubuntu или Debian, имеют размер, по крайней мере, 30-40 МБ (так что они будут использовать гораздо больше времени, чтобы загрузить первый раз, когда CronJob запускается на новом узле). Мы рассмотрели особенности размещения Drupal проектов в Kubernetes. В продолжении этой статьи детально рассмотрим как мы запустили наш продукт в кластере Kubernetes. DevOps