2.7 GitOps: Git как единый источник истины

2.7 GitOps: Git как единый источник истины #

🎯 Что такое GitOps простыми словами #

GitOps — это методология, где Git репозиторий содержит полное описание желаемого состояния вашей инфраструктуры и приложений. Специальные агенты автоматически синхронизируют реальное состояние с тем, что описано в Git.

🚗 Аналогия с автопилотом #

Традиционный CI/CD = водитель:
👨‍💼 CI/CD system → kubectl apply → Kubernetes
     ↓                    ↓             ↓
   "Толкает"        "Надеется"    "Может сломаться"

GitOps = автопилот:  
📋 Git → ArgoCD постоянно проверяет → Kubernetes
   ↓           ↓                          ↓
"Описание"  "Умный агент"            "Всегда синхронно"

🔄 Принципы GitOps #

1. Declarative - Декларативное описание #

# Описываем ЖЕЛАЕМОЕ состояние, не шаги
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3  # Хотим 3 пода
  template:
    spec:
      containers:
      - name: app
        image: myapp:v1.2.0  # Хотим эту версию

2. Versioned - Все под версионным контролем #

# Вся инфраструктура в Git
infrastructure-repo/
├── apps/
│   ├── frontend/
│   │   └── deployment.yaml
│   └── backend/
│       └── deployment.yaml
├── infrastructure/
│   ├── monitoring/
│   └── ingress/
└── environments/
    ├── dev/
    ├── staging/
    └── prod/

3. Immutable - Неизменяемость #

❌ kubectl edit deployment webapp  # Изменение напрямую

✅ Изменение через Git:
1. Изменить YAML в репозитории
2. Создать Pull Request  
3. После merge - ArgoCD применит изменения автоматически

4. Pulled - Агент сам забирает изменения #

Traditional Push Model:
CI/CD → проталкивает изменения → Kubernetes

GitOps Pull Model:  
Git ← ArgoCD постоянно проверяет ← Kubernetes

🛠️ ArgoCD: Популярный GitOps инструмент #

🟢 Установка ArgoCD в Kubernetes #

# Создать namespace
kubectl create namespace argocd

# Установить ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Проверить статус подов
kubectl get pods -n argocd

# Получить пароль админа
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

# Port forwarding для доступа к UI
kubectl port-forward svc/argocd-server -n argocd 8080:443

Доступ к ArgoCD UI #

# Открыть браузер: https://localhost:8080
# Логин: admin
# Пароль: (из команды выше)

🎯 Практический пример: Деплой приложения через GitOps #

Структура Git репозитория #

my-app-gitops/
├── applications/
│   └── webapp/
│       ├── deployment.yaml
│       ├── service.yaml
│       └── ingress.yaml
├── argocd-apps/
│   └── webapp-app.yaml
└── environments/
    ├── dev/
    │   └── kustomization.yaml
    ├── staging/
    │   └── kustomization.yaml
    └── prod/
        └── kustomization.yaml

🟢 Базовое приложение #

# applications/webapp/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: nginx:1.21
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  selector:
    app: webapp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP

ArgoCD Application манифест #

# argocd-apps/webapp-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: webapp
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  
  # Источник - Git репозиторий
  source:
    repoURL: https://github.com/username/my-app-gitops
    targetRevision: HEAD
    path: applications/webapp
  
  # Назначение - Kubernetes кластер
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  
  # Политики синхронизации
  syncPolicy:
    automated:
      prune: true      # Удалять ресурсы, которых нет в Git
      selfHeal: true   # Восстанавливать изменения вне Git
    syncOptions:
    - CreateNamespace=true

Применение ArgoCD приложения #

# Применить ArgoCD app
kubectl apply -f argocd-apps/webapp-app.yaml

# Посмотреть статус
kubectl get applications -n argocd

# Посмотреть детали
kubectl describe application webapp -n argocd

🟡 Продвинутый пример: Multi-environment с Kustomize #

Базовая конфигурация #

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 1  # Базовое значение
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:latest  # Будет переопределена
        ports:
        - containerPort: 8080
        env:
        - name: ENVIRONMENT
          value: "base"
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- deployment.yaml
- service.yaml

