如何通过入口将服务暴露给外部 Kubernetes 集群?

How to expose a service to outside Kubernetes cluster via ingress?

我正在努力将 AWS 集群中的服务暴露给外部并通过浏览器访问它。由于我的没有得到任何答案,我决定从几个方面简化问题。

首先,我创建了一个无需任何配置即可运行的部署。基于this article,我做到了

  1. kubectl create namespace tests

  2. 基于 paulbouwer/hello-kubernetes:1.8 创建了文件 probe-service.yaml 并部署了它 kubectl create -f probe-service.yaml -n tests:

    apiVersion: v1
    kind: Service
    metadata:
      name: hello-kubernetes-first
    spec:
      type: ClusterIP
      ports:
      - port: 80
        targetPort: 8080
      selector:
        app: hello-kubernetes-first
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-kubernetes-first
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: hello-kubernetes-first
      template:
        metadata:
          labels:
            app: hello-kubernetes-first
        spec:
          containers:
          - name: hello-kubernetes
            image: paulbouwer/hello-kubernetes:1.8
            ports:
            - containerPort: 8080
            env:
            - name: MESSAGE
              value: Hello from the first deployment!
    
  3. 创建了 ingress.yaml 并应用了它 (kubectl apply -f .\probes\ingress.yaml -n tests)

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: hello-kubernetes-ingress
    spec:
      rules:
      - host: test.projectname.org
        http:
          paths:
          - pathType: Prefix
            path: "/test"
            backend:
              service:
                name: hello-kubernetes-first
                port:
                  number: 80
      - host: test2.projectname.org
        http:
          paths:
          - pathType: Prefix
            path: "/test2"
            backend:
              service:
                name: hello-kubernetes-first
                port:
                  number: 80
      ingressClassName: nginx
    

其次,我可以看到 DNS 实际上指向集群并且应用了入口规则:

现在,抛开 TLS 问题(那些值得单独提问),为什么我可以获得 Cannot GET /test?看起来入口控制器 (ingress-nginx) 得到了规则(否则它不会区分路径;这就是为什么我不显示 DNS 设置,尽管它们在上一个问题中有描述)但没有显示简单的 hello-kubernetes /test 的页面 returns 这个简单的 404 消息。 这是为什么? 可能会出现什么问题? 如何调试?

一些调试信息:

PS 为了回答安德鲁,我确实尝试过设置 HTTPS 但它似乎没有帮助,所以我没有将我尝试的内容包括在最初的问题中。然而,这就是我所做的:

  1. 已安装证书管理器,目前没有自定义图表:kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.5.4/cert-manager.yaml

  2. 基于 cert-manager's tutorial and 创建了一个具有以下配置的 ClusterIssuer:

    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: letsencrypt-backoffice
    
    spec:
      acme:
        server: https://acme-staging-v02.api.letsencrypt.org/directory
        # use https://acme-v02.api.letsencrypt.org/directory after everything is fixed and works
        privateKeySecretRef: # this secret will be created in the namespace of cert-manager
          name: letsencrypt-backoffice-private-key
        # email: <will be used for urgent alerts about expiration etc>
    
        solvers:
        # TODO: add for each domain/second-level domain/*.projectname.org
        - selector:
            dnsZones:
              - test.projectname.org
              - test2.projectname.org
          # haven't made it to work yet, so switched to the simpler to configure http01 challenge
          # dns01:
          #   route53:
          #     region: ... # that of load balancer (but we also have ...)
          #     accessKeyID: <of IAM user with access to Route53>
          #     secretAccessKeySecretRef: # created that
          #       name: route53-credentials-secret
          #       key: secret-access-key
          #     role: arn:aws:iam::645730347045:role/cert-manager
          http01:
            ingress:
              class: nginx
    

    并通过 kubectl apply -f issuer.yaml

    应用它
  3. 在同一文件中创建了 2 个证书并再次应用:

    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: letsencrypt-certificate
    spec:
      secretName: tls-secret
      issuerRef:
        kind: ClusterIssuer
        name: letsencrypt-backoffice
      commonName: test.projectname.org
      dnsNames:
      - test.projectname.org
    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: letsencrypt-certificate-2
    spec:
      secretName: tls-secret-2
      issuerRef:
        kind: ClusterIssuer
        name: letsencrypt-backoffice
      commonName: test2.projectname.org
      dnsNames:
      - test2.projectname.org
    
  4. 确保证书颁发正确(跳过痛苦的部分,结果是:kubectl get certificates 表明两个证书都有 READY = true 和两个 tls 秘密都已创建)

  5. 发现我的入口在另一个命名空间中,入口规范中的 tls 秘密 can only 在同一命名空间中被引用(还没有尝试过通配符证书和 --default-ssl-certificate选项),所以对于每个将它们复制到 tests 命名空间:

    1. 打开现有机密,例如 kubectl edit secret tls-secret-2,复制数据和注释
    2. 在测试中创建了一个空的(不透明的)秘密:kubectl create secret generic tls-secret-2-copy -n tests
    3. 打开它 (kubectl edit secret tls-secret-2-copy -n tests) 并插入数据和注释
  6. 在入口 spec 中,添加了 tls 位:

    tls:
    - hosts:
      - test.projectname.org
      secretName: tls-secret-copy
    - hosts:
      - test2.projectname.org
      secretName: tls-secret-2-copy
    
  7. 我希望这会有所帮助,但实际上它没有任何区别(我得到 ERR_TOO_MANY_REDIRECTS 不相关的路径,从 http 重定向到 https,NET::ERR_CERT_AUTHORITY_INVALID 在 https 和 Cannot GET /test 如果我坚持要访问该页面)

好吧,我还没有弄清楚这个问题 yet (edit: figured, but the solution is ArgoCD-specific), but for this test service it seems that path resolving is the source。它可能不是唯一的来源(要在 test2 子域上重新测试),但是当我在托管区域(test3,之前未在任何地方使用过)中创建一个新的子域并通过 A 条目将其指向负载均衡器时(作为 AWS 控制台中的“别名”),然后向入口添加一个具有 / 路径的新规则,如下所示:

  - host: test3.projectname.org
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: hello-kubernetes-first
            port:
              number: 80

我终于在 http://test3.projectname.org 上得到了 hello kubernetes 的东西。经过多次 attempts/research 和 .

的一些帮助后,我在 TLS 方面取得了成功

但是我没有成功进行实际调试:查看kubectl logs -n nginx <pod name, see kubectl get pod -n nginx>并不能真正帮助理解传递给服务的路径,而且很难理解理解(甚至找不到这些 IP 的来源:它们不是我的,LB 的,服务​​的集群 IP;我也不明白 tests-hello-kubernetes-first-80 代表什么——它只是命名空间、服务名称和端口的串联,没有对象具有这样的名称,包括入口):

192.168.14.57 - - [14/Nov/2021:12:02:58 +0000] "GET /test2 HTTP/2.0" 404 144
 "-" "<browser's user-agent header value>" 448 0.002
 [tests-hello-kubernetes-first-80] [] 192.168.49.95:8080 144 0.000 404 <some hash>

任何更多关于调试的指示都会有所帮助;也欢迎关于正确路径的建议~重写 nginx-ingress。

由于您使用自己的答案来补充问题,我将回答您提出的所有问题,同时提供分而治之的策略来解决 kubernetes 网络问题。

最后给大家一些nginx和IP的解答

这是正确的

- host: test3.projectname.org
http:
  paths:
  - pathType: Prefix
    path: "/"
    backend:
      service:
        name: hello-kubernetes-first
        port:
          number: 80

使用 Ingress 进行故障排除

  1. DNS
  2. 入口
  3. 服务
  4. 连播
  5. 证书

1.DNS

你可以使用命令dig查询DNS

挖掘google.com

  1. 入口

入口控制器不查找 IP,它只查找 headers

您可以使用任何可以更改 header 的工具强制主机,例如 curl

curl --header 'Host: test3.projectname.com' http://123.123.123.123(您的 public IP)

  1. 服务

您可以通过创建 ubuntu/centos pod、使用 kubectl exec -it podname -- bash 并尝试使用集群

卷曲您的服务表单来确保您的服务正常工作
  1. 连播

你明白了

192.168.14.57 - - [14/Nov/2021:12:02:58 +0000] "GET /test2 HTTP/2.0" 404 144
 "-" "<browser's user-agent header value>" 448 0.002

这部分 GET /test2 表示请求从 DNS 获取地址,从互联网一路走来,找到您的集群,找到您的入口控制器,通过服务到达您的 pod。恭喜!您的入口正在运行!

但为什么返回 404?

传递给服务以及从服务到pod的路径是/test2

你有 nginx 可以提供的名为 test2 的文件吗?你在 nginx 中有一个带有 test2 前缀的上游配置吗?

这就是为什么您从 nginx 而不是入口控制器获得 404。

这些 IP 是内部 IP,请记住,互联网流量在集群边界结束,现在您处于内部网络中。这是正在发生的事情的粗略描述

假设您正在从笔记本电脑访问它。您的笔记本电脑的 IP 为 192.168.123.123,但您家的地址为 7.8.9.1,因此当您的请求到达集群时,集群会看到 7.8.9.1 请求 test3.projectname.com.

集群寻找入口控制器,找到合适的配置并将请求传递到服务,服务再将请求传递到 pod。

所以,

your router can see your private IP (192.168.123.123)
Your cluster(ingress) can see your router's IP (7.8.9.1)
Your service can see the ingress's IP (192.168.?.?)
Your pod can see the service's IP (192.168.14.57)

这是一个传球游戏。 如果你想在你的 nginx 日志中看到 public IP,你需要自定义它以获得 X-Real-IP header,这通常是 load-balancers/ingresses/ambassador/proxies 放置实际请求者的地方 public IP.