Просто алерт. Просто Арго.
Прилетает алёрт: HPA maxed out.
HPA: keda-hpa-vmagent-scaler Cluster: stg-**-uswest1 Current value: 10 (max)
Сперва я вообще задумался - а нахрена этот алерт? Что он мне дает? Ну уперлось в максимум, и что? Все остальное работает ок, никаких других алертов.
Спросил у умных людей, умные люди дали умные советы, что может быть неверные триггеры трешхолда, может быть сервис в максимуме и скоро будут ошибки. Ладно, аргумент.
Первым делом смотрю что там с HPA:
NAME REFERENCE TARGETS MIN MAX REPLICAS
keda-hpa-vmagent-scaler VMAgent/... 5694m/40 (avg), memory: 50%/40% 2 10 10
130/256 = 50%. Цель триггера - 40%, то есть 102 MiB.
Это физически недостижимо - vmagent столько и держит в памяти просто чтобы работать, независимо от нагрузки. Горизонтальный скейлинг тут не поможет: добавишь реплик, каждая всё равно будет жрать те же 130 MiB.
Решение простое - поднять лимит. 384Mi > утилизация падает до 34%, HPA успокоится.
Правлю values в репозитории с ArgoCD-приложениями для кластера stg-*-uswest1:
resources:
limits:
memory: 384Mi
requests:
memory: 384MiOperation cannot be fulfilled on scaledobjects.keda.sh "vmagent-scaler": the object has been modified; please apply your changes to the latest version and try again. Retrying attempt #1
Классический 409. ArgoCD читает объект, хочет запатчить - а за это время KEDA уже успел его обновить.
Проверяю как часто это происходит:
kubectl -n vm get scaledobject vmagent-scaler -o jsonpath='{.metadata.resourceVersion}'
sleep 5
kubectl -n vm get scaledobject vmagent-scaler -o jsonpath='{.metadata.resourceVersion}'
# 157227839 → 157227898 за 5 секундKEDA пишет в ScaledObject каждые 1-2 секунды. Статусы, условия, метрики - всё туда.
Ретрай через 10, 20, 40 секунд не помогал - KEDA всегда успевал раньше.
В конфиге ApplicationSet уже был блок:
ignoreDifferences:
- group: keda.sh
kind: ScaledObject
managedFieldsManagers:
- keda-operator
- keda-metrics-adapterНо не работает. Пошёл смотреть кто реально владеет полями в объекте:
kubectl -n vm get scaledobject vmagent-scaler --show-managed-fields -o json manager: keda | op: Update manager: argocd-controller | op: Apply
А я что написал? keda-operator. А реальное имя - просто keda.
Дело в том, что ignoreDifferences влияет только на то, что ArgoCD показывает в дифе.
На сам apply - никак не влияет. ArgoCD всё равно патчит объект при синке.
Это я понял позже.
Изрядно помучавшись с другими подобными попытками я снова пришел к умным людям, которые снова дали умные советы, в том числе сервер сайд апплай.
Почитал документацию, вроде красиво, ок.
Логика была такая: с SSA ArgoCD перестаёт посылать resourceVersion в патче, значит 409 уйдёт.
Добавил ServerSideApply=true в syncOptions для vm-приложений.
Тут выяснился интересный нюанс. В ApplicationSet у нас базовый шаблон уже имел ServerSideApply=true:
syncPolicy:
syncOptions:
- ServerSideApply=trueНо в templatePatch для vm-секции был такой кусок:
syncPolicy:
syncOptions:
- RespectIgnoreDifferences=trueИ он молча перезаписывал базовый список вместо того чтобы добавить к нему.
Итого vm-приложения жили без SSA всё это время, хотя казалось что SSA включён. 🤡
Добавил ServerSideApply=true явно в vm-секцию. Применил. Синканул.
Please review the fields above--they currently have other managers. Please re-run the apply command with the --force-conflicts flag.
Хм. Это уже не 409 от гонки - это SSA ownership conflict.
Стало хуже. 🙃
- До SSA: транзиентная 409, иногда сама проходила с третьей попытки.
- После SSA: постоянный конфликт владения полями, не проходит никогда.
Смотрю что именно KEDA записывает в managedFields:
kubectl get <resource> --show-managed-fields -o json \ | jq -r '.metadata.managedFields[].manager'
KEDA через CSA (client-side apply) владеет spec.advanced.scalingModifiers.
ArgoCD через SSA тоже хочет применить spec.advanced (оно есть в Helm-темплейте).
Добавил jsonPointers для этого поля:
jsonPointers: - /metadata/resourceVersion - /metadata/finalizers - /spec/advanced/scalingModifiers - /status
Потому что ignoreDifferences + RespectIgnoreDifferences=true - это "не синкай ресурс если разница ТОЛЬКО в этих полях". Но если ресурс синкается по другой причине (а он синкался, потому что менялись лимиты памяти в VMAgent) - ArgoCD применяет объект целиком.
Думаю, ну раз нужен --force-conflicts, может есть способ сказать ArgoCD "применяй этот ресурс с --force-conflicts"?
Добавил в Helm-темплейт ScaledObject:
annotations: argocd.argoproj.io/sync-options: Force=true
Тут я вовремя остановился и проверил что это вообще делает.
Force=true в ArgoCD - это delete + recreate ресурса при каждом синке.
Не --force-conflicts для SSA. Это вообще другое и довольно опасное.
Что реально сработало
Пока я всё это ковырял, читал документацию, смотрел менеджед поля и логи, понял в чём корень.
Проблема - смешанный ownership: KEDA пишет в ScaledObject через CSA (старый client-side apply), ArgoCD пытается применить через SSA. Когда два разных механизма клеймят одно поле - кубернетис говорит "разберитесь между собой".
И ignoreDifferences тут не поможет никак - он только про вычисление диффа, не про применение.
Самое простое решение - удалить ScaledObject и дать ArgoCD пересоздать его с нуля через SSA.
- ArgoCD - единственный SSA-owner всех полей в объекте
- KEDA потом пишет scalingModifiers через CSA - это его поле, арго его не трогает
- Поля не пересекаются > конфликта нет
kubectl -n vm delete scaledobject vmagent-scaler
ArgoCD пересоздал. Синк прошёл с первого раза. Всё зелёное. 🎉
Следом HPA отскейлился обратно - vmagent с новым лимитом 384Mi держит ~34% утилизации, ниже порога 40%.
Через 10 минут (stabilizationWindowSeconds: 600 на scaleDown) реплики упали с 10 до 2.
Что поправили в итоге
В Terraform-модулях - ApplicationSet для всех mt-кластеров:
- Добавили
ServerSideApply=trueявно в vm-секциюtemplatePatch(чтобы он не терялся при override) - Добавили keda в managedFieldsManagers (было только keda-operator и keda-metrics-adapter, реального имени менеджера не было)
- Добавили
/spec/advanced/scalingModifiersи/metadata/finalizersв jsonPointers - теперь ArgoCD корректно игнорирует эти поля при диффе и не показывает ложные OutOfSync
В репозитории с ArgoCD-приложениями - values для stg-mt-uswest1:
Итоги
- ignoreDifferences - только про отображение диффа. Не про apply. Всегда.
- CSA + SSA на одном объекте = смешанный ownership = проблемы. Лечится пересозданием.
- Имена менеджеров надо проверять в реальном объекте, не угадывать:
kubectl get <resource> --show-managed-fields -o json \ | jq -r '.metadata.managedFields[].manager'
- templatePatch в ApplicationSet переписывает поля целиком, а не мержит. Если в базовом шаблоне есть syncOptions: [ServerSideApply=true], а в templatePatch ты пишешь syncOptions: [RespectIgnoreDifferences=true] - SSA пропадает молча.
- Иногда самый быстрый путь - удалить объект и дать системе пересоздать его в правильном состоянии.
Несколько часов потратил на то, что решилось одной командой kubectl delete.