Требования к инфраструктуре промышленного уровня
Попробуйте такой забавный эксперимент: пройдитесь по своей компании и поспрашивайте, каковы требования для перехода в промышленную среду. Чаще всего первые пять человек дадут пять разных ответов. Один из них упомянет необходимость в измерении показателей и оповещениях; другой расскажет о планировании емкости и высокой доступности; кто-то начнет разглагольствовать об автоматических тестах и разборе кода; а кто-то затронет тему шифрования, аутентификации и укрепления серверов. Если вам повезет, один из респондентов может вспомнить о резервном копировании данных и агрегации журнальных записей. У большинства компаний нет четкого списка требований к выпуску промышленной системы. Значит, каждый элемент инфраструктуры развертывается немного по-своему и ему может недоставать каких-то критически важных функций.
Чтобы улучшить эту ситуацию, хочу поделиться с вами контрольным списком задач для подготовки инфраструктуры промышленного уровня (табл. 6.2). Этот список охватывает большинство ключевых моментов, которые необходимо учитывать при развертывании инфраструктуры в промышленной среде.
Таблица 6.2. Список задач для подготовки инфраструктуры промышленного уровня
Задача
Описание
Примеры инструментов
Установка
Установите исполняемые файлы ПО и все зависимости
Bash, Chef, Ansible, Puppet
Конфигурация
Сконфигурируйте ПО на этапе выполнения, включая настройки портов, сертификаты TLS, обнаружение сервисов, выбор центральных и дополнительных серверов, репликацию и т. д.
Bash, Chef, Ansible, Puppet
Инициализация
Инициализируйте инфраструктуру, включая серверы, балансировщики нагрузки, сетевую конфигурацию, параметры брандмауэра, права доступа IAM и т. д.
Terraform, CloudFormation
Развертывание
Разверните сервис поверх инфраструктуры. Выкатывайте обновления с нулевым временем простоя, включая «сине-зеленые», скользящие и канареечные развертывания
Terraform, CloudFormation, Kubernetes, ECS
Высокая доступность
Система должна выдерживать перебои в работе отдельных процессов, серверов, сервисов, вычислительных центров и регионов
Несколько вычислительных центров и регионов, репликация, автомасштабирование, распределение нагрузки
Масштабируемость
Масштабируйте систему в зависимости от нагрузки. Это касается как горизонтального (больше серверов), так и вертикального масштабирования (более крупные серверы)
Автомасштабирование, репликация, сегментирование, кэширование, стратегия «разделяй и властвуй»
Производительность
Оптимизируйте использование процессора, памяти, диска, сети и графического адаптера. Это относится к ускорению запросов, эталонному тестированию, нагрузочному тестированию и профилированию
Dynatrace, valgrind, VisualVM, ab, Jmeter
Сеть
Сконфигурируйте статические и динамические IP-адреса, порты, обнаружение сервисов, брандмауэры, DNS, а также доступ по SSH и VPN
Облака VPC, брандмауэры, маршрутизаторы, регистраторы DNS, OpenVPN
Безопасность
Шифрование при передаче (TLS) и на диске, аутентификация, авторизация, управление конфиденциальными данными, укрепление серверов
ACM, Let’s Encrypt, KMS, Cognito, Vault, CIS
Показатели
Показатели доступности, бизнес-показатели, показатели приложения, показатели серверов, события, наблюдаемость, трассировка и оповещения
CloudWatch, DataDog, New Relic, Honeycomb
Журнальные записи
Организуйте чередование журнальных файлов на диске. Агрегируйте журнальные данные в центральном месте
CloudWatch Logs, ELK, Sumo Logic, Papertrail
Резервное копирование и восстановление
Проводите плановое резервное копирование БД, кэшей и других данных. Реплицируйте данные для разделения регионов или учетных записей
RDS, ElastiCache, репликация
Оптимизация расходов
Выбирайте подходящие типы серверов, используйте прерываемые и резервируемые серверы, применяйте автомасштабирование и избавляйтесь от ненужных ресурсов
Автомасштабирование, прерываемые и резервируемые серверы
Документация
Документируйте свой код, архитектуру и практикуемые методики. Создавайте плейбуки на случай разных происшествий
README, вики, Slack
Тесты
Пишите для своего инфраструктурного кода автоматические тесты. Выполняйте их после каждой фиксации кода и по ночам
Terratest, inspec, serverspec, kitchen-terraform
Большинству разработчиков известно о первых нескольких задачах: установке, конфигурации, инициализации и развертывании. А вот то, что идет дальше, застает людей врасплох. Например, подумали ли вы об устойчивости своего сервиса и о том, что произойдет в случае поломки сервера? А если выйдет из строя балансировщик нагрузки или весь вычислительный центр? Сетевые задачи тоже славятся своими подводными камнями: VPC, VPN, обнаружение сервисов и доступ по SSH — все это неотъемлемые элементы инфраструктуры, на подготовку которых могут уйти месяцы; но, несмотря на это, их часто полностью игнорируют при планировании проектов об оценке сроков. О задачах безопасности, таких как шифрование данных при передаче с помощью TLS, настройка аутентификации и выработка механизма хранения конфиденциальных данных, тоже часто вспоминают в последний момент.
Каждый раз, когда вы начинаете работать над новым участком инфраструктуры, не забывайте пройтись по этому списку. Не все пункты обязательные в каждом конкретном случае, но вы должны сознательно и явно документировать, какие компоненты вы реализовали, а какие решили пропустить и почему.
Инфраструктурные модули промышленного уровня
Теперь вы знаете, какие задачи необходимо выполнить для каждого элемента инфраструктуры. Поговорим о рекомендуемых подходах к построению универсальных модулей для реализации этих задач. Мы рассмотрим такие темы.
• Мелкие модули.
• Компонуемые модули.
• Тестируемые модули.
• Модули, готовые к выпуску.
• Модули вне Terraform.
Небольшие модули
Разработчики, которые только знакомятся с Terraform и IaC в целом, часто описывают всю свою инфраструктуру для всех окружений (Dev, Stage, Prod и т. д.) в едином файле или модуле. Как уже обсуждалось в разделе «Изоляция файлов состояния» на с. 112, это плохая идея. Я на этом не останавливаюсь и утверждаю следующее: большие модули, которые содержат более нескольких сотен строчек кода или развертывают больше нескольких тесно связанных между собой элементов инфраструктуры, должны считаться вредными.
Вот лишь некоторые из их недостатков.
•Большие модули медленны. Если вся ваша инфраструктура описана в одном модуле Terraform, выполнение любой команды будет занимать много времени. Мне встречались модули такого размера, что на выполнение команды terraformplan уходило 5–6 минут!
• Большие модули небезопасны. Если вся ваша инфраструктура описана в одном большом модуле, любое изменение потребует доступа ко всему коду. Это означает, что почти любой пользователь должен иметь права администратора, что противоречит принципу минимальных привилегий.
• Большие модули несут в себе риски. Если сложить все яйца в одну корзину, ошибка в любом месте может сломать все на свете. Например, при внесении небольшого изменения в клиентское приложение вы можете допустить опечатку или запустить не ту команду, в результате чего будет удалена ваша промышленная база данных.
• Большие модули сложно понять. Чем больше кода вы размещаете в одном месте, тем сложнее одному человеку его постичь целиком. А без понимания инфраструктуры, с которой вы работаете, можно допустить большую ошибку.
• Большие модули трудно разбирать. Разбор модуля, состоящего из нескольких десятков строк кода, не составляет труда. Разбор модуля, который состоит из нескольких тысяч строк кода, практически невозможен. Более того, это не только замедляет работу команды terraformplan, но и делает ее вывод настолько огромным, что никому не захочется его читать. А это означает, что никто не заметит ту небольшую красную строчку, которая предупреждает об удалении вашей базы данных.
•Большие модули сложно тестировать. Тестировать инфраструктурный код трудно, а если его очень много — то практически невозможно. Мы вернемся к этому в главе 7.
Если подытожить, ваш код должен состоять из небольших модулей, каждый из которых делает что-то одно. Это вовсе не новая или спорная идея. Вы много раз об этом слышали, только немного в другом контексте, как, например, в книге «Чистый код»51:
Первое правило функций: они должны быть компактными. Второе правило функций: они должны быть еще компактнее.
Роберт Мартин
Представьте, что вы используете язык программирования общего назначения, такой как Java, Python или Ruby, и вам попалась одна огромная функция длиной 20 000 строк:
def huge_function(data_set)
x_pca = PCA(n_components=2).fit_transform(X_train)
clusters = clf.fit_predict(X_train)
ax = plt.subplots(1, 2, figsize=(4))
ay = plt.subplots(0, 2, figsize=(2))
fig = plt.subplots(3, 4, figsize=(5))
fig.subplots_adjust(top=0.85)
predicted = svc_model.predict(X_test)
images_and_predictions = list(zip(images_test, predicted))
for x in 0..xlimit
ax[0].scatter(X_pca[x], X_pca[1], c=clusters)
ax[0].set_title('Predicted Training Labels')
ax[1].scatter(X_pca[x], X_pca[1], c=y_train)
ax[1].set_title('Actual Training Labels')
ax[2].scatter(X_pca[x], X_pca[1], c=clusters)
end
for y in 0..ylimit
ay[0].scatter(X_pca[y], X_pca[1], c=clusters)
ay[0].set_title('Predicted Training Labels')
ay[1].scatter(X_pca[y], X_pca[1], c=y_train)
ay[1].set_title('Actual Training Labels')
ay[2].scatter(X_pca[y], X_pca[1], c=clusters)