Nginx Ingress Controller请求头路由配置问题排查
Let's break down your problem and walk through the solutions step by step:
First: Fixing the initial syntax error
The unknown directive error you encountered was definitely due to missing spacing in the variable assignment. The correct syntax for setting the variable is:
set $service_name "backend-data";
Once you fixed that, the config generated without errors—but routing still failed. That's because modifying the built-in $service_name variable from Nginx Ingress Controller isn't a reliable approach.
Why redefining $service_name doesn't work
The $service_name variable is managed internally by the Ingress Controller. It sets this variable early in the location block (as you saw in the generated config), and while your configuration-snippet can overwrite it, the controller's pre-generated proxy logic might not actually use the updated value. Even if it does, relying on modifying built-in variables can lead to unexpected issues with health checks, metrics, or future controller updates.
Better approaches to implement header-based routing
Here are three robust, supported ways to achieve your goal:
Approach 1: Custom proxy logic with server-snippet and configuration-snippet
You can define your own target service variable and override the proxy pass behavior directly:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: api-multi-back annotations: nginx.ingress.kubernetes.io/server-snippet: | # Define a custom variable to hold our target service set $target_service "backend-default"; # Update the variable if the Content-Type matches if ($http_content_type ~* "multipart/form-data.*") { set $target_service "backend-data"; } nginx.ingress.kubernetes.io/configuration-snippet: | # Override the default proxy pass to use our custom variable proxy_pass http://$target_service.my-namespace.svc.cluster.local:80; # Replace `my-namespace` with your actual Kubernetes namespace spec: rules: - host: example.com http: paths: - backend: serviceName: backend-default # Required placeholder (won't be used) servicePort: 80 path: /api
Notes:
- You must provide a valid
backendentry as a placeholder—otherwise the controller will reject the ingress config. - Manually construct the full service DNS name using
$target_service.$namespace.svc.cluster.localto ensure proper service discovery.
Approach 2: Two separate Ingress resources with header filtering
Split your routing into two ingresses, each handling a specific header condition. This is more aligned with Kubernetes' declarative style:
# Default backend for requests without multipart/form-data apiVersion: extensions/v1beta1 kind: Ingress metadata: name: api-default-back spec: rules: - host: example.com http: paths: - backend: serviceName: backend-default servicePort: 80 path: /api --- # Backend for requests with multipart/form-data apiVersion: extensions/v1beta1 kind: Ingress metadata: name: api-data-back annotations: nginx.ingress.kubernetes.io/configuration-snippet: | # Block requests that don't match the header if ($http_content_type !~* "multipart/form-data.*") { return 404; } spec: rules: - host: example.com http: paths: - backend: serviceName: backend-data servicePort: 80 path: /api
Pros: Clear, modular configuration—each ingress maps to one backend, making it easier to manage and monitor.
Approach 3: Use Canary Ingress for header-based traffic splitting
If you're looking for a native, managed solution (great for gray-scale testing or traffic shifting), use the Ingress Controller's canary features:
# Main ingress: handles all default traffic apiVersion: extensions/v1beta1 kind: Ingress metadata: name: api-main-back spec: rules: - host: example.com http: paths: - backend: serviceName: backend-default servicePort: 80 path: /api --- # Canary ingress: catches requests with the target header apiVersion: extensions/v1beta1 kind: Ingress metadata: name: api-canary-data annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "Content-Type" # Match exact value or use regex for partial matches nginx.ingress.kubernetes.io/canary-by-header-pattern: "multipart/form-data.*" spec: rules: - host: example.com http: paths: - backend: serviceName: backend-data servicePort: 80 path: /api
Pros: No custom Nginx snippets needed—this is fully supported by the Ingress Controller, and it's easy to adjust or disable the canary traffic later.
Final Takeaway
Avoid modifying built-in controller variables like $service_name—it's unstable and can break controller features. Instead, use one of the approaches above: custom proxy logic for full control, split ingresses for clarity, or canary ingress for native traffic splitting.
内容的提问来源于stack exchange,提问作者Kelindil

