Ныряем в готовые кластеры Kubernetes с Deckhouse и werf

ntfdn1m2pyv21lzjn_5c7uji3jg.png

Российские облачные провайдеры начали предоставлять неплохие managed-решения для Kubernetes. Однако многие из них требуют доводки до ума и установки большого количества компонентов, направленных на сбор логов, мониторинг и доступ к кластеру. Это вынуждает пользователей собирать свой собственный бандл с Prometheus, Grafana и т.д., что крайне неудобно и требует дополнительных усилий.

Вот и я, столкнувшись с Managed Kubernetes от Selectel, захотел использовать что-то готовое, желательно от российских разработчиков. Я обратил внимание на платформу Deckhouse, которую к тому моменту можно уже было ставить в готовые кластеры k8s. В этой статье я расскажу про свой путь интеграции D8 и werf в инфраструктуру Selectel и те проблемы, с которыми столкнулся в процессе.

wyufzyghnf4wrkfnt_brg2mwbbg.png
Я прекрасно осведомлен, что EE-версия не требует столько усилий и запускается поверх облака Selectel с cloud provider OpenStack, однако данная опция мне недоступна. Поэтому работать будем с тем, что есть, а именно — с облачной платформой, Managed Kubernetes и Container Registry.


Подсчет инфраструктуры


Будем считать, что у нас уже есть Git-хранилище. Я предпочитаю использовать GitLab, поэтому дальнейшая интеграция будет именно с ним.

Для начала рассчитаем ресурсы. В идеологии Deckhouse есть разделение узлов на конкретные роли:

  • Master — узлы Control Plane. На них крутится ETCD и K8S Apiserver. В Selectel они будут от нас скрыты.
  • System — узлы для системных компонентов. Сюда встанут различные компоненты мониторинга. Сюда же стоит вынести и другие кластерные компоненты — например, контроллер Container Storage Interface (CSI). Скорее всего это будут узлы со значительным объемом памяти.
  • Frontend — узлы для ingress и всяческого приема трафика. На них запускается Ingress, VPN и тому подобное.
  • Worker — узлы общего назначения, для приложений.

buchpztky0je1lvgs6hcwmwomr4.png
Чтобы просто запустить приложение, то есть для минимального кластера, будет достаточно нескольких нод из группы Worker. Однако создание продакшена потребует разделения ролей, а также настройку автоскейлинга. Именно поэтому важно создание нод группы System — в отличие от Worker, на них будет постоянный набор компонентов и эта группа будет относительно стабильна.


В различных примерах конфигурации, которые я приведу в статье, будут показаны примеры как для минимального кластера, так и production-ready решения. Они будут разделены по файлам с суффиксами:

  • simple — для простого кластера,
  • ha — для отказоустойчивого решения.


Для своего проекта я выбрал следующую конфигурацию:

  • два Frontend-узла,
  • три Worker-узла,
  • один System-узел.


Создание инфраструктуры


При создании инфраструктуры я решил сосредоточиться на повторяемости своих действий. Поэтому меньше действий руками, больше — через утилиты!

buchpztky0je1lvgs6hcwmwomr4.png
Воспользуемся Terraform — стандартом индустрии. Тем более у облака Selectel есть свой terraform provider в актуальном состоянии. Он поможет нам исключительно с PaaS-сервисами, такими как базы, кластера k8s. Для работы с виртуальными машинами и сетью в облаке нам потребуется еще и OpenStack terraform provider. Также у Selectel есть отличный репозиторий с примерами — его мы и будем использовать при создании виртуальной инфраструктуры.


В ходе работ я выяснил, что пример создания проекта с ключом не работает. О чем впоследствии был заведен issue.

Запустим наш проект и получим готовый кластер:

terraform init
env TF_VAR_sel_token=yyy_xxx TF_VAR_user_password=xxx terraform plan
env TF_VAR_sel_token=yyy_xxx TF_VAR_user_password=xxx terraform apply


Подготовка кластера Deckhouse


Теперь, когда мы получили готовый кластер, можно подготовить его к установке Deckhouse. Чтобы провести установку, нам потребуется создать Storage Class для нашего региона. Я собрал все это в один Chart, который:

  • при первичной установке создает Storage Class для кластера, исходя из зоны,
  • при повторной установке создает Ingress, Auth provider и другие ресурсы D8.


Скачаем конфигурацию кластера из панели Selectel:

ybfvrsgk-d-jpxqbqtiwx6rlbpy.png


Установим Сhart в кластер:

export KUBECONFIG=~/.kube/d8test.yaml
werf converge --skip-build


Установка Deckhouse


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

