Ныряем в готовые кластеры Kubernetes с Deckhouse и werf
Российские облачные провайдеры начали предоставлять неплохие managed-решения для Kubernetes. Однако многие из них требуют доводки до ума и установки большого количества компонентов, направленных на сбор логов, мониторинг и доступ к кластеру. Это вынуждает пользователей собирать свой собственный бандл с Prometheus, Grafana и т.д., что крайне неудобно и требует дополнительных усилий.
Вот и я, столкнувшись с Managed Kubernetes от Selectel, захотел использовать что-то готовое, желательно от российских разработчиков. Я обратил внимание на платформу Deckhouse, которую к тому моменту можно уже было ставить в готовые кластеры k8s. В этой статье я расскажу про свой путь интеграции D8 и werf в инфраструктуру Selectel и те проблемы, с которыми столкнулся в процессе.
Я прекрасно осведомлен, что 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 — узлы общего назначения, для приложений.
Чтобы просто запустить приложение, то есть для минимального кластера, будет достаточно нескольких нод из группы Worker. Однако создание продакшена потребует разделения ролей, а также настройку автоскейлинга. Именно поэтому важно создание нод группы System — в отличие от Worker, на них будет постоянный набор компонентов и эта группа будет относительно стабильна.
В различных примерах конфигурации, которые я приведу в статье, будут показаны примеры как для минимального кластера, так и production-ready решения. Они будут разделены по файлам с суффиксами:
- simple — для простого кластера,
- ha — для отказоустойчивого решения.
Для своего проекта я выбрал следующую конфигурацию:
- два Frontend-узла,
- три Worker-узла,
- один System-узел.
Создание инфраструктуры
При создании инфраструктуры я решил сосредоточиться на повторяемости своих действий. Поэтому меньше действий руками, больше — через утилиты!
Воспользуемся 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:
Установим С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
Внимательный читатель заметит, что у нас есть нестандартные настройки для 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 минут.
Установим С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, который раз в час перезапускает поды.
Документация по обоим чартам находится в связанных репозиториях.
Продолжим погружение
Чего же нам не хватает для полной утилизации инфраструктуры Selectel? Конечно же, быстрой сборки и доставки приложений в новоиспеченный кластер.
На текущий момент ноды кластера Selectel работают на относительно старом образе с Ubuntu 18.04 и ядром четвертой ветки. Это не позволит нам полностью раскрыть werf вкупе с Gitlab Kubernetes runner. По имеющейся у меня информации, вскоре образ для нод будет обновлен. До тех пор сборку будем производить на выделенных виртуальных машинах с Gitlab Runner. Чтобы их настроить, укажите количество раннеров в Terraform. Также потребуется активировать создание bastion хоста. Сами раннеры можно легко сконфигурировать с помощью Ansible. Шаг с установкой werf на Gitlab Runner я намеренно пропускаю, так как он и так отлично расписан в официальной документации.
Когда сборочная инфраструктура будет готова, перейдем к созданию Kubeconfig для работы werf. Можно воспользоваться статьей на сайте Deckhouse.
С начала года Selectel начал предоставлять Container Registry as a Service (CRaaS) в облаке. Это значит, что доставка приложений будет еще быстрее, ведь скачка образов происходит в рамках одной инфраструктуры по быстрым сетям провайдера. Но не все так просто — об этом сервисе мы поговорим отдельно.
werf и CRaaS
Статья писалась продолжительное время, во время бета-тестирования CRaaS. Описанные проблемы были успешно решены с техподдержкой Selectel. В werf добавлены подсказки для работы с Container Registry от Selectel.
Создадим реестр и получим к нему доступ. Подробнее в документации Selectel.
Создадим простейший проект с 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.
К счастью, это можно исправить. У CRaaS был развитый API, который позволял сделать очистку. API позволяло:
- быстро получить список тегов и репозиториев,
- сделать очистку образов,
- получить токен для доступа к Registry.
Сходу нашлась и проблема, которую можно отнести к проблемам реализации Container Registry от Selectel: werf позволял класть образы в корень репозитория — например, cr.selcloud.ru/example:67a03ef931a263cd83c0fd3dcd7d4468f12d44e13755dad35e3f, но ни API, ни личный кабинет облака не отображали подобные образы. Баг-репорт я отправил в техподдержку Selectel, а в werf добавил подсказку.
К слову, на стороне Selectel сейчас тоже существует проверка:
Мне же в тот момент пришлось пересоздать реестр — других вариантов не было.
Пришлось скорректировать 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, с чьего разрешения я и публикую эти наработки.