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