Kubernetes的Service类型详解

1.1 Service介绍

在Kubernetes中,Service资源解决了Pod IP地址不固定的问题,提供了一种更稳定和可靠的服务访问方式。以下是Service的一些关键特性和工作原理:

  • Service的稳定性:由于Pod可能会因为故障、重启或扩容而获得新的IP地址,直接使用Pod的IP来访问服务是不可靠的。Service通过提供一个固定的虚拟IP(ClusterIP)作为访问入口,使得服务访问变得更加稳定。

  • Service的类型:Kubernetes支持不同类型的Service,包括ClusterIP、NodePort、LoadBalancer和ExternalName,每种类型适用于不同的访问场景:

    • ClusterIP:为Service在集群内部提供一个固定的虚拟IP,只有集群内部的客户端可以访问此服务。

    • NodePort:在所有节点的特定端口上公开Service,使得外部可以通过<NodeIP>:<NodePort>访问服务。

    • LoadBalancer:在NodePort的基础上,通过云服务商的负载均衡器对外提供服务,适用于公有云环境。

    • ExternalName:将服务映射到外部服务的DNS名称,不通过kube-proxy进行代理。

  • Service的负载均衡:Service可以对关联的Pod进行轮询或随机的负载均衡,使得请求可以均匀地分发到各个Pod上。

  • Service的发现机制:Kubernetes中的Pod可以通过DNS或环境变量来发现Service。通过DNS,Pod可以通过Service的名称和命名空间来解析Service的ClusterIP。

  • Service和Pod的关系:Service通过标签选择器(label selector)与一组Pod关联。当Service创建后,kube-proxy或相关的网络插件会监控Pod的变化,并更新Service的后端列表(Endpoints。

  • Headless Service:一种特殊的Service,不分配ClusterIP,而是通过DNS返回Pod的IP列表,适用于需要直接访问每个Pod的场景,如StatefulSets。

  • Service的端口:Service可以定义一个或多个端口,将外部请求映射到Pod的特定端口上。端口分为Service端口(port)、Pod端口(targetPort)和NodePort(仅NodePort类型Service)

image-20240514095437913

  • Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。

image-20240514095556728

1.2 kube-proxy三种工作模式

1.2.1 userspace模式

  • userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。

  • 该模式下,kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。

  • kube-proxyuserspace模式下的工作原理如下:

  • Service的监听kube-proxy为每个Service创建一个监听端口,这个端口对应Service的ClusterIP和端口号。当客户端向Service的ClusterIP:Port发送请求时,请求会被iptables规则捕获并重定向到kube-proxy的监听端口上。

  • 负载均衡kube-proxy根据定义的负载均衡算法(如轮询、随机等)从提供该服务的所有Pod中选择一个Pod,然后与该Pod建立连接,并将请求转发到选定的Pod上。kube-proxy在此过程中充当了四层(TCP/UDP)负载均衡器的角色。

  • 用户空间的开销:由于kube-proxyuserspace模式下运行,数据包需要在内核空间和用户空间之间来回拷贝,这会导致额外的性能开销。虽然这种方法比较稳定,但是在高负载情况下,由于数据拷贝的开销,效率相对较低。

  • 其他模式:除了userspace模式,kube-proxy还支持iptables模式和ipvs模式,这两种模式效率更高,因为它们直接在内核空间工作,减少了数据拷贝的开销。iptables模式通过修改网络规则来实现负载均衡,而ipvs模式则是内核级别的负载均衡,通常提供更好的性能和更复杂的负载均衡特性。

image-20240514100818525

1.2.2 iptables模式

  • iptables模式下,kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向ClusterIP的请求重定向到一个Pod IP。

  • 该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。

  • iptables模式下的kube-proxy工作原理如下:

    • 创建规则:在iptables模式下,kube-proxy为每个Service后端的Pod创建相应的iptables规则。当请求到达Service的ClusterIP时,这些规则将请求直接重定向到后端Pod的IP地址上。

    • 直接重定向:与userspace模式不同,在iptables模式下,kube-proxy不进行实际的流量转发,而是通过修改网络规则来实现负载均衡。这意味着流量直接从客户端通过网络路由到目的Pod,而不需要经过kube-proxy进程。

    • 效率提升:由于避免了用户空间和内核空间之间的上下文切换以及数据拷贝,iptables模式通常比userspace模式有更高的网络性能。

    • 简化的LB策略:在iptables模式下,kube-proxy不实现复杂的负载均衡策略,如会话保持或智能负载均衡。它仅根据轮询策略简单地分发请求到各个Pod。

    • 重试限制:由于iptables规则直接将流量路由到Pod,如果选定的Pod不可用,请求将失败,而kube-proxy不会进行重试。这与userspace模式不同,在userspace模式下,kube-proxy可以检测到连接失败并重新选择另一个Pod进行重试。

    • 安全性iptables模式下,流量直接从客户端路由到Pod,这意味着Pod的网络安全策略需要正确配置,以确保不会直接暴露给不受信任的网络。

  • iptables模式提供了一种高效的方式来实现Service的负载均衡,但牺牲了一定程度的灵活性和容错能力。选择合适的模式需要根据具体的应用需求和性能要求来决定。对于需要高性能和简单负载均衡策略的场景,iptables模式是一个不错的选择。而对于需要复杂负载均衡策略和容错能力的场景,userspace模式或ipvs模式可能更合适。

image-20240514101428737

1.2.3 ipvs模式

  • ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法。

  • ipvs(IP Virtual Server)模式是kube-proxy的另一种工作模式,它在很多方面都优于iptables模式:

    • 内核级负载均衡:与iptables不同,ipvs是在内核空间实现的,这意味着它可以直接进行网络包的转发,而不需要在用户空间和内核空间之间来回拷贝数据。因此,ipvs可以提供更低的延迟和更高的数据包转发速率。

    • 高效的性能:由于ipvs在内核中实现,它通常能够提供比iptables更高的性能,尤其是在高负载和大规模集群中。

    • 丰富的负载均衡算法ipvs支持多种负载均衡算法,包括轮询(round-robin)、最小连接(least-connection)、源IP哈希(source IP hashing)等,这为实现复杂的负载均衡策略提供了可能。

    • Session persistenceipvs支持会话保持(Session persistence),这意味着来自同一客户端的请求可以始终被路由到同一个后端Pod,这对于需要维持会话状态的应用非常重要。

    • 健康检查ipvs可以根据Pod的健康状态来路由流量,只将请求转发到健康的Pod上,从而提高服务的可用性。

    • SNAT优化ipvs可以更有效地处理源地址转换(SNAT),这对于使用NodePortLoadBalancer服务类型时,从外部网络访问集群内部服务非常重要。

    • 直接路由ipvs使用直接路由(Direct Routing)模式,这意味着流量不需要经过额外的网络地址转换,从而减少了资源消耗。

    • 状态同步kube-proxyipvs模式下,会监控Pod和Service的变化,并将这些变化实时同步到ipvs规则中。

image-20240514101729240

  • 此模式必须安装ipvs内核模块,否则会降级为iptables

[root@K8s-master-01 ~]# kubectl edit configmap kube-proxy -n kube-system    
    mode: "ipvs"  #修改此处,默认为空
    
[root@K8s-master-01 ~]# kubectl get pod -n kube-system
NAME                                 READY   STATUS    RESTARTS   AGE
coredns-74586cf9b6-96v5x             1/1     Running   0          3d2h
coredns-74586cf9b6-f5q7h             1/1     Running   0          3d2h
etcd-k8s-master-01                      1/1     Running   0          3d2h
kube-apiserver-k8s-master-01            1/1     Running   0          3d2h
kube-controller-manager-k8s-master-01   1/1     Running   0          3d2h
kube-flannel-ds-6gqmv                1/1     Running   0          3d
kube-flannel-ds-g7zcj                1/1     Running   0          3d
kube-flannel-ds-hh52b                1/1     Running   0          3d
kube-proxy-glhml                     1/1     Running   0          3d1h
kube-proxy-klcs2                     1/1     Running   0          3d1h
kube-proxy-x9v8k                     1/1     Running   0          3d2h
kube-scheduler-k8s-master-01            1/1     Running   0          3d2h
[root@K8s-master-01 ~]# kubectl delete pod kube-proxy-glhml kube-proxy-klcs2 kube-proxy-x9v8k -n kube-system
pod "kube-proxy-glhml" deleted
pod "kube-proxy-klcs2" deleted
pod "kube-proxy-x9v8k" deleted

1.3 Service类型

  • Service的资源清单文件:

---
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
  name: service # 资源名称
  namespace: dev # 命名空间
spec: # 描述
  selector: # 标签选择器,用于确定当前service代理哪些pod
    app: nginx
  type: ClusterIP # Service类型,指定service的访问方式
  clusterIP: None # 虚拟服务的ip地址,设置为None表示创建Headless Service
  sessionAffinity: ClientIP # session亲和性,支持ClientIP、None两个选项
  ports: # 端口信息
    - protocol: TCP
      port: 3017 # service端口
      targetPort: 5003 # pod端口
  • ClusterIP

    • 默认类型。Kubernetes 为 Service 分配一个虚拟的 IP 地址(ClusterIP),这个 IP 只能在集群内部访问。

    • 适用于在集群内部提供服务发现和负载均衡,而不对外公开服务。

  • NodePort

    • 在集群的所有节点上打开一个静态端口(NodePort),外部可以通过任何节点的 IP 地址加这个端口来访问 Service。

    • 适用于需要从集群外部访问服务的场景,但不像 LoadBalancer 那样需要云服务提供商的支持。

  • LoadBalancer

    • 与 NodePort 类似,但会利用云服务提供商的负载均衡器来分发流量。

    • 适用于需要高可用性和可扩展性的场景,并且外部访问流量较大时推荐使用。

    • 注意:需要云服务商支持,并且可能涉及额外的费用。

  • ExternalName

    • 不分配 ClusterIP,而是将服务映射到一个外部的 DNS 名称。

    • 适用于将集群外部的服务(如数据库服务)映射到 Kubernetes 集群内部,使得集群内的服务可以像调用集群内的服务一样调用这些外部服务。

1.4 Service使用

1.4.1 环境准备

  • 创建三个Pod

[root@K8s-master-01 ~]# vim deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pc-deployment
  namespace: test
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
​
[root@K8s-master-01 ~]# kubectl apply -f deployment.yaml 
deployment.apps/pc-deployment created
​
[root@k8s-master-01 ~]# kubectl get pod -n test -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
pc-deployment-5cb65f68db-2gb6n   1/1     Running   0          25m   10.224.154.196   k8s-node-01   <none>           <none>
pc-deployment-5cb65f68db-bp9vd   1/1     Running   0          25m   10.224.44.196    k8s-node-02   <none>           <none>
pc-deployment-5cb65f68db-z66sn   1/1     Running   0          25m   10.224.154.195   k8s-node-01   <none>           <none>
​
[root@K8s-master-01 ~]# kubectl get pod -n test --show-labels
NAME                             READY   STATUS    RESTARTS   AGE   LABELS
pc-deployment-5cb65f68db-2gb6n   1/1     Running   0          28m   app=nginx-pod,pod-template-hash=5cb65f68db
pc-deployment-5cb65f68db-bp9vd   1/1     Running   0          28m   app=nginx-pod,pod-template-hash=5cb65f68db
pc-deployment-5cb65f68db-z66sn   1/1     Running   0          28m   app=nginx-pod,pod-template-hash=5cb65f68db
​
​
# 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的IP地址不一致)
[root@K8s-master-01 ~]# kubectl exec -it pc-deployment-5cb65f68db-2gb6n -n test /bin/bash
root@pc-deployment-5cb65f68db-2gb6n:/# echo "`hostname -I` web-01" > /usr/share/nginx/html/index.html
root@pc-deployment-5cb65f68db-2gb6n:/# exit
​
[root@K8s-master-01 ~]# kubectl exec -it pc-deployment-5cb65f68db-bp9vd -n test /bin/bash
root@pc-deployment-5cb65f68db-bp9vd:/# echo "`hostname -I` web-02" > /usr/share/nginx/html/index.html           
root@pc-deployment-5cb65f68db-bp9vd:/#  exit
​
[root@K8s-master-01 ~]# kubectl exec -it pc-deployment-6769786c4c-qn9ls -n test /bin/bash
root@pc-deployment-6769786c4c-qn9ls:/# echo "`hostname -I` web-03" > /usr/share/nginx/html/index.html
root@pc-deployment-6769786c4c-qn9ls:/# exit
​
[root@K8s-master-01 ~]# curl 10.244.2.2
10.244.2.2  web-01
[root@K8s-master-01 ~]# curl 10.244.2.3
10.244.2.3  web-02
[root@K8s-master-01 ~]# curl 10.244.1.4
10.244.1.4  web-03

1.4.2 ClusterIP类型的Service

  • ClusterIP 类型的 Service 是 Kubernetes 中最基本的服务之一,它提供了一种稳定的、只限于集群内部访问的方式。以下是 ClusterIP 类型 Service 的一些关键特点:

    • 虚拟 IP 地址:ClusterIP 类型的 Service 会被分配一个虚拟的 IP 地址(Cluster IP),这个 IP 地址在 Service 的整个生命周期中保持不变。

    • 集群内部通信:Pods 可以通过这个固定的 Cluster IP 来访问 Service,从而实现集群内部的服务发现和负载均衡,而无需关心后端 Pods 的具体 IP 地址。

    • 不对外公开:ClusterIP 类型的 Service 不会被分配外部可访问的 IP 地址,因此它只能从集群内部访问,不能从集群外部直接访问。

    • 负载均衡:Kubernetes 会自动为 ClusterIP 类型的 Service 提供简单的负载均衡,将请求分发到后端的多个 Pods 上。

    • 会话亲和性:可以通过设置 sessionAffinity 属性来控制会话亲和性,确保来自同一客户端的请求始终被路由到同一个后端 Pod。

    • DNS 解析:在集群内部,可以通过 Service 名称进行 DNS 解析,解析结果为分配给该 Service 的 Cluster IP。

    • 适用于内部服务:ClusterIP 类型的 Service 非常适合用于需要在集群内部通信的内部服务,如数据库、缓存服务等。

[root@K8s-master-01 ~]# vim service-clusterip.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
  namespace: test
spec:
  selector:
    app: nginx-pod
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
​
[root@K8s-master-01 ~]# kubectl apply -f service-clusterip.yaml 
service/svc-clusterip created
​
[root@K8s-master-01 ~]# kubectl get svc,pod -n test
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/svc-clusterip   ClusterIP   10.110.170.62   <none>        80/TCP    78s
​
NAME                                 READY   STATUS    RESTARTS   AGE
pod/pc-deployment-6769786c4c-62d94   1/1     Running   0          5m48s
pod/pc-deployment-6769786c4c-fv6jj   1/1     Running   0          5m48s
pod/pc-deployment-6769786c4c-qn9ls   1/1     Running   0          5m48s
​
[root@K8s-master-01 ~]# kubectl describe svc svc-clusterip -n test
Name:              svc-clusterip
Namespace:         test
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.110.170.62
IPs:               10.110.170.62
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.4:80,10.244.2.2:80,10.244.2.3:80
Session Affinity:  None
Events:            <none>
​
#访问ClusterIP
[root@K8s-master-01 ~]# for ((i=1;i<=6;i++)) do  curl 10.110.170.62; done  #集群内部访问(只能再集群内部fan
10.244.2.3  web-02
10.244.2.2  web-01
10.244.1.4  web-03
10.244.2.3  web-02
10.244.2.2  web-01
10.244.1.4  web-03
​
[root@K8s-master-01 ~]# ipvsadm -Ln | grep -A3  10.110.170.99:80  #默认负载均衡算法为轮询
TCP  10.110.170.62:80 rr
  -> 10.244.2.3:80                Masq    1      0          0         
  -> 10.244.2.2:80                Masq    1      0          0         
  -> 10.244.1.4:80                Masq    1      0          0   

1.4.3 Endpoint类型的Service

  • Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,它是根据service配置文件中selector描述产生的。

  • 一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换句话说,service和pod之间的联系是通过endpoints实现的。

  • Endpoint 类型 Service 的一些特点:

    • 直接使用 Endpoints:Endpoint 类型的服务不提供标准的负载均衡器或代理,它允许客户端直接与 Endpoints 对象中的 Pod 进行通信。

    • 无需代理:与标准的 Service 类型不同,Endpoint 类型的服务不会创建 kube-proxy 监听的端点。这意味着,使用这种服务类型的客户端需要能够直接解析和连接到 Endpoints 对象中定义的 Pod IP 地址。

    • 适用于特定场景:这种服务类型适用于那些需要绕过 Kubernetes 服务发现机制的特定场景,例如,当您需要直接与 Pod 通信以进行调试或使用自定义的负载均衡器时。

    • 手动管理:使用 Endpoint 类型的服务时,您需要手动管理 Endpoints 对象,包括添加和删除端点。

    • 不提供服务发现:由于它不通过标准的 Kubernetes 服务发现机制,所以它不提供自动的服务发现功能。

    • 安全性:直接与 Pod 通信可能会带来安全风险,因为 Pod 的 IP 地址可能会变化,而且没有内置的负载均衡或健康检查机制。

    • 使用场景限制:Endpoint 类型的服务通常只在特定的用例中使用,比如当您需要直接控制网络流量或使用自定义的网络策略时。

image-20240514213245379

[root@K8s-master-01 ~]# kubectl get endpoints -n test -o wide
NAME            ENDPOINTS                                   AGE
svc-clusterip   10.244.1.4:80,10.244.2.2:80,10.244.2.3:80   93s

在 Kubernetes 中,负载分发策略用于确定如何将传入的网络请求分配到后端的 Pod 上。以下是您提到的两种策略的详细说明:

  • 默认的 kube-proxy 策略

    • 随机(Random):kube-proxy 随机选择一个后端 Pod 来处理传入的请求。这种策略适用于大多数情况,因为它可以提供良好的负载均衡,并且实现起来相对简单。

    • 轮询(Round Robin):kube-proxy 按顺序将请求分发给后端的每个 Pod。当所有 Pod 都具有相同的处理能力时,这种策略可以确保每个 Pod 接收到相同数量的请求。

    除了随机和轮询,Kubernetes 还提供了其他几种负载分发策略,包括:

    • 最小连接数(Least Connections):选择当前活跃连接数最少的 Pod。

    • 源 IP(Source IP):确保来自同一个客户端 IP 的所有请求都发送到同一个 Pod,以保持会话的一致性。

  • 会话保持

    • Kubernetes 的服务可以配置会话保持,也称为会话亲和性或粘性会话。当启用会话保持时,来自同一个客户端的所有请求都将被路由到同一个后端 Pod,直到该会话结束。

    • 这种策略对于需要保持用户会话状态的应用非常有用,例如购物车应用或需要用户登录信息的应用。

    • 会话保持可以通过两种方式实现:

      • 客户端 IP:基于客户端的 IP 地址进行会话保持,确保来自同一 IP 的请求总是路由到同一个 Pod。

      • Cookie 基于会话:kube-proxy 可以生成一个特殊的哈希值,存储在客户端的 Cookie 中,以确保来自同一个 Cookie 的请求被路由到同一个 Pod。

      • 此模式可以使在spec中添加 sessionAffinity: ClientIP选项,表示Service将使用客户端IP地址来进行会话亲和性(session affinity)。当一个客户端请求连接到Service时,Service将根据客户端的IP地址将请求转发到一个稳定的Pod上。这意味着来自同一个客户端的所有请求在默认情况下都会被转发到同一个Pod上,直到该Pod不再可用。

[root@K8s-master-01 ~]# kubectl delete -f service-clusterip.yaml 
service "svc-clusterip" deleted
[root@K8s-master-01 ~]# vim service-clusterip.yaml 
---
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
  namespace: test
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.110.170.99    #可以指定IP
  type: ClusterIP
  sessionAffinity: ClientIP   #表示Service将使用客户端IP地址来进行会话亲和性
  ports:
    - port: 80
      targetPort: 80
​
[root@K8s-master-01 ~]# kubectl apply -f service-clusterip.yaml 
service/svc-clusterip created
​
[root@K8s-master-01 ~]# kubectl describe service/svc-clusterip -n test
Name:              svc-clusterip
Namespace:         test
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.110.170.99
IPs:               10.110.170.99
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.4:80,10.244.2.2:80,10.244.2.3:80
Session Affinity:  ClientIP
Events:            <none>
​
[root@K8s-master-01 ~]# ipvsadm -Ln | grep -A3  10.110.170.99:80
TCP  10.110.170.99:80 rr persistent 10800   #会话保持,在10800秒内始终由最稳定的一台提供访问
  -> 10.244.1.4:80                Masq    1      0          0         
  -> 10.244.2.2:80                Masq    1      0          0         
  -> 10.244.2.3:80                Masq    1      0          0     
  
[root@K8s-master-01 ~]# for ((i=1;i<=6;i++)) do  curl 10.110.170.99; done
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.3  web-02

1.4.4 HeadLiness类型的Service

  • 在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。

  • 在 Kubernetes 中,存在一种不分配 Cluster IP 的 Service 类型,称为 "Headless Service"。

  • Headless Service 的特点如下:

    • 不分配 Cluster IP:与标准的 ClusterIP 类型 Service 不同,Headless Service 不会为服务分配一个虚拟 IP 地址。

    • 通过域名访问:Pods 可以通过 Service 的 DNS 名称直接访问到后端的 Pods。由于没有 Cluster IP,客户端不能直接通过一个固定的 IP 地址来访问服务,而是通过 DNS 解析来获取后端 Pods 的实际 IP 地址。

    • 自定义负载均衡:开发者可以自由地实现自己的负载均衡策略,而不是依赖 Kubernetes 提供的内置负载均衡。

    • DNS 解析:Kubernetes 会为每个后端 Pod 创建一个 DNS 记录。这意味着通过 DNS 查询,客户端可以获取到后端所有 Pod 的 IP 地址。

    • 适用于无头服务:Headless Service 特别适合于那些需要直接与 Pod 通信的场景,例如,当使用 StatefulSets 部署具有唯一身份的 Pods 时。

[root@K8s-master-01 ~]# vim service-headliness.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
  namespace: test
spec:
  selector:
    app: nginx-pod
  clusterIP: None   #这里IP设为None
  type: ClusterIP
  sessionAffinity: ClientIP
  ports:
    - port: 80
      targetPort: 80
​
[root@K8s-master-01 ~]# kubectl apply -f service-headliness.yaml 
service/svc-clusterip created
[root@K8s-master-01 ~]# kubectl describe service/svc-clusterip -n test  #这里集群IP为None
Name:              svc-clusterip
Namespace:         test
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                None
IPs:               None
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.4:80,10.244.2.2:80,10.244.2.3:80
Session Affinity:  ClientIP
Events:            <none>
​
# 查看域名解析
[root@K8s-master-01 ~]# kubectl exec -it pc-deployment-6769786c4c-62d94 -n test /bin/bash
root@pc-deployment-6769786c4c-62d94:/# cat /etc/resolv.conf 
search test.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
​
[root@K8s-master-01 ~]# nslookup svc-clusterip.test.svc.cluster.local 10.96.0.10
Server:         10.96.0.10
Address:        10.96.0.10#53
​
Name:   svc-clusterip.test.svc.cluster.local
Address: 10.244.2.2
Name:   svc-clusterip.test.svc.cluster.local
Address: 10.244.2.3
Name:   svc-clusterip.test.svc.cluster.local
Address: 10.244.1.4
​
[root@K8s-master-01 ~]# sed -i 's/^nameserver/#nameserver/' /etc/resolv.conf
[root@K8s-master-01 ~]# echo 'nameserver 10.96.0.10' >> /etc/resolv.conf   #指定DNS
​
[root@K8s-master-01 ~]# for ((i=1;i<=6;i++)) do  curl svc-clusterip.test.svc.cluster.local; done
10.244.1.4  web-03
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.3  web-02
10.244.2.2  web-01

每个pod有唯一的主机名,并且有唯一的域名

  • 格式:主机名称.service名称.名称空间.svc.cluster.local

  • 举例:nginx-statefulset-0.default.svc.cluster.local

1.4.5 NodePort类型的Service

  • 之前的样例中,创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上,然后就可以通过 NodeIp: NodePort 来访问Service了。

  • NodePort 类型的 Service 是 Kubernetes 中用于将服务暴露给集群外部的一种方式。下面是 NodePort 类型 Service 的一些关键点:

    • 端口映射:NodePort 类型的 Service 会在集群的所有节点上打开一个静态端口(NodePort),这个端口范围通常是 30000-32767。这个端口将被映射到 Service 所代理的后端 Pods 的端口上。

    • 通过节点 IP 访问:外部用户可以通过 <NodeIP>:<NodePort> 的形式访问到这个 Service。这里的 NodeIP 是集群中任何节点的 IP 地址,而 NodePort 是 Service 映射的端口号。

    • 无需额外配置:与 LoadBalancer 类型相比,NodePort 不需要云服务商提供的额外配置或支持,因此它在不支持 LoadBalancer 的环境中非常有用。

    • 安全性考虑:由于 NodePort 会在所有节点上开放端口,因此需要考虑到安全性问题。通常建议使用防火墙规则来限制访问,只允许特定的 IP 地址访问这些端口。

    • 适用于简单的外部访问:NodePort 适合于简单的外部访问需求,但对于生产环境,通常推荐使用 LoadBalancer 或 Ingress,因为它们提供了更好的性能、安全性和高级路由功能。

image-20240516112136442

[root@K8s-master-01 ~]# vim service-nodeport.yaml 
---
apiVersion: v1
kind: Service
metadata:
  name: svc-nodeport
  namespace: test
spec:
  selector:
    app: nginx-pod
  type: NodePort
  ports:
    - port: 80     # Service 在所有节点上监听的端口号
      nodePort: 32522  # 默认的取值范围是:30000-32767, 如果不指定,会默认分配
      targetPort: 80  # 后端 Pod 监听的端口号
​
[root@K8s-master-01 ~]# kubectl apply -f service-nodeport.yaml 
service/svc-nodeport created
​
[root@K8s-master-01 ~]# kubectl get svc,pod -n test -o wide
NAME                   TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service/svc-nodeport   NodePort   10.102.4.48   <none>        80:32522/TCP   5s    app=nginx-pod
​
NAME                                 READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
pod/pc-deployment-6769786c4c-62d94   1/1     Running   0          74m   10.244.2.2   k8s-node-02   <none>           <none>
pod/pc-deployment-6769786c4c-fv6jj   1/1     Running   0          74m   10.244.2.3   k8s-node-02   <none>           <none>
pod/pc-deployment-6769786c4c-qn9ls   1/1     Running   0          74m   10.244.1.4   k8s-node-01   <none>           <none>
  • 外部访问测试

image-20240516113258620

[root@K8s-master-01 ~]# ipvsadm -Ln | grep -A3 32522
TCP  192.168.110.21:32522 rr
  -> 10.244.1.4:80                Masq    1      0          0         
  -> 10.244.2.2:80                Masq    1      0          1         
  -> 10.244.2.3:80                Masq    1      0          1         
--
TCP  10.244.0.0:32522 rr
  -> 10.244.1.4:80                Masq    1      0          0         
  -> 10.244.2.2:80                Masq    1      0          0         
  -> 10.244.2.3:80                Masq    1      0          0       

1.4.6 LoadBalancer类型的Service

  • LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。

  • LoadBalancer 类型的 Service 是 Kubernetes 中用于将服务暴露给外部世界的一种方式,它通常需要云服务商的支持。以下是 LoadBalancer 类型 Service 的一些关键特点:

    • 外部负载均衡器:LoadBalancer 类型的 Service 会利用云服务商提供的外部负载均衡器(如 AWS ELB、Azure Load Balancer 或 GCP Load Balancer)来暴露服务。

    • 自动配置:Kubernetes 会自动配置负载均衡器,将外部请求路由到后端的 Pods。

    • 外部 IP 地址:LoadBalancer 会提供一个或多个外部可访问的 IP 地址,这些 IP 地址可以是公网 IP 或云服务商的内部 IP。

    • 自动扩展:许多云服务商的负载均衡器支持自动扩展,这意味着它们可以根据流量自动调整后端 Pods 的数量。

    • 安全性和性能:LoadBalancer 提供了额外的安全和性能优势,因为它们通常包括内置的防火墙规则、SSL 终止和 DDoS 防护。

    • 适用于生产环境:由于其高级特性和性能,LoadBalancer 类型的 Service 非常适合生产环境。

    • 可能涉及费用:使用云服务商的负载均衡器可能会产生额外的费用。

image-20240516114217789

1.4.6.1 开源 Kubernetes 负载均衡器
1. MetalLB
  • 介绍:MetalLB 是一个为 Kubernetes 集群设计的负载均衡器,特别适用于裸金属环境。它通过在集群中运行的组件来监控服务对象的变化,为 LoadBalancer 类型的服务提供实现。10^

  • 工作原理:MetalLB 通过标准的路由协议(如 ARP、NDP 或 BGP)来实现负载均衡。它有两种工作模式:Layer2 模式和 BGP 模式。在 Layer2 模式下,MetalLB 会在集群中的一个节点上处理所有服务 IP 的流量,而在 BGP 模式下,它通过与网络路由器建立 BGP 会话来实现真正的负载均衡。2^11^

  • 特点:MetalLB 易于部署,支持自动和静态 IP 地址分配,并且可以与 kube-proxy 紧密集成。

2. PureLB
  • 介绍:PureLB 是另一种负载均衡器,它使用自定义资源(CRD)进行配置,由两部分组成:分配器负责分配 IP 地址,而 lbnodeagent 负责在所有节点上配置节点网络。

  • 工作原理:PureLB 的 Layer2 工作模式特点在于它会在 Kubernetes 集群的受管节点上创建一个虚拟网卡(如 kube-lb0),并通过任意路由协议实现 ECMP(等价多路径),以进行负载均衡和流量分发。

  • 特点:PureLB 可以根据单个 VIP 选择节点,并将多个 VIP 分散到不同节点,以此来避免节点负载失衡。

3. OpenELB
  • 介绍:OpenELB 是由 KubeSphere 社区发起的云原生负载均衡器,现已成为 CNCF 沙箱项目。它适用于物理机和虚拟化环境,利用 BGP 和 ECMP 实现高性能和高可用性。

  • 工作原理:OpenELB 支持两种工作模式:Layer2 模式和 BGP 模式。在 BGP 模式下,它通过与集群的边界路由器建立 BGP 连接来实现高效的流量调度和管理。OpenELB 还支持 ECMP,允许在多条路径之间进行负载均衡和流量分发。6^

  • 特点:OpenELB 易于与 Kubernetes 集成,提供了丰富的监控和日志功能,支持动态服务发现、流量调度和安全防护等高级特性。

特性/项目MetalLBPureLBOpenELB
介绍适用于裸金属环境的负载均衡器使用 CRD 配置的负载均衡器云原生负载均衡器,CNCF 沙箱项目
支持环境裸金属、云环境裸金属、云环境物理机、虚拟化环境
工作原理ARP/NDP/BGP任意路由协议BGP/ECMP
Layer2 模式支持支持支持
BGP 模式支持不适用支持
IP 地址分配自动和静态不明确动态服务发现
节点负载均衡不适用支持支持
与 kube-proxy 集成紧密集成不明确支持
监控和日志基本不明确丰富
安全性基本不明确支持
高级特性基本不明确高级特性支持
社区活跃度
1.4.6.2 什么是OpenELB
  • Kubernetes(K8s)中常用的三种服务暴露方式是方法:

    • NodePort:通过在集群的所有节点上开放一个静态端口(NodePort),可以对外提供服务。这种方式简单易用,但如您所述,由于每个服务都需要一个唯一的端口,因此在大规模部署时可能会遇到端口耗尽的问题。

    • Ingress:Ingress是一个API对象,它管理外部访问到集群内服务的HTTP和HTTPS路由。它提供了URL路由、负载均衡、SSL终止、名称基的虚拟托管等功能。Ingress主要用于7层(应用层)的路由。

    • LoadBalancer:通常由云服务提供商提供,它是一个可以自动分配公网IP并进行负载均衡的服务。使用LoadBalancer可以简化外部访问集群内服务的过程。

  • 对于私有云集群,虽然不直接使用公有云服务,但仍然可以模拟LoadBalancer的行为。OpenELB就是这样一个工具,它允许在私有云或裸金属环境中模拟云服务提供商的LoadBalancer功能。OpenELB通过使用BGP(边界网关协议)或其他机制来实现负载均衡,它可以与Kubernetes集群无缝集成,提供类似于云服务提供商的负载均衡服务。

  • OpenELB通过CNCF(云原生计算基金会)TOC(技术监督委员会)的审核,意味着它符合云原生技术的标准和最佳实践,这对于推动其在社区中的采用和信任是有帮助的。

  • 在私有云环境中使用OpenELB或其他类似的负载均衡解决方案,可以为Kubernetes集群提供强大的网络功能,而无需依赖特定的云服务提供商。这为私有云用户提供了更大的灵活性和控制力。

1.4.6.2 安装 OpenELB
  • 前提:首先需要为 kube-proxy 启用 strictARP ,以便 Kubernetes 集群中的所有网卡停止响应其他网卡的ARP 请求,而由 OpenELB 处理 ARP 请求.

# 注意:是修改,默认strictARP 是 false
[root@k8s-master-01 ~]# kubectl edit configmap kube-proxy -n kube-system
ipvs:
  ...
      strictARP: true
  ...
  • 安装部署

[root@k8s-master-01 ~]# wget -c https://gitee.com/kong-xiangyuxcz/svn/releases/download/openelab-0.5.1/openelb.yaml
​
[root@k8s-master-01 ~]# sed -i 's#k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1#registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1#g' openelb.yaml     # 修改镜像
​
# 不成功也已手动拉取,调度到哪个节点在哪个上拉
$ docker pull kubesphere/openelb:v0.5.1
$ docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1
$ docker pull kubesphere/kube-keepalived-vip:0.35
                
[root@k8s-master-01 ~]# kubectl apply -f openelb.yaml 
[root@k8s-master-01 ~]# kubectl get pod -n openelb-system
NAME                               READY   STATUS      RESTARTS   AGE
openelb-admission-create-wgkh5     0/1     Completed   0          113m
openelb-admission-patch-mcmsc      0/1     Completed   1          113m
openelb-keepalive-vip-4jnvm        1/1     Running     0          112m
openelb-keepalive-vip-vdnb5        1/1     Running     0          112m
openelb-manager-6696fffb47-8nhh9   1/1     Running     0          113m
  • 添加 EIP 池

[root@k8s-master-01 ~]# vim ip-pool.yaml
---
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
  name: eip-pool
spec:
  address: 192.168.110.200-192.168.110.240
  protocol: layer2
  disable: false
  interface: ens33
​
[root@k8s-master-01 ~]# kubectl apply -f ip-pool.yaml 
eip.network.kubesphere.io/eip-pool created
​
[root@k8s-master-01 ~]# kubectl get eip
NAME       CIDR                              USAGE   TOTAL
eip-pool   192.168.110.200-192.168.110.240           41
1.4.6.3 配置 Service 为 LoadBalancer

把 Service 类型修改为 LoadBalancer,同时 annotations 中添加如下三行:

[root@k8s-master-01 ~]# vim svc-lb.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: svc-lb
  namespace: test
  annotations:
    lb.kubesphere.io/v1alpha1: openelb
    protocol.openelb.kubesphere.io/v1alpha1: layer2
    eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
  selector:
    app: nginx-pod
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80
​
[root@k8s-master-01 ~]# kubectl apply -f svc-lb.yaml 
service/svc-lb created
[root@k8s-master-01 ~]# kubectl get svc -n test
NAME     TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE
svc-lb   LoadBalancer   10.105.56.123   192.168.110.200   80:30193/TCP   6s
1.4.6.4 测试
[root@k8s-master-01 ~]# for ((i=1;i<=6;i++)) do curl 192.168.110.200; done   # 负载均衡
10.224.44.196  web-02
10.224.154.196  web-01
10.224.154.195  web-03
10.224.44.196  web-02
10.224.154.196  web-01
10.224.154.195  web-03
  • 外部访问

image-20240520142817592

1.4.7 ExternalName类型的Service

  • ExternalName类型的Service用于引入集群外部的服务,它通过 externalName 属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。

  • ExternalName 类型的 Service 在 Kubernetes 中用于将一个 DNS 名称映射到一个外部的服务,而不需要在集群内创建任何代理或负载均衡器。这种方式非常适合于将集群内部的应用程序指向外部的数据库、API 服务或其他不在 Kubernetes 管理下的资源。

  • ExternalName 类型 Service 的一些关键特点:

    • 无 Cluster IP:ExternalName 类型的 Service 不分配 Cluster IP。

    • DNS 解析:在 Kubernetes 集群内部,当对这种类型的 Service 进行 DNS 解析时,将直接返回 externalName 字段指定的值。

    • 简单配置:只需要指定一个 externalName 和一个可选的 port

    • 直接访问:Pods 可以直接通过 Service 名称访问外部服务,就像访问集群内的其他服务一样。

    • 适用于外部资源:适合于引用那些不在 Kubernetes 控制范围内的外部服务。

    • 无需特殊网络配置:不需要任何特殊的网络配置或云服务提供商支持。

image-20240516114724022

  • 配置实例

apiVersion: v1
kind: Service
metadata:
  name: my-external-service
spec:
  type: ExternalName
  externalName: api.example.com  # 外部服务的地址
  ports:
    - port: 443  # 外部服务监听的端口
  • 27
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值