Автор статьи: Рустем Галиев

IBM Senior DevOps Engineer & Integration Architect. Официальный DevOps ментор и коуч в IBM

Привет, Хабр!

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

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

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

Архитектурные аспекты

Каждый раз, когда мы разрабатываем новое ИТ-решение, нам необходимо учитывать определенные архитектурные аспекты, которые являются неотъемлемой частью любого чертежа систем. Архитектурные аспекты включают в себя: Доступность, Масштабируемость, Отказоустойчивость, Наблюдаемость.

Доступность

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

Высокодоступная архитектура содержит компоненты, которые работают вместе, чтобы обеспечить следующие тактики доступности:

  • Обнаружение неисправностей: отвечает за обнаружение любых неисправностей в системе.

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

  • Предотвращение сбоев: поддерживает постоянную избыточность на обслуживающих и вспомогательных компонентах.

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

Масштабируемость

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

Существует четыре типа масштабирования ресурсов. Давайте рассмотрим каждый из них в следующем списке:

  • Scaling up: также известное как вертикальное масштабирование. Увеличивает размер ИТ-ресурса, чтобы повысить пропускную способность системы для нагрузки.

  • Scaling out: также известен как горизонтальное масштабирование. Он добавляет новые экземпляры ИТ-ресурсов для расширения возможностей системы.

  • Scaling down: это обратная сторона масштабирования. Уменьшает размер ИТ-ресурсов, чтобы уменьшить емкость системы для рабочей нагрузки.

  • Scaling in: это противоположно масштабированию. Он удаляет экземпляры ИТ-ресурсов, чтобы уменьшить нагрузку на систему.

Масштабируемость и облачная эластичность — разные свойства. В то время как масштабируемость относится к добавлению (или удалению) ИТ-ресурсов, эластичность — это способность адаптироваться к изменениям рабочей нагрузки путем автоматического создания или удаления облачных ресурсов.

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

Отказоустойчивость — это свойство ИТ-системы, инфраструктуры или приложения функционировать после значительного сбоя на уровне компонентов. Мы говорим, что архитектура устойчива, если она переживает незапланированные сбои.

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

Наблюдаемость

Как принцип SRE, наблюдаемость — это способность определять состояние ИТ-системы, инфраструктуры или приложения путем проверки их сигналов и выходных данных. Говорят, что архитектура доступна для наблюдения, если она предоставляет подсказки о своих внутренних условиях посредством данных метрик, событий, логов и трассировок (MELT).

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

Также говоря об архитектурных аспектах, нужно вспомнить и о The Twelve-Factor App.

The Twelve-Factor App (12-factor app) является методологией разработки приложений, разработанной для создания современных, масштабируемых и поддерживаемых веб-приложений. Она предоставляет набор рекомендаций и практик, которые помогают разработчикам создавать гибкие, устойчивые к изменениям и портативные приложения.

Вот основные принципы, определяющие The Twelve-Factor App:

  1. Код базируется на версиях системы контроля версий (Version Control): Приложение должно храниться в системе контроля версий, такой как Git, и каждое изменение должно быть отслеживаемым.

  2. Зависимости должны быть явными: Все зависимости приложения, включая библиотеки и внешние сервисы, должны быть явно объявлены и изолированы.

  3. Конфигурация должна быть отделена от кода: Конфигурация приложения, такая как настройки базы данных или внешних сервисов, должна храниться в отдельном месте и быть доступной для изменения без перекомпиляции кода.

  4. Среда выполнения должна быть разделена: приложение должно быть построено таким образом, чтобы оно было полностью самодостаточным и могло быть запущено в любой среде выполнения без изменений.

  5. Сборка, релиз и выполнение должны быть строго разделены: Процессы сборки (build), релиза (release) и выполнения (run) должны быть явно разделены. Каждый этап должен выполняться с использованием четко определенных и изолированных компонентов.

  6. Процессы должны быть масштабируемыми по горизонтали: Приложение должно масштабироваться путем добавления экземпляров, а не увеличения ресурсов одного экземпляра. Каждый экземпляр должен быть полностью заменяемым и одинаковым.

  7. Состояние должно быть storageless: приложение не должно сохранять состояние в локальных дисковых системах или памяти. Вместо этого, любое состояние должно храниться во внешних хранилищах, таких как базы данных или кеш-серверы.

  8. Сервисы должны быть легковесными и обрабатываемыми: приложение должно быть разбито на небольшие и автономные сервисы, которые могут обрабатывать только один тип задачи. Каждый сервис должен быть независимым и горизонтально масштабируемым.

  9. Параллельное развертывание: приложение должно быть развернуто в нескольких экземплярах (или же репликах), и каждое развертывание должно быть независимым. Новые версии приложения должны быть развернуты параллельно с предыдущими версиями, и откат к предыдущей версии должен быть возможен.

  10. Журналирование как поток событий: все события приложения, такие как журналирование ошибок или системных событий, должны быть потоковыми и не зависеть от файловой системы. Журналы должны быть обработаны и агрегированы в отдельном процессе.

  11. Административные задачи должны выполняться через one-time-процессы: любые административные задачи, такие как миграции базы данных или управление конфигурацией, должны быть выполнены через отдельный процесс, который запускается единожды и завершается после выполнения задачи.

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

