Kubernetes - 如何使用 NodePort 服务从集群外部访问 nginx 负载平衡

Kubernetes - How to access nginx load balancing from outside the cluster using a NodePort service

我有一个带有主节点和另外两个节点的 Kubernetes 集群:

sudo kubectl get nodes
NAME                STATUS    ROLES     AGE       VERSION
kubernetes-master   Ready     master    4h        v1.10.2
kubernetes-node1    Ready     <none>    4h        v1.10.2
kubernetes-node2    Ready     <none>    34m       v1.10.2

它们每个都 运行 在 VirtualBox Ubuntu VM 上,可从访客计算机访问:

kubernetes-master (192.168.56.3)
kubernetes-node1  (192.168.56.4)
kubernetes-node2 (192.168.56.6)

我部署了一个有两个副本的 nginx 服务器,每个 kubernetes-node-x 有一个 pod:

sudo kubectl get pods -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP               NODE
nginx-deployment-64ff85b579-5k5zh   1/1       Running   0          8s        192.168.129.71   kubernetes-node1
nginx-deployment-64ff85b579-b9zcz   1/1       Running   0          8s        192.168.22.66    kubernetes-node2

接下来,我将 nginx 部署的服务公开为 NodePort,以便从集群外部访问它:

sudo kubectl expose deployment/nginx-deployment --type=NodePort

sudo kubectl get services 
NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes         ClusterIP   10.96.0.1      <none>        443/TCP        4h
nginx-deployment   NodePort    10.96.194.15   <none>        80:32446/TCP   2m

sudo kubectl describe service nginx-deployment
Name:                     nginx-deployment
Namespace:                default
Labels:                   app=nginx
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP:                       10.96.194.15
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  32446/TCP
Endpoints:                192.168.129.72:80,192.168.22.67:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

我可以直接使用节点 IP 访问节点中的每个 pod

kubernetes-node1 http://192.168.56.4:32446/
kubernetes-node2 http://192.168.56.6:32446/

但是,我认为 K8s 提供了某种外部集群 ip,可以平衡外部对节点的请求。那个IP是什么??

But, I thought that K8s provided some kind of external cluster ip that balanced the requests to the nodes from the outside. What is that IP??

  • 集群 IP 是集群内部的。不对外暴露,用于跨集群互通。

  • 确实,您有 LoadBanacer 类型的服务可以执行您需要的技巧,只是它依赖于云提供商或 minikube/docker edge 才能正常工作。

I can access each pod in a node directly using their node IP

  • 实际上,您不会以这种方式单独访问它们。 NodePort 做了一些不同的技巧,因为它本质上是在任何暴露的节点 IP 上对来自外部的请求进行负载平衡。简而言之,如果您使用暴露的 NodePort 访问任何节点的 IP,kube-proxy 将确保所需的服务获得它,然后服务通过活动 pods 进行循环,因此尽管您访问了特定的节点 IP,您不一定会在该特定节点上获得 pod 运行ning。您可以在此处找到更多详细信息:https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0,正如作者所说,技术上不是最准确的表示,但试图在逻辑层面上显示 NodePort 暴露所发生的事情:

  • 作为旁注,为了在裸机上执行此操作并执行 ssl 等操作,您需要提供自己的入口。比如说,将一个 nginx 放在特定节点上,然后将所有你想要公开的适当服务(注意服务的 fqdn)作为上游引用,这些服务可以 运行 在多个节点上根据需要拥有尽可能多的 nginx - 你不需要自从 k8s 运行 开始演出以来,就不需要处理确切的细节了。这样你就有了一个已知 IP 地址的节点(入口 nginx),它正在处理传入流量并将其重定向到 k8s 内部的服务,这些服务可以 运行 跨任何节点。我不喜欢 ascii 艺术,但会尝试一下:

    (outside) -> ingress (nginx) +--> my-service FQDN (running accross nodes):
                 [node-0]        |      [node-1]: my-service-pod-01 with nginx-01
                                 |      [node 2]: my-service-pod-02 with nginx-02
                                 |      ...
                                 +--> my-second-service FQDN
                                 |      [node-1]: my-second-service-pod with apache?
                                 ...
    

    在上面的草图中,您在 node-0(已知 IP)上有 nginx 入口,它接收外部流量,然后处理我的服务(运行在两个 pods 上的两个节点上)和我的-second-service(单 pod)作为上游。您只需要在服务上公开 FQDN 即可,无需担心特定节点的 IP 详细信息。您可以在文档中找到更多信息:https://kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/

    同样比我的 ansi-art 更好的是这种表示来自与前一点相同的link,它说明了入口背后的想法:

已更新评论

Why isn't the service load balancing the used pods from the service?

  • 发生这种情况的原因有多种。根据您的 Liveness 和 Readiness Probes 的配置方式,服务可能仍然不会将 pod 视为停止服务。由于 k8s 等分布式系统的这种异步特性,当 pods 被删除时,我们会遇到暂时的请求丢失,例如滚动更新等。其次,根据您的 kube-proxy 的配置方式,您可以通过一些选项来限制它。通过使用 --nodeport-addresses 的官方文档 (https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport),您可以更改节点代理行为。原来循环法是旧的 kube-proxy 行为,显然新的应该是随机的。最后,为了排除浏览器的连接和会话问题,您是否也从匿名会话中尝试过?您是否在本地缓存了 DNS?

Something else, I killed the pod from node1, and when calling to node1 it didn't use the pod from node 2.

  • 这有点奇怪。可能与上面提到的探测器有关。根据官方文档,情况不应如此。我们让 NodePort 的行为与上面提到的官方文档一致:and each Node will proxy that port (the same port number on every Node) into your Service。但如果你是这种情况,那么可能是 LB 或 Ingress,甚至可能是具有外部地址的 ClusterIP(见下文)都可以为你解决问题。

if the service is internal (ClusterIP) ... does it load balance to any of the pods in the nodes

  • 绝对是的。还有一件事,您可以使用此行为还可以在 'standard' 端口范围内公开 'load balanced' 行为,而不是来自 NodePort 的 30k+。这是我们用于入口控制器的服务清单的摘录。

    apiVersion: v1
    kind: Service
    metadata:
        namespace: ns-my-namespace
        name: svc-nginx-ingress-example
        labels:
            name: nginx-ingress-example
            role: frontend-example
            application: nginx-example
    spec:
        selector:
            name: nginx-ingress-example
            role: frontend-example
            application: nginx-example
        ports:
        - protocol: TCP
          name: http-port
          port: 80
          targetPort: 80
        - protocol: TCP
          name: ssl-port
          port: 443
          targetPort: 443
        externalIPs:
        - 123.123.123.123
    

    请注意,在上面的示例中,用 externalIPs 公开的虚构 123.123.123.123 表示我们的工作节点之一的 IP 地址。 Pods 运行ning in svc-nginx-ingress-example 服务根本不需要在这个节点上,但它们仍然可以将流量路由到它们(并且也在 pods 之间进行负载均衡) 当该 ip 在指定端口上被命中时。