Deploiement d'applications avec Kubernetes : Guide Complet
Kubernetes est devenu la plateforme de reference pour l'orchestration de conteneurs, utilisee par 96% des entreprises selon la CNCF. Dans ce guide complet, nous allons explorer les meilleures pratiques pour deployer vos applications en production.
Architecture Kubernetes
<svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="masterGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#326ce5"/>
<stop offset="100%" style="stop-color:#2756b3"/>
</linearGradient>
<linearGradient id="workerGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="100%" style="stop-color:#16a34a"/>
</linearGradient>
</defs>
<rect width="800" height="500" fill="#0f172a"/>
<!-- Control Plane -->
<rect x="50" y="30" width="700" height="140" rx="10" fill="#1e293b" stroke="#475569" stroke-width="2"/>
<text x="400" y="55" text-anchor="middle" fill="#94a3b8" font-size="14" font-weight="bold">Control Plane (Masters)</text>
<!-- Control Plane Components -->
<g transform="translate(100, 90)">
<rect x="-40" y="-30" width="80" height="60" rx="8" fill="url(#masterGrad)"/>
<text y="5" text-anchor="middle" fill="white" font-size="11" font-weight="bold">API Server</text>
</g>
<g transform="translate(220, 90)">
<rect x="-40" y="-30" width="80" height="60" rx="8" fill="url(#masterGrad)"/>
<text y="5" text-anchor="middle" fill="white" font-size="11" font-weight="bold">etcd</text>
</g>
<g transform="translate(340, 90)">
<rect x="-40" y="-30" width="80" height="60" rx="8" fill="url(#masterGrad)"/>
<text y="-5" text-anchor="middle" fill="white" font-size="10" font-weight="bold">Controller</text>
<text y="10" text-anchor="middle" fill="white" font-size="10" font-weight="bold">Manager</text>
</g>
<g transform="translate(460, 90)">
<rect x="-40" y="-30" width="80" height="60" rx="8" fill="url(#masterGrad)"/>
<text y="5" text-anchor="middle" fill="white" font-size="11" font-weight="bold">Scheduler</text>
</g>
<g transform="translate(580, 90)">
<rect x="-40" y="-30" width="80" height="60" rx="8" fill="url(#masterGrad)"/>
<text y="-5" text-anchor="middle" fill="white" font-size="10" font-weight="bold">Cloud</text>
<text y="10" text-anchor="middle" fill="white" font-size="10" font-weight="bold">Controller</text>
</g>
<!-- Worker Nodes -->
<g transform="translate(150, 300)">
<rect x="-100" y="-80" width="200" height="160" rx="10" fill="#1e293b" stroke="#22c55e" stroke-width="2"/>
<text y="-55" text-anchor="middle" fill="#22c55e" font-size="12" font-weight="bold">Worker Node 1</text>
<rect x="-80" y="-40" width="70" height="40" rx="5" fill="url(#workerGrad)"/>
<text x="-45" y="-15" text-anchor="middle" fill="white" font-size="9">kubelet</text>
<rect x="10" y="-40" width="70" height="40" rx="5" fill="url(#workerGrad)"/>
<text x="45" y="-15" text-anchor="middle" fill="white" font-size="9">kube-proxy</text>
<rect x="-80" y="20" width="160" height="50" rx="5" fill="#374151"/>
<text y="50" text-anchor="middle" fill="#9ca3af" font-size="10">Pods (Containers)</text>
</g>
<g transform="translate(400, 300)">
<rect x="-100" y="-80" width="200" height="160" rx="10" fill="#1e293b" stroke="#22c55e" stroke-width="2"/>
<text y="-55" text-anchor="middle" fill="#22c55e" font-size="12" font-weight="bold">Worker Node 2</text>
<rect x="-80" y="-40" width="70" height="40" rx="5" fill="url(#workerGrad)"/>
<text x="-45" y="-15" text-anchor="middle" fill="white" font-size="9">kubelet</text>
<rect x="10" y="-40" width="70" height="40" rx="5" fill="url(#workerGrad)"/>
<text x="45" y="-15" text-anchor="middle" fill="white" font-size="9">kube-proxy</text>
<rect x="-80" y="20" width="160" height="50" rx="5" fill="#374151"/>
<text y="50" text-anchor="middle" fill="#9ca3af" font-size="10">Pods (Containers)</text>
</g>
<g transform="translate(650, 300)">
<rect x="-100" y="-80" width="200" height="160" rx="10" fill="#1e293b" stroke="#22c55e" stroke-width="2"/>
<text y="-55" text-anchor="middle" fill="#22c55e" font-size="12" font-weight="bold">Worker Node 3</text>
<rect x="-80" y="-40" width="70" height="40" rx="5" fill="url(#workerGrad)"/>
<text x="-45" y="-15" text-anchor="middle" fill="white" font-size="9">kubelet</text>
<rect x="10" y="-40" width="70" height="40" rx="5" fill="url(#workerGrad)"/>
<text x="45" y="-15" text-anchor="middle" fill="white" font-size="9">kube-proxy</text>
<rect x="-80" y="20" width="160" height="50" rx="5" fill="#374151"/>
<text y="50" text-anchor="middle" fill="#9ca3af" font-size="10">Pods (Containers)</text>
</g>
<!-- Connections -->
<line x1="400" y1="170" x2="150" y2="220" stroke="#475569" stroke-width="2" stroke-dasharray="5,5"/>
<line x1="400" y1="170" x2="400" y2="220" stroke="#475569" stroke-width="2" stroke-dasharray="5,5"/>
<line x1="400" y1="170" x2="650" y2="220" stroke="#475569" stroke-width="2" stroke-dasharray="5,5"/>
<text x="400" y="480" text-anchor="middle" fill="#64748b" font-size="12">L'API Server orchestre la communication entre le Control Plane et les Worker Nodes</text>
</svg>
Concepts fondamentaux
Pods
Un Pod est la plus petite unite deployable dans Kubernetes. Il peut contenir un ou plusieurs conteneurs qui partagent le meme reseau et stockage.
apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app: my-app
version: v1
spec:
containers:
- name: app
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 3
Deployments
Les Deployments gerent les ReplicaSets et fournissent des fonctionnalites comme les mises a jour progressives et les rollbacks.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas: 3
revisionHistoryLimit: 5 # Garder 5 versions pour rollback
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: my-app
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
spec:
# Eviter la colocation sur le meme noeud
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: my-app
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: my-registry/my-app:v1.2.3
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: metrics
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
envFrom:
- configMapRef:
name: app-config
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health/live
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /health/startup
port: http
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30 # 30 * 5 = 150s max startup
volumeMounts:
- name: config-volume
mountPath: /app/config
readOnly: true
- name: cache-volume
mountPath: /app/cache
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
volumes:
- name: config-volume
configMap:
name: app-config
- name: cache-volume
emptyDir: {}
securityContext:
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
terminationGracePeriodSeconds: 30
serviceAccountName: my-app-sa
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: my-app
Services et exposition
Types de Services
<svg viewBox="0 0 800 300" xmlns="http://www.w3.org/2000/svg">
<rect width="800" height="300" fill="#0f172a"/>
<!-- ClusterIP -->
<g transform="translate(130, 150)">
<rect x="-100" y="-60" width="200" height="120" rx="10" fill="#1e293b" stroke="#3b82f6" stroke-width="2"/>
<text y="-35" text-anchor="middle" fill="#3b82f6" font-size="14" font-weight="bold">ClusterIP</text>
<text y="-10" text-anchor="middle" fill="#94a3b8" font-size="11">IP interne au cluster</text>
<text y="10" text-anchor="middle" fill="#64748b" font-size="10">Pod-to-Pod uniquement</text>
<text y="40" text-anchor="middle" fill="#22c55e" font-size="10">Par defaut</text>
</g>
<!-- NodePort -->
<g transform="translate(350, 150)">
<rect x="-100" y="-60" width="200" height="120" rx="10" fill="#1e293b" stroke="#f97316" stroke-width="2"/>
<text y="-35" text-anchor="middle" fill="#f97316" font-size="14" font-weight="bold">NodePort</text>
<text y="-10" text-anchor="middle" fill="#94a3b8" font-size="11">Port sur chaque noeud</text>
<text y="10" text-anchor="middle" fill="#64748b" font-size="10">30000-32767</text>
<text y="40" text-anchor="middle" fill="#f59e0b" font-size="10">Dev/Test</text>
</g>
<!-- LoadBalancer -->
<g transform="translate(570, 150)">
<rect x="-100" y="-60" width="200" height="120" rx="10" fill="#1e293b" stroke="#22c55e" stroke-width="2"/>
<text y="-35" text-anchor="middle" fill="#22c55e" font-size="14" font-weight="bold">LoadBalancer</text>
<text y="-10" text-anchor="middle" fill="#94a3b8" font-size="11">IP externe (Cloud)</text>
<text y="10" text-anchor="middle" fill="#64748b" font-size="10">AWS ELB, GCP LB, Azure LB</text>
<text y="40" text-anchor="middle" fill="#22c55e" font-size="10">Production</text>
</g>
<!-- Arrows -->
<path d="M235 150 L245 150" stroke="#475569" stroke-width="2" marker-end="url(#arrowService)"/>
<path d="M455 150 L465 150" stroke="#475569" stroke-width="2" marker-end="url(#arrowService)"/>
<defs>
<marker id="arrowService" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#475569"/>
</marker>
</defs>
<text x="400" y="280" text-anchor="middle" fill="#64748b" font-size="12">Niveau d'exposition croissant</text>
</svg>
Service ClusterIP (Communication interne)
apiVersion: v1
kind: Service
metadata:
name: my-app-service
labels:
app: my-app
spec:
type: ClusterIP
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- name: grpc
port: 9000
targetPort: 9000
protocol: TCP
sessionAffinity: None # ou ClientIP pour sticky sessions
Service avec Headless (StatefulSets)
apiVersion: v1
kind: Service
metadata:
name: my-db-headless
spec:
clusterIP: None # Headless service
selector:
app: my-db
ports:
- port: 5432
targetPort: 5432
Ingress (Exposition HTTP/HTTPS)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
# Nginx Ingress Controller
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "50"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
# cert-manager pour TLS automatique
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
- api.example.com
secretName: app-tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1-service
port:
number: 80
- path: /v2
pathType: Prefix
backend:
service:
name: api-v2-service
port:
number: 80
Helm : Gestionnaire de packages
Helm simplifie le deploiement d'applications complexes via des charts reutilisables.
Structure d'un Chart Helm
my-app-chart/
├── Chart.yaml # Metadata du chart
├── values.yaml # Valeurs par defaut
├── values-prod.yaml # Override production
├── templates/
│ ├── _helpers.tpl # Fonctions template
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ └── serviceaccount.yaml
└── charts/ # Dependances
Chart.yaml
apiVersion: v2
name: my-app
description: My Application Helm Chart
type: application
version: 1.0.0
appVersion: "2.1.0"
dependencies:
- name: postgresql
version: "12.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "17.x.x"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
values.yaml
# Application
replicaCount: 3
image:
repository: my-registry/my-app
tag: "latest"
pullPolicy: IfNotPresent
# Resources
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Autoscaling
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
# Service
service:
type: ClusterIP
port: 80
targetPort: 8080
# Ingress
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: app-tls
hosts:
- app.example.com
# Probes
probes:
liveness:
path: /health/live
initialDelaySeconds: 30
periodSeconds: 10
readiness:
path: /health/ready
initialDelaySeconds: 5
periodSeconds: 5
# Pod Disruption Budget
pdb:
enabled: true
minAvailable: 1
# Dependencies
postgresql:
enabled: true
auth:
database: myapp
existingSecret: db-credentials
redis:
enabled: false
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "my-app.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
livenessProbe:
httpGet:
path: {{ .Values.probes.liveness.path }}
port: http
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
readinessProbe:
httpGet:
path: {{ .Values.probes.readiness.path }}
port: http
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
envFrom:
- configMapRef:
name: {{ include "my-app.fullname" . }}-config
- secretRef:
name: {{ include "my-app.fullname" . }}-secrets
Commandes Helm essentielles
# Installation
helm install my-app ./my-app-chart -n production --create-namespace -f values-prod.yaml
# Mise a jour
helm upgrade my-app ./my-app-chart -n production -f values-prod.yaml
# Installation/Mise a jour en une commande
helm upgrade --install my-app ./my-app-chart -n production -f values-prod.yaml
# Voir les valeurs utilisees
helm get values my-app -n production
# Historique des releases
helm history my-app -n production
# Rollback
helm rollback my-app 2 -n production # Revenir a la revision 2
# Desinstallation
helm uninstall my-app -n production
# Template local (debug)
helm template my-app ./my-app-chart -f values-prod.yaml > manifest.yaml
Kustomize : Configuration declarative
Kustomize permet de personnaliser des manifestes Kubernetes sans templates, en utilisant des overlays.
Structure Kustomize
my-app/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/
├── development/
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ └── patches/
│ └── replicas.yaml
├── staging/
│ ├── kustomization.yaml
│ └── patches/
│ └── resources.yaml
└── production/
├── kustomization.yaml
├── namespace.yaml
└── patches/
├── replicas.yaml
├── resources.yaml
└── hpa.yaml
base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app.kubernetes.io/name: my-app
app.kubernetes.io/managed-by: kustomize
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
namePrefix: prod-
commonLabels:
environment: production
resources:
- ../../base
- namespace.yaml
patches:
- path: patches/replicas.yaml
- path: patches/resources.yaml
- path: patches/hpa.yaml
configMapGenerator:
- name: app-config
behavior: merge
literals:
- LOG_LEVEL=warn
- ENVIRONMENT=production
secretGenerator:
- name: app-secrets
type: Opaque
files:
- secrets/database-url.txt
options:
disableNameSuffixHash: true
images:
- name: my-app
newName: my-registry.com/my-app
newTag: v2.1.0
replicas:
- name: my-app
count: 5
overlays/production/patches/resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
Commandes Kustomize
# Preview des manifestes
kubectl kustomize overlays/production
# Appliquer
kubectl apply -k overlays/production
# Diff avant application
kubectl diff -k overlays/production
# Build et redirection
kustomize build overlays/production > production-manifests.yaml
Autoscaling
Horizontal Pod Autoscaler (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 20
metrics:
# CPU
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# Memory
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# Metriques custom (Prometheus Adapter)
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
# Metriques externes
- type: External
external:
metric:
name: queue_messages_ready
selector:
matchLabels:
queue: my-queue
target:
type: AverageValue
averageValue: "100"
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 5 min avant scale down
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0 # Scale up immediat
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
Vertical Pod Autoscaler (VPA)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: my-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
updatePolicy:
updateMode: "Auto" # Off, Initial, Recreate, Auto
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2000m
memory: 2Gi
controlledResources: ["cpu", "memory"]
Pod Disruption Budget (PDB)
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
spec:
# Au moins 2 pods disponibles
minAvailable: 2
# OU maximum 1 pod indisponible
# maxUnavailable: 1
selector:
matchLabels:
app: my-app
Securite
RBAC (Role-Based Access Control)
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: production
---
# Role (namespace-scoped)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: production
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: production
roleRef:
kind: Role
name: my-app-role
apiGroup: rbac.authorization.k8s.io
Network Policies
# Deny all par defaut
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Autoriser le trafic specifique
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-app-network-policy
namespace: production
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Ingress
- Egress
ingress:
# Depuis l'Ingress Controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
# Depuis d'autres pods du meme namespace
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
# Vers la base de donnees
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
# DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# HTTPS externe
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 443
Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
# Enforce restricted security
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
# Warn pour baseline
pod-security.kubernetes.io/warn: baseline
pod-security.kubernetes.io/warn-version: latest
Strategies de deploiement
Rolling Update (par defaut)
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25% # ou nombre absolu
maxSurge: 25% # ou nombre absolu
Blue-Green avec Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 5
strategy:
blueGreen:
activeService: my-app-active
previewService: my-app-preview
autoPromotionEnabled: false # Promotion manuelle
scaleDownDelaySeconds: 30
prePromotionAnalysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: my-app-preview
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:v2
Canary avec Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 10
strategy:
canary:
canaryService: my-app-canary
stableService: my-app-stable
trafficRouting:
nginx:
stableIngress: my-app-ingress
steps:
- setWeight: 5
- pause: {duration: 5m}
- setWeight: 20
- pause: {duration: 10m}
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 80
- pause: {duration: 5m}
analysis:
templates:
- templateName: success-rate
startingStep: 2 # Commencer l'analyse au step 2
args:
- name: service-name
value: my-app-canary
AnalysisTemplate pour Rollouts
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 1m
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(http_requests_total{status=~"2..",service="{{args.service-name}}"}[5m]))
/
sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))
Troubleshooting
Commandes de diagnostic
# Etat general du cluster
kubectl cluster-info
kubectl get nodes -o wide
kubectl top nodes
# Pods
kubectl get pods -n production -o wide
kubectl describe pod <pod-name> -n production
kubectl logs <pod-name> -n production --tail=100 -f
kubectl logs <pod-name> -n production -c <container-name> # Multi-container
kubectl logs <pod-name> -n production --previous # Container precedent
# Events
kubectl get events -n production --sort-by='.lastTimestamp'
kubectl get events -n production --field-selector type=Warning
# Debugging interactif
kubectl exec -it <pod-name> -n production -- /bin/sh
kubectl run debug --rm -it --image=busybox -- /bin/sh
# Port-forward pour debug
kubectl port-forward pod/<pod-name> 8080:8080 -n production
kubectl port-forward svc/<service-name> 8080:80 -n production
# Copier des fichiers
kubectl cp <pod-name>:/path/to/file ./local-file -n production
Problemes courants et solutions
1. Pod en CrashLoopBackOff
# Diagnostic
kubectl describe pod <pod-name>
kubectl logs <pod-name> --previous
# Causes courantes:
# - Application crash (voir logs)
# - Probe qui echoue (augmenter initialDelaySeconds)
# - Ressources insuffisantes (augmenter limits)
# - Configuration invalide (verifier ConfigMaps/Secrets)
2. Pod en Pending
# Diagnostic
kubectl describe pod <pod-name>
# Causes courantes:
# - Ressources insuffisantes sur les noeuds
kubectl describe nodes | grep -A 5 "Allocated resources"
# - PVC non bound
kubectl get pvc -n production
# - Affinity/Taint non satisfait
kubectl get nodes --show-labels
3. ImagePullBackOff
# Verifier le secret de registry
kubectl get secrets -n production
kubectl get pod <pod-name> -o jsonpath='{.spec.imagePullSecrets}'
# Creer le secret si manquant
kubectl create secret docker-registry regcred \
--docker-server=my-registry.com \
--docker-username=user \
--docker-password=pass \
-n production
4. Service inaccessible
# Verifier le service
kubectl get svc <service-name> -n production -o wide
kubectl get endpoints <service-name> -n production
# Tester la connectivite
kubectl run test --rm -it --image=busybox -- wget -qO- http://<service-name>.<namespace>.svc.cluster.local
# Verifier les labels
kubectl get pods -l app=my-app -n production
Script de diagnostic complet
#!/bin/bash
# k8s-diag.sh - Diagnostic complet Kubernetes
NAMESPACE=${1:-default}
APP=${2:-""}
echo "=== Kubernetes Diagnostic ==="
echo "Namespace: $NAMESPACE"
echo "App: $APP"
echo ""
echo "--- Nodes Status ---"
kubectl get nodes -o wide
echo -e "\n--- Pods Status ---"
if [ -n "$APP" ]; then
kubectl get pods -n $NAMESPACE -l app=$APP -o wide
else
kubectl get pods -n $NAMESPACE -o wide
fi
echo -e "\n--- Events (last 20) ---"
kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' | tail -20
echo -e "\n--- Services ---"
kubectl get svc -n $NAMESPACE
echo -e "\n--- Ingress ---"
kubectl get ingress -n $NAMESPACE
echo -e "\n--- Resource Usage ---"
kubectl top pods -n $NAMESPACE 2>/dev/null || echo "Metrics server not available"
echo -e "\n--- HPA Status ---"
kubectl get hpa -n $NAMESPACE
echo -e "\n--- PDB Status ---"
kubectl get pdb -n $NAMESPACE
if [ -n "$APP" ]; then
echo -e "\n--- Deployment Status ---"
kubectl describe deployment $APP -n $NAMESPACE | head -50
echo -e "\n--- Recent Logs ---"
kubectl logs -l app=$APP -n $NAMESPACE --tail=50
fi
echo -e "\n=== End Diagnostic ==="
Best Practices Production
Checklist Pre-deploiement
## Pre-Deployment Checklist
### Images
- [ ] Image tag specifique (pas :latest)
- [ ] Image scannee pour vulnerabilites
- [ ] Image signee (Sigstore/Cosign)
### Resources
- [ ] Requests et Limits definis
- [ ] Resource Quotas sur le namespace
- [ ] LimitRange configure
### Probes
- [ ] Liveness probe configuree
- [ ] Readiness probe configuree
- [ ] Startup probe si necessaire
### Securite
- [ ] ServiceAccount dedie
- [ ] RBAC minimal
- [ ] SecurityContext restrictif
- [ ] Network Policies
- [ ] Pod Security Standards
### Haute Disponibilite
- [ ] replicas >= 2
- [ ] PodDisruptionBudget
- [ ] Anti-affinity (spread across nodes/zones)
- [ ] TopologySpreadConstraints
### Configuration
- [ ] ConfigMaps pour config non sensible
- [ ] Secrets pour donnees sensibles
- [ ] External Secrets si applicable
### Monitoring
- [ ] Metriques exposees (/metrics)
- [ ] ServiceMonitor pour Prometheus
- [ ] Dashboards Grafana
- [ ] Alertes configurees
Labels recommandes
metadata:
labels:
# Kubernetes standard labels
app.kubernetes.io/name: my-app
app.kubernetes.io/instance: my-app-production
app.kubernetes.io/version: "2.1.0"
app.kubernetes.io/component: backend
app.kubernetes.io/part-of: my-platform
app.kubernetes.io/managed-by: helm
# Custom labels
environment: production
team: platform
cost-center: engineering
Conclusion
Le deploiement sur Kubernetes necessite une bonne comprehension des concepts fondamentaux et l'application rigoureuse des meilleures pratiques. Points cles a retenir :
- Commencez simple : Pod -> Deployment -> Service -> Ingress
- Securisez des le depart : RBAC, Network Policies, Pod Security
- Automatisez : Helm ou Kustomize pour la gestion des configurations
- Monitorez : Probes, metriques, logs centralises
- Preparez la HA : Replicas, PDB, anti-affinity
Ressources
- Documentation officielle Kubernetes
- Helm Documentation
- Kustomize Documentation
- Argo Rollouts
- Kubernetes Patterns
Besoin d'aide pour deployer vos applications Kubernetes en production ? Decouvrez mes services de consulting et formations.