# ============================================================================= # Frontend Next.js 14 (sortie standalone) # ============================================================================= # RAPPEL CRITIQUE : NEXT_PUBLIC_API_URL est fige au moment du BUILD de l'image # (next.config.js, bloc `env`). Une image construite pour la preprod pointera # toujours vers api.preprod.xpeditis.com, quelles que soient les variables # injectees ici. L'image du frontend doit donc etre RECONSTRUITE pour la prod, # jamais promue depuis la preprod. Le workflow cd-main.yml applique cette regle. --- apiVersion: apps/v1 kind: Deployment metadata: name: xpeditis-frontend namespace: xpeditis-prod labels: app.kubernetes.io/name: xpeditis-frontend app.kubernetes.io/component: web app.kubernetes.io/part-of: xpeditis spec: replicas: 2 revisionHistoryLimit: 5 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 selector: matchLabels: app.kubernetes.io/name: xpeditis-frontend template: metadata: labels: app.kubernetes.io/name: xpeditis-frontend app.kubernetes.io/component: web app.kubernetes.io/part-of: xpeditis spec: imagePullSecrets: - name: regcred topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app.kubernetes.io/name: xpeditis-frontend securityContext: runAsNonRoot: true runAsUser: 1001 runAsGroup: 1001 fsGroup: 1001 seccompProfile: type: RuntimeDefault terminationGracePeriodSeconds: 30 containers: - name: frontend image: rg.fr-par.scw.cloud/weworkstudio/xpeditis-frontend:latest imagePullPolicy: IfNotPresent ports: - name: http containerPort: 3000 protocol: TCP env: - name: NODE_ENV value: "production" - name: PORT value: "3000" - name: HOSTNAME value: "0.0.0.0" # Lues cote serveur uniquement (route handlers, server actions). # Le code client, lui, a deja la valeur figee au build. - name: NEXT_PUBLIC_API_URL value: "https://api.xpeditis.com" - name: NEXT_PUBLIC_APP_URL value: "https://app.xpeditis.com" - name: NEXT_TELEMETRY_DISABLED value: "1" startupProbe: httpGet: path: /api/health port: http initialDelaySeconds: 5 periodSeconds: 3 failureThreshold: 30 livenessProbe: httpGet: path: /api/health port: http periodSeconds: 20 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /api/health port: http initialDelaySeconds: 3 periodSeconds: 10 timeoutSeconds: 3 resources: requests: cpu: 200m memory: 384Mi limits: cpu: 1000m memory: 1Gi securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] # Le build standalone n'ecrit qu'en cache : on peut donc verrouiller # la racine et n'ouvrir que les deux repertoires necessaires. readOnlyRootFilesystem: true volumeMounts: - name: next-cache mountPath: /app/.next/cache - name: tmp mountPath: /tmp lifecycle: preStop: exec: command: ["sh", "-c", "sleep 5"] volumes: - name: next-cache emptyDir: sizeLimit: 512Mi - name: tmp emptyDir: sizeLimit: 128Mi --- apiVersion: v1 kind: Service metadata: name: xpeditis-frontend namespace: xpeditis-prod labels: app.kubernetes.io/name: xpeditis-frontend spec: type: ClusterIP selector: app.kubernetes.io/name: xpeditis-frontend ports: - name: http port: 3000 targetPort: http protocol: TCP --- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: xpeditis-frontend namespace: xpeditis-prod spec: minAvailable: 1 selector: matchLabels: app.kubernetes.io/name: xpeditis-frontend