commonLabels:
  version: v1.0.0

Development окружение #

# environments/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: dev

resources:
- ../../base

patchesStrategicMerge:
- deployment-patch.yaml

images:
- name: myapp
  newTag: dev-latest

commonLabels:
  environment: development
# environments/dev/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: webapp
        env:
        - name: ENVIRONMENT
          value: "development"
        - name: DEBUG
          value: "true"
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"

Production окружение #

# environments/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: production

resources:
- ../../base

patchesStrategicMerge:
- deployment-patch.yaml

images:
- name: myapp
  newTag: v1.2.3  # Стабильная версия

commonLabels:
  environment: production
# environments/prod/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3  # Больше реплик в prod
  template:
    spec:
      containers:
      - name: webapp
        env:
        - name: ENVIRONMENT
          value: "production"
        - name: DEBUG
          value: "false"
        resources:
          requests:
            memory: "128Mi"
            cpu: "250m"
          limits:
            memory: "256Mi"
            cpu: "500m"

ArgoCD Apps для разных окружений #

# argocd-apps/dev-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: webapp-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/username/my-app-gitops
    targetRevision: HEAD
    path: environments/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

🟠 App of Apps Pattern #

Управление множественными приложениями #

# app-of-apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: app-of-apps
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/username/platform-gitops
    targetRevision: HEAD
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
# apps/monitoring.yaml  
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: monitoring
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/username/platform-gitops
    targetRevision: HEAD
    path: monitoring
  destination:
    server: https://kubernetes.default.svc
    namespace: monitoring
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

🔴 Продвинутые возможности ArgoCD #

Custom Health Checks #

# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  resource.customizations.health.argoproj.io_Rollout: |
    hs = {}
    if obj.status ~= nil then
      if obj.status.replicas ~= nil and obj.status.updatedReplicas ~= nil and obj.status.availableReplicas ~= nil then
        if obj.status.replicas == obj.status.updatedReplicas and obj.status.replicas == obj.status.availableReplicas then
          hs.status = "Healthy"
          hs.message = "Rollout is healthy"
          return hs
        end
      end
    end
    hs.status = "Progressing"
    hs.message = "Waiting for rollout to finish"
    return hs

Pre/Post Sync Hooks #

apiVersion: batch/v1
kind: Job
metadata:
  name: database-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
  template:
    spec:
      containers:
      - name: migration
        image: migrate/migrate
        command: ["migrate", "-path", "/migrations", "-database", "postgres://...", "up"]
      restartPolicy: Never

Sync Waves для упорядочивания #

# Сначала применить ConfigMaps
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  annotations:
    argocd.argoproj.io/sync-wave: "1"
data:
  config.yaml: |
    database:
      host: postgres
---
# Потом Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  annotations:
    argocd.argoproj.io/sync-wave: "2"
spec:
  # ... спецификация

🔄 Flux: Альтернатива ArgoCD #

Flux v2 архитектура #

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  Source         │    │  Kustomize       │    │  Helm           │
│  Controller     │    │  Controller      │    │  Controller     │
│                 │    │                  │    │                 │
│ Git, Helm Repo, │    │ Kustomizations   │    │ Helm Releases   │
│ Bucket Sources  │    │                  │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                        │                        │
         └────────────────────────┼────────────────────────┘
                                  │
                    ┌─────────────────────────┐
                    │   Notification          │
                    │   Controller            │
                    │                         │
                    │ Slack, Discord,         │
                    │ Webhook notifications   │
                    └─────────────────────────┘

Установка Flux #

# Установить Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash

# Проверить prerequisite  
flux check --pre

# Bootstrap в Git репозиторий
flux bootstrap github \
  --owner=username \
  --repository=fleet-infra \
  --branch=main \
  --path=./clusters/my-cluster \
  --personal

Flux GitRepository и Kustomization #

# GitRepository resource
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: webapp-source
  namespace: flux-system
spec:
  interval: 1m
  ref:
    branch: main
  url: https://github.com/username/webapp-config