Соблюдение принципов The Twelve-Factor App позволяет создавать приложения, которые легко масштабируются, поддерживаются и развиваются со временем. Эти принципы способствуют удобству разработки, развертывания и масштабирования приложений, а также помогают снизить сложность и изоляцию компонентов приложения.

Разделение и балансировка рабочей нагрузки

Как SRE, одна из наших основных задач — обеспечить разделение и балансировку рабочей нагрузки между общими ресурсами и вспомогательными службами. Разделение нагрузки влияет на надежность, поскольку оно изолирует сбои в небольших частях общей совокупности. Балансировка нагрузки также повышает надежность за счет выделения менее загруженных ресурсов для обработки нового запроса, что снижает задержку. Далее мы разберемся с каждой концепцией.

Разделение

Как правило, мы разделяем рабочую нагрузку на основе профилей потребителей. Мы можем использовать множество элементов критериев для разделения входящих запросов, но некоторые из них часто встречаются в проектах. Если решение представляет собой продукт с глобальным развертыванием, когда пользователи находятся во многих странах. Разделение нагрузки по географическим регионам является хорошей практикой. Несколько экземпляров приложения должны быть доступны в разных частях земного шара. Таким образом, если в какой-то области возникнет простой, это повлияет только на пользователей, обслуживаемых локальным экземпляром приложения.

Еще один способ разделения рабочей нагрузки — просмотр уровня пользовательских задач. В зависимости от задачи или операции она обрабатывается определенной группой ресурсов. Если что-то выходит из строя, это ставит под угрозу только подмножество функций приложения. Например, микросервисы для создания, чтения, обновления и удаления (CRUD) конечных точек, связанных с операциями продаж, выполняются в другой” ячейке”, чем корпоративные операции с персоналом.

Хотя разделение и балансировка — разные понятия, балансировщики нагрузки выполняют и то, и другое. Далее проверим балансировочную часть.

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

Давайте разберемся, как программное обеспечение балансировки нагрузки (или аппаратное обеспечение) решает, куда направить новый запрос или пользователя:

  • Round Robin: возможно, самый известный алгоритм, он равномерно распределяет нагрузку на всех рабочих. Немного углубимся в этот алгоритм: когда применяется Round Robin в балансировке нагрузки, каждый новый запрос или пакет данных направляется на следующий доступный сервер в циклическом порядке. После каждого запроса выбирается следующий сервер в списке, и процесс повторяется. Это позволяет распределить нагрузку равномерно между серверами и обеспечить более эффективное использование ресурсов. Если еще глубже копать, то работа алгоритма Round Robin основана на использовании квантов времени (time quantum) или временных интервалов. Каждая задача получает определенный квант времени для выполнения, и после истечения этого времени, управление передается следующей задаче в очереди. Если задача закончила свою работу до истечения кванта времени, она извлекается из очереди, и следующая задача получает свой квант времени.

  • Least connections: перенаправляет новый запрос или пользователя на воркер с наименьшим количеством подключений.

  • Least time: перенаправляет новый запрос или пользователя на рабочий процесс с наименьшей задержкой.

  • Hash: направляет новый запрос пользователя на основе функции хеш-кода. Эта функция вычисляет следующего рабочего на основе IP-адреса инициатора или других полей.

Балансировщики нагрузки

Балансировщик нагрузки (LB) — это специальная программная функция или аппаратное устройство, отвечающее за балансировку нагрузки. Есть много поставщиков и облачных провайдеров, которые предлагают такую возможность. Каждый из них имеет свою терминологию, но по сути у нас есть четыре типа балансировщиков нагрузки:

  • Аппаратные LB: это первые типы LB, появившиеся на рынке. Поскольку у них есть выделенные аппаратные процессоры, они могут обеспечить огромную пропускную способность.

  • Программно-определяемые LB: обычно присутствующие в гиперскейлерах, они представляют собой виртуальные функции внутри облачной платформы.

  • LB уровня приложения: также известные как LB уровня 7 (L7), они работают с протоколами HTTP и HTTPS. Некоторые поставщики также предлагают LB уровня 4 (L4) на основе протоколов TCP или UDP.

  • LB сетевого уровня: также известные как LB уровня 3 (L3), они работают по протоколу IP.

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

