Перейти к основному содержимому

Практикум GitOps — drift и откат

Drift (расхождение) — когда состояние кластера отличается от Git. GitOps делает Git единственным источником правды: откат и исправления тоже идут через commit. Экстренные правки kubectl без записи в Git создают временный drift, который Argo CD может автоматически отменить.

Предварительно завершите шаг 3 с работающим rolling update.


Предварительные требования​

НастройкаГде проверить
selfHeal: trueargocd/demo-app.yaml или UI Application spec
automated.prune: trueтам же
Application Syncedargocd app get demo-nginx

Архитектура drift и self-heal​


1. Обнаружение drift​

Измените кластер в обход Git — увеличьте число реплик:

kubectl scale deployment/demo-nginx -n demo --replicas=5
kubectl get deployment demo-nginx -n demo

Ожидаемый вывод сразу после scale:

NAME READY UP-TO-DATE AVAILABLE AGE
demo-nginx 5/5 5 5 20m

Через несколько секунд при selfHeal: true Argo CD вернёт replicas: 2 из Git. В UI статус пройдёт OutOfSync → Syncing → Synced.

Проверка:

kubectl get deployment demo-nginx -n demo -o jsonpath='{.spec.replicas}{"\n"}'

Ожидаемое значение после self-heal: 2.

Вывод для практики: экстренный kubectl scale без commit — временная мера на минуты. Зафиксируйте желаемое число реплик в Git или отключите selfHeal осознанно (только для особых случаев на dev).

Другой пример drift — ручное изменение env:

kubectl set env deployment/demo-nginx -n demo DEMO=manual
argocd app diff demo-nginx

Diff покажет лишнюю переменную; после sync Argo CD удалит её.


2. Откат через Git​

Предпочтительный rollback в GitOps — новый commit, который отменяет проблемный.

cd infra-gitops-lab
git log --oneline -3
git revert HEAD --no-edit
git push origin main

Ожидаемое поведение:

  1. Argo CD видит новый SHA на main.
  2. Sync применяет предыдущий образ и конфигурацию из Git.
  3. UI History показывает revert commit.

Проверка образа:

kubectl get deployment demo-nginx -n demo -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'

Должен вернуться tag из commit до bump.

Способы отката​

СпособGit обновляетсяDrift после отката
git revert + pushДаНет — Git и кластер совпадают
kubectl rollout undoНетДа — кластер и Git расходятся
Rollback в Argo CD UI к старому Git SHAНет (если только UI)Зависит от sync policy

kubectl rollout undo допустим как break-glass на минуты, но затем нужен commit в Git с тем же образом, иначе следующий sync или selfHeal вернёт проблемную версию.


3. Sync policy для prod​

На dev удобна полная автоматика. На production часто включают ручной sync или окна деплоя.

PolicyКогда применять
automated + selfHealdev и staging
Manual sync + approvalproduction
Sync windowsзапрет deploy в нерабочие часы

Пример manual sync (без блока automated):

syncPolicy:
syncOptions:
- CreateNamespace=true

Пример sync window (фрагмент Application):

syncPolicy:
syncOptions:
- CreateNamespace=true
automated:
prune: true
selfHeal: true
# syncWindows задают в AppProject — см. документацию Argo CD

При manual sync оператор нажимает Sync в UI после review PR. Теория — GitOps.


4. Статусы OutOfSync и Degraded​

СтатусЗначение
OutOfSyncGit и live manifest различаются
SyncedСостояние применено из Git
HealthyРесурсы работают (Pod Running)
DegradedРесурс сломан (CrashLoop, failed probe)
ProgressingИдёт rollout или sync

Application может быть Synced и Degraded одновременно — Git применён, но Pod падает. Лечится правкой манифеста в Git, не только sync.


5. Очистка lab​

kind delete cluster --name gitops-lab
# или: minikube delete

Удалите port-forward процессы и локальный clone при необходимости.


Проверка шага 4​

  1. Выполните scale до 5 и убедитесь, что replicas вернулись к 2.
  2. Сделайте git revert после bump образа и проверьте tag в Deployment.
  3. Объясните разницу между OutOfSync и Degraded своими словами.

