问题描述
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
解决方法
我设法为您描述的两个入口对象找到了一个可行的解决方案。对于您提供的示例,入口将无法将您引导至 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$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","x-request-id": "96a949a407dae653f739db01fefce7bf","query": {
"x": "10"
},"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