为什么入口总是只向特定服务节点端口返回 502 错误?

问题描述

我有一个 alpine docker 镜像可以在 apache 服务器 (PHP 7.4) EXPOSE 80 上运行我的原始 PHP 网站。
我想使用入口控制器在 Kubernetes(GKE) 上运行映像。
我正在使用 gcloud 命令将映像推送到 google 容器注册表。
部署和服务都没有错误,成功创建为NodePort。
我部署的ingress来自google tutorials(https://cloud.google.com/community/tutorials/nginx-ingress-gke)
现在在我的入口中有:

  • 34.68.78.46.xip.io/
  • 34.68.78.46.xip.io/hello
  • 34.68.78.46.xip.io/jb(/|$)(.*)

/hello 与教程的配置相同,运行良好。
/jb 与我在下面提到的配置相同,并且总是返回 502 错误
GCP 控制台中的入口详细信息未显示警告或错误

我已经检查过:
Kubernetes GKE Ingress : 502 Server Error
GKE Ingress: 502 error when downloading file
502 Server Error Google kubernetes

这是部署文件

apiVersion: apps/v1
kind: Deployment
Metadata:
  name: jomlahbazar-deployment
spec:
  selector:
    matchLabels:
      greeting: jomlah
      department: bazar
  replicas: 1
  template:
    Metadata:
      labels:
        greeting: jomlah
        department: bazar
    spec:
      containers:
      - name: jomlah
        image: "us.gcr.io/third-nature-273904/jb-img-1-0:v1"
        ports:
        - containerPort: 80
        env:
        - name: "PORT"
          value: "80"

这里是服务文件

apiVersion: v1
kind: Service
Metadata:
  name: jomlahbazar-service
spec:
  type: NodePort
  selector:
    greeting: jomlah
    department: bazar
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

在入口文件中:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
Metadata:
  name: ingress-resource
  annotations:
    kubernetes.io/ingress.class: "Nginx"
    Nginx.ingress.kubernetes.io/ssl-redirect: "false"
    Nginx.ingress.kubernetes.io/rewrite-target: /$2
    Nginx.ingress.kubernetes.io/use-regex: "true"
    Nginx.ingress.kubernetes.io/add-base-url : "true"
spec:
  rules:
  - host: 34.68.78.46.xip.io
    http:
      paths:
      - path: /
        backend:
          serviceName: jomlahbazar-service
          servicePort: 80
      - path: /hello
        backend:
          serviceName: hello-app
          servicePort: 8080
      - path: /jb(/|$)(.*)
        backend:
          serviceName: jomlahbazar-service
          servicePort: 80     

这里是入口描述:

Name:             ingress-resource
Namespace:        default
Address:          34.68.78.46
Default backend:  default-http-backend:80 (10.20.1.6:8080)
Rules:
  Host                Path  Backends
  ----                ----  --------
  34.68.78.46.xip.io
                      /              jomlahbazar-service:80 (<none>)
                      /hello         hello-app:8080 (10.20.2.61:8080)
                      /jb(/|$)(.*)   jomlahbazar-service:80 (<none>)
Annotations:
  kubernetes.io/ingress.class:                       Nginx
  Nginx.ingress.kubernetes.io/add-base-url:          true
  Nginx.ingress.kubernetes.io/rewrite-target:        /$2
  Nginx.ingress.kubernetes.io/ssl-redirect:          false
  Nginx.ingress.kubernetes.io/use-regex:             true
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","Metadata":{"annotations":{"kubernetes.io/ingress.class":"Nginx","Nginx.ingress.kubernetes.io/add-base-url":"true","Nginx.ingress.kubernetes.io/rewrite-target":"/$2","Nginx.ingress.kubernetes.io/ssl-redirect":"false","Nginx.ingress.kubernetes.io/use-regex":"true"},"name":"ingress-resource","namespace":"default"},"spec":{"rules":[{"host":"34.68.78.46.xip.io","http":{"paths":[{"backend":{"serviceName":"jomlahbazar-service","servicePort":80},"path":"/"},{"backend":{"serviceName":"hello-app","servicePort":8080},"path":"/hello"},{"backend":{"serviceName":"jomlahbazar-service","path":"/jb(/|$)(.*)"}]}}]}}

Events:
  Type    Reason          Age                 From                      Message
  ----    ------          ----                ----                      -------
  normal  AddedOrUpdated  36m (x6 over 132m)  nginx-ingress-controller  Configuration for default/ingress-resource was added or updated

kubectl get ing ingress-resource -o yaml 的输出

apiVersion: extensions/v1beta1
kind: Ingress
Metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"networking.k8s.io/v1beta1","path":"/jb(/|$)(.*)"}]}}]}}
    kubernetes.io/ingress.class: Nginx
    Nginx.ingress.kubernetes.io/add-base-url: "true"
    Nginx.ingress.kubernetes.io/rewrite-target: /$2
    Nginx.ingress.kubernetes.io/ssl-redirect: "false"
    Nginx.ingress.kubernetes.io/use-regex: "true"
  creationTimestamp: "2021-02-11T06:00:07Z"
  generation: 5
  name: ingress-resource
  namespace: default
  resourceVersion: "2195351"
  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/ingress-resource
  uid: 74dc822f-91cb-4902-991b-1ad298f44ae6
