Déployez votre app Angular sur Kubernetes avec Helm : chart complet, ingress nginx, secrets, autoscaling HPA et release management multi-environnements.
Architecture Kubernetes + Helm pour Angular
Déployer une application Angular sur Kubernetes consiste à empaqueter le bundle statique généré par ng build dans une image Docker servie par nginx, puis à orchestrer l'ensemble (Deployment, Service, Ingress, autoscaling, secrets) via un chart Helm. Helm agit comme un gestionnaire de paquets : un seul chart, plusieurs releases paramétrées pour dev, staging et production.
Vue d'ensemble du déploiement
Internet
│
▼
┌─────────────────────────────┐
│ Ingress nginx + TLS │ ← cert-manager (Let's Encrypt)
│ app.example.com │
└──────────────┬──────────────┘
│
▼
┌──────────────────────┐
│ Service (ClusterIP) │ port 80 → 8080
└──────────┬───────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ Pod #1 │ │ Pod #2 │ │ Pod #N │ ← HPA (CPU 70 %)
│ nginx │ │ nginx │ │ nginx │
│ Angular│ │ Angular│ │ Angular│
└────┬───┘ └────┬───┘ └────┬───┘
│ │ │
└─────────────┴─────────────┘
│
▼
┌────────────┐
│ ConfigMap │ ← env.js runtime
│ Secret │ ← API keys
└────────────┘
Pourquoi Helm et pas du YAML brut ?
| Approche | YAML brut (kubectl apply) | Helm Chart |
|---|---|---|
| Multi-environnements | Copier-coller 3 dossiers | 1 chart + N values.yaml |
| Versionning | Git tags manuels | Chart.yaml.version + helm history |
| Rollback | kubectl rollout undo par ressource |
helm rollback <release> 3 en 1 commande |
| Réutilisation | Aucune | Chart publiable sur OCI registry |
| Templating | envsubst, kustomize |
Go templates natifs |
Coûts mensuels approximatifs (cluster managé)
| Provider | Control plane | 2 nodes (t3.medium / e2-medium) | Total / mois |
|---|---|---|---|
| AWS EKS | ~73 $ | ~60 $ | ~135 $ |
| GCP GKE Standard | ~73 $ | ~50 $ | ~125 $ |
| Azure AKS | 0 $ (free tier) | ~70 $ | ~70 $ |
| DigitalOcean DOKS | 0 $ | ~24 $ (2 × s-2vcpu) | ~24 $ |
Créer le chart Helm — structure et fichiers
Helm fournit un scaffold prêt à l'emploi via helm create. On supprime ensuite les templates inutiles (ServiceAccount par défaut, tests basiques) et on adapte au contexte Angular SPA.
Initialisation du chart
# Prérequis : Helm 3.14+, kubectl, accès cluster
helm version
kubectl get nodes
# Génération du squelette
helm create angular-app
cd angular-app
# Structure générée
tree
# .
# ├── Chart.yaml ← métadonnées du chart
# ├── values.yaml ← valeurs par défaut
# ├── charts/ ← sous-charts (vide ici)
# └── templates/
# ├── _helpers.tpl ← fonctions Go template
# ├── deployment.yaml
# ├── service.yaml
# ├── ingress.yaml
# ├── hpa.yaml
# ├── serviceaccount.yaml
# ├── NOTES.txt
# └── tests/
Chart.yaml — métadonnées et version
# Chart.yaml — décrit le chart lui-même
apiVersion: v2
name: angular-app
description: Helm chart pour déployer une application Angular sur Kubernetes
type: application
# version : version du chart (incrémenter à chaque modif des templates)
version: 1.0.0
# appVersion : version de l'application Angular packagée
# Récupérée souvent depuis package.json côté CI
appVersion: "19.0.4"
keywords:
- angular
- frontend
- spa
- nginx
maintainers:
- name: AngularForAll
email: contact@angularforall.com
url: https://angularforall.com
# Helm 3.7+ : permet de publier sur OCI registry (ECR, GHCR, ACR)
home: https://angularforall.com
sources:
- https://github.com/votre-org/angular-app
values.yaml — toutes les valeurs paramétrables
# values.yaml — défauts utilisés si rien n'est surchargé
replicaCount: 2
image:
repository: ghcr.io/votre-org/angular-app
pullPolicy: IfNotPresent
tag: "" # par défaut = .Chart.AppVersion
imagePullSecrets:
- name: ghcr-registry-secret
nameOverride: ""
fullnameOverride: ""
# Configuration runtime injectée via ConfigMap (window.__env)
runtimeConfig:
apiUrl: "https://api.example.com"
enableAnalytics: false
featureFlags:
newDashboard: false
# Secrets référencés (créés hors chart pour sécurité)
secrets:
apiKeySecretName: angular-api-key
apiKeySecretKey: API_KEY
service:
type: ClusterIP
port: 80
targetPort: 8080 # port exposé par nginx dans le conteneur
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: angular-app-tls
hosts:
- app.example.com
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 64Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
probes:
liveness:
path: /healthz
initialDelaySeconds: 10
readiness:
path: /healthz
initialDelaySeconds: 5
nodeSelector: {}
tolerations: []
affinity: {}
image.tag: "" volontairement vide laisse Helm utiliser .Chart.AppVersion par défaut. Cela force la cohérence entre la version applicative et l'image Docker pushée.
Image Docker Angular — Dockerfile multi-stage
# Dockerfile à la racine du projet Angular
# Étape 1 : build du bundle de production
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --no-audit --no-fund
COPY . .
RUN npm run build -- --configuration=production
# Étape 2 : runtime nginx (image finale ~25 MB)
FROM nginx:1.27-alpine
# Configuration nginx custom (gzip, cache, fallback SPA)
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Bundle Angular
COPY --from=build /app/dist/angular-app/browser /usr/share/nginx/html
# Script env.js sera écrasé au runtime par le ConfigMap
COPY env.template.js /usr/share/nginx/html/env.js
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
Templates Deployment et Service
Le Deployment décrit les pods nginx servant le bundle Angular. Avec Helm, chaque champ est paramétrable via la syntaxe Go template {{ .Values.* }}.
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "angular-app.fullname" . }}
labels:
{{- include "angular-app.labels" . | nindent 4 }}
spec:
# Si HPA actif, on laisse l'autoscaler gérer replicas
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "angular-app.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 1 pod en plus pendant le déploiement
maxUnavailable: 0 # zéro downtime
template:
metadata:
annotations:
# Force le rollout du pod si le ConfigMap change
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
{{- include "angular-app.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: angular
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
# Probes : crucial pour le rolling update zéro-downtime
livenessProbe:
httpGet:
path: {{ .Values.probes.liveness.path }}
port: http
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: 10
readinessProbe:
httpGet:
path: {{ .Values.probes.readiness.path }}
port: http
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: 5
resources:
{{- toYaml .Values.resources | nindent 12 }}
# Injection de la clé API depuis Kubernetes Secret
env:
- name: API_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.secrets.apiKeySecretName }}
key: {{ .Values.secrets.apiKeySecretKey }}
# Montage du ConfigMap runtime en remplacement de env.js
volumeMounts:
- name: runtime-config
mountPath: /usr/share/nginx/html/env.js
subPath: env.js
readOnly: true
volumes:
- name: runtime-config
configMap:
name: {{ include "angular-app.fullname" . }}-config
templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "angular-app.fullname" . }}
labels:
{{- include "angular-app.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "angular-app.selectorLabels" . | nindent 4 }}
templates/configmap.yaml — config runtime Angular
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "angular-app.fullname" . }}-config
labels:
{{- include "angular-app.labels" . | nindent 4 }}
data:
# env.js chargé par index.html AVANT le bootstrap Angular
# Permet de changer apiUrl sans rebuild de l'image Docker
env.js: |
(function (window) {
window.__env = window.__env || {};
window.__env.apiUrl = "{{ .Values.runtimeConfig.apiUrl }}";
window.__env.enableAnalytics = {{ .Values.runtimeConfig.enableAnalytics }};
window.__env.featureFlags = {
newDashboard: {{ .Values.runtimeConfig.featureFlags.newDashboard }}
};
}(this));
index.html, ajoutez <script src="env.js"></script> avant le bundle Angular. Vos services lisent ensuite (window as any).__env.apiUrl. Une seule image Docker fonctionne pour tous les environnements — le ConfigMap injecte les bons paramètres au déploiement.
Ingress nginx, TLS et cert-manager
L'Ingress expose l'application sur Internet via un nom de domaine. Combiné à cert-manager, il provisionne automatiquement les certificats Let's Encrypt et les renouvelle tous les 60 jours.
Installation préalable des dépendances cluster
# 1) ingress-nginx — controller Ingress officiel
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.service.type=LoadBalancer
# Récupérer l'IP publique du LoadBalancer (à pointer en DNS A record)
kubectl -n ingress-nginx get svc ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}'
# 2) cert-manager — émission automatique des certificats TLS
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--set crds.enabled=true \
--version v1.16.1
ClusterIssuer Let's Encrypt (production)
# cluster-issuer.yaml — appliquer une seule fois sur le cluster
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
# kubectl apply -f cluster-issuer.yaml
templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "angular-app.fullname" . -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "angular-app.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ $fullName }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
Lors du helm install, cert-manager détecte l'annotation cert-manager.io/cluster-issuer et déclenche automatiquement la résolution ACME HTTP-01. Un certificat valide est livré dans le Secret angular-app-tls en moins de 60 secondes.
letsencrypt-staging comme issuer afin d'éviter d'atteindre le rate limit Let's Encrypt (5 certificats par domaine et par semaine en production).
Autoscaling avec HorizontalPodAutoscaler
Le HorizontalPodAutoscaler (HPA) ajoute ou supprime des pods en fonction de la charge CPU et mémoire. Pour qu'il fonctionne, le metrics-server doit être installé sur le cluster (présent par défaut sur GKE/AKS, à installer manuellement sur EKS).
Vérifier metrics-server
# Test : si la commande renvoie des valeurs, metrics-server fonctionne
kubectl top nodes
kubectl top pods -A
# Sinon (notamment sur EKS) :
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
templates/hpa.yaml
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "angular-app.fullname" . }}
labels:
{{- include "angular-app.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "angular-app.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
# Comportement : éviter le yo-yo (scale up rapide, scale down progressif)
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100 # peut doubler le nombre de pods en 60s
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # attend 5 min avant de réduire
policies:
- type: Percent
value: 50 # supprime au max 50 % des pods par période
periodSeconds: 60
{{- end }}
Tester l'autoscaling sous charge
# Générer du trafic avec hey (https://github.com/rakyll/hey)
hey -z 5m -c 100 https://app.example.com/
# Observer la montée en charge
kubectl get hpa -w
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
# angular-app Deployment/angular-app 52%/70%, 38%/80% 2 10 2
# angular-app Deployment/angular-app 89%/70%, 41%/80% 2 10 4
# angular-app Deployment/angular-app 74%/70%, 45%/80% 2 10 6
scaleUp: 60s et scaleDown: 300s empêchent l'effet yo-yo. Le scale-up reste réactif pour absorber les pics, mais le scale-down attend 5 minutes de stabilité avant de retirer des pods — vital pour les sessions utilisateur Angular.
Multi-environnements : values dev/staging/prod
Un seul chart Helm, trois fichiers de surcharge. Chaque environnement vit dans son propre namespace Kubernetes pour isoler ressources, secrets et quotas.
values-dev.yaml
# env/values-dev.yaml — environnement de développement
replicaCount: 1
image:
repository: ghcr.io/votre-org/angular-app
tag: develop # branche develop poussée par CI
runtimeConfig:
apiUrl: "https://dev-api.example.com"
enableAnalytics: false
featureFlags:
newDashboard: true # toutes les features flags activées
ingress:
hosts:
- host: dev.app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: angular-app-dev-tls
hosts:
- dev.app.example.com
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging # certificats de test
resources:
limits: { cpu: 200m, memory: 128Mi }
requests: { cpu: 50m, memory: 32Mi }
autoscaling:
enabled: false # pas d'autoscaling en dev
values-staging.yaml
# env/values-staging.yaml — pré-production iso-prod
replicaCount: 2
image:
tag: "1.4.0-rc.1" # release candidate avant prod
runtimeConfig:
apiUrl: "https://staging-api.example.com"
enableAnalytics: true
featureFlags:
newDashboard: true
ingress:
hosts:
- host: staging.app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: angular-app-staging-tls
hosts:
- staging.app.example.com
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 4
targetCPUUtilizationPercentage: 75
values-prod.yaml
# env/values-prod.yaml — production
replicaCount: 3
image:
tag: "1.4.0" # tag immuable, jamais latest
runtimeConfig:
apiUrl: "https://api.example.com"
enableAnalytics: true
featureFlags:
newDashboard: false # rollout progressif via flag
ingress:
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: angular-app-prod-tls
hosts:
- app.example.com
resources:
limits: { cpu: 1000m, memory: 512Mi }
requests: { cpu: 200m, memory: 128Mi }
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
# Anti-affinity : éviter que tous les pods soient sur le même node
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values: [angular-app]
topologyKey: kubernetes.io/hostname
Commandes de déploiement par environnement
# Création des namespaces (une fois)
kubectl create namespace angular-dev
kubectl create namespace angular-staging
kubectl create namespace angular-prod
# Création des secrets de registry et API key (par namespace)
kubectl create secret docker-registry ghcr-registry-secret \
--docker-server=ghcr.io \
--docker-username=USER --docker-password=$GHCR_TOKEN \
--namespace angular-prod
kubectl create secret generic angular-api-key \
--from-literal=API_KEY=$PROD_API_KEY \
--namespace angular-prod
# Déploiement DEV
helm upgrade --install angular-dev ./angular-app \
--namespace angular-dev \
--values env/values-dev.yaml \
--wait --timeout 5m
# Déploiement STAGING
helm upgrade --install angular-staging ./angular-app \
--namespace angular-staging \
--values env/values-staging.yaml \
--wait --timeout 5m
# Déploiement PROD avec dry-run préalable
helm upgrade --install angular-prod ./angular-app \
--namespace angular-prod \
--values env/values-prod.yaml \
--dry-run --debug
helm upgrade --install angular-prod ./angular-app \
--namespace angular-prod \
--values env/values-prod.yaml \
--atomic --wait --timeout 10m
--atomic est crucial en production : si le déploiement échoue à mi-parcours (timeout, probe en erreur), Helm rollback automatiquement vers la version précédente. Combiné à --wait, vous obtenez une garantie de zéro état partiel.
GitOps avec ArgoCD (alternative à helm upgrade manuel)
# argocd-app-prod.yaml — déclaration de l'app à syncer depuis Git
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: angular-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/votre-org/angular-app-deploy.git
targetRevision: main
path: charts/angular-app
helm:
valueFiles:
- ../../env/values-prod.yaml
destination:
server: https://kubernetes.default.svc
namespace: angular-prod
syncPolicy:
automated:
prune: true # supprime les ressources orphelines
selfHeal: true # corrige les drifts manuels
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
# kubectl apply -f argocd-app-prod.yaml
Release management : upgrade, rollback, helm test
Helm conserve l'historique de chaque release dans un Secret Kubernetes. Cela permet de revenir à n'importe quelle version précédente en une seule commande, sans toucher à kubectl.
Inspecter l'historique d'une release
# Liste des releases sur tous les namespaces
helm list -A
# Historique des révisions d'une release
helm history angular-prod -n angular-prod
# REVISION UPDATED STATUS CHART APP VERSION
# 1 2026-04-12 14:22:18 superseded angular-app-1.0.0 19.0.2
# 2 2026-04-25 09:15:03 superseded angular-app-1.1.0 19.0.3
# 3 2026-05-02 10:48:55 superseded angular-app-1.2.0 19.0.4
# 4 2026-05-08 11:30:12 deployed angular-app-1.3.0 19.0.4
# Inspecter les valeurs effectives d'une révision donnée
helm get values angular-prod -n angular-prod --revision 3
helm get manifest angular-prod -n angular-prod --revision 3
Rollback en 30 secondes
# Rollback à la révision précédente (= dernière "superseded")
helm rollback angular-prod -n angular-prod
# Rollback à une révision spécifique
helm rollback angular-prod 3 -n angular-prod --wait --timeout 5m
# Vérifier
helm history angular-prod -n angular-prod
# REVISION UPDATED STATUS CHART APP VERSION
# 4 2026-05-08 11:30:12 superseded angular-app-1.3.0 19.0.4
# 5 2026-05-08 11:42:01 deployed angular-app-1.2.0 19.0.4 ← rollback
templates/tests/test-connection.yaml — helm test
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "angular-app.fullname" . }}-test-connection"
labels:
{{- include "angular-app.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
restartPolicy: Never
containers:
- name: smoke-test
image: curlimages/curl:8.10.1
command: ['sh', '-c']
args:
- |
set -e
# Vérifie que le service répond bien
curl -fsS http://{{ include "angular-app.fullname" . }}:{{ .Values.service.port }}/healthz
echo "✓ /healthz OK"
# Vérifie que index.html est servi
curl -fsS http://{{ include "angular-app.fullname" . }}:{{ .Values.service.port }}/ | grep -q '<app-root>'
echo "✓ index.html contains <app-root>"
# Vérifie que env.js contient bien la bonne apiUrl
curl -fsS http://{{ include "angular-app.fullname" . }}:{{ .Values.service.port }}/env.js | grep -q '{{ .Values.runtimeConfig.apiUrl }}'
echo "✓ env.js contains correct apiUrl"
Lancer les tests après chaque déploiement
# Tests post-déploiement
helm test angular-prod -n angular-prod
# NAME: angular-prod
# LAST DEPLOYED: Fri May 8 11:42:01 2026
# NAMESPACE: angular-prod
# STATUS: deployed
# TEST SUITE: angular-prod-test-connection
# Last Started: Fri May 8 11:43:15 2026
# Last Completed: Fri May 8 11:43:18 2026
# Phase: Succeeded
Lint et validation avant push
# Vérification syntaxique du chart
helm lint ./angular-app
# Rendu local du template avec values prod (sans déployer)
helm template angular-prod ./angular-app \
--values env/values-prod.yaml \
--namespace angular-prod \
--debug
# Diff entre l'état actuel et la prochaine release (plugin helm-diff)
helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade angular-prod ./angular-app \
--values env/values-prod.yaml \
--namespace angular-prod
- Tag image Docker immuable (
1.4.0, jamaislatest) helm lintethelm template --debugsans erreurhelm diff upgraderevu et validé- Secrets API créés dans le namespace cible
- Probes liveness/readiness pointant sur
/healthz - Resource requests/limits définis (jamais à vide)
- HPA actif avec
minReplicas: 3en prod - PodAntiAffinity pour répartir sur plusieurs nodes
--atomic --waitsur la commande de releasehelm testpasse avec succès- Sauvegarde de la révision N-1 documentée pour rollback
restricted) sur vos namespaces production, scannez les images avec Trivy ou Grype dans la CI, et n'utilisez jamais le ServiceAccount default — créez-en un dédié avec les RBAC minimums nécessaires.
Conclusion
Helm transforme un déploiement Kubernetes Angular en un workflow industriel : un chart unique, plusieurs values-*.yaml par environnement, un helm upgrade --atomic reproductible et un helm rollback en une commande. Les pièces fondamentales — Deployment avec rolling update, Service ClusterIP, Ingress nginx, cert-manager pour TLS, HPA pour la charge, ConfigMap pour la config runtime — composent une architecture production-grade en moins de 500 lignes de YAML templatées.
La prochaine étape consiste à brancher ce chart dans un pipeline GitOps avec ArgoCD ou Flux : chaque commit sur la branche main déclenche un sync automatique. Combiné à un dépôt séparé pour les manifests (pattern repo applicatif vs repo de déploiement), vous obtenez traçabilité totale, rollback Git natif, et zéro accès kubectl direct sur la production. Côté coût, partez d'un cluster AKS ou DOKS (control plane gratuit) avec 2 nodes pour minimiser la facture en phase d'apprentissage.
- Empaqueter Angular dans une image nginx multi-stage (~25 MB finale)
- Externaliser
apiUrlvia ConfigMap +window.__env— une image, plusieurs envs - Utiliser
--atomic --waiten production pour rollback automatique en cas d'échec - Versionner
Chart.yamlà chaque modification de templates, séparé deappVersion - Activer cert-manager + Let's Encrypt pour des certificats TLS gratuits et auto-renouvelés
- Configurer HPA avec
scaleDown.stabilizationWindowSeconds: 300pour éviter le yo-yo - Tester chaque release avec
helm test(smoke tests via curl dans un Pod éphémère) - Adopter ArgoCD pour le sync GitOps automatique au lieu de
helm upgrademanuel - Choisir AKS ou DOKS pour le free tier control plane si le budget est serré (~70 $/mois total)