Устранение неполадок​

СимптомДействие
selfHeal не срабатываетПроверьте automated.selfHeal в Application
revert не меняет образКонфликт revert — решите в Git и push
Prune удалил лишний ресурсРесурс убрали из Git — ожидаемое поведение prune

Заметки по безопасности​

  1. Break-glass kubectl должен логироваться; после инцидента — commit в Git.
  2. Sync windows снижают риск деплоя в пик нагрузки.
  3. Секреты по-прежнему в Vault, не в infra-repo.

Итоги практикума​

Вы прошли цикл Git as source of truth → Argo CD → Kubernetes. Следующие шаги:

Итоги · Чек-лист


Sync windows — пример для prod​

Sync windows задают, когда automated sync разрешён. Конфигурация на уровне AppProject:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: prod-project
namespace: argocd
spec:
syncWindows:
- kind: allow
schedule: "0 9-17 * * 1-5"
duration: 8h
applications:
- demo-nginx
manualSync: true
- kind: deny
schedule: "0 0-9 * * *"
duration: 9h
applications:
- "*"

Расписание в cron — deploy только в рабочие часы. Manual sync в deny window заблокирован.


AppProject и RBAC​

Ограничьте, кто может sync production Application:

spec:
roles:
- name: deployer
policies:
- p, proj:prod-project:deployer, applications, sync, prod-project/demo-nginx, allow
groups:
- platform-team

Интеграция с SSO через Dex — за рамками lab, см. GitOps 8.12/4.


kubectl rollout undo — break-glass сценарий​

Если prod горит и revert в Git займёт минуты:

kubectl rollout undo deployment/demo-nginx -n demo
kubectl rollout status deployment/demo-nginx -n demo

Сразу после стабилизации — commit в Git с тем же образом, иначе drift:

# в infra-gitops-lab — верните image в deployment.yaml к working tag
git commit -am "fix: align Git with emergency rollback image"
git push

Finalizers и удаление Application​

Application с finalizer resources-finalizer.argocd.argoproj.io удаляет cascade ресурсы в demo при удалении Application. Lab cleanup:

kubectl delete application demo-nginx -n argocd
kubectl get all -n demo
# ресурсы demo-nginx удалены Argo CD

Мониторинг drift в production​

  1. Prometheus metrics Argo CD — argocd_app_info, argocd_app_sync_total.
  2. Alert на sync_status != Synced дольше N минут.
  3. Policy — запрет kubectl edit для dev без break-glass role.

DevSecOps — 8.12/3.


Расширенная проверка selfHeal​

Тест с ConfigMap (если добавите в Git):

kubectl edit configmap -n demo
# добавьте ключ в обход Git
sleep 30
kubectl get application demo-nginx -n argocd -o jsonpath='{.status.sync.status}{"\n"}'
# OutOfSync → Synced после selfHeal

Глоссарий шага 4​

ТерминОпределение
DriftРасхождение live state и Git
Self-healАвто-sync к Git при drift
PruneУдаление ресурсов, отсутствующих в Git
RevertНовый commit, отменяющий предыдущий
Break-glassЭкстренное ручное вмешательство с последующим fix в Git

Полный walkthrough drift и отката​

Раунд 1 — демонстрация selfHeal​

kubectl get deployment demo-nginx -n demo -o jsonpath='replicas={.spec.replicas}{"\n"}'
kubectl scale deployment/demo-nginx -n demo --replicas=5
kubectl get deployment demo-nginx -n demo
sleep 30
kubectl get deployment demo-nginx -n demo -o jsonpath='replicas={.spec.replicas}{"\n"}'
argocd app get demo-nginx

Ожидаемое поведение:

  1. Сразу после scale — replicas=5.
  2. Через 30–120 сек при selfHeal — replicas=2.
  3. Argo CD — Sync Status Synced, кратковременно был OutOfSync.

Раунд 2 — drift через env​

kubectl set env deployment/demo-nginx -n demo DRIFT_TEST=manual
argocd app diff demo-nginx

Ожидаемый diff — лишняя переменная DRIFT_TEST. После automated sync переменная исчезнет.

Раунд 3 — откат образа через git revert​

