Kubernetes NGINX Ingress 控制器 - 如果存在查询字符串,则路由不同

Kubernetes NGINX Ingress controller - different route if query string exists

Kubernetes 的 Nginx 入口控制器是否有可能根据查询字符串是否存在路由到不同服务的入口规则?例如..

/foo/bar -> 路由到服务 A

/foo/bar?x=10 -> 路由到服务 B

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - host: xxxx.com
    http:
      paths:
      - path: /foo/bar(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: serviceA
            port:
              number: 8001
      - path: /foo/bar(/|$)(.*)\?
        pathType: Prefix
        backend:
          service:
            name: serviceB
            port:
              number: 8002

我设法为您描述的两个入口对象找到了一个可行的解决方案。对于您提供的入口示例,将无法将您引导至 service-b,因为 nginx 根本不匹配查询字符串。这很好解释here

Ingress 根据路径选择合适的备份。所以我为第二个后端准备了单独的路径,并将条件重定向到第一个路径,这样当请求到达 /tmp 路径时,它使用 service-b 后端并从请求中修剪 tmp 部分。

所以这是与 /foo/bar 匹配 backend-a

的入口
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
            if ($args ~ .+){
                      rewrite ^ http://xxxx.com/foo/bar/tmp permanent;
                      }
spec:
  rules:
  - host: xxxx.com
    http:
      paths:
      - path: /foo/bar
        pathType: Prefix
        backend:
          serviceName: service-a
          servicePort: 80

这是匹配 /foo/bar? 的入口以及 backend-b

之后的任何内容
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress-rewrite
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /foo/bar
spec:
  rules:
  - host: xxxx.com
    http:
      paths:
      - path: /foo/bar/tmp(.*)
        backend:
          serviceName: service-b
          servicePort: 80

请注意,以前的配置残留物可能会阻止该解决方案正常运行。在这种情况下,清理、重新部署和入口控制器重启应该会有所帮助。

这里有一些测试可以证明这一点。首先,我将 xxxx.com 添加到 /etc/hosts:

➜  ~ cat /etc/hosts
127.0.0.1       localhost
192.168.59.2 xxxx.com

- 这里我们测试第一个路径 /foo/bar:

➜  ~ curl -L -v http://xxxx.com/foo/bar        
*   Trying 192.168.59.2...
* TCP_NODELAY set
* Connected to xxxx.com (192.168.59.2) port 80 (#0)
> GET /foo/bar HTTP/1.1 <----- See path here! 
> Host: xxxx.com
> User-Agent: curl/7.52.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Tue, 13 Apr 2021 12:30:00 GMT
< Content-Type: application/json; charset=utf-8
< Content-Length: 644
< Connection: keep-alive
< X-Powered-By: Express
< ETag: W/"284-P+J4oZl3lklvyqdp6FEGTPVw/VM"
< 
{
  "path": "/foo/bar",
  "headers": {
    "host": "xxxx.com",
    "x-request-id": "1f7890a47ca1b27d2dfccff912d5d23d",
    "x-real-ip": "192.168.59.1",
    "x-forwarded-for": "192.168.59.1",
    "x-forwarded-host": "xxxx.com",
    "x-forwarded-port": "80",
    "x-forwarded-proto": "http",
    "x-scheme": "http",
    "user-agent": "curl/7.52.1",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "xxxx.com",
  "ip": "192.168.59.1",
  "ips": [
    "192.168.59.1"
  ],
  "protocol": "http",
  "query": {},
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "service-a" <------ Pod hostname that response came from.

- 这里我们正在测试第一个路径 /foo/bar:

➜  ~ curl -L -v http://xxxx.com/foo/bar\?x\=10 
*   Trying 192.168.59.2...
* TCP_NODELAY set
* Connected to xxxx.com (192.168.59.2) port 80 (#0)
> GET /foo/bar?x=10 HTTP/1.1 <--------- The requested path! 
> Host: xxxx.com
> User-Agent: curl/7.52.1
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Date: Tue, 13 Apr 2021 12:31:58 GMT
< Content-Type: text/html
< Content-Length: 162
< Connection: keep-alive
< Location: http://xxxx.com/foo/bar/tmp?x=10
< 
* Ignoring the response-body
* Curl_http_done: called premature == 0
* Connection #0 to host xxxx.com left intact
* Issue another request to this URL: 'http://xxxx.com/foo/bar/tmp?x=10'
* Found bundle for host xxxx.com: 0x55d6673218a0 [can pipeline]
* Re-using existing connection! (#0) with host xxxx.com
* Connected to xxxx.com (192.168.59.2) port 80 (#0)
> GET /foo/bar/tmp?x=10 HTTP/1.1
> Host: xxxx.com
> User-Agent: curl/7.52.1
> Accept: */*
>  
{
  "path": "/foo/bar",
  "headers": {
    "host": "xxxx.com",
    "x-request-id": "96a949a407dae653f739db01fefce7bf",
    "x-real-ip": "192.168.59.1",
    "x-forwarded-for": "192.168.59.1",
    "x-forwarded-host": "xxxx.com",
    "x-forwarded-port": "80",
    "x-forwarded-proto": "http",
    "x-scheme": "http",
    "user-agent": "curl/7.52.1",
    "accept": "*/*"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "xxxx.com",
  "ip": "192.168.59.1",
  "ips": [
    "192.168.59.1"
  ],
  "protocol": "http",
  "query": {
    "x": "10"
  },
  "subdomains": [],
  "xhr": false,
  "os": {
    "hostname": "service-b" <-----Service-b host name!
  },
  "connection": {}

对于我使用 mendhak/http-https-echo 图片的回复:

apiVersion: v1
kind: Pod
metadata:
  name: service-b
  labels:
    app: echo2
spec:
  containers:
  - name: service-b #<-------- service-b host name
    image: mendhak/http-https-echo
    ports:
    - containerPort: 80