Как сделать Kubernetes еще круче: секреты безупречной работы

56bec617385e7b97c9a77fbbf7c177bc.jpeg

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

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

Привет, Хабр, меня зовут Артур Мечетин, и в этой статье мы со Станиславом Столбовым из Byndyusoft расскажем о том, как повысили стабильность приложений в К8s кластерах с высокой критичностью для бизнеса.

Сделали мы это на продукте «Система управления складом»: приемка, хранение, сборка и отгрузка. Данные процессы поддерживают разные команды с единым процессом CI/CD, но разными зонами ответственности. Мы стремимся улучшать производительность систем и отказоустойчивость, потому что сталкивались с проблемами доступности наших продуктовых решений из-за ошибок технических специалистов. Вследствие этого задались вопросами повышения качества инфраструктуры. Одним из решений стало использование нескольких K8s кластеров с применением Istio-Multicluster.

Микросервисы, которые мы создаем, работают в K8s кластере, интегрированы друг с другом, с DBaaS (PostgreSQL, Kafka, Mongo, Rеdis и др.) и приложениями других команд. Всем важно сохранять высокий SLI. 5-минутная остановка сервиса грозит многомиллионными потерями для бизнеса. 

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

1414a1910322e2183a586ea9524db067.jpeg

Но что если на 45 этаже произошел пожар? Паника начинается на соседних этажах, а потом может распространиться и на другие. А что если на водоканале отключили воду во всем микрорайоне? Точно так же проблемы могут происходить и с K8s  кластерами. Как только происходит что-то непредвиденное, пользователи становятся недовольны и это влияет на весь бизнес. Проблемы случаются, ни одна IT-компания не гарантирует SLA 100%. Даже такой гигант, как Yandex Cloud, гарантирует SLA (Service Level Agreement, уровень обслуживания) на уровне 99,9% для облачных сервисов (до 8h 41m 38s в год).

Несколько раз непредвиденные обстоятельства происходили и в нашей инфраструктуре. Это побудило нас исследовать технологию объединения K8s кластеров Istio Multicluster.

Ссылки:

https://istio.io/

Задачи, которые решает Istio

Задачу поделили на несколько этапов.

  • Подготовка proof of concept и проверка гипотез

  • Доработка CI/CD

  • Подготовка среды

  • Дистрибуция сервисов

Базовая модель работы сетевой архитектуры на проекте с несколькими микросервисами выглядела следующим образом.

