Всем привет, в этом посте я покажу вам обходной путь для решения проблемы в старой версии Operator SDK (если быть точным, v0.18.2).
Мы пишем собственных операторов для множества платформенных проектов. Когда мы начинаем писать операторы, есть версия v0.18.2. И в этой версии мы разработали несколько операторов.
Проблема возникает при парсинге метрик оператора, если оператор запускает более одного модуля.
Ваши показатели выглядят так:
Похоже, что 2 наших модуля не могут обслуживать метрики, и это приводит к появлению ошибок «в соединении отказано» на prometheus.
Описание проблемы - ошибка метрики
Когда вы запускаете оператор, созданный Operator SDK (v0.18.2), есть некоторые автоматически сгенерированные ресурсы для сбора показателей от нашего оператора.
Эти ресурсы:
- ServiceMonitor (очистка Прометея)
- Услуга
Давайте проверим эти ресурсы. Имя нашего оператора - «платформа-оператор».
Как видите, ресурс ServiceMonitor находит операторов, использующих службу с меткой «name: platform-operator».
В ресурсе Service мы видим, что он выбирает поды с помощью селектора name: platform-operator.
Это означает, что наш скребок prometheus отправляет запросы любому модулю, имеющему метку «name: platform-operator».
Я сказал, что ошибка возникает, когда вы запускаете оператор более чем на одном модуле. Но почему?
Чтобы понять это, нам нужно взглянуть на работающие модули операторов и исходный код.
Когда вы запускаете оператор с более чем одним модулем (скажем, 3), он выбирает один модуль в качестве ведущего. А другие поды работают в состоянии ожидания (чтобы стать лидером).
Оператор создает ConfigMap, чтобы зафиксировать лидерство. Таким образом, это гарантирует, что только один модуль будет работать в качестве лидера.
А другие модули (не являющиеся лидерами) печатают эти журналы:
Все идет нормально. Мы понимаем, что 2 пода работают в состоянии ожидания. Но это не объясняет ошибку «в соединении отказано». Это здоровые стручки, и они успешно бегают.
Итак, давайте попробуем отправить запрос на конечную точку метрик внутри этих модулей.
Сначала мы отправляем запрос на лидерный блок:
kubectl exec -it -n platform platform-operator-6c2b12hh3 -- curl localhost:8383/metrics
Мы успешно получили метрики.
Во-вторых, давайте попробуем отправить тот же запрос другим модулям операторов:
Хорошо, мы столкнулись с той же ошибкой, что и в Prometheus.
Но почему? Почему эти капсулы отказываются подключаться?
Давайте посмотрим на исходный код нашего оператора.
В автоматически сгенерированном файле cmd/manager/main.go
есть часть кода:
Мы видим, что оператор пытается стать лидером, а затем запускает код addMetrics(ctx, cfg)
. Не забываем, что наш оператор начинает работать после строки err = leader.Become(ctx, "platform-operator-lock")
.
Функция addMetrics отвечает за создание ресурсов Service и ServiceMonitor:
Мы знаем, что 2 модуля непрерывно печатают журнал «Не лидер. Ожидающий".
Итак, давайте заглянем внутрь функции leader.Become.
В этой функции мы видим цикл, который постоянно пытается выбрать лидера, и здесь мы видим наш журнал «Не лидер. Жду ».
Это объясняет, почему другие модули отказываются от соединений. Они не запускаются! Они все еще ждут, чтобы стать лидером. Они находятся в цикле на строке err = leader.Become(ctx, "platform-operator-lock")
.
Решение
В версии Operator SDK v0.18.2 нет обходного пути. Таким образом, вы можете рассмотреть возможность обновления вашего оператора до более новой версии. Но, как и в нашем случае, если невозможно обновить его в ближайшее время, вы можете реализовать обходной путь, который мы сделали.
Мы подумали, что если для запросов метрик может обслуживаться только лидерный модуль, мы каким-то образом сможем перенаправить весь запрос метрики в лидерский модуль.
С этой точки зрения мы решили добавить дополнительную метку к группе лидеров. Также мы помещаем эту метку в селектор ресурса Service.
Чтобы решить указанную проблему, мы реализовали следующую логику:
После метода addMetrics
мы получаем модуль лидера и маркируем его словом «leader = true», затем обновляем модуль.
После этого мы обновляем Сервис, добавляя метку «leader = true» в поле селектора.
Таким образом, мы гарантируем, что запросы метрики будут поступать только к ведущему модулю.
Спасибо, что дочитали до сих пор.
Вы можете найти реализацию кода обхода в этом репозитории: