Standalone Nginx Ingress

In a multi-AZ deployment, each Kloudfuse cluster requires its own ingress-nginx controller pinned to its availability zone. Because the controller must be independently manageable during a failover, it is installed as a separate Helm release outside the Kloudfuse chart rather than using the bundled ingress-nginx.

Disable the Bundled ingress-nginx

Before installing a standalone controller, disable the ingress-nginx controller that is bundled with the Kloudfuse Helm chart. Add the following to your custom-values.yaml:

ingress-nginx:
  enabled: false
yaml
Only one ingress controller can be active in a Kloudfuse cluster at a time. If you are using Envoy Gateway instead of ingress-nginx, these values are already set by the Envoy Gateway configuration. See Configure Nginx Ingress for details.

Configure the Standalone Controller

Create a values.yaml for the ingress-nginx Helm chart. The controller must be pinned to the target AZ’s node group via affinity and tolerations, and assigned a unique ingressClass name so that Kloudfuse resources target the correct controller.

rbac:
  scope: false
controller:
  resources:
    requests:
      cpu: 10000m
      memory: 15000Mi
  scope:
    enabled: false
  containerName: nginx-controller
  allowSnippetAnnotations: false
  config:
    client-header-buffer-size: "16k"
    large-client-header-buffers: "4 16k"
    proxy-buffer-size: "64k"
    proxy-buffers: "8 64k"
    proxy-busy-buffers-size: "128k"
    location-snippet: |
      if ($request_uri = /) {
        add_header Cache-Control 'no-cache, must-revalidate, proxy-revalidate, max-age=0';
        expires off;
      }
      if ($request_uri ~* \.(?:html|js|css|gif|jpe?g|png)) {
        add_header Cache-Control 'no-cache, must-revalidate, proxy-revalidate, max-age=0';
        expires off;
      }
  addHeaders:
    X-Content-Type-Options: "nosniff"
    X-Frame-Options: "SAMEORIGIN"
    Content-Security-Policy: "default-src 'self' https://*.jsdelivr.net/ https://fonts.googleapis.com/ https://apis.google.com/js/ https://fonts.gstatic.com/ 'unsafe-inline' 'unsafe-eval'; worker-src 'self' blob:;"
    Strict-Transport-Security: "max-age=31536000; includeSubDomains"
  metrics:
    enabled: true
  extraArgs:
    enable-metrics: true
  podAnnotations:
    prometheus.io/path: /metrics
    prometheus.io/port: "10254"
    prometheus.io/scrape: "true"

  ingressClassResource:
    enabled: true
    name: <az-name>   (1)
  ingressClass: <az-name>
  watchIngressWithoutClass: false
  externalTrafficPolicy: Local
  service:
    enableHttp: false
    loadBalancerIP: "<EXTERNAL_LB_IP>"  (2)
    external:
      enabled: true
    internal:
      enabled: true
      loadBalancerIP: "<INTERNAL_LB_IP>"  (3)
      annotations:
        networking.gke.io/load-balancer-type: "Internal"
        cloud.google.com/load-balancer-type: "Internal"

  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: ng_label
            operator: In
            values:
            - <az-name>
  tolerations:
  - key: "ng_taint"
    operator: "Equal"
    value: "<az-name>"
    effect: "NoSchedule"
yaml
1 az-name: A short identifier for the AZ (for example, az1 or az2). Used as the ingress class name and node affinity label. Must match the node group labels applied to the AZ’s nodes.
2 EXTERNAL_LB_IP: The external static IP address for ingest traffic. Map this to your public DNS hostname (for example, observability-ingest.example.com).
3 INTERNAL_LB_IP: The internal IP address for UI/query traffic. Map this to your internal DNS hostname (for example, observability.example.com).

A self-managed controller must also forward the X-Request-ID header to Kloudfuse. Merge the following fields into the controller block above:

Kloudfuse expects every request that reaches its services to carry an X-Request-ID header, and its authentication subrequest relies on the same header. The bundled ingress-nginx controller is configured to do this automatically. A NGINX controller that you manage yourself is not — without the configuration below the header can arrive empty, and the Kloudfuse services that depend on it fail to resolve it.

If you run your own NGINX ingress configuration, add the following to the controller so it preserves an incoming X-Request-ID (or generates one when the header is missing) and forwards it to every backend:

controller:
  # Forward X-Request-ID to all backends.
  proxySetHeaders:
    X-Request-ID: $kf_req_id
  config:
    # Keep generate-request-id at its default ("true"). The built-in $req_id is
    # used by the auth subrequest and error pages; disabling it leaves those with
    # an empty X-Request-ID when the incoming header is missing.
    generate-request-id: "true"
    http-snippet: |
      # Preserve incoming X-Request-ID; generate one (via the core $request_id,
      # which is always present) when the header is missing.
      map $http_x_request_id $kf_req_id {
        default $http_x_request_id;
        ""      $request_id;
      }
yaml

Install the Standalone Controller

helm upgrade --install <release-name> ingress-nginx/ingress-nginx \ (1)
  --namespace <release-name> --create-namespace \
  --version <version> \ (2)
  -f values.yaml
1 Replace <release-name> with a name that identifies the AZ (for example, ingress-az1 or ingress-az2). Use the same value for both the release name and namespace so each AZ controller is isolated.
2 Replace <version> with the ingress-nginx chart version required for your environment. See the ingress-nginx releases page for available versions.

Repeat this step for each AZ, using a distinct <release-name> and a corresponding values.yaml with the correct AZ-specific settings.