apiVersion: deckhouse.io/v1
kind: InitConfiguration
deckhouse:
  releaseChannel: Alpha
  bundle: Managed
  configOverrides:
    global:
      modules:
        publicDomainTemplate: '%s.d8test.example.com'
      storageClass: "fast.ru-3a"
    certManagerEnabled: true
    deckhouseWebEnabled: true
    chronyEnabled: false
    userAuthnCrdEnabled: true
    userAuthnEnabled: true
    userAuthzEnabled: true
    namespaceConfiguratorEnabled: true
    namespaceConfigurator:
      configurations:
      - annotations:
          extended-monitoring.flant.com/enabled: "true"
        includeNames:
        - "production"
        - "loki-logging"
    dashboard:
      accessLevel: ClusterAdmin
      auth:
        allowScale: true
        externalAuthentication:
          authURL: http://auth.dashboard-auth.svc.cluster.local/_tech/is_auth
          authSignInURL: https://dashboard.d8test.example.com/_tech/auth
          useBearerTokens: true

wyufzyghnf4wrkfnt_brg2mwbbg.pngВнимательный читатель заметит, что у нас есть нестандартные настройки для Kube Dashboard. К этому мы вернемся позже. Примеры конфигураций можно найти тут.

Произведем установку:

docker run --pull=always -it -v "$PWD/config.yml:/config.yml" \
  -v "$HOME/.kube/d8test.yaml:/kubeconfig" registry.deckhouse.io/deckhouse/ce/install:alpha bash
dhctl bootstrap-phase install-deckhouse --kubeconfig=/kubeconfig --config=/config.yml


Deckhouse устанавливается без проблем за 5 минут.

fnfkqjy7cky7_cf6htdhvxre7p4.png


Установим Сhart в кластер еще раз, чтобы развернулись недостающие ресурсы:

export KUBECONFIG=~/.kube/d8test.yaml
werf converge --skip-build


После применения оставшихся ресурсов останется дождаться получения прямого IP нашим кластером:

export KUBECONFIG=~/.kube/d8test.yaml
kubectl –n d8-ingress get svc


После чего потребуется направить на него DNS-запись *.d8test.example.com для компонентов.

Но что мы получим на выходе?

Managed K8S + D8 = ❤


А на выходе — полнофункциональный кластер с мониторингом, cert-manager, Ingress на основе nginx и возможностью сбора логов встроенным агентом на базе Vector.

Конечно, не обошлось и без ложки дегтя. Так как control plane контролирует Selectel, у нас нет возможности настроить multitenancy или же сконфигурировать Dex как OIDC-провайдер. Итого, что не работает:

  • Периодически отваливается сбор логов с ошибкой версии объекта:
  • Watcher Stream received an error. Retrying. error=WatchError (ErrorResponse { status: «Failure», message: «too old resource version: 114794284 (114797507)», reason: «Expired», code: 410 }).
  • Нет авторизации в Kubernetes API через Dex.
  • Не работает incluster VPN.


К счастью, это можно обойти. В Dashboard есть возможность внешней авторизации — именно ее я и использовал, сделав специальный Chart c nginx, который возвращает в Dashboard token. Установить его можно командой werf converge.

С логами я разобрался аналогичным образом — создав Chart, который раз в час перезапускает поды.

Документация по обоим чартам находится в связанных репозиториях.

1k7bto4ll6kmlckzsvnaox4oupc.png

Продолжим погружение


Чего же нам не хватает для полной утилизации инфраструктуры Selectel? Конечно же, быстрой сборки и доставки приложений в новоиспеченный кластер.

На текущий момент ноды кластера Selectel работают на относительно старом образе с Ubuntu 18.04 и ядром четвертой ветки. Это не позволит нам полностью раскрыть werf вкупе с Gitlab Kubernetes runner. По имеющейся у меня информации, вскоре образ для нод будет обновлен. До тех пор сборку будем производить на выделенных виртуальных машинах с Gitlab Runner. Чтобы их настроить, укажите количество раннеров в Terraform. Также потребуется активировать создание bastion хоста. Сами раннеры можно легко сконфигурировать с помощью Ansible. Шаг с установкой werf на Gitlab Runner я намеренно пропускаю, так как он и так отлично расписан в официальной документации.

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

buchpztky0je1lvgs6hcwmwomr4.png
С начала года Selectel начал предоставлять Container Registry as a Service (CRaaS) в облаке. Это значит, что доставка приложений будет еще быстрее, ведь скачка образов происходит в рамках одной инфраструктуры по быстрым сетям провайдера. Но не все так просто — об этом сервисе мы поговорим отдельно.