Архитектура является высокодоступной, если она автоматически переживает сбой компонента. Либо другой работающий компонент берет на себя рабочую нагрузку от неисправной части, либо новый элемент приводится в активное состояние для обработки нагрузки. Первая называется схемой «активный-активный», а вторая — «активный-резервный».

Когда один из экземпляров внутри кластера выходит из строя, LB перестает отправлять ему новые запросы. В активно-активном режиме LB выберет следующий рабочий экземпляр. Однако в режиме «активный-резервный» экземпляр с дефектом запускает процесс отработки отказа, который переводит резервный экземпляр в активное состояние, пока он пытается восстановить неисправный экземпляр.

Операция отработки отказа — это интенсивный вычислительный процесс, поскольку для этого необходимо выделить новый экземпляр с теми же ресурсами и наборами данных. Поскольку для завершения требуется некоторое время, пользователи почувствуют влияние в течение этого переходного периода. По этой причине предпочтительнее использовать активно-активный подход. С другой стороны, содержание нескольких активных экземпляров обходится дороже, так как требует больше ресурсов, а данные необходимо совместно использовать или реплицировать между экземплярами. Следовательно, не всегда возможно использовать схему «активный-активный», например, когда у нас есть большие наборы данных, репликация которых в двух местах требует непомерно больших затрат.

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

Если используется традиционный активный резерв, SRE должны сначала задокументировать процесс отработки отказа в виде модуля Runbook. Затем они должны разумно протестировать процесс аварийного переключения и автоматизировать всю процедуру. Самая сложная часть автоматизации аварийного переключения — это быстрое обнаружение, когда активный элемент находится в состоянии сбоя. Мы рекомендуем использовать подход с несколькими критериями, а не полагаться только на проверку связи, например. Кроме того, SRE рассматривают возможность передачи полномочий от одного участника к другому, когда сценарий определяет сбой. Мы можем применять шаблоны проектирования, чтобы сгладить отказоустойчивый переход для пользователей.

Масштабирование вверх и вниз — горизонтальное против вертикального

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

Горизонтальное масштабирование означает добавление экземпляров или узлов в инфраструктуру приложений. В LB будут новые участники, которые разделят рабочую нагрузку в группе балансировки нагрузки. Например, представьте, что у нас есть группа из трех виртуальных машин (ВМ) под LB, и мы масштабируем эти ВМ, если добавляем новую ВМ. Другими словами, мы увеличиваем количество воркеров, но не их мощность.

Преимущества горизонтального масштабирования: 

  • Непрерывная доступность, т.е. нет необходимости изменять любую работающую виртуальную машину или узел.

  • Нет ограничений из-за аппаратных возможностей.

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

Минусы:

  • Стоимость вызывает беспокойство, поскольку новые виртуальные машины или узлы стоят больше, чем просто дополнительное выделение ЦП.

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

Плюсы вертикального масштабирования следующие:

  • Оптимизация затрат, поскольку вы не увеличиваете количество ворукров.

Минусы вертикального масштабирования заключаются в следующем:

  • Изменение мощности не является плавным. Мы должны остановить текущие виртуальные машины или узлы и подключить новые с измененными требованиями к ресурсам.

  • Аппаратные или гипервизорные ограничения, поскольку не существует бесконечных ЦП или памяти.

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

Также не стоит забывать про автомасштабирование.

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

Например автомасштабирование Google Cloud Platform реализует политику автомасштабирования для группы экземпляров ВМ. Он может автоматически масштабировать количество ВМ в группе на основе показателей использования:

  • Целевое среднее использование ЦП

  • Объем обслуживания в фунтах стерлингов

  • Любые метрики целевого облачного мониторинга

Например будет выглядеть так:

resource "google_compute_autoscaler" "instance 1" {
...
  autoscaling_policy {
	max_replicas	= 7
	min_replicas	= 3
	cooldown_period = 60
	cpu_utilization {
  	target = 0.7
	}
  }
}

Данная политика автоматического масштабирования гласит, что если в целевой группе экземпляров используется более 70% vCPU, она будет масштабироваться путем добавления до 7 виртуальных машин.

Давайте применим данные требования к инфраструктуре и создадим ее в GCP

Для этого создадим файл main.tf со следующим содержимым

terraform {
  required_providers {
	google = {
  	source  = "hashicorp/google"
  	version = "3.5.0"
	}
  }
}

provider "google" {
  credentials = file("project-service-account-key.json")
  project = "project1"
  region  = "europe-central2"
  zone	= "europe-central2-a"
}

resource "google_compute_network" "vpc_network" {
  name = "project-network"
}

resource "google_compute_autoscaler" "foobar" {
  name   = "autoscaler"
  zone   = "europe-central2-a"
  target = google_compute_instance_group_manager.foobar.id
  autoscaling_policy {
	max_replicas	= 7
	min_replicas	= 3
	cooldown_period = 60
	cpu_utilization {
  	target = 0.7
	}
  }
}

