nginx 入口和重写目标
nginx ingress & rewrite-target
我有一个 pod 可以响应对 /api/
的请求
我想重写 /auth/api/ 到 /api/ 的请求。
使用 Ingress (nginx),我认为使用 ingress.kubernetes.io/rewrite-target: 注释我可以这样做:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api
backend:
serviceName: myapi
servicePort: myapi-port
然而发生的事情是 /auth/ 被传递给 service/pod 并且正确地抛出 404。我一定是误解了重写注释。
有没有办法通过 k8s 和入口来做到这一点?
我创建了以下有效的示例,我将对其进行解释。对于 运行 这个最小示例,运行 这些命令:
$ minikube start
$ minikube addons enable ingress # might take a while for ingress pod to bootstrap
$ kubectl apply -f kubernetes.yaml
$ curl https://$(minikube ip)/auth/api/ --insecure
success - path: /api/
$ curl https://$(minikube ip)/auth/api --insecure
failure - path: /auth/api
$ curl https://$(minikube ip)/auth/api/blah/whatever --insecure
success - path: /api/blah/whatever
正如您所注意到的,入口重写注释似乎对尾部斜杠非常讲究。如果不存在尾部斜杠,则不会重写请求。但是,如果提供尾部斜杠,请求 uri 将被重写,您的代理将按预期运行。
从入口控制器内部检查生成的 nginx.conf
文件后,导致此行为的代码行是:
rewrite /auth/api/(.*) api/ break;
这一行告诉我们,只有匹配第一个参数的请求才会被第二个参数指定的路径重写。
我相信这是值得的错误。
kubernetes.yaml
---
apiVersion: v1
kind: Service
metadata:
name: ingress-rewite-example
spec:
selector:
app: ingress-rewite-example
ports:
- name: nginx
port: 80
protocol: TCP
targetPort: 80
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ingress-rewite-example
spec:
template:
metadata:
labels:
app: ingress-rewite-example
spec:
containers:
- name: ingress-rewite-example
image: fbgrecojr/office-hours:so-47837087
imagePullPolicy: Always
ports:
- containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-rewite-example
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /auth/api
backend:
serviceName: ingress-rewite-example
servicePort: 80
main.go
package main
import (
"fmt"
"strings"
"net/http"
)
func httpHandler(w http.ResponseWriter, r *http.Request) {
var response string
if strings.HasPrefix(r.URL.Path, "/api") {
response = "success"
} else {
response = "failure"
}
fmt.Fprintf(w, response + " - path: " + r.URL.Path + "\n")
}
func main() {
http.HandleFunc("/", httpHandler)
panic(http.ListenAndServe(":80", nil))
}
我不知道这是否仍然是个问题,但从 0.22 版开始,您似乎需要使用捕获组将值传递给 rewrite-target 值
来自可用的 nginx 示例 here
Starting in Version 0.22.0, ingress definitions using the annotation nginx.ingress.kubernetes.io/rewrite-target are not backwards compatible with previous versions. In Version 0.22.0 and beyond, any substrings within the request URI that need to be passed to the rewritten path must explicitly be defined in a capture group.
对于您的特定需求,像这样的东西应该可以解决问题
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api/
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api(/|$)(.*)
backend:
serviceName: myapi
servicePort: myapi-port
我有一个 pod 可以响应对 /api/
的请求我想重写 /auth/api/ 到 /api/ 的请求。
使用 Ingress (nginx),我认为使用 ingress.kubernetes.io/rewrite-target: 注释我可以这样做:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api
backend:
serviceName: myapi
servicePort: myapi-port
然而发生的事情是 /auth/ 被传递给 service/pod 并且正确地抛出 404。我一定是误解了重写注释。
有没有办法通过 k8s 和入口来做到这一点?
我创建了以下有效的示例,我将对其进行解释。对于 运行 这个最小示例,运行 这些命令:
$ minikube start
$ minikube addons enable ingress # might take a while for ingress pod to bootstrap
$ kubectl apply -f kubernetes.yaml
$ curl https://$(minikube ip)/auth/api/ --insecure
success - path: /api/
$ curl https://$(minikube ip)/auth/api --insecure
failure - path: /auth/api
$ curl https://$(minikube ip)/auth/api/blah/whatever --insecure
success - path: /api/blah/whatever
正如您所注意到的,入口重写注释似乎对尾部斜杠非常讲究。如果不存在尾部斜杠,则不会重写请求。但是,如果提供尾部斜杠,请求 uri 将被重写,您的代理将按预期运行。
从入口控制器内部检查生成的 nginx.conf
文件后,导致此行为的代码行是:
rewrite /auth/api/(.*) api/ break;
这一行告诉我们,只有匹配第一个参数的请求才会被第二个参数指定的路径重写。
我相信这是值得的错误。
kubernetes.yaml
---
apiVersion: v1
kind: Service
metadata:
name: ingress-rewite-example
spec:
selector:
app: ingress-rewite-example
ports:
- name: nginx
port: 80
protocol: TCP
targetPort: 80
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: ingress-rewite-example
spec:
template:
metadata:
labels:
app: ingress-rewite-example
spec:
containers:
- name: ingress-rewite-example
image: fbgrecojr/office-hours:so-47837087
imagePullPolicy: Always
ports:
- containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-rewite-example
annotations:
ingress.kubernetes.io/rewrite-target: /api
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- http:
paths:
- path: /auth/api
backend:
serviceName: ingress-rewite-example
servicePort: 80
main.go
package main
import (
"fmt"
"strings"
"net/http"
)
func httpHandler(w http.ResponseWriter, r *http.Request) {
var response string
if strings.HasPrefix(r.URL.Path, "/api") {
response = "success"
} else {
response = "failure"
}
fmt.Fprintf(w, response + " - path: " + r.URL.Path + "\n")
}
func main() {
http.HandleFunc("/", httpHandler)
panic(http.ListenAndServe(":80", nil))
}
我不知道这是否仍然是个问题,但从 0.22 版开始,您似乎需要使用捕获组将值传递给 rewrite-target 值 来自可用的 nginx 示例 here
Starting in Version 0.22.0, ingress definitions using the annotation nginx.ingress.kubernetes.io/rewrite-target are not backwards compatible with previous versions. In Version 0.22.0 and beyond, any substrings within the request URI that need to be passed to the rewritten path must explicitly be defined in a capture group.
对于您的特定需求,像这样的东西应该可以解决问题
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapi-ing
annotations:
ingress.kubernetes.io/rewrite-target: /api/
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /auth/api(/|$)(.*)
backend:
serviceName: myapi
servicePort: myapi-port