werf и CRaaS

wyufzyghnf4wrkfnt_brg2mwbbg.png
Статья писалась продолжительное время, во время бета-тестирования CRaaS. Описанные проблемы были успешно решены с техподдержкой Selectel. В werf добавлены подсказки для работы с Container Registry от Selectel.


Создадим реестр и получим к нему доступ. Подробнее в документации Selectel.

dhalyz3bfqyptefvqe5oli2c4li.png


Создадим простейший проект с Pipeline в Gitlab:

variables:
  WERF_REPO: ${SEL_REGISTRY_DOMAIN}/${SEL_REGISTRY}

before_script:
  - type trdl && . $(trdl use werf 1.2 ea)
  - werf version
  - type werf && source $(werf ci-env gitlab --as-file)
  - werf cr login ${SEL_REGISTRY_DOMAIN}

after_script:
  - werf cr logout ${SEL_REGISTRY_DOMAIN}

stages:
  - deploy

.base_deploy:
  stage: deploy
  script:
    - werf converge
  except: [schedules]
  tags: [werf]

Deploy to test:
  extends: .base_deploy
  environment:
    name: test
  when: manual

Deploy to production:
  extends: .base_deploy
  environment:
    name: production
  when: manual


Кроме того, нам потребуются несколько переменных в CI/CD GitLab:

  • WERF_KUBECONFIG_BASE64 — его мы получили в предыдущем параграфе,
  • SEL_REGISTRY_DOMAIN со значением cr.selcloud.ru
  • SEL_REGISTRY c именем реестра — в нашем случае example,
  • WERF_USERNAME со значением token,
  • WERF_PASSWORD с токеном, который мы получили из панели Selectel.


На удивление, все заработало сразу и без проблем. Однако на первых порах образы были не оптимизированы и предоставленные в рамках беты бесплатные 20 ГБ довольно быстро закончились. Встал вопрос очистки.

В Pipeline добавился блок очистки, аналогичный представленному в документации werf:

stages:
  - deploy
  - cleanup

.base_clean_up:
  stage: cleanup
  script:
    - werf cleanup
             rules:
             - if: $CI_PIPELINE_SOURCE == "schedule"
               when: always
  tags: [werf]
  resource_group: cleanup

Clean up:
  extends: .base_clean_up


В этот момент стало понятно, что werf не поддерживает очистку данной реализации Registry.

suihbxcs6uuanxskn-kihyeajzm.png


К счастью, это можно исправить. У CRaaS был развитый API, который позволял сделать очистку. API позволяло:

  • быстро получить список тегов и репозиториев,
  • сделать очистку образов,
  • получить токен для доступа к Registry.


Сходу нашлась и проблема, которую можно отнести к проблемам реализации Container Registry от Selectel: werf позволял класть образы в корень репозитория — например, cr.selcloud.ru/example:67a03ef931a263cd83c0fd3dcd7d4468f12d44e13755dad35e3f, но ни API, ни личный кабинет облака не отображали подобные образы. Баг-репорт я отправил в техподдержку Selectel, а в werf добавил подсказку.

v3nfbgpskijootdfz9zytudaimg.png


К слову, на стороне Selectel сейчас тоже существует проверка:

h5ff5yws6fb_co_fcq_yfqiycb0.png


Мне же в тот момент пришлось пересоздать реестр — других вариантов не было.
Пришлось скорректировать Pipeline, исправив переменную WERF_REPO:

variables:
  WERF_REPO: ${SEL_REGISTRY_DOMAIN}/${SEL_REGISTRY}/${CI_PROJECT_PATH}


Так и появилась первая реализация CRaaS в werf и добавлен раздел в документацию.

На этом приключения не закончились. В реализации были не учтены некоторые вещи:

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


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

.base_clean_up:
  stage: cleanup
  variables:
    WERF_PARALLEL: 0
  script:
    - werf cleanup
  rules:
  - if: $CI_PIPELINE_SOURCE == "schedule"
    when: always
  tags: [werf]
  resource_group: cleanup


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

Осенью столкнулся с другой бедой — любое удаление образа из Registry приводило к полной блокировке реестра. Пришлось перенастроить работу garbage collector на стороне GitLab, перенеся очистку на глубокую ночь по Москве. Сообщил о проблеме техподдержке Selectel, и они оперативно ее исправили.

Неделю назад CRaaS вышел в коммерческий релиз, что сопровождается обновлением API. Поэтому в разработке третья версия работы с CRaaS для werf. Stay tuned!

Итоговый Pipeline, который я использую у себя в проекте, вы сможете найти на GitHub.

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

© Habrahabr.ru