resource "google_compute_network" "vpc_network" {
  name = "network"
}

resource "google_compute_instance_template" "foobar" {
  name       	= "instance-template"
  machine_type   = "e2-medium"
  can_ip_forward = false
  tags = ["foo", "bar"]
  disk {
	source_image = data.google_compute_image.cos_97.id
  }
  network_interface {
	network = google_compute_network.vpc_network.name
  }
  metadata = {
	foo = "bar"
  }
  service_account {
	scopes = ["userinfo-email", "compute-ro", "storage-ro"]
  }
}

resource "google_compute_target_pool" "foobar" {
  name = "target-pool"
}

resource "google_compute_instance_group_manager" "foobar" {
  name = "igm"
  zone = "europe-central2-a"
  version {
	instance_template  = google_compute_instance_template.foobar.id
	name           	= "primary"
  }

  target_pools   	= [google_compute_target_pool.foobar.id]
  base_instance_name = "foobar"
}

data "google_compute_image" "cos_97" {
  family  = "cos-97-lts"
  project = "cos-cloud"
}

module "load_balancer" {
  source  = "GoogleCloudPlatform/lb/google"
  version = "~> 2.2.0"
  region   	= "europe-central2-a"
  name     	= "load-balancer"
  service_port = 80
  target_tags  = ["target-pool"]
  network  	= google_compute_network.vpc_network.name
}

В этом блоке мы объявляем шесть различных ресурсов следующим образом:

  • Политика автомасштабирования (google_compute_autoscaler)

  • Сеть VPC (google_compute_network)

  • Шаблон инстанса (google_compute_instance_template)

  • Таргет пул (google_compute_target_pool)

  • Менеджер группы экземпляров (google_compute_instance_group_manager)

  • LB (балансировщик нагрузки)

    Project-service-account-key.json - тут вставляем свой ключ который создаем из GCP → IAM → Service Accounts → Manage Keys

    Более подробно.

Ну и четыре команды:

  • terraform init

  • terraform fmt

  • terraform validate

  • terraform apply

    You are breathtaking! 


Напоследок хочу порекомендовать открытый урок, посвященный вариантам отказоустойчивой архитектуры, который пройдет уже сегодня в 20:00. На встрече рассмотрим доступные способы создания надежных систем. Также обсудим, какие компромиссы приходится принимать, и какой стратегии придерживаться.

Комментарии (4)


  1. LaserPro
    22.06.2023 13:53

    Это кто-то читал? Тем кто еще не читал, советую и не читать.

    То ли кривой перевод, то ли сразу ChatGpt.
    Если все-таки перевод, то поделитесь ссылкой на оригинал, может там будет понятнее.

    Мы поможем вам определить, как мы разделяем и распределяем нагрузку между различными типами работников в центре обработки данных

    Каких работниках? зачем нам определять как вы разделяете?

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

    Учитывать и учитывать и учитывать? А что за чертежи?

    архитектура доступна для наблюдения, если она предоставляет подсказки и подсказки о своих внутренних условиях

    Нужно больше подсказок. Особенно об условиях.

    Код базируется на контролируемых версиях системы контроля версий

    we need to go deeper...

    Состояние должно быть бесхранилищным

    Бесхранилищный... это шедевр

    Сервисы должны быть легковесными и обработчиками.

    На улице шли двое - дождь и я.

    Приложение должно быть развернуто на нескольких экземплярах.

    На нескольких экземплярах чего?

    Административные задачи должны выполняться через один-time-процессы

    один-time-процессы - интересный термин. В копилку к "бесхранилищный".

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

    Первая задача дня - это вместо дейлика, каждое утро?

    даже боюсь читать дальше...


    1. MaxRokatansky Автор
      22.06.2023 13:53

      Не перевод и не ChatGPT. Тут третий вариант - автор иностранец, пишущий на нескольких языках. Русский для него не является родным, в связи с чем порой случаются непредсказуемые речевые обороты. Текст исправим и будем внимательнее, спасибо!


      1. lexore
        22.06.2023 13:53

        Автор плохо знает русский и поэтому вы доверили ему написать статью на русском без вычитки?) Даже плохо владеющий русским языком автор не будет писать "подсказки и подсказки" или "учитывать и учитывать". А так же вряд ли будет произвольно переводить на русский давно устоявшиеся термины вроде "бесхранилищным" (stateless), работников (workers) и "один-time-процессы".

        И потом. У автора в linkedin несколько лет опыта работы в Казахстане (г. Астана), а русский язык указан, как "Владение в совершенстве".

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


        1. MaxRokatansky Автор
          22.06.2023 13:53

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