> For the complete documentation index, see [llms.txt](https://academy.cegedim.cloud/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://academy.cegedim.cloud/francais/calcul/conteneurs-k8s/k8s-didacticiels/k8s-migration-gateway-api.md).

# Migration vers la Gateway API (NGF)

Plan de migration étape par étape depuis le contrôleur NGINX Ingress vers NGINX Gateway Fabric (NGF) sur les clusters cegedim.cloud

## Contexte

Le contrôleur NGINX Ingress (`ingress-nginx`) a atteint sa fin de vie. Le projet upstream ne reçoit plus de nouvelles fonctionnalités — uniquement des correctifs de sécurité, pour une durée limitée. **NGINX Gateway Fabric (NGF)** est son remplaçant sur cegedim.cloud, implémentant la Kubernetes Gateway API standard.

Ce guide couvre la migration des clusters existants depuis `ingress-nginx` vers NGF.

{% hint style="info" %}
Les clusters provisionnés avec **Traefik** ou **Istio** supportent déjà la Gateway API nativement — ce plan de migration s'applique uniquement aux clusters utilisant le contrôleur NGINX Ingress (ingress-nginx).
{% endhint %}

Pour une explication complète de la Gateway API et de NGF, voir [Gateway API](/francais/calcul/conteneurs-k8s/k8s-architecture/gateway-api.md).

## Processus RFC

La migration des clusters existants est gérée via **deux RFCs** — et non une RFC par cluster :

| RFC       | Périmètre                         | Condition                                                                                   |
| --------- | --------------------------------- | ------------------------------------------------------------------------------------------- |
| **RFC 1** | Tous les clusters hors production | Valide la procédure sur tous les environnements hors-prod en une seule opération coordonnée |
| **RFC 2** | Tous les clusters de production   | Applique la procédure validée sur tous les clusters de prod après la RFC hors-prod réussie  |

## État Actuel

```mermaid
graph LR
    F5i[F5 VIP Interne] -->|port 80/443| Ni[nginx-int\nhostPort 80/443]
    F5e[F5 VIP Externe] -->|port 8081/8443| Ne[nginx-ext\nhostPort 8081/8443]

    Ni -->|Règles Ingress| B1[backend-a]
    Ni -->|Règles Ingress| B2[backend-b]
    Ne -->|Règles Ingress| B3[backend-c]
    Ne -->|Règles Ingress| B4[backend-d]

    subgraph nœuds-ingress
        Ni
        Ne
    end
```

## État Cible

```mermaid
graph LR
    F5i[F5 VIP Interne] -->|80/443| NGFi[Pod NGINX\ninternal-gateway\nhostPort 80/443]
    F5e[F5 VIP Externe] -->|8081/8443| NGFe[Pod NGINX\nexternal-gateway\nhostPort 8081/8443]

    NGFi -->|HTTPRoute| B1[backend-a]
    NGFi -->|HTTPRoute| B2[backend-b]
    NGFe -->|HTTPRoute| B3[backend-c]
    NGFe -->|HTTPRoute| B4[backend-d]

    subgraph namespace nginx-gateway
        CP[Plan de contrôle NGF\nGatewayClass: nginx]
    end

    CP -.gère.-> NGFi
    CP -.gère.-> NGFe

    subgraph nœuds-ingress
        NGFi
        NGFe
    end
```

## Jour 1 — Migration Plateforme (Zéro Impact Client)

Le Jour 1 est réalisé par **l'équipe plateforme cegedim.cloud**. Aucun changement F5, aucun changement aux ressources Ingress des clients, aucune interruption de service.

### Flux de Trafic Durant le Jour 1

```mermaid
graph LR
    F5i[F5 VIP Interne] -->|80/443 inchangé| NGFi[Pod NGINX\nhostPort 80/443]
    F5e[F5 VIP Externe] -->|8081/8443 inchangé| NGFe[Pod NGINX\nhostPort 8081/8443]

    NGFi -->|catch-all| Ni[nginx-int ClusterIP]
    NGFe -->|catch-all| Ne[nginx-ext ClusterIP]

    Ni -->|Ingress| B1[backend-a]
    Ni -->|Ingress| B2[backend-b]
    Ne -->|Ingress| B3[backend-c]
    Ne -->|Ingress| B4[backend-d]

    subgraph nginx-gateway
        CP[Plan de contrôle NGF]
        CP -.gère.-> NGFi
        CP -.gère.-> NGFe
    end

    subgraph ingress-nginx
        Ni
        Ne
    end
```

### Étapes du Jour 1

1. Déployer NGF via Helm dans le namespace `nginx-gateway` (nouveau namespace — aucun conflit avec les ressources existantes)
2. Créer les ressources Gateway `internal-gateway` et `external-gateway`
3. Créer le `ReferenceGrant` autorisant les HTTPRoutes dans `nginx-gateway` à cibler des Services dans `ingress-nginx`
4. `nginx-int` libère les hostPorts 80/443 — le DaemonSet `internal-gateway` les prend
5. `nginx-ext` libère les hostPorts 8081/8443 — le DaemonSet `external-gateway` les prend
6. Créer les HTTPRoutes catch-all : `internal-gateway` → ClusterIP `nginx-int`, `external-gateway` → ClusterIP `nginx-ext`
7. **Config F5 : aucun changement. Clients : aucun impact.** Le trafic transite NGF → nginx-int/nginx-ext → backends, sans changement.

{% hint style="warning" %}
**TLSRoute** n'est pas supporté dans NGF (canal expérimental, CRD non installé). Les clients dont le F5 ne termine **pas** le SSL et qui ont besoin d'un passthrough TLS brut doivent ouvrir un ticket ITCare pour discuter de leur cas spécifique avant la RFC 1.
{% endhint %}

## Jour 2+ — Playbook de Migration Applicative

Après le Jour 1, les équipes applicatives peuvent migrer leurs services **indépendamment, dans n'importe quel ordre, à leur propre rythme**. Il n'y a pas de date limite — les ressources Ingress legacy continuent de fonctionner via la route catch-all jusqu'à ce qu'elles soient explicitement migrées.

### Étapes de Migration par Service

Pour chaque service :

1. Créer un `HTTPRoute` avec le hostname exact pointant directement vers le `Service` backend
2. Supprimer la ressource `Ingress` correspondante
3. Vérifier — la spécificité du hostname garantit que la nouvelle route prend automatiquement la priorité sur le catch-all

### Exemple : Migration d'un Service Simple

```yaml
# Avant (Ingress)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app1
  namespace: app1-ns
spec:
  rules:
  - host: app1.internal.exemple.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app1-service
            port:
              number: 8080
```

```yaml
# Après (HTTPRoute)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app1
  namespace: app1-ns
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - "app1.internal.exemple.com"       # correspondance exacte -> priorité sur le catch-all automatiquement
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: app1-service
      port: 8080
```

### Exemple : Migration Partielle par Chemin

Migrer un sous-ensemble de chemins pour un hostname pendant que le reste transite toujours par `nginx-int` via le catch-all :

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app1-partiel
  namespace: app1-ns
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - "app1.internal.exemple.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/v2                # chemin migré -> nouveau backend
    backendRefs:
    - name: app1-v2-service
      port: 8080
  # Les autres chemins pour app1.internal.exemple.com transitent toujours par nginx-int via le catch-all
```

### Référence de Migration des Annotations

| Annotation NGINX Ingress                              | Équivalent NGF                                 | Effort |
| ----------------------------------------------------- | ---------------------------------------------- | ------ |
| `rewrite-target: /$2` (suppression de préfixe)        | `URLRewrite` + `ReplacePrefixMatch`            | Faible |
| `rewrite-target: /chemin-statique`                    | `URLRewrite` + `ReplaceFullPath`               | Faible |
| `use-regex: "true"`                                   | `path.type: RegularExpression`                 | Faible |
| `server-snippet` / `configuration-snippet` (en-têtes) | filtre `ResponseHeaderModifier`                | Faible |
| `permanent-redirect`                                  | filtre `RequestRedirect` (statusCode: 301)     | Faible |
| `proxy-read/send/connect-timeout`                     | `UpstreamSettingsPolicy`                       | Faible |
| `proxy-body-size`                                     | `ClientSettingsPolicy`                         | Faible |
| `x-forwarded-prefix`                                  | filtre `RequestHeaderModifier`                 | Faible |
| `auth-type: basic`                                    | `AuthenticationFilter` (alpha)                 | Moyen  |
| `whitelist-source-range`                              | `SnippetsFilter` (alpha)                       | Moyen  |
| Limitation de débit (`limit-rps` / `limit-rpm`)       | `RateLimitPolicy` (alpha)                      | Moyen  |
| `use-regex` + réécriture avec groupe de capture       | `SnippetsFilter` (alpha) + directive `rewrite` | Moyen  |

{% hint style="info" %}
Les fonctionnalités alpha (`AuthenticationFilter`, `RateLimitPolicy`, `SnippetsFilter`) sont opérationnelles mais présentent un risque de stabilité d'API lors des mises à jour de NGF. `SnippetsFilter` est désactivé par défaut et doit être activé par l'équipe plateforme. Contactez ITCare si votre application utilise ces patterns.
{% endhint %}

## Exemples de cas d'usage

Les 15 cas d'usage suivants ont été validés sur des clusters cegedim.cloud. Chacun présente la ressource NGINX Ingress d'origine et son équivalent NGF.

{% hint style="info" %}
Les cas d'usage marqués **(alpha)** font appel à des ressources spécifiques à NGF (`SnippetsFilter`, `AuthenticationFilter`, `RateLimitPolicy`, `ClientSettingsPolicy`) dont l'API peut évoluer lors des mises à jour de NGF. `SnippetsFilter` est désactivé par défaut — ouvrez un ticket ITCare pour en faire la demande.
{% endhint %}

### 01 — Suppression de préfixe

Supprimer un préfixe de chemin avant de transmettre au backend (ex. `/myapp/hello` → `/hello`).

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: prefix-rewrite
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /myapp(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: prefix-rewrite
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /myapp
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 02 — Réécriture statique de chemin

Remplacer l'intégralité du chemin de la requête par une valeur fixe (`/probe` → `/health`).

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: static-rewrite
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /health
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /probe
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: static-rewrite
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /probe
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplaceFullPath
          replaceFullPath: /health
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 03 — Correspondance par expression régulière

Faire correspondre des chemins à l'aide d'une expression régulière (ex. `/api/v1/resource` et `/api/v42/resource` correspondent ; `/api/vX/resource` ne correspond pas).

{% hint style="warning" %}
`RegularExpression` est une fonctionnalité étendue de la Gateway API. Avec NGF, ajoutez `sectionName: http` dans le `parentRef` pour éviter une condition `InvalidListener` sur le listener HTTPS.
{% endhint %}

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: regex-match
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /api/v[0-9]+/resource
        pathType: ImplementationSpecific
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: regex-match
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
    sectionName: http
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: RegularExpression
        value: "/api/v[0-9]+/resource"
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 04 — En-têtes de sécurité en réponse

Injecter des en-têtes de sécurité standard (`Content-Security-Policy`, `X-Content-Type-Options`, `X-XSS-Protection`) sur chaque réponse.

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: security-headers
  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |
      add_header Content-Security-Policy "default-src 'self'" always;
      add_header X-Content-Type-Options "nosniff" always;
      add_header X-XSS-Protection "1; mode=block" always;
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: security-headers
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ResponseHeaderModifier
      responseHeaderModifier:
        set:
        - name: Content-Security-Policy
          value: "default-src 'self'"
        - name: X-Content-Type-Options
          value: nosniff
        - name: X-XSS-Protection
          value: "1; mode=block"
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 05 — Authentification basique (alpha)

Protéger une route avec une authentification HTTP Basic adossée à un secret htpasswd.

{% hint style="warning" %}
Le type de secret NGF est `nginx.org/htpasswd`, différent du secret `Opaque` standard utilisé par NGINX Ingress Controller.

```bash
kubectl create secret generic basic-auth-secret \
  --type=nginx.org/htpasswd \
  --from-literal=auth=$(htpasswd -nb admin password) \
 
```

{% endhint %}

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: basic-auth
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth-secret
    nginx.ingress.kubernetes.io/auth-realm: "Protected Area"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: AuthenticationFilter
metadata:
  name: basic-auth
spec:
  type: Basic
  basic:
    realm: "Protected Area"
    secretRef:
      name: basic-auth-secret
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: basic-auth
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: AuthenticationFilter
        name: basic-auth
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 06 — Liste blanche d'IP (alpha)

Autoriser les requêtes uniquement depuis des plages CIDR spécifiques ; rejeter toutes les autres avec un 403.

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ip-allowlist
  annotations:
    nginx.ingress.kubernetes.io/whitelist-source-range: "10.26.0.0/16,192.168.0.0/16"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
metadata:
  name: ip-allowlist
spec:
  snippets:
  - context: http.server.location
    value: |
      allow 10.26.0.0/16;
      allow 192.168.0.0/16;
      deny all;
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: ip-allowlist
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: SnippetsFilter
        name: ip-allowlist
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 07 — Redirection permanente

Rediriger un chemin vers un hôte et/ou un chemin différent avec un HTTP 301.

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: redirect
  annotations:
    nginx.ingress.kubernetes.io/permanent-redirect: "https://new.example.com/landing"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /old-path
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: redirect
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /old-path
    filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        hostname: new.example.com
        path:
          type: ReplaceFullPath
          replaceFullPath: /landing
        statusCode: 301
```

{% endtab %}
{% endtabs %}

### 08 — Timeouts proxy (alpha)

Configurer les timeouts NGINX par route : connexion, envoi et lecture.

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: timeouts
  annotations:
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
metadata:
  name: timeouts
spec:
  snippets:
  - context: http.server.location
    value: |
      proxy_connect_timeout 10s;
      proxy_send_timeout    60s;
      proxy_read_timeout    120s;
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: timeouts
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: SnippetsFilter
        name: timeouts
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 09 — Limite de taille de corps de requête (alpha)

Limiter la taille maximale du corps de la requête (ex. rejeter les uploads au-delà de 700 Ko avec un 413).

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: body-size
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "700k"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /upload
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: body-size
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /upload
    backendRefs:
    - name: my-service
      port: 80
---
apiVersion: gateway.nginx.org/v1alpha1
kind: ClientSettingsPolicy
metadata:
  name: body-size
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: body-size
  body:
    maxSize: 700k
```

{% endtab %}
{% endtabs %}

### 10 — Limitation de débit (alpha)

Limiter les requêtes par IP client. Le `RateLimitPolicy` NGF utilise la sémantique NGINX `limit_req` — le code de rejet est configurable (503 par défaut avec ingress-nginx, 429 avec NGF).

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rate-limit
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "1"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: RateLimitPolicy
metadata:
  name: rate-limit
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: rate-limit
  rateLimit:
    local:
      rules:
      - zoneSize: 10m
        key: "$binary_remote_addr"
        rate: 1r/s
    rejectCode: 429
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: rate-limit
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 11 — Réécriture avec groupes de capture regex (alpha)

Réordonner des segments de chemin à l'aide de groupes de capture regex (ex. `/foo/status/bar` → `/bar/foo`). Il n'existe pas d'équivalent standard dans la Gateway API — `SnippetsFilter` est requis.

{% hint style="warning" %}
C'est le cas de migration le plus complexe. Le `SnippetsFilter` injecte un bloc `location ~ /status/` dans le contexte serveur NGINX. L'`HTTPRoute` doit utiliser `path.type: RegularExpression` (et non `PathPrefix /`) pour éviter qu'un bloc `location ^~` n'empêche l'évaluation de la location regex injectée. Remplacez `my-app_my-service_80` par le nom upstream NGF réel : `{namespace}_{service-name}_{port}`.
{% endhint %}

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: regex-capture-rewrite
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2/$1
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /(.+)/status/(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
metadata:
  name: regex-swap-rewrite
spec:
  snippets:
  - context: http.server
    value: |
      location ~ /status/ {
          rewrite ^/(.+)/status/(.*)$ /$2/$1 break;
          return 400;
          proxy_pass http://my-app_my-service_80;
      }
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: regex-swap-rewrite
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
    sectionName: http
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: RegularExpression
        value: /.*
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: SnippetsFilter
        name: regex-swap-rewrite
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 12 — En-têtes dynamiques via variables NGINX (alpha)

Injecter des en-têtes utilisant des variables NGINX runtime (`$host`, `$request_id`, etc.) dans les réponses au client ou dans les requêtes vers le backend. Les filtres standard `ResponseHeaderModifier` et `RequestHeaderModifier` ne supportent que des valeurs statiques — utilisez `SnippetsFilter` pour les valeurs dynamiques.

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: escape-hatch
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      add_header X-Custom-Host $host always;
      add_header X-Custom-RequestID $request_id always;
      proxy_set_header X-Custom-Host $host;
      proxy_set_header X-Custom-RequestID $request_id;
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
metadata:
  name: escape-hatch
spec:
  snippets:
  - context: http.server.location
    value: |
      add_header X-Custom-Host $host always;
      add_header X-Custom-RequestID $request_id always;
      proxy_set_header X-Custom-Host $host;
      proxy_set_header X-Custom-RequestID $request_id;
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: escape-hatch
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: SnippetsFilter
        name: escape-hatch
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 13 — Retry sur échec upstream (alpha)

Retenter silencieusement les requêtes échouées sur un autre pod avant de renvoyer une erreur au client. Évite qu'un pod défaillant unique ne génère des erreurs 502 lorsque des pods sains sont disponibles.

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: next-upstream
  annotations:
    nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout"
    nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "3"
    nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "0"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
metadata:
  name: next-upstream
spec:
  snippets:
  - context: http.server.location
    value: |
      proxy_next_upstream error timeout;
      proxy_next_upstream_tries 3;
      proxy_next_upstream_timeout 0;
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: next-upstream
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: SnippetsFilter
        name: next-upstream
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 14 — X-Forwarded-Prefix

Informer le backend du préfixe de chemin public sous lequel il est exposé. Nécessaire lorsque la gateway supprime un préfixe et que le backend génère des URLs absolues ou des redirections (Spring Boot, Django, etc.).

{% hint style="info" %}
L'annotation `nginx.ingress.kubernetes.io/x-forwarded-prefix` est peu fiable depuis ingress-nginx v1.12+. Utilisez directement `configuration-snippet` avec `proxy_set_header` comme indiqué ci-dessous.
{% endhint %}

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: x-forwarded-prefix
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header X-Forwarded-Prefix "/myapp";
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /myapp
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: x-forwarded-prefix
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /myapp
    filters:
    - type: RequestHeaderModifier
      requestHeaderModifier:
        set:
        - name: X-Forwarded-Prefix
          value: /myapp
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

### 15 — IP client réelle depuis F5 (alpha)

Restaurer l'IP client réelle depuis l'en-tête `X-Forwarded-For` positionné par le load balancer F5. Sans cette configuration, les backends voient l'IP du F5 sur chaque requête.

{% hint style="info" %}
En alternative au `SnippetsFilter` (portée par route), l'équipe plateforme peut configurer la réécriture de l'IP réelle globalement pour toutes les routes via une ressource `NginxProxy`. Contactez ITCare si une configuration à l'échelle du cluster est préférable.
{% endhint %}

{% tabs %}
{% tab title="NGINX Ingress" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: use-forwarded-headers
  annotations:
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "10.0.0.0/8"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.mycluster.ccs.cegedim.cloud
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80
```

{% endtab %}

{% tab title="NGF (HTTPRoute)" %}

```yaml
apiVersion: gateway.nginx.org/v1alpha1
kind: SnippetsFilter
metadata:
  name: use-forwarded-headers
spec:
  snippets:
  - context: http.server
    value: |
      real_ip_header X-Forwarded-For;
      real_ip_recursive on;
      set_real_ip_from 10.0.0.0/8;
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: use-forwarded-headers
spec:
  parentRefs:
  - name: internal-gateway
    namespace: nginx-gateway
  hostnames:
  - myapp.mycluster.ccs.cegedim.cloud
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.nginx.org
        kind: SnippetsFilter
        name: use-forwarded-headers
    backendRefs:
    - name: my-service
      port: 80
```

{% endtab %}
{% endtabs %}

## Calendrier Récapitulatif

| Phase       | Responsable          | Description                                                                                                               |
| ----------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Jour 1**  | Équipe plateforme    | NGF déployé ; nginx-int/nginx-ext libèrent les hostPorts ; HTTPRoutes catch-all créées ; F5 inchangé ; zéro impact client |
| **Jour 2+** | Équipes applicatives | Migration des ressources Ingress vers HTTPRoutes, service par service, dans n'importe quel ordre                          |
| **Jour N**  | Équipe plateforme    | Décommissionnement de `nginx-int` une fois tout le trafic interne migré vers les HTTPRoutes NGF                           |
| **Jour M**  | Équipe plateforme    | Décommissionnement de `nginx-ext` une fois tout le trafic externe migré vers les HTTPRoutes NGF                           |

## Support

Pour demander une assistance à la migration ou signaler un problème de compatibilité, ouvrez un ticket via **ITCare** avec le label `Migration Gateway API`.

Pour un guide de migration annotation par annotation au-delà du tableau ci-dessus, référez-vous à la documentation officielle de migration de NGINX Gateway Fabric :

{% embed url="<https://docs.nginx.com/nginx-gateway-fabric/how-to/migrate-from-nginx-ingress/>" %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://academy.cegedim.cloud/francais/calcul/conteneurs-k8s/k8s-didacticiels/k8s-migration-gateway-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