Балансировщик принимает от пользователей запросы и направляет в кластер. Далее сайдкар Envoy (часть Istio, запущенный как Sidecar-контейнер в каждом Pod«е) для Ingress-Controller перехватывает запрос, обогащая полезными штуками (Mutual TLS, observability, Circuit Breaker), направляет в сторону Pod«а приложения. Решение, куда направить запрос, принимается на основе информации, которую получает Envoy от Istio-Сontrol при любом изменении в контроллере. (Подробнее о принципах работы Istio можно почитать в статье https://habr.com/ru/companies/oleg-bunin/articles/726958/)

Proof-of-Concept

Два имеющихся кластера в разных ДЦ объединяли в Istio Multicluster.

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

  1. Включили модуль istio для Deckhouse.

    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: istio
    spec:
      version: 2
      enabled: true
  2. На Namespace приложения в двух кластерах установили Label istio-injection: enabled, чтобы Istio подключал свои сайдкары для каждого Pod в рамках Namespace.

  3. Включили Istio Multicluster в модуле Istio для Deckhouse с помощью параметра istio.spec.settings.multicluster.enabled = true.

  4. Для Ingress-Controller«а включили Istio Sidecar с помощью параметра
    ingressnginxcontroller.spec.enableIstioSidecar = true.

  5. Для двух кластеров в модуль kube-dns добавили общий clusterDomainAliases.
    Было:

    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: kube-dns
    spec:
      version: 1
      enabled: true
      settings:
        clusterDomainAliases:
        - cluster.local

    Стало:

    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: kube-dns
    spec:
      version: 1
      enabled: true
      settings:
        clusterDomainAliases:
        - cluster.local
        - alpha.p.mesh
  1. Для двух кластеров в модуль control-plane-manager добавили в общий certSAN.

    Было:

    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: control-plane-manager
    spec:
      version: 1
      enabled: true
      settings:
        apiserver:
          certSANs:
          - kubernetes.default.svc.cluster.local

    Стало:

    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: control-plane-manager
    spec:
      version: 1
      enabled: true
      settings:
        apiserver:
          certSANs:
          - kubernetes.default.svc.cluster.local
          - kubernetes.default.svc.alpha.p.mesh

Важно: после операции API-сервер начинает перезапускаться до тех пор, пока не применится новая конфигурация.

  1. Установили общий кластерный домен alpha.p.mesh для обоих кластеров с помощью dhctl. 

    Пример запуска:

    kubectl -n d8-system exec -ti deploy/terraform-auto-converger -- dhctl config edit cluster-configuration

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

  1. Связали кластеры с помощью CR IstioMulticluster.

Далее мы написали Helm templates для этого в репозитории с приложениями Argo CD, и это позволило нам переиспользовать конфиг для других кластеров.

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

Схема работы:

d7687d99614c475ad4ae0663c0d3697c.gif

В случае если микросервис в зоне A недоступен, система Istio перенаправляет запросы на работающий кластер в зоне B. Ни один запрос не пропадает.

4cd1cd1ac39e187d8b955c3bad8540d2.gif

До использования Multicluster рабочий pipeline CI/CD умел деплоить только в один кластер, нужно было гарантировать доставку во второй. Сейчас сервисы последовательно заливаются в два кластера в разных дата-центрах, предусмотрели роллбэк в случае fail. Для ускорения и более качественного CD-процесса деплой должен быть параллельным + Canary deploy + Blue/Green, но это пока не реализовано.

Подготовка инфраструктуры с имеющимися Argo CD, permissions, RBAC, Nginx, Prometheus rules: требуют много согласований, т.к. много внешних интеграций/зависимостей. Все межсервисные интеграции у нас решаются через api gateway (Kong), команда сама управляет api своих приложений. Сложно было объяснять коллегам, что у нас что-то меняется (т.к. пользователей много), поэтому большую часть работы проводили бесшовно. Юзеры=разработчики заливали свои микросервисы, не обращая внимания, что сервисы параллельно теперь живут в новом кластере до какого-то времени.

Готовность проекта к переключению в мультикластерность

Итак, команда разработчиков обучена, все микросервисы синхронизированы между кластерами, подготовлены новые точки доступа в приложения, настроены алерты в новом кластере. Пользователи системы должны начать использовать новые урлы для работы с системой. Тут тоже предусмотрен CI/CD, который обновляет приложения на терминалах (Android-устройства), это сильно помогло.

Предпоследний этап: пользователи начали работать через новый урл. Успех. Всем спасибо. 

Стресс-тест: останавливаем приложения в одном из кластеров, наблюдаем за работой приложений.

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

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

Что хочется, но не получается: Istio — ценный инструмент, по умолчанию предоставляет много полезного. Но observability недостаточно для настроек алертинга. Хотелось использовать Istio для подсчета SLO, т. к., считаю, это самый правильный вариант. Но в текущем варианте метрик от Istio не хватает: нет деления по URI, по методам.

Оверхед: инфраструктурных ресурсов требуется больше, за это приходится платить. У всего есть своя цена. ИБ-долг рассчитывается на каждую VM с приложением, поэтому количество долга выросло, хотя это мотивирует на работу с уязвимостями в приложениях.

Есть несколько важных моментов, которые рекомендую предусмотреть при использовании Istio-Multicluster.

  • Настраивайте Locality Failover, это поможет избежать большого трафика между кластерами.

  • При большом числе микросервисов и кластере контролируйте, какие из них должны использовать Istio. Мы делаем это лейблом на namespace, но также можно внутри неймспейса точечно отключить/включить с помощью лейбла sidecar.istio.io/inject на pod.

  • Ограничьте рассылку информации от Istio к envoy только на нужные namespace через CRD Sidecar, сводка по умолчанию отправляется на все pod«ы envoy и может перегрузить сеть и controlplane.

  • Запуск Istio контейнера должен происходить до запуска проверок основного пода.
    Deckhouse feature 

    annotations:
      proxy.istio.io/config: |-
        holdApplicationUntilProxyStarts: true
  • Остановка Istio контейнера должна происходить после остановки основного пода
    Deckhouse feature

    annotations:
      inject.istio.io/templates: sidecar,d8-hold-istio-proxy-termination-until-application-stops

Какие трудности возникали

  • При попытке настроить PoC два дня пытались понять, в чем проблема, когда сервисы упорно не хотели видеть друг друга. Дебаг показал, что в одном из кластеров версия Istio 1.13, в другом — 1.16. После обновления до 1.16 проблема ушла.

  • Некоторые сервисы долго обрабатывают sigterm, Istio останавливается сразу: нужно предусмотреть, чтобы Istio sidecar ждал остановки основного pod«a.

  • На некоторых сервисах не настроили Locality Failover, и при сетевом сбое получили недоступность 50% запросов на несколько минут.

  • При включенном Locality Failover трафик распределялся неравномерно между подами в одном кластере ввиду особенностей приоритетов балансировки Istio и Envoy. В нашем случае ноды в одном кластере находились в разных зонах. Поэтому Istio считал, что поды одного приложения не локальны друг к другу и отправлял больше запросов к 2 подам в одной зоне, пересылая немного запросов на 3-й pod.

    Мы решили это сменой приоритета наtopology.istio.io/network, единого для всего кластера. Чуть подробнее про настройку приоритетов можно прочитать тут.

    Пример конфигурации:

    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: example
    spec:
      host: example.host
      trafficPolicy:
        loadBalancer:
          localityLbSetting:
            enabled: true
            failoverPriority:
            - "topology.istio.io/network"

Ресурсы вне K8s

Postgresql также разнесли по разным ЦОД, использовали кластер Patroni + etcd.

Kafka, mongo пока оставлены без внимания, т. к. менее приоритетны, но, скорее всего, дойдет очередь и до них.

Итог

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

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

© Habrahabr.ru