MFormations
Modern DevOps Engineering

Chapitre 13

13 - GitOps

13 - GitOps

Chapitre 13 : GitOps

13.1 Introduction a GitOps

13.1.1 Definition et principes

Le GitOps est une methodologie de gestion d'infrastructure et d'applications qui utilise Git comme source de verite unique. Les trois principes fondamentaux sont :

  1. Declaratif : L'etat desire du systeme est decrit de maniere declarative
  2. Versionne : L'etat desire est stocke dans Git (historique, audit, rollback)
  3. Synchronisation automatique : Un operateur maintient en permanence l'etat du cluster conforme a Git

13.1.2 GitOps vs CI/CD traditionnel

AspectCI/CD TraditionnelGitOps
Source de veritePipeline CIRepository Git
Mode de deploiementPush (CI push vers cluster)Pull (operateur pull depuis Git)
RollbackCommande manuelleGit revert
AuditLogs de pipelineGit history
Acces clusterCI a besoin d'accesOperateur a acces, pas CI
Derive de configurationPossibleDetectee et corrigee

13.1.3 Architecture GitOps

                         Git Repository
                              │
                    Git Push / Pull Request
                              │
                              v
                    GitOps Operator
                   (ArgoCD / Flux)
                      │     │
                      v     v
              ┌────────┴────────┐
              │   Kubernetes    │
              │   Cluster       │
              └─────────────────┘

13.2 ArgoCD

13.2.1 Architecture ArgoCD

ArgoCD est un GitOps operator CNCF graduate :

User ──> ArgoCD API / UI ──> Git Repository
              │                    │
              │               Poll / Webhook
              │                    │
              v                    v
         Kubernetes ────────── Sync Status
              │
              v
         Applications (Deployments, Services, etc.)

Composants :

  • API Server : Interface gRPC/REST
  • Repository Server : Clone et cache les repos Git
  • Application Controller : Maintient la synchronisation
  • Redis : Cache

13.2.2 Installation

# Installation avec Helm
helm repo add argo https://argoproj.github.io/argo-helm
helm upgrade --install argocd argo/argo-cd \
  --namespace argocd --create-namespace \
  --set server.ingress.enabled=true \
  --set server.ingress.hosts=["argocd.example.com"]

# Obtenir le mot de passe admin
kubectl get secret argocd-initial-admin-secret \
  -n argocd -o jsonpath="{.data.password}" | base64 -d

13.2.3 Application et ApplicationSet

Application simple :

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/company/myapp-config.git
    targetRevision: main
    path: kubernetes/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - PruneLast=true

ApplicationSet (multi-cluster/multi-env) :

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-multi-env
spec:
  generators:
    - list:
        elements:
          - cluster: production
            url: https://kubernetes.default.svc
            namespace: prod
          - cluster: staging
            url: https://staging-cluster.example.com
            namespace: stg
  template:
    metadata:
      name: 'myapp-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/company/myapp-config.git
        targetRevision: main
        path: 'kubernetes/overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: '{{namespace}}'

13.2.4 Sync Waves et Hooks

Les Sync Waves permettent d'ordonner le deploiement :

# ConfigMap (wave 0)
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  annotations:
    argocd.argoproj.io/sync-wave: "0"
data:
  config.yaml: |
    key: value
---
# Deployment (wave 1)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  annotations:
    argocd.argoproj.io/sync-wave: "1"
spec:
  template:
    spec:
      containers:
        - name: app
          image: myapp:latest
---
# Service (wave 2)
apiVersion: v1
kind: Service
metadata:
  name: myapp
  annotations:
    argocd.argoproj.io/sync-wave: "2"
spec:
  ports:
    - port: 80

PreSync / PostSync Hooks :

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
        - name: migration
          image: myapp-migration:latest
          command: ["alembic", "upgrade", "head"]
      restartPolicy: Never

13.2.5 Rollback

# Rollback via CLI
argocd app rollback myapp 3

# Rollback via UI
# Applications > myapp > HISTORY AND ROLLBACK > Select revision > ROLLBACK

# Rollback via Git (recommandé)
git revert HEAD
git push origin main
# ArgoCD va automatiquement synchroniser

13.3 Flux

13.3.1 Architecture Flux

Flux est un GitOps operator developpe par Weaveworks :

