Kubernetes 服务类型概述

Kubernetes 中的服务是什么?

在 Kubernetes 环境中,您可以拥有数百甚至数千个临时的 pod。 无论是因为节点被缩减,pod 副本被缩减,还是 pod 被重新调度到新节点,pod 的 IP 地址都无法保证。 Pod 的 IP 地址是在它被调度到一个特定的节点之后并且在它被引导之前分配的。

鉴于我们处于云原生环境中,我们希望能够水平扩展 Pod。 因此,要跟踪我们所有应用程序的所有 IP 地址将非常困难。 Kubernetes 有一种资源类型可以解决这个不断变化的 Pod IP 的问题,称为服务。 Kubernetes 服务允许您创建单个常量 IP 地址,其中包含一组包含相同服务的 pod 的 IP 地址。 这非常有用,因为服务 IP 保持静态,而 pod IP 可以不断变化。 您永远不必担心没有正确的 IP 地址。

服务怎样运作?

服务怎样运作? 怎样配置一个?

让我们首先创建一个包含三个 Nginx pod 实例的部署。

apiVersion: apps/v1

  kind: Deployment

metadata:

  labels:

    app: nginx

  name: nginx

spec:

  replicas: 3

  selector:

    matchLabels:

      app: nginx

    spec:

      containers:

      - image: nginx

        name: nginx

应用部署清单后,我们可以看到我们有三个使用 IP 运行的 nginx 实例 10.244.242.66, 10.244.242.67, 10.244.230,199. 另外,请注意我们分配给这些 pod 的标签 app=nginx. 这对于服务怎样监控这些 pod 至关重要。

$ kubectl get pods -owide --show-labels

NAME                    READY   STATUS    RESTARTS   AGE     IP               NODE                      LABELS

nginx-8f458dc5b-6vcxt   1/1     Running   0          2m27s   10.244.242.66    nodepool-a-95e9c8e86208   app=nginx,pod-template-hash=8f458dc5b

nginx-8f458dc5b-ktvtf   1/1     Running   0          2m27s   10.244.242.67    nodepool-a-95e9c8e86208   app=nginx,pod-template-hash=8f458dc5b

nginx-8f458dc5b-mlkwd   1/1     Running   0          2m27s   10.244.230.199   nodepool-a-11aa1dc199fa   app=nginx,pod-template-hash=8f458dc5b

现在,让我们定义我们的服务清单,它在下面定义了定义的细分。

apiVersion: v1

kind: Service

metadata:

  name: nginx

  labels:

    app: nginx

spec:

  selector:

    app: nginx

  type: ClusterIP

  ports:

  - name: 80-80

    port: 80

    protocol: TCP

    targetPort: 80

当我们开始分解这个清单时,我们注意到那种是 Service. 接下来,元数据部分应该很熟悉,因为它与其他 Kubernetes 资源上的元数据字段相同。 规范部分定义了我们的服务将怎样以及与什么进行交互。

selector 场地。 您可以在此字段中定义您希望此服务监控哪些 pod。 这是通过标签匹配完成的。 你会注意到这 selector 有一个定义 app: nginx. 如果您还记得,这些是我们在部署清单中为我们的 pod 定义的标签。 通过这个选择器定义,我们声明任何带有标签的 pod app=nginx 将成为这项服务的一部分。

接下来,我们有 type 场地。 这定义了我们想要的服务类型。 稍后我们将深入研究服务类型,但我们现在刚刚将其定义为 ClusterIP.

最后,我们有 ports 场地。 在这里,我们定义了流量将怎样以及在何处流经服务。 请注意,此字段接受一个数组,以便您可以定义多个条目。

让我们分解端口中的每个字段:

  • name:您可以为给定的端口条目分配一个特定的名称。

  • 端口:这是服务将侦听的端口。

  • targetPort:这是服务将请求转发到的端口。 这应该与 pod 正在侦听的端口匹配。

  • 协议:您希望服务侦听并与之交互的特定协议。

现在我们对服务清单的作用以及它怎样与 pod 交互有了基本的了解,我们部署清单并检查服务。

$ k get svc

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE

kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   90m

nginx        ClusterIP   10.108.176.62   <none>        80/TCP    18m



$ kubectl describe svc nginx

Name:              nginx

Namespace:         default

Labels:            app=nginx

Annotations:       <none>

Selector:          app=nginx

Type:              ClusterIP

IP Family Policy:  SingleStack

IP Families:       IPv4

IP:                10.108.176.62

IPs:               10.108.176.62

Port:              80-80  80/TCP