cd infra-gitops-lab
git log --oneline -3
git revert HEAD --no-edit
git push origin main
kubectl rollout status deployment/demo-nginx -n demo --timeout=120s
kubectl get deployment demo-nginx -n demo -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'

Ожидаемо — образ вернулся к версии до bump (например nginx:1.27-alpine).


Ожидаемый вывод argocd app diff при drift​

argocd app diff demo-nginx

Фрагмент при scale без Git:

===== apps/Deployment demo/demo-nginx ======
spec.replicas
- 2
+ 5

Пустой вывод после selfHeal — кластер совпал с Git.


Типичные ошибки шага 4​

СимптомОжидаемое при OKРешение
selfHeal не срабатывает 5+ минreplicas=2Проверьте automated.selfHeal: true в Application
revert конфликтpush успешенРазрешите конфликт в Git вручную
Prune удалил Serviceресурс был убран из GitВерните YAML в repo или отключите prune
OutOfSync постоянноSyncedПроверьте ignoreDifferences в Application
kubectl undo + driftGit ≠ clusterCommit с тем же образом в infra-repo

Предупреждения безопасности​

  1. Break-glass kubectl логируйте — после инцидента обязателен commit в Git.
  2. Sync windows снижают риск деплоя в пик нагрузки.
  3. Секреты — Vault, не в infra-repo.
  4. Manual sync на prod — без automated selfHeal до завершения review.
  5. Rollback в UI без Git commit создаёт временный drift — документируйте.

Чек-лист завершения практикума GitOps​

#НавыкКак проверить
1Git — source of truthОбъясните без kubectl apply на prod
2Application + auto-synckubectl get app demo-nginx -n argocd
3Релиз через commitHistory с bump commit
4Drift detectionscale → selfHeal → replicas=2
5Откат git revertобраз вернулся
6OutOfSync / Degradedразличаете статусы
7Sync policy prodmanual + windows — на словах

Итоги · Чек-лист


Скрипт проверки drift (bash)​

#!/bin/bash
# drift-check.sh — запускать после scale в обход Git
kubectl scale deployment/demo-nginx -n demo --replicas=5
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
R=$(kubectl get deployment demo-nginx -n demo -o jsonpath='{.spec.replicas}')
S=$(kubectl get application demo-nginx -n argocd -o jsonpath='{.status.sync.status}' 2>/dev/null)
echo "t=${i}0s replicas=$R sync=$S"
[ "$R" = "2" ] && echo "selfHeal OK" && exit 0
sleep 10
done
echo "FAIL: selfHeal не вернул replicas=2" && exit 1

Ожидаемый финал — selfHeal OK за 1–2 минуты.


Сравнение способов отката​

СпособGit обновляетсяDrift послеКогда использовать
git revert + pushданетПредпочтительный
git reset + force pushданетТолько dev, осторожно
kubectl rollout undoнетдаBreak-glass минуты
Argo UI rollback SHAзависитвозможенПосле fix в Git
Новый commit с old imageданетАльтернатива revert

Очистка lab — полная последовательность​

kubectl delete application demo-nginx -n argocd
kubectl delete namespace demo --wait=false
kubectl delete namespace argocd --wait=false
kind delete cluster --name gitops-lab
# остановите port-forward

Ожидаемо — kind delete завершается без ошибки, контекст kind-gitops-lab исчезает из kubeconfig.


Вопросы для самопроверки​

  1. Почему kubectl scale без Git — временная мера при включённом selfHeal?
  2. Чем git revert лучше kubectl rollout undo в GitOps модели?
  3. Когда Application Synced, но Degraded — что чинить первым?
  4. Зачем sync windows на production Application?
  5. Что делает prune: true при удалении YAML из Git?

Ответы должны ссылаться на Git как source of truth и на разделы 1–3 practicum.


Экспорт Application YAML для аудита​

kubectl get application demo-nginx -n argocd -o yaml > demo-nginx-export.yaml
grep -E 'selfHeal|prune|repoURL' demo-nginx-export.yaml

Ожидаемо — selfHeal true, prune true, ваш repoURL. Файл храните локально, не с секретами в public fork.