Git Repository ──> Source Controller
                        │
                   Notification Controller
                        │
              ┌─────────┴─────────┐
              │                   │
     KustomizeController    HelmController
              │                   │
              v                   v
         Kubernetes          Helm Releases

Composants :

  • Source Controller : Gere les sources (Git, Helm, S3)
  • KustomizeController : Applique les manifests Kustomize
  • HelmController : Gere les releases Helm
  • NotificationController : Notifications Slack, Teams, etc.

13.3.2 Installation

# Installation avec CLI
curl -s https://fluxcd.io/install.sh | bash
flux bootstrap github \
  --owner=company \
  --repository=flux-config \
  --path=clusters/production \
  --personal

# Verification
flux check

13.3.3 KustomizeController

# kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 5m
  path: ./kubernetes/overlays/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: myapp-source
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: myapp
      namespace: production
  postBuild:
    substitute:
      environment: production
      region: eu-west-1

13.3.4 HelmController

# helm-release.yaml
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: myapp
  namespace: production
spec:
  interval: 5m
  chart:
    spec:
      chart: myapp
      sourceRef:
        kind: HelmRepository
        name: myapp-charts
      interval: 1h
  values:
    replicaCount: 3
    image:
      tag: 1.2.3
    ingress:
      enabled: true
      hosts:
        - myapp.example.com

13.3.5 NotificationController

# notification.yaml
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Alert
metadata:
  name: slack-alerts
  namespace: flux-system
spec:
  providerRef:
    name: slack
  eventSeverity: info
  eventSources:
    - kind: Kustomization
      name: '*'
---
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Provider
metadata:
  name: slack
  namespace: flux-system
spec:
  type: slack
  address: https://hooks.slack.com/services/T00/B00/xxxxx

13.4 Kustomize

13.4.1 Structure de projet

Kustomize permet de personnaliser des manifests sans templates :

kubernetes/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   └── service.yaml
└── overlays/
    ├── development/
    │   ├── kustomization.yaml
    │   └── patch-replicas.yaml
    └── production/
        ├── kustomization.yaml
        ├── patch-replicas.yaml
        └── ingress.yaml

13.4.2 Base

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

resources:
  - deployment.yaml
  - service.yaml

commonLabels:
  app: myapp
  managed-by: kustomize

commonAnnotations:
  version: 1.0.0
  environment: base
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: app
          image: myapp:latest
          ports:
            - containerPort: 8080

13.4.3 Overlays

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base
  - ingress.yaml

patches:
  - path: patch-replicas.yaml
    target:
      kind: Deployment
      name: myapp

configMapGenerator:
  - name: app-config
    literals:
      - ENVIRONMENT=production
      - LOG_LEVEL=info

images:
  - name: myapp
    newTag: 1.2.3

namespace: production
# overlays/production/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
# overlays/production/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
spec:
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp
                port:
                  number: 80

13.5 Helm

13.5.1 Structure d'un Chart

myapp-chart/
├── Chart.yaml
├── values.yaml
├── values.production.yaml
├── templates/
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   ├── hpa.yaml
│   └── tests/
│       └── test-connection.yaml
└── charts/
    └── redis-*.tgz

13.5.2 Chart.yaml et valeurs

# Chart.yaml
apiVersion: v2
name: myapp
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "1.16.0"

dependencies:
  - name: redis
    version: "17.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled
# values.yaml
replicaCount: 1

image:
  repository: myregistry.io/myapp
  tag: latest
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: false

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 200m
    memory: 256Mi

13.5.3 Templates

# templates/_helpers.tpl
{{- define "myapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "myapp.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "myapp.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - containerPort: {{ .Values.service.targetPort }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          env:
            - name: ENVIRONMENT
              value: {{ .Values.environment | quote }}

13.6 Resume

  • GitOps utilise Git comme source de verite unique pour l'infrastructure
  • ArgoCD est le GitOps operator le plus populaire
  • Flux offre une alternative avec des controllers specialises
  • Kustomize permet de personnaliser les manifests sans templates
  • Helm est le gestionnaire de paquets standard pour Kubernetes
  • Les Sync Waves et Hooks permettent d'ordonner les deploiements
  • L'ApplicationSet permet de deployer sur multiples clusters/environnements
  • GitOps + CI/CD = Pipeline CI pour build + GitOps pour deploy