TargetPort:        80/TCP

Endpoints:         10.244.230.199:80,10.244.242.66:80,10.244.242.67:80

Session Affinity:  None

Events:            <none>

describe 命令中的所有内容都应与清单定义一致,除了两个字段 IPEndpoints.

IP 字段定义服务的 IP 地址。 这是可用于访问服务背后的 Pod 的静态 IP。

Endpoints 字段定义当前分配给此服务的 pod IP。 您注意到这些 IP 来自我们的 Nginx 部署。

在我们继续讨论其他服务类型之前,让我们展示缩小 Nginx 部署并看看该服务将怎样处理这种变化。

$ kubectl scale deployment/nginx --replicas 1

deployment.apps/nginx scaled



$ kubectl get pods -owide

NAME                    READY   STATUS    RESTARTS   AGE   IP               NODE                      NOMINATED NODE   READINESS GATES

nginx-8f458dc5b-mlkwd   1/1     Running   0          43m   10.244.230.199   nodepool-a-11aa1dc199fa   <none>           <none>



$ kubectl describe service nginx | grep Endpoints  

Endpoints:         10.244.230.199:80



$ kubectl scale deployment/nginx --replicas 5

deployment.apps/nginx scaled



$ kubectl get pods -owide

NAME                    READY   STATUS    RESTARTS   AGE    IP               NODE                      NOMINATED NODE   READINESS GATES

nginx-8f458dc5b-9pmhb   1/1     Running   0          108s   10.244.242.69    nodepool-a-95e9c8e86208   <none>           <none>

nginx-8f458dc5b-mlkwd   1/1     Running   0          47m    10.244.230.199   nodepool-a-11aa1dc199fa   <none>           <none>

nginx-8f458dc5b-r2pcp   1/1     Running   0          108s   10.244.242.70    nodepool-a-95e9c8e86208   <none>           <none>

nginx-8f458dc5b-vgwv6   1/1     Running   0          108s   10.244.242.68    nodepool-a-95e9c8e86208   <none>           <none>

nginx-8f458dc5b-x6thl   1/1     Running   0          108s   10.244.230.200   nodepool-a-11aa1dc199fa   <none>           <none>



$ kubectl describe service nginx | grep Endpoints

Endpoints:         10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

您可以看到,向上和向下扩展 Pod 会立即反映在服务中,允许单个静态 IP 访问 Pod。

服务类型

在服务资源中,存在三种不同的类型。 它们如下:

  1. 集群IP

  2. 节点端口

  3. 负载均衡器

    这些都有自己的行为和特定用例,因此了解何时使用每一个非常重要。 在接下来的部分中,我们将深入探讨其中的每一个。

集群IP

ClusterIP 是“最基本”的服务类型。它将创建一个静态 IP 地址,该地址位于与 Pod IP 范围不同的子网上,并通过标签选择器监视一组 Pod。

ClusterIP 允许 pod 轻松地相互通信,因为 pod 可以向静态 IP 发送请求,也可以使用 Kubernetes DNS 向 {service-name}.{namespace}. 在之前 example 我们部署,DNS 本来是 nginx.default. 使用 ClusterIP 有一些限制。 最大的问题是无法将这项服务暴露给外界。 这是服务类型 nodePortloadBalancer 应该使用。

节点端口

NodePort 服务建立在 ClusterIP 类型。 它暴露了 ClusterIP 通过在工作节​​点上打开一个端口来转发流量到外部世界。 这意味着如果您有 50 个工作节点,每个工作节点都会在分配的端口上侦听,即使 pod 不在该工作节点上。

创建一个 NodePort 服务唯一的区别是定义 Type 作为 NodePort

kind: Service

metadata:

  labels:

    app: nginx

  name: nginx

spec:

  ports:

  - name: 80-80

    port: 80

    protocol: TCP

    targetPort: 80

    # nodePort: 30001

  selector:

    app: nginx

  type: NodePort

你可能会注意到 nodePort 被注释掉了。 这是因为如果你不定义 nodePortports 部分,Kubernetes 将自动从范围内分配一个随机端口 30000-32767. 如果你想定义一个特定的端口,它必须在这个范围内完成。

部署 NodePort 服务清单并让我们对其进行审查。

$ kubectl get svc

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE

kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        135m

nginx        NodePort    10.98.246.253   <none>        80:30828/TCP   5s



$ kubectl describe svc nginx

Name:                     nginx

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     NodePort

IP Family Policy:         SingleStack

IP Families:              IPv4

IP:                       10.98.246.253