spec:
  rules:
  - host: 34.68.78.46.xip.io
    http:
      paths:
      - backend:
          serviceName: jomlahbazar-service
          servicePort: 80
        path: /
      - backend:
          serviceName: hello-app
          servicePort: 8080
        path: /hello
      - backend:
          serviceName: jomlahbazar-service
          servicePort: 80
        path: /jb(/|$)(.*)
status:
  loadBalancer:
    ingress:
    - ip: 34.68.78.46

解决方法

我已经在我的 GKE 集群上运行了一些测试。我已经使用 2 个 hello world 应用程序 v1 和 v2 复制了您的行为。

场景 1

硬件 1

    spec:
      containers:
      - name: hello1
        image: gcr.io/google-samples/hello-app:1.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

SVC HW1

spec:
  type: NodePort
  selector:
    key: app
  ports:
    - port: 80
      targetPort: 8080

硬件 2

    spec:
      containers:
      - name: hello2
        image: gcr.io/google-samples/hello-app:2.0
        env:
        - name: "PORT"
          value: "80"

SVC HW2

spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
  selector:
    app: hello2

入口

spec:
  rules:
  -  http:
      paths:
      - path: /hello2
        backend:
          serviceName: h2
          servicePort: 80
      - path: /hello
        backend:
          serviceName: fs
          servicePort: 80

输出:

$ curl 34.117.70.75/hello
Hello,world!
Version: 1.0.0
Hostname: fd-c6d79cdf8-7rmmd

$ curl 34.117.70.75/hello2

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>

在此场景中,部署配置为创建将侦听 port: 80 的 pod。当您跳过在部署中配置 containerPort 时,Kubernetes 会自动使用 containerPort 中与 port 中设置的相同的端口。您可以使用 netstat 命令进行验证。

$ kubectl exec -ti h2-deploy-6dbf5b7899-g7rbj -- bin/sh
/ # netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 :::80                   :::*                    LISTEN      1/hello-app
/ #

在您的服务中设置 containerPort: 8080,因此服务期望流量通过端口 8080。由于 pod 仅在 80 上侦听并且流量达到 8080,因此您会收到 502 错误。

场景 2

将值从“80”更改为“8080”并应用新配置后。

$ curl 34.117.70.75/hello
Hello,world!
Version: 1.0.0
Hostname: fd-c6d79cdf8-7rmmd

$ curl 34.117.70.75/hello2
Hello,world!
Version: 2.0.0
Hostname: h2-deploy-5f5ccfbf9f-fjhrb

网络统计:

$ kubectl exec -ti f2-deploy-5f5ccfbf9f-fjhrb -- bin/sh
/ # netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 :::8080                 :::*                    LISTEN      1/hello-app

解决方案

解决方案 1

您应该将应用程序部署更改为:

        env:
        - name: "PORT"
          value: "8080"

并应用新配置。

解决方案 2 使用containerPort

    spec:
      containers:
      - name: jb
        image: "us.gcr.io/third-nature-273904/jb-img-1-0:v3"
        ports:
        - containerPort: 8080

注意

请注意,如果您创建了自己的映像并在您的 EXPOSE 中使用了 Dockerfile,则您应该将部署配置为使用此特定端口。

如果您仍有问题,请告诉我。