768 lines
18 KiB
Markdown
768 lines
18 KiB
Markdown
# 09 — Manifests Kubernetes complets
|
|
|
|
Tous les fichiers YAML de déploiement de Xpeditis. Créez un dossier `k8s/` à la racine du projet.
|
|
|
|
---
|
|
|
|
## Structure des fichiers
|
|
|
|
```
|
|
k8s/
|
|
├── 00-namespaces.yaml
|
|
├── 01-secrets.yaml # ← À remplir avec vos valeurs (ne pas committer)
|
|
├── 02-configmaps.yaml
|
|
├── 03-backend-deployment.yaml
|
|
├── 04-backend-service.yaml
|
|
├── 05-frontend-deployment.yaml
|
|
├── 06-frontend-service.yaml
|
|
├── 07-ingress.yaml
|
|
├── 08-hpa.yaml
|
|
└── 09-pdb.yaml
|
|
```
|
|
|
|
---
|
|
|
|
## 00 — Namespaces
|
|
|
|
```yaml
|
|
# k8s/00-namespaces.yaml
|
|
---
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: xpeditis-prod
|
|
labels:
|
|
environment: production
|
|
app.kubernetes.io/managed-by: hetzner-k3s
|
|
```
|
|
|
|
```bash
|
|
kubectl apply -f k8s/00-namespaces.yaml
|
|
```
|
|
|
|
---
|
|
|
|
## 01 — Secrets (⚠️ ne jamais committer ce fichier dans Git)
|
|
|
|
Ajoutez `k8s/01-secrets.yaml` à votre `.gitignore`.
|
|
|
|
```yaml
|
|
# k8s/01-secrets.yaml ← AJOUTER AU .gitignore
|
|
---
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: backend-secrets
|
|
namespace: xpeditis-prod
|
|
type: Opaque
|
|
stringData:
|
|
# Application
|
|
NODE_ENV: "production"
|
|
PORT: "4000"
|
|
API_PREFIX: "api/v1"
|
|
APP_URL: "https://app.xpeditis.com"
|
|
FRONTEND_URL: "https://app.xpeditis.com"
|
|
|
|
# Base de données (choisir Option A ou B)
|
|
# === Option A : Neon.tech ===
|
|
DATABASE_HOST: "ep-xxx.eu-central-1.aws.neon.tech"
|
|
DATABASE_PORT: "5432"
|
|
DATABASE_USER: "xpeditis"
|
|
DATABASE_PASSWORD: "<NEON_PASSWORD>"
|
|
DATABASE_NAME: "xpeditis"
|
|
DATABASE_SSL: "true"
|
|
DATABASE_SYNC: "false"
|
|
DATABASE_LOGGING: "false"
|
|
# === Option B : Self-hosted ===
|
|
# DATABASE_HOST: "10.0.1.100" # IP privée Hetzner du serveur PG
|
|
# DATABASE_PORT: "6432" # PgBouncer
|
|
# DATABASE_USER: "xpeditis"
|
|
# DATABASE_PASSWORD: "<PG_PASSWORD>"
|
|
# DATABASE_NAME: "xpeditis_prod"
|
|
# DATABASE_SYNC: "false"
|
|
# DATABASE_LOGGING: "false"
|
|
|
|
# Redis (choisir Option A ou B)
|
|
# === Option A : Upstash ===
|
|
REDIS_HOST: "your-redis.upstash.io"
|
|
REDIS_PORT: "6379"
|
|
REDIS_PASSWORD: "<UPSTASH_TOKEN>"
|
|
REDIS_DB: "0"
|
|
# === Option B : Self-hosted ===
|
|
# REDIS_HOST: "redis.xpeditis-prod.svc.cluster.local"
|
|
# REDIS_PORT: "6379"
|
|
# REDIS_PASSWORD: "<REDIS_PASSWORD>"
|
|
# REDIS_DB: "0"
|
|
|
|
# JWT
|
|
JWT_SECRET: "<CHAINE_ALEATOIRE_64_CHARS>"
|
|
JWT_ACCESS_EXPIRATION: "15m"
|
|
JWT_REFRESH_EXPIRATION: "7d"
|
|
|
|
# OAuth2 Google
|
|
GOOGLE_CLIENT_ID: "<GOOGLE_CLIENT_ID>"
|
|
GOOGLE_CLIENT_SECRET: "<GOOGLE_CLIENT_SECRET>"
|
|
GOOGLE_CALLBACK_URL: "https://api.xpeditis.com/api/v1/auth/google/callback"
|
|
|
|
# OAuth2 Microsoft
|
|
MICROSOFT_CLIENT_ID: "<MICROSOFT_CLIENT_ID>"
|
|
MICROSOFT_CLIENT_SECRET: "<MICROSOFT_CLIENT_SECRET>"
|
|
MICROSOFT_CALLBACK_URL: "https://api.xpeditis.com/api/v1/auth/microsoft/callback"
|
|
|
|
# Email (Brevo SMTP — remplace SendGrid)
|
|
SMTP_HOST: "smtp-relay.brevo.com"
|
|
SMTP_PORT: "587"
|
|
SMTP_SECURE: "false"
|
|
SMTP_USER: "<BREVO_LOGIN>"
|
|
SMTP_PASS: "<BREVO_SMTP_KEY>"
|
|
SMTP_FROM: "noreply@xpeditis.com"
|
|
|
|
# Hetzner Object Storage (remplace MinIO)
|
|
AWS_S3_ENDPOINT: "https://fsn1.your-objectstorage.com"
|
|
AWS_ACCESS_KEY_ID: "<HETZNER_ACCESS_KEY>"
|
|
AWS_SECRET_ACCESS_KEY: "<HETZNER_SECRET_KEY>"
|
|
AWS_REGION: "eu-central-1"
|
|
AWS_S3_BUCKET: "xpeditis-prod"
|
|
|
|
# Carrier APIs
|
|
MAERSK_API_KEY: "<MAERSK_API_KEY>"
|
|
MAERSK_API_URL: "https://api.maersk.com/v1"
|
|
MSC_API_KEY: "<MSC_API_KEY>"
|
|
MSC_API_URL: "https://api.msc.com/v1"
|
|
CMACGM_API_URL: "https://api.cma-cgm.com/v1"
|
|
CMACGM_CLIENT_ID: "<CMACGM_CLIENT_ID>"
|
|
CMACGM_CLIENT_SECRET: "<CMACGM_CLIENT_SECRET>"
|
|
HAPAG_API_URL: "https://api.hapag-lloyd.com/v1"
|
|
HAPAG_API_KEY: "<HAPAG_API_KEY>"
|
|
ONE_API_URL: "https://api.one-line.com/v1"
|
|
ONE_USERNAME: "<ONE_USERNAME>"
|
|
ONE_PASSWORD: "<ONE_PASSWORD>"
|
|
|
|
# Stripe
|
|
STRIPE_SECRET_KEY: "sk_live_<...>"
|
|
STRIPE_WEBHOOK_SECRET: "whsec_<...>"
|
|
STRIPE_SILVER_MONTHLY_PRICE_ID: "price_<...>"
|
|
STRIPE_SILVER_YEARLY_PRICE_ID: "price_<...>"
|
|
STRIPE_GOLD_MONTHLY_PRICE_ID: "price_<...>"
|
|
STRIPE_GOLD_YEARLY_PRICE_ID: "price_<...>"
|
|
STRIPE_PLATINIUM_MONTHLY_PRICE_ID: "price_<...>"
|
|
STRIPE_PLATINIUM_YEARLY_PRICE_ID: "price_<...>"
|
|
|
|
# Sécurité
|
|
BCRYPT_ROUNDS: "12"
|
|
SESSION_TIMEOUT_MS: "7200000"
|
|
RATE_LIMIT_TTL: "60"
|
|
RATE_LIMIT_MAX: "100"
|
|
|
|
# Monitoring
|
|
SENTRY_DSN: "<SENTRY_DSN>"
|
|
---
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: frontend-secrets
|
|
namespace: xpeditis-prod
|
|
type: Opaque
|
|
stringData:
|
|
NEXT_PUBLIC_API_URL: "https://api.xpeditis.com"
|
|
NEXT_PUBLIC_APP_URL: "https://app.xpeditis.com"
|
|
NEXT_PUBLIC_API_PREFIX: "api/v1"
|
|
NEXTAUTH_URL: "https://app.xpeditis.com"
|
|
NEXTAUTH_SECRET: "<CHAINE_ALEATOIRE_32_CHARS>"
|
|
GOOGLE_CLIENT_ID: "<GOOGLE_CLIENT_ID>"
|
|
GOOGLE_CLIENT_SECRET: "<GOOGLE_CLIENT_SECRET>"
|
|
MICROSOFT_CLIENT_ID: "<MICROSOFT_CLIENT_ID>"
|
|
MICROSOFT_CLIENT_SECRET: "<MICROSOFT_CLIENT_SECRET>"
|
|
NODE_ENV: "production"
|
|
```
|
|
|
|
```bash
|
|
# Générer les secrets aléatoires
|
|
echo "JWT_SECRET=$(openssl rand -base64 48)"
|
|
echo "NEXTAUTH_SECRET=$(openssl rand -base64 24)"
|
|
|
|
# Appliquer (après avoir rempli les valeurs)
|
|
kubectl apply -f k8s/01-secrets.yaml
|
|
|
|
# Vérifier (sans voir les valeurs)
|
|
kubectl get secret backend-secrets -n xpeditis-prod -o jsonpath='{.data}' | jq 'keys'
|
|
```
|
|
|
|
---
|
|
|
|
## 02 — ConfigMaps (variables non-sensibles)
|
|
|
|
```yaml
|
|
# k8s/02-configmaps.yaml
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: backend-config
|
|
namespace: xpeditis-prod
|
|
data:
|
|
# Ces valeurs ne sont pas sensibles
|
|
LOG_LEVEL: "info"
|
|
TZ: "Europe/Paris"
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: frontend-config
|
|
namespace: xpeditis-prod
|
|
data:
|
|
TZ: "Europe/Paris"
|
|
```
|
|
|
|
---
|
|
|
|
## 03 — Deployment Backend NestJS
|
|
|
|
```yaml
|
|
# k8s/03-backend-deployment.yaml
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: xpeditis-backend
|
|
namespace: xpeditis-prod
|
|
labels:
|
|
app: xpeditis-backend
|
|
version: "latest"
|
|
spec:
|
|
replicas: 2
|
|
selector:
|
|
matchLabels:
|
|
app: xpeditis-backend
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxSurge: 1
|
|
maxUnavailable: 0 # Zero downtime deployment
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: xpeditis-backend
|
|
version: "latest"
|
|
annotations:
|
|
prometheus.io/scrape: "true"
|
|
prometheus.io/port: "4000"
|
|
prometheus.io/path: "/api/v1/health"
|
|
spec:
|
|
# Anti-affinité : pods sur nœuds différents
|
|
affinity:
|
|
podAntiAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
podAffinityTerm:
|
|
labelSelector:
|
|
matchExpressions:
|
|
- key: app
|
|
operator: In
|
|
values:
|
|
- xpeditis-backend
|
|
topologyKey: kubernetes.io/hostname
|
|
|
|
# Temps de grâce pour les connexions WebSocket
|
|
terminationGracePeriodSeconds: 60
|
|
|
|
containers:
|
|
- name: backend
|
|
# L'image est mise à jour par le CI/CD (doc 11)
|
|
image: ghcr.io/<VOTRE_ORG>/xpeditis-backend:latest
|
|
imagePullPolicy: Always
|
|
ports:
|
|
- containerPort: 4000
|
|
name: http
|
|
protocol: TCP
|
|
|
|
# Variables d'environnement depuis les Secrets
|
|
envFrom:
|
|
- secretRef:
|
|
name: backend-secrets
|
|
- configMapRef:
|
|
name: backend-config
|
|
|
|
# Resources (MVP — ajuster selon les métriques réelles)
|
|
resources:
|
|
requests:
|
|
cpu: "500m"
|
|
memory: "512Mi"
|
|
limits:
|
|
cpu: "2000m"
|
|
memory: "1.5Gi"
|
|
|
|
# Health checks
|
|
startupProbe:
|
|
httpGet:
|
|
path: /api/v1/health
|
|
port: 4000
|
|
initialDelaySeconds: 20
|
|
periodSeconds: 5
|
|
failureThreshold: 12 # 60 secondes max au démarrage
|
|
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /api/v1/health
|
|
port: 4000
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
successThreshold: 1
|
|
failureThreshold: 3
|
|
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /api/v1/health
|
|
port: 4000
|
|
initialDelaySeconds: 60
|
|
periodSeconds: 30
|
|
failureThreshold: 3
|
|
|
|
# Lifecycle hook pour graceful shutdown
|
|
lifecycle:
|
|
preStop:
|
|
exec:
|
|
command: ["/bin/sh", "-c", "sleep 10"] # Laisse le temps au LB de retirer le pod
|
|
|
|
# Pull depuis GHCR (GitHub Container Registry)
|
|
imagePullSecrets:
|
|
- name: ghcr-credentials
|
|
|
|
# Redémarrage automatique
|
|
restartPolicy: Always
|
|
```
|
|
|
|
---
|
|
|
|
## 04 — Service Backend
|
|
|
|
```yaml
|
|
# k8s/04-backend-service.yaml
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: xpeditis-backend
|
|
namespace: xpeditis-prod
|
|
labels:
|
|
app: xpeditis-backend
|
|
spec:
|
|
selector:
|
|
app: xpeditis-backend
|
|
ports:
|
|
- name: http
|
|
port: 4000
|
|
targetPort: 4000
|
|
protocol: TCP
|
|
type: ClusterIP
|
|
```
|
|
|
|
---
|
|
|
|
## 05 — Deployment Frontend Next.js
|
|
|
|
```yaml
|
|
# k8s/05-frontend-deployment.yaml
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: xpeditis-frontend
|
|
namespace: xpeditis-prod
|
|
labels:
|
|
app: xpeditis-frontend
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: xpeditis-frontend
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxSurge: 1
|
|
maxUnavailable: 0
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: xpeditis-frontend
|
|
spec:
|
|
terminationGracePeriodSeconds: 30
|
|
|
|
containers:
|
|
- name: frontend
|
|
image: ghcr.io/<VOTRE_ORG>/xpeditis-frontend:latest
|
|
imagePullPolicy: Always
|
|
ports:
|
|
- containerPort: 3000
|
|
name: http
|
|
|
|
envFrom:
|
|
- secretRef:
|
|
name: frontend-secrets
|
|
- configMapRef:
|
|
name: frontend-config
|
|
|
|
resources:
|
|
requests:
|
|
cpu: "250m"
|
|
memory: "256Mi"
|
|
limits:
|
|
cpu: "1000m"
|
|
memory: "768Mi"
|
|
|
|
startupProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 3000
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 5
|
|
failureThreshold: 12
|
|
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 3000
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
failureThreshold: 3
|
|
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 3000
|
|
initialDelaySeconds: 30
|
|
periodSeconds: 30
|
|
failureThreshold: 3
|
|
|
|
lifecycle:
|
|
preStop:
|
|
exec:
|
|
command: ["/bin/sh", "-c", "sleep 5"]
|
|
|
|
imagePullSecrets:
|
|
- name: ghcr-credentials
|
|
|
|
restartPolicy: Always
|
|
```
|
|
|
|
---
|
|
|
|
## 06 — Service Frontend
|
|
|
|
```yaml
|
|
# k8s/06-frontend-service.yaml
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: xpeditis-frontend
|
|
namespace: xpeditis-prod
|
|
labels:
|
|
app: xpeditis-frontend
|
|
spec:
|
|
selector:
|
|
app: xpeditis-frontend
|
|
ports:
|
|
- name: http
|
|
port: 3000
|
|
targetPort: 3000
|
|
protocol: TCP
|
|
type: ClusterIP
|
|
```
|
|
|
|
---
|
|
|
|
## 07 — Ingress (Traefik + TLS)
|
|
|
|
```yaml
|
|
# k8s/07-ingress.yaml
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: xpeditis-ingress
|
|
namespace: xpeditis-prod
|
|
annotations:
|
|
# TLS via cert-manager
|
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
|
|
# Traefik config
|
|
traefik.ingress.kubernetes.io/router.entrypoints: "websecure"
|
|
traefik.ingress.kubernetes.io/router.tls: "true"
|
|
|
|
# Sticky sessions pour WebSocket Socket.IO
|
|
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
|
|
traefik.ingress.kubernetes.io/service.sticky.cookie.name: "XPEDITIS_BACKEND"
|
|
traefik.ingress.kubernetes.io/service.sticky.cookie.secure: "true"
|
|
traefik.ingress.kubernetes.io/service.sticky.cookie.httponly: "true"
|
|
|
|
# Timeout pour les longues requêtes (carrier APIs = jusqu'à 30s)
|
|
traefik.ingress.kubernetes.io/router.middlewares: "xpeditis-prod-ratelimit@kubernetescrd"
|
|
|
|
# Headers de sécurité
|
|
traefik.ingress.kubernetes.io/router.middlewares: "xpeditis-prod-headers@kubernetescrd"
|
|
|
|
spec:
|
|
ingressClassName: traefik
|
|
tls:
|
|
- hosts:
|
|
- api.xpeditis.com
|
|
- app.xpeditis.com
|
|
secretName: xpeditis-tls-prod
|
|
|
|
rules:
|
|
# API Backend NestJS
|
|
- host: api.xpeditis.com
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: xpeditis-backend
|
|
port:
|
|
number: 4000
|
|
|
|
# Frontend Next.js
|
|
- host: app.xpeditis.com
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: xpeditis-frontend
|
|
port:
|
|
number: 3000
|
|
---
|
|
# Middleware : headers de sécurité
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: Middleware
|
|
metadata:
|
|
name: headers
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
headers:
|
|
customRequestHeaders:
|
|
X-Forwarded-Proto: "https"
|
|
customResponseHeaders:
|
|
X-Frame-Options: "SAMEORIGIN"
|
|
X-Content-Type-Options: "nosniff"
|
|
X-XSS-Protection: "1; mode=block"
|
|
Referrer-Policy: "strict-origin-when-cross-origin"
|
|
Permissions-Policy: "geolocation=(), microphone=(), camera=()"
|
|
contentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
|
|
stsSeconds: 31536000
|
|
stsIncludeSubdomains: true
|
|
stsPreload: true
|
|
---
|
|
# Middleware : rate limiting Traefik (en plus du rate limiting NestJS)
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: Middleware
|
|
metadata:
|
|
name: ratelimit
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
rateLimit:
|
|
average: 100
|
|
burst: 50
|
|
period: 1m
|
|
sourceCriterion:
|
|
ipStrategy:
|
|
depth: 1
|
|
```
|
|
|
|
---
|
|
|
|
## 08 — Horizontal Pod Autoscaler
|
|
|
|
```yaml
|
|
# k8s/08-hpa.yaml
|
|
---
|
|
# HPA Backend
|
|
apiVersion: autoscaling/v2
|
|
kind: HorizontalPodAutoscaler
|
|
metadata:
|
|
name: backend-hpa
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
scaleTargetRef:
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
name: xpeditis-backend
|
|
minReplicas: 2
|
|
maxReplicas: 15
|
|
metrics:
|
|
- type: Resource
|
|
resource:
|
|
name: cpu
|
|
target:
|
|
type: Utilization
|
|
averageUtilization: 70
|
|
- type: Resource
|
|
resource:
|
|
name: memory
|
|
target:
|
|
type: Utilization
|
|
averageUtilization: 80
|
|
behavior:
|
|
scaleUp:
|
|
stabilizationWindowSeconds: 60
|
|
policies:
|
|
- type: Pods
|
|
value: 2
|
|
periodSeconds: 60
|
|
scaleDown:
|
|
stabilizationWindowSeconds: 300 # 5 min avant de réduire
|
|
policies:
|
|
- type: Pods
|
|
value: 1
|
|
periodSeconds: 120
|
|
---
|
|
# HPA Frontend
|
|
apiVersion: autoscaling/v2
|
|
kind: HorizontalPodAutoscaler
|
|
metadata:
|
|
name: frontend-hpa
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
scaleTargetRef:
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
name: xpeditis-frontend
|
|
minReplicas: 1
|
|
maxReplicas: 8
|
|
metrics:
|
|
- type: Resource
|
|
resource:
|
|
name: cpu
|
|
target:
|
|
type: Utilization
|
|
averageUtilization: 70
|
|
behavior:
|
|
scaleDown:
|
|
stabilizationWindowSeconds: 300
|
|
```
|
|
|
|
---
|
|
|
|
## 09 — PodDisruptionBudget
|
|
|
|
```yaml
|
|
# k8s/09-pdb.yaml
|
|
---
|
|
# Garantit qu'au moins 1 pod backend est toujours disponible pendant les maintenances
|
|
apiVersion: policy/v1
|
|
kind: PodDisruptionBudget
|
|
metadata:
|
|
name: backend-pdb
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
minAvailable: 1
|
|
selector:
|
|
matchLabels:
|
|
app: xpeditis-backend
|
|
---
|
|
apiVersion: policy/v1
|
|
kind: PodDisruptionBudget
|
|
metadata:
|
|
name: frontend-pdb
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
minAvailable: 1
|
|
selector:
|
|
matchLabels:
|
|
app: xpeditis-frontend
|
|
```
|
|
|
|
---
|
|
|
|
## Secret GHCR (GitHub Container Registry)
|
|
|
|
Pour que Kubernetes puisse pull les images depuis GHCR :
|
|
|
|
```bash
|
|
# Créer un Personal Access Token GitHub avec scope: read:packages
|
|
# https://github.com/settings/tokens/new
|
|
|
|
kubectl create secret docker-registry ghcr-credentials \
|
|
--namespace xpeditis-prod \
|
|
--docker-server=ghcr.io \
|
|
--docker-username=<VOTRE_USERNAME_GITHUB> \
|
|
--docker-password=<VOTRE_GITHUB_PAT> \
|
|
--docker-email=<VOTRE_EMAIL>
|
|
```
|
|
|
|
---
|
|
|
|
## Déploiement complet
|
|
|
|
```bash
|
|
# Appliquer tous les manifests dans l'ordre
|
|
kubectl apply -f k8s/00-namespaces.yaml
|
|
kubectl apply -f k8s/01-secrets.yaml # Après avoir rempli les valeurs
|
|
kubectl apply -f k8s/02-configmaps.yaml
|
|
kubectl apply -f k8s/03-backend-deployment.yaml
|
|
kubectl apply -f k8s/04-backend-service.yaml
|
|
kubectl apply -f k8s/05-frontend-deployment.yaml
|
|
kubectl apply -f k8s/06-frontend-service.yaml
|
|
kubectl apply -f k8s/07-ingress.yaml
|
|
kubectl apply -f k8s/08-hpa.yaml
|
|
kubectl apply -f k8s/09-pdb.yaml
|
|
|
|
# Ou tout d'un coup
|
|
kubectl apply -f k8s/
|
|
|
|
# Suivre le déploiement
|
|
kubectl rollout status deployment/xpeditis-backend -n xpeditis-prod
|
|
kubectl rollout status deployment/xpeditis-frontend -n xpeditis-prod
|
|
|
|
# Voir les pods
|
|
kubectl get pods -n xpeditis-prod -w
|
|
|
|
# Voir les logs
|
|
kubectl logs -f deployment/xpeditis-backend -n xpeditis-prod
|
|
kubectl logs -f deployment/xpeditis-frontend -n xpeditis-prod
|
|
|
|
# Vérifier le certificat TLS
|
|
kubectl get certificate -n xpeditis-prod
|
|
# NAME READY SECRET AGE
|
|
# xpeditis-tls-prod True xpeditis-tls-prod 2m
|
|
```
|
|
|
|
---
|
|
|
|
## Migration des jobs TypeORM
|
|
|
|
Le déploiement inclut automatiquement les migrations via le `startup.js` dans le Dockerfile. Si vous avez besoin de lancer les migrations manuellement :
|
|
|
|
```bash
|
|
# Job de migration one-shot
|
|
cat > /tmp/migration-job.yaml << 'EOF'
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: xpeditis-migrations
|
|
namespace: xpeditis-prod
|
|
spec:
|
|
template:
|
|
spec:
|
|
restartPolicy: OnFailure
|
|
containers:
|
|
- name: migrations
|
|
image: ghcr.io/<VOTRE_ORG>/xpeditis-backend:latest
|
|
command: ["node", "dist/migration-runner.js"]
|
|
envFrom:
|
|
- secretRef:
|
|
name: backend-secrets
|
|
imagePullSecrets:
|
|
- name: ghcr-credentials
|
|
EOF
|
|
|
|
kubectl apply -f /tmp/migration-job.yaml
|
|
kubectl wait --for=condition=complete job/xpeditis-migrations -n xpeditis-prod --timeout=300s
|
|
kubectl logs job/xpeditis-migrations -n xpeditis-prod
|
|
kubectl delete job xpeditis-migrations -n xpeditis-prod
|
|
```
|