IPs:                      10.98.246.253

Port:                     80-80  80/TCP

TargetPort:               80/TCP

NodePort:                 80-80  30828/TCP

Endpoints:                10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

Session Affinity:         None

External Traffic Policy:  Cluster

Events:                   <none>

在这里,我们看到一个名为 NodePort 与价值 30828/TCP. 这是分配给此服务的端口和协议。 现在,如果我们使用该端口向我们的任何工作节点 IP 地址发送请求,请求将被发送到服务,然后被发送到我们的 pod。

$ kubectl get nodes -owide

NAME                      STATUS   ROLES    AGE    VERSION   INTERNAL-IP   EXTERNAL-IP     OS-IMAGE                       KERNEL-VERSION    CONTAINER-RUNTIME

nodepool-a-11aa1dc199fa   Ready    <none>   140m   v1.24.4   10.1.96.5     45.77.152.173   Debian GNU/Linux 10 (buster)   4.19.0-22-amd64   containerd://1.6.8

nodepool-a-95e9c8e86208   Ready    <none>   140m   v1.24.4   10.1.96.4     140.82.44.105   Debian GNU/Linux 10 (buster)   4.19.0-22-amd64   containerd://1.6.8

$ curl 140.82.44.105:30828

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>



$ curl 45.77.152.173:30828

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

虽然这是一种将我们的服务暴露给外部世界的有用方式,但它不能很好地扩展。 在高度自动扩展的 Kubernetes 集群中,工作节点可能是短暂的,因为它们可能会不断地上下旋转。 这让我们遇到了与 pod 类似的问题,我们可以在其中找出 IP 是什么,但这并不意味着它们会一直存在。 如果您的服务需要静态外部 IP 地址,则 LoadBalancer 服务类型将满足该需求。

负载均衡器

类似于怎样 NodePortÂ建立在 ClusterIP, LoadBalancer服务建立在之上 NodePort. 随着 LoadBalancer 服务,每个工作节点继续具有分配给特定服务的唯一端口。 但是,现在部署并配置了 L4 以路由到特定的工作节点和端口。 一个高级的请求流程如下:一个请求被发送到负载均衡器,请求被转发到特定节点端口上的工作节点,然后被传递到特定的服务,最后被发送到一个 pod。

负载均衡器

配置一个 LoadBalancer 如果需要,服务会涉及更多。 一个非常简单的配置可以如下,只需 type 字段被更改为 LoadBalancer.

apiVersion: v1

kind: Service

metadata:

  labels:

    app: nginx

  name: nginx

spec:

  ports:

  - name: 80-80

    port: 80

    protocol: TCP

    targetPort: 80

  selector:

    app: nginx

  type: LoadBalancer

为了 example,如果您希望更改负载均衡器以强制 SSL 重定向、配置防火墙规则、使用 HTTPS 而不是 HTTP,或者使用不同的平衡算法,那么这将需要定义 Annotations 为您服务。

这是一个这样的 example LoadBalancer 显现。

apiVersion: v1

kind: Service

metadata:

  annotations:

    service.beta.kubernetes.io/vultr-loadbalancer-protocol: "http"

    service.beta.kubernetes.io/vultr-loadbalancer-https-ports: "443"

    # You will need to have created a TLS Secret and pass in the name as the value

    service.beta.kubernetes.io/vultr-loadbalancer-ssl: "ssl-secret"

    service.beta.kubernetes.io/vultr-loadbalancer-algorithm: "least_connections"

    service.beta.kubernetes.io/vultr-loadbalancer-ssl-redirect: "true"

    service.beta.kubernetes.io/vultr-loadbalancer-firewall-rules: "0.0.0.0/80;0.0.0.0/443"

  labels:

    app: nginx

  name: nginx

spec:

  ports:

    - port: 80

      name: "http"

    - port: 443

      name: "https"

  selector:

    app: nginx

  type: LoadBalancer

有关更多详细信息,请参阅我们的 所有可用注释的列表 可与 Vultr 负载均衡器一起使用。

当你第一次部署你的 LoadBalancer 服务,您会看到 EXTERNAL-IP 部分是 pending

$ portfolio [talk-postman] ⚡  k get svc

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE

kubernetes   ClusterIP      10.96.0.1        <none>        443/TCP        150m

nginx        LoadBalancer   10.103.252.120   <pending>     80:30092/TCP   4s



$ portfolio [talk-postman] ⚡  kubectl describe service nginx

Name:                     nginx

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     LoadBalancer

IP Family Policy:         SingleStack

IP Families:              IPv4