---
# Kustomization resource
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: webapp
  namespace: flux-system
spec:
  interval: 10m
  path: "./kustomize"
  prune: true
  sourceRef:
    kind: GitRepository
    name: webapp-source
  targetNamespace: default

🔐 Безопасность в GitOps #

Sealed Secrets для безопасного хранения секретов #

# Установка Sealed Secrets controller
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.18.0/controller.yaml

# Установка kubeseal CLI
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.18.0/kubeseal-0.18.0-linux-amd64.tar.gz
tar -xzf kubeseal-0.18.0-linux-amd64.tar.gz
sudo install -m 755 kubeseal /usr/local/bin/kubeseal

Создание SealedSecret #

# Создать обычный Secret
echo -n mypassword | kubectl create secret generic mysecret --dry-run=client --from-file=password=/dev/stdin -o yaml > mysecret.yaml

# Зашифровать Secret
kubeseal -f mysecret.yaml -w mysealedsecret.yaml

# Теперь можно безопасно коммитить mysealedsecret.yaml в Git
# mysealedsecret.yaml (пример результата)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: mysecret
  namespace: default
spec:
  encryptedData:
    password: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEQAx...
  template:
    metadata:
      name: mysecret
      namespace: default

RBAC для GitOps операторов #

# ArgoCD RBAC
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argocd
data:
  policy.default: role:readonly
  policy.csv: |
    # DevOps team - full access
    g, devops-team, role:admin
    
    # Developers - read only
    g, developers, role:readonly
    
    # Only senior developers can sync
    g, senior-developers, role:sync
    
    # Custom role for app-specific access
    p, role:app-admin, applications, *, */*, allow
    p, role:app-admin, repositories, *, *, allow
    g, app-team, role:app-admin

📊 Мониторинг GitOps #

Prometheus метрики ArgoCD #

# ServiceMonitor для ArgoCD
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: argocd-metrics
  namespace: argocd
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: argocd-metrics
  endpoints:
  - port: metrics

Grafana дашборд для ArgoCD #

{
  "dashboard": {
    "title": "ArgoCD Overview",
    "panels": [
      {
        "title": "Application Health",
        "type": "stat",
        "targets": [
          {
            "expr": "argocd_app_health_status",
            "legendFormat": "{{name}} - {{health_status}}"
          }
        ]
      },
      {
        "title": "Sync Status",
        "type": "piechart",
        "targets": [
          {
            "expr": "count by (sync_status) (argocd_app_info)",
            "legendFormat": "{{sync_status}}"
          }
        ]
      }
    ]
  }
}

Alerting правила #

# PrometheusRule для ArgoCD
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: argocd-alerts
spec:
  groups:
  - name: argocd
    rules:
    - alert: ArgoCDAppNotSynced
      expr: argocd_app_info{sync_status!="Synced"} == 1
      for: 15m
      annotations:
        summary: "ArgoCD Application не синхронизировано"
        description: "Application {{ $labels.name }} в статусе {{ $labels.sync_status }} более 15 минут"
    
    - alert: ArgoCDAppUnhealthy
      expr: argocd_app_info{health_status!="Healthy"} == 1
      for: 5m
      annotations:
        summary: "ArgoCD Application нездоровое"
        description: "Application {{ $labels.name }} в статусе {{ $labels.health_status }}"

🚀 CI/CD Integration с GitOps #

GitHub Actions для обновления манифестов #

# .github/workflows/update-manifests.yml
name: Update K8s manifests

on:
  push:
    branches: [main]

jobs:
  update-manifests:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout app repo
      uses: actions/checkout@v3
    
    - name: Build and push Docker image
      run: |
        docker build -t myapp:${{ github.sha }} .
        docker push myapp:${{ github.sha }}
    
    - name: Checkout config repo
      uses: actions/checkout@v3
      with:
        repository: username/k8s-config
        token: ${{ secrets.CONFIG_REPO_TOKEN }}
        path: k8s-config
    
    - name: Update image tag
      run: |
        cd k8s-config
        sed -i "s|image: myapp:.*|image: myapp:${{ github.sha }}|" environments/staging/deployment.yaml
        
    - name: Commit and push
      run: |
        cd k8s-config
        git config user.name "GitHub Actions"
        git config user.email "actions@github.com"
        git add .
        git commit -m "Update image to ${{ github.sha }}"
        git push

ArgoCD Image Updater #

# Автоматическое обновление образов
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: webapp
  annotations:
    argocd-image-updater.argoproj.io/image-list: myapp=registry.com/myapp
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/git-branch: main
spec:
  # ... остальная конфигурация

🎯 Типичные проблемы и решения #

Проблема: Drift Detection #

# Кто-то изменил что-то напрямую в кластере
kubectl edit deployment webapp
# replicas: 2 → 3

# ArgoCD обнаружит расхождение и покажет "Out of Sync"
# Решение: включить selfHeal для автоматического восстановления

Проблема: Секреты в Git #

❌ Проблема: Где хранить database пароли?

✅ Решения:
1. Sealed Secrets - шифрование в Git
2. External Secrets Operator - интеграция с Vault/AWS Secrets
3. SOPS - шифрование файлов в Git
4. ArgoCD Vault Plugin

Проблема: Environment Promotion #

# Автоматический promotion между средами
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: webapp-prod
  annotations:
    argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
  source:
    repoURL: https://github.com/username/k8s-config
    targetRevision: HEAD
    path: environments/prod
  syncPolicy:
    automated:
      prune: false    # Осторожнее в prod!
      selfHeal: false # Ручной контроль

🏆 Лучшие практики GitOps #

1. Repository Structure #

✅ DO:
- Отдельные репозитории для кода и конфигурации
- Логическая структура директорий
- Consistent naming conventions
- Environment-specific branches/directories

❌ DON'T:
- Смешивать application code и k8s manifests
- Один монолитный репозиторий для всего
- Hardcoded values в манифестах

2. Security #

✅ DO:
- Encrypted secrets (Sealed Secrets, SOPS)
- RBAC для GitOps операторов  
- Branch protection rules
- Signed commits для critical changes

❌ DON'T:
- Plain text secrets в Git
- Over-permissive RBAC
- Direct cluster access
- Unreviewed changes в prod

3. Monitoring #

✅ DO:
- Monitor sync status и health
- Alert на drift и failures
- Dashboard для операционной видимости
- Audit logs для compliance

❌ DON'T:
- Игнорировать Out of Sync статусы
- Отсутствие мониторинга GitOps операторов
- Manual interventions без логирования

🎯 Практические задания #

🟢 Задание 1: Базовый GitOps #

  1. Установить ArgoCD в minikube
  2. Создать Git репозиторий с простым приложением
  3. Настроить ArgoCD Application
  4. Сделать изменение через Git и проверить автосинхронизацию

🟡 Задание 2: Multi-environment #

  1. Создать структуру с base и overlays
  2. Настроить dev, staging, prod окружения
  3. Использовать разные image tags для сред
  4. Настроить promotion pipeline

🟠 Задание 3: Secrets Management #

  1. Установить Sealed Secrets controller
  2. Создать encrypted секреты
  3. Использовать их в приложениях
  4. Настроить rotation секретов

🔴 Задание 4: Production Setup #

  1. Настроить RBAC и security policies
  2. Создать мониторинг и алертинг
  3. Implement blue-green deployment
  4. Настроить disaster recovery

🔗 Полезные ресурсы #

Документация #

Инструменты #

  • Kustomize - шаблонизация Kubernetes манифестов
  • Sealed Secrets - безопасное хранение секретов
  • SOPS - шифрование файлов

Обучение #

🎯 Заключение #

GitOps — это эволюция DevOps практик для cloud-native мира. Основные преимущества:

Single Source of Truth — Git как единственный источник истины
Declarative — описываем желаемое состояние
Auditable — полная история изменений
Secure — контролируемый доступ через Git
Recoverable — простое восстановление из Git

Помните: GitOps не заменяет CI/CD, а дополняет его. CI занимается сборкой и тестированием, GitOps — деплоем и управлением состоянием.


Следующий раздел: 2.10 Service Mesh