Multi-environments
Table of Contents
- Management strategies
- Promotion between environments
- Kustomize for multi-env
- Helm for multi-env
- ApplicationSet (ArgoCD)
- Hands-on exercises
1 - Management strategies
Typical environments
Strategy 1: Branch per environment
main → Production
staging → Staging
develop → Development
# ArgoCD - Application par branche
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-prod
spec:
source:
targetRevision: main # Branche
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-staging
spec:
source:
targetRevision: staging
Strategy 2: Directory per environment
gitops-repo/
├── apps/myapp/
│ ├── base/
│ └── overlays/
│ ├── dev/
│ ├── staging/
│ └── prod/
# Applications ciblant différents dossiers
spec:
source:
path: apps/myapp/overlays/prod
Strategy 3: Repo per environment
org/gitops-dev
org/gitops-staging
org/gitops-prod
Comparison
| Strategy | Pros | Cons |
|---|---|---|
| Branch | Simple, PR for promotion | Merge conflicts |
| Directory | Atomic commits, unified view | Duplication |
| Repo | Total isolation, permissions | Fragmentation |
🔝 Back to table of contents
2 - Promotion between environments
Promotion workflow
Manual promotion (PR)
# 1. Tester en staging
git checkout staging
git merge develop
git push
# 2. Vérifier le déploiement staging
# 3. Promouvoir en production
git checkout main
git merge staging
git push
Automatic promotion (Image Automation)
# Flux Image Automation
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
name: myapp
spec:
imageRepositoryRef:
name: myapp
policy:
semver:
range: 1.x.x
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: myapp
spec:
git:
checkout:
ref:
branch: main
commit:
author:
email: [email protected]
name: Flux
messageTemplate: 'Update {{.AutomationObject.Name}}'
update:
path: ./apps/myapp
Gates and Approvals
# ArgoCD Sync Waves
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
argocd.argoproj.io/sync-wave: "1" # Ordre de sync
# Flux - Dépendance entre Kustomizations
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
spec:
dependsOn:
- name: infrastructure # Déployer infra d'abord
🔝 Back to table of contents
3 - Kustomize for multi-env
Structure
apps/myapp/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ └── replicas-patch.yaml
├── staging/
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ └── replicas-patch.yaml
└── prod/
├── kustomization.yaml
├── namespace.yaml
├── replicas-patch.yaml
├── resources-patch.yaml
└── hpa.yaml
Common base
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
app: myapp
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
template:
spec:
containers:
- name: myapp
image: myapp:latest
env:
- name: LOG_LEVEL
value: info
resources:
requests:
memory: "64Mi"
cpu: "50m"
Dev Overlay
# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
resources:
- ../../base
- namespace.yaml
patches:
- path: replicas-patch.yaml
images:
- name: myapp
newTag: dev-latest
configMapGenerator:
- name: myapp-config
behavior: merge
literals:
- LOG_LEVEL=debug
Prod Overlay
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
- ../../base
- namespace.yaml
- hpa.yaml
patches:
- path: replicas-patch.yaml
- path: resources-patch.yaml
images:
- name: myapp
newTag: v1.2.3
configMapGenerator:
- name: myapp-config
behavior: merge
literals:
- LOG_LEVEL=warn
# overlays/prod/replicas-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5
# overlays/prod/resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1"
🔝 Back to table of contents
4 - Helm for multi-env
Values files structure
apps/myapp/
├── Chart.yaml
├── values.yaml # Base
├── values-dev.yaml
├── values-staging.yaml
└── values-prod.yaml
values.yaml (base)
replicaCount: 1
image:
repository: myapp
tag: latest
resources:
requests:
memory: 64Mi
cpu: 50m
config:
logLevel: info
values-prod.yaml
replicaCount: 5
image:
tag: v1.2.3
resources:
requests:
memory: 512Mi
cpu: 500m
limits:
memory: 1Gi
cpu: 1
config:
logLevel: warn
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
ArgoCD with Helm
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-prod
spec:
source:
repoURL: https://github.com/org/gitops-repo
path: apps/myapp
helm:
valueFiles:
- values.yaml
- values-prod.yaml # Override
Flux with Helm
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: myapp
namespace: production
spec:
chart:
spec:
chart: ./apps/myapp
sourceRef:
kind: GitRepository
name: gitops-repo
values:
replicaCount: 5
image:
tag: v1.2.3
🔝 Back to table of contents
5 - ApplicationSet (ArgoCD)
Concept
ApplicationSet automatically generates ArgoCD Applications.
List Generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp
spec:
generators:
- list:
elements:
- env: dev
namespace: dev
replicas: "1"
- env: staging
namespace: staging
replicas: "2"
- env: prod
namespace: production
replicas: "5"
template:
metadata:
name: 'myapp-{{env}}'
spec:
project: default
source:
repoURL: https://github.com/org/gitops-repo
path: 'apps/myapp/overlays/{{env}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: true
Git Generator
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp-envs
spec:
generators:
- git:
repoURL: https://github.com/org/gitops-repo
revision: HEAD
directories:
- path: apps/myapp/overlays/*
template:
metadata:
name: 'myapp-{{path.basename}}'
spec:
source:
path: '{{path}}'
destination:
namespace: '{{path.basename}}'
Cluster Generator (Multi-cluster)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp-clusters
spec:
generators:
- clusters:
selector:
matchLabels:
env: production
template:
metadata:
name: 'myapp-{{name}}'
spec:
destination:
server: '{{server}}'
namespace: production
🔝 Back to table of contents
6 - Hands-on exercises
Quiz
Q1. Which strategy avoids merge conflicts?
Answer
Directory per environment because all environments are on the same branch (main). Changes are isolated in different folders.
Q2. How to deploy the same app to 3 clusters with ArgoCD?
Answer
Use an ApplicationSet with a Cluster Generator:
generators:
- clusters:
selector:
matchLabels:
env: production
Exercise: Multi-env configuration
Create the Kustomize overlays for dev and prod with these differences:
| Aspect | Dev | Prod |
|---|---|---|
| Replicas | 1 | 5 |
| Memory | 128Mi | 1Gi |
| Log level | debug | warn |
Solution
# overlays/dev/kustomization.yaml
namespace: dev
resources:
- ../../base
configMapGenerator:
- name: config
literals:
- LOG_LEVEL=debug
# overlays/prod/kustomization.yaml
namespace: prod
resources:
- ../../base
patches:
- path: deployment-patch.yaml
configMapGenerator:
- name: config
literals:
- LOG_LEVEL=warn
# overlays/prod/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5
template:
spec:
containers:
- name: myapp
resources:
requests:
memory: 1Gi
🔝 Back to table of contents
Key takeaways
- 3 strategies: Branch, Directory, Repo per env
- Directory avoids merge conflicts
- Promotion via PR or automatic (Image Automation)
- Kustomize: base + overlays
- Helm: values files per environment
- ApplicationSet: automatic multi-env generation