IP:                       10.103.252.120

IPs:                      10.103.252.120

Port:                     80-80  80/TCP

TargetPort:               80/TCP

NodePort:                 80-80  30092/TCP

Endpoints:                10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

Session Affinity:         None

External Traffic Policy:  Cluster

Events:

  Type     Reason                  Age               From                Message

  ----     ------                  ----              ----                -------

  Normal   EnsuringLoadBalancer    8s (x4 over 44s)  service-controller  Ensuring load balancer

  Warning  SyncLoadBalancerFailed  8s (x4 over 44s)  service-controller  Error syncing load balancer: failed to ensure load balancer: load-balancer is not yet active - current status: pending

您注意到这里 Kubernetes 已经部署了一个 Vultr 负载均衡器,并且正在不断地通信以检查负载均衡器是否已被配置并分配了一个 IP。

配置 Vultr 负载均衡器并准备好流量后,您将在 EXTERNAL-IP 字段和 LB 已得到保证”。这意味着 Kubernetes 现在可以控制相应地监视和更新负载均衡器。另外,请注意分配了一个唯一的节点端口。

$ portfolio [talk-postman] ⚡  k get svc

NAME         TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE

kubernetes   ClusterIP      10.96.0.1        <none>         443/TCP        154m

nginx        LoadBalancer   10.103.252.120   45.63.15.140   80:30092/TCP   4m9s



$ kubectl describe service nginx

Name:                     nginx

Namespace:                default

Labels:                   app=nginx

Annotations:              <none>

Selector:                 app=nginx

Type:                     LoadBalancer

IP Family Policy:         SingleStack

IP Families:              IPv4

IP:                       10.103.252.120

IPs:                      10.103.252.120

LoadBalancer Ingress:     45.63.15.140

Port:                     80-80  80/TCP

TargetPort:               80/TCP

NodePort:                 80-80  30092/TCP

Endpoints:                10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...

Session Affinity:         None

External Traffic Policy:  Cluster

Events:

  Type     Reason                  Age                    From                Message

  ----     ------                  ----                   ----                -------

  Warning  SyncLoadBalancerFailed  2m21s (x5 over 3m37s)  service-controller  Error syncing load balancer: failed to ensure load balancer: load-balancer is not yet active - current status: pending

  Normal   EnsuringLoadBalancer    61s (x6 over 3m37s)    service-controller  Ensuring load balancer

  Normal   EnsuredLoadBalancer     60s                    service-controller  Ensured load balancer

为了验证我们的 Vultr 负载均衡器是否正常工作,我们可以向 EXTERNAL-IP,这是我们的负载均衡器公共 IP 地址。

$ curl 45.63.15.140

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

你可以看看 LoadBalancer 服务类型作为一个了不起的解决方案。 但是,它们有一个缺点。 您不能定义任何类型的 URL 路径,这不允许灵活性或将其路由到多个服务。 这 LoadBalancer 服务绑定到单个服务,因此如果您想从集群中公开另一个应用程序,您将部署另一个 Vultr 负载均衡器,如果您有多个应用程序,它可以快速增加成本。

这个问题的解决方案不是另一种服务类型,而是一种不同的 Kubernetes 资源,称为 ingress.

入口

一个 ingress resource 是一种更灵活的方式来定义外部世界可以访问的服务。 这里最大的卖点之一是您可以定义单个入口资源,并将此路由路由到您定义的子域、端口或 URL 路径下的所有服务。 它们还提供大量其他功能,例如速率限制、身份验证、自动 TLS、加权路由等等。 所有这些都可以通过一个负载均衡器来完成 Ingress 资源与 LoadBalancer 服务类型,唯一的区别是部署负载均衡器充当代理将所有流量发送到 Ingress 资源,它充当集群中的 L7 负载均衡器。

美人鱼图

这里唯一需要注意的是 Kubernetes 没有内部入口控制器。 那里有各种入口控制器,都具有不同类型的功能。 最终,在决定安装哪个入口控制器之前,这取决于您的需求。 有关配置指南,请参阅入口控制器的文档。

一些比较流行的 Ingress 控制器是:

总结

虽然这篇文章可能会让您对不同类型的 Kubernetes 服务和入口控制器有一个很好的了解,但这只是涉及服务。 这些服务还有更多配置选项和一些免费功能,例如网络策略。 请查看下面列出的一些链接以获取更多信息。

一些有用的链接:

文章标题 名称(可选) 电子邮件(可选) 描述

发送建议

注:本教程在Vultr VPS上测试通过,如需部署请前往Vultr.com