首页 > 解决方案 > Kubernetes NGINX 入口控制器 - 如果存在查询字符串,则路由不同

问题描述

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

/foo/bar -> 到 serviceA 的路由

/foo/bar?x=10 -> 路由到 serviceB

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

标签: nginxkuberneteskubernetes-ingress

解决方案


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

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$1
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

推荐阅读