GKE 上的 nginx-ingress 无法路由到已配置服务的路径

nginx-ingress on GKE fails to route to path for configured service

我正在尝试为 GKE 集群配置 nginx 入口并在配置的子域上定义路径。似乎即使我能够成功 ping 主机,并且域绑定正确完成,但每当我尝试访问配置的路径时,我都会收到 404 返回。

我的目标是能够为我的入口控制器配置一个静态 IP,并在不同路径上公开多个服务。

您可以在下面找到我的部署文件 - 我要补充的另一件事是我使用 Terraform 来自动配置和部署 GCP 和 Kubernetes 资源。

成功配置 GKE 集群后,我首先从 here 部署官方 nginx-ingress 控制器 - 在我的 Terraform 脚本下方,该脚本使用我在 GCP 上配置的自定义静态 IP 配置和部署控制器。

resource "helm_release" "nginx" {
  name  = "nginx"
  chart = "nginx-stable/nginx-ingress"
  timeout = 900

  set {
    name = "controller.stats.enabled"
    value = true
  }

  set {
    name  = "controller.service.type"
    value = "LoadBalancer"
  }

  set {
    name  = "controller.service.loadBalancerIP"
    value = "<MY_STATIC_IP_ADDRESS>"
  }
}

在我也通过 Terraform 部署的入口配置下方:

resource "kubernetes_ingress" "ingress" {
  wait_for_load_balancer = true

  metadata {
    name = "app-ingress"

    annotations = {
        "kubernetes.io/ingress.class": "nginx"
        "nginx.ingress.kubernetes.io/rewrite-target": "/"
        "kubernetes.io/ingress.global-static-ip-name": <MY_STATIC_IP_ADDRESS>
    }
  }

  spec {
    rule {
      host = custom.my_domain.com
      http {
        path {
          backend {
            service_name = "app-service"
            service_port = 5000
          }

          path = "/app"
        }
      }
    }
  }
}

以及从 GCP 获取的结果入口配置:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/ingress.global-static-ip-name: static-ip-name
    nginx.ingress.kubernetes.io/rewrite-target: /
  creationTimestamp: "2021-04-14T20:28:41Z"
  generation: 7
  name: app-ingress
  namespace: default
  resourceVersion: HIDDEN
  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/app-ingress
  uid: HIDDEN
spec:
  rules:
  - host: custom.my_domain.com
    http:
      paths:
      - backend:
          serviceName: app-service
          servicePort: 5000
        path: /app
status:
  loadBalancer:
    ingress:
    - ip: <MY_STATIC_IP_ADDRESS>

kubectl describe ingress app-ingress 命令的输出:

Name:             app-ingress
Namespace:        default
Address:          <MY_STATIC_IP_ADDRESS>
Default backend:  default-http-backend:80 (192.168.10.8:8080)
Rules:
  Host                  Path  Backends
  ----                  ----  --------
  custom.my_domain.com
                        /app   app-service:5000 (192.168.10.11:5000)
Annotations:            kubernetes.io/ingress.class: nginx
                        kubernetes.io/ingress.global-static-ip-name: static-ip-name
                        nginx.ingress.kubernetes.io/rewrite-target: /
Events:
  Type    Reason          Age                From                      Message
  ----    ------          ----               ----                      -------
  Normal  AddedOrUpdated  16m (x6 over 32m)  nginx-ingress-controller  Configuration for default/app-ingress was added or updated

我使用以下配置文件部署了我试图公开的应用程序:

pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: default

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  type: NodePort
  ports:
    - port: 5000
      targetPort: 5000
      protocol: TCP
      name: http

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 1
  template:
    spec:
      restartPolicy: Always
      volumes:
        - name: app-pvc
          persistentVolumeClaim:
            claimName: app-pvc
      containers:
      - name: app-container
        image: "eu.gcr.io/<PROJECT_ID>/<IMAGE_NAME>:VERSION_TAG"
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 5000
        livenessProbe:
          tcpSocket:
            port: 5000
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          tcpSocket:
            port: 5000
          initialDelaySeconds: 15
          periodSeconds: 20
        volumeMounts:
          - mountPath: "/data"
            name: app-pvc

一切都已成功部署,因为我可以通过 运行 以下命令通过配置的服务在本地直接连接到应用程序:

kubectl port-forward service/app-service 5000:5000

这让我可以在我的浏览器中访问该应用程序,并且一切正常。

为了确保我配置的 <MY_STATIC_IP_ADDRESS> 正确绑定到 custom.my_domain.com,我尝试对主机执行 ping 操作,但确实得到了正确的响应:

ping custom.my_domain.com

Pinging custom.my_domain.com [<MY_STATIC_IP_ADDRESS>] with 32 bytes of data:
Reply from <MY_STATIC_IP_ADDRESS>: bytes=32 time=36ms TTL=113
Reply from <MY_STATIC_IP_ADDRESS>: bytes=32 time=37ms TTL=113
Reply from <MY_STATIC_IP_ADDRESS>: bytes=32 time=36ms TTL=113
Reply from <MY_STATIC_IP_ADDRESS>: bytes=32 time=45ms TTL=113

Ping statistics for <MY_STATIC_IP_ADDRESS>:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 36ms, Maximum = 45ms, Average = 38ms

即使一切似乎都按预期工作,但每当我尝试在浏览器中导航到 custom.my_domain.com/app 时,即使在等待超过 30 米以确保入口配置已在 GCP 上正确注册:

这是显示在我的 nginx-controller pod 日志中的条目:

<HIDDEN_LOCAL_IP> - - [14/Apr/2021:21:18:10 +0000] "GET /app/ HTTP/1.1" 404 232 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0" "-"

更新 #1

看来,如果我更新入口以直接在 / 路径上公开目标服务,它就会按预期工作。在更新的配置下方。尽管如此,如果我尝试设置任何其他路径,它似乎不起作用。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/ingress.global-static-ip-name: static-ip-name
    nginx.ingress.kubernetes.io/rewrite-target: /
  creationTimestamp: "2021-04-14T20:28:41Z"
  generation: 7
  name: app-ingress
  namespace: default
  resourceVersion: HIDDEN
  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/app-ingress
  uid: HIDDEN
spec:
  rules:
  - host: custom.my_domain.com
    http:
      paths:
      - backend:
          serviceName: app-service
          servicePort: 5000
        path: /
status:
  loadBalancer:
    ingress:
    - ip: <MY_STATIC_IP_ADDRESS>

更新 #2

看完评论区@jccampanero 分享的资料后,我得到了一个工作配置。

而不是使用 nginx-stable which is referenced on the official nginx website, I used the one here 并相应地更新了我的 Terraform 脚本以使用与我拥有的配置完全相同的脚本。

之后,我不得不按照文档 here 更新我的入口 - 在更新的配置下方:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/ingress.global-static-ip-name: static-ip-name
    nginx.ingress.kubernetes.io/rewrite-target: /
  creationTimestamp: "2021-04-14T20:28:41Z"
  generation: 7
  name: app-ingress
  namespace: default
  resourceVersion: HIDDEN
  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/app-ingress
  uid: HIDDEN
spec:
  rules:
  - host: custom.my_domain.com
    http:
      paths:
      - backend:
          serviceName: app-service
          servicePort: 5000
        path: /app(/|$)(.*)
status:
  loadBalancer:
    ingress:
    - ip: <MY_STATIC_IP_ADDRESS>

正如问题评论和问题本身所指出的那样,@vladzam 对此进行了很好的记录,有两个是问题的原因。

一方面,通过 Helm stable 通道可用的 nginx 入口控制器似乎是 deprecated in favor of the new ingress-nginx controller - please, see the Github repo and the official documentation

另一方面,这似乎是与 Rewrite target 注释的定义有关的问题。根据 docs:

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.

因此,有必要修改入口资源的定义以考虑到这一变化。例如:

$ echo '
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: rewrite
  namespace: default
spec:
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /something(/|$)(.*)
' | kubectl create -f -

问题本身提供了准确的入口资源定义。