• Service:服务发现与负载均衡


    Service存在的意义

    Service是k8s中将运行在一组Pods上的应用程序暴露为网络服务的抽象方法。

    • 服务发现:找到提供同一服务的一组Pod;
    • 负载均衡:定义一组Pod的访问策略。

    举个例子,考虑一个图片处理后端,它运行了3个副本。这些副本是可互换的,前端不需要关心它们调用了哪个后端副本。 然而组成这一组后端程序的Pod实际上可能会发生变化(被销毁或重建), 前端客户端不应该也没必要知道,而且也不需要跟踪这一组后端的状态。Service定义的抽象能够解耦这种关联。

    Service与Pod的关系:

    • Service通过标签关联一组Pod;
    • Service为一组Pod提供负载均衡能力。

    Service的定义与创建

    Service的创建

    方法一:为已有的deployment创建service。

    kubectl expose deployment
    
    • 1

    方法二:直接创建Service。

    生成模板文件:

    kubectl create service clusterip my-service --tcp=80:9376 --dry-run=client -o yaml > service.yaml
    
    • 1

    编辑yaml文件:

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      selector:
        app: MyApp
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    修改后创建服务:

    kubectl apply -f service.yaml
    
    • 1

    查看Service状态:

    kubectl get service
    
    • 1

    查看Service关联的Pod地址:

    kubectl get ep
    
    • 1

    多端口Servcie定义

    对于某些服务,需要公开多个端口。相应地,Service也要配置多个端口定义,通过端口名称区分。

    apiVersion: v1
    kind: Service
    metadata:
      name: web
    spec:
      selector:
        app: web
      ports:
        - name: http
    	  protocol: TCP
          port: 80
          targetPort: 80
    	- name: https
    	  protocol: TCP
          port: 443
          targetPort: 443
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Service的常用类型

    可以通过ServiceTypes指定Service的类型,有以下几种选择:

    • ClusterIP:通过集群的内部IP暴露服务,选择该值时服务只能够在集群内部访问。是默认的类型;
    • NodePort:通过每个节点上的IP和静态端口(NodePort)暴露服务。NodePort服务会路由到自动创建的ClusterIP服务。 通过请求<节点IP>:<节点端口>,可以从集群外部访问一个NodePort服务。
    • LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。外部负载均衡器可以将流量路由到自动创建的NodePort服务和ClusterIP服务上。
    • ExternalName:通过返回CNAME和对应值,可以将服务映射到externalName字段的内容(例如foo.bar.example.com)。无需创建任何类型代理。

    :需要使用kube-dns 1.7及以上版本或者CoreDNS 0.0.8及以上版本才能使用ExternalName类型。

    ClusterIP

    分配一个稳定的IP地址,即vip,只能在集群内部访问(在Pod中或者Node上都能访问)。

    spec:
      type: ClusterIP
      ports:
        - port: 80
    	  protocol: TCP
    	  targetPort: 80
      selector:
        app: web
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    NodePort

    在每个节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定的内部集群IP地址。

    • 访问地址::
    • 端口范围:30000-32767
    spec:
      type: NodePort
      ports:
      - port: 80
        protocol: TCP
    	targetPort: 80
    	nodePort: 30009
      selector:
        app: web
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    LoadBalancer

    与NodePort类似,在每个节点上启用一个端口来暴露服务。除此之外,k8s会请求底层云平台(比如AWS、阿里云、腾讯云)上的负载均衡器,将每个NodeIP:NodePort作为后端加进去。

    Service代理模式

    Service底层实现主要有iptables和ipvs两种网络模式,决定了如何转发流量。默认为iptables模式。

    查看使用的Service代理模式:

    #查看kube-proxy的Pod名称
    [root@k8s-master ~]# kubectl get pod -n kube-system
    #查看Pod日志
    [root@k8s-master ~]# kubectl logs kube-proxy-58kfs -n kube-system
    I0806 00:16:02.180484       1 server_others.go:561] "Unknown proxy mode, assuming iptables proxy" proxyMode=""
    I0806 00:16:02.305394       1 server_others.go:206] "Using iptables Proxier"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    iptables

    这种模式下,kube-proxy会监视Kubernetes控制节点对Service对象和Endpoints对象的添加和移除。 对每个Service,它会配置iptables规则,从而捕获到达该Service的clusterIP和端口的请求,进而将请求重定向到Service的一组后端中的某个Pod上面。 对于每个Endpoints对象,它也会配置iptables规则,这个规则会选择一个后端组合。使用iptables处理流量具有较低的系统开销,因为流量由Linux netfilter处理, 而无需在用户空间和内核空间之间切换。

    查看负载均衡规则:

    #iptables-save命令用于将linux内核中的iptables表导出到标准输出
    iptables-save | grep <SERVICE-NAME>
    
    • 1
    • 2

    实验1
    部署Nginx前端web服务:

    [root@k8s-master ~]# cat nginx-deploy.yaml
    apiVersion: apps/v1   #api版本
    kind: Deployment      #资源类型
    metadata:             #资源的元数据
      name: nginx-web
      namespace: default
    spec:                 #资源规格
      replicas: 3         #副本数量
      selector:           #标签选择器
        matchLabels:      #与下面Pod的labels保持一致
          app: nginx-web
      #被控制对象
      template:           #Pod模板
        metadata:
          labels:         #Pod的标签
            app: nginx-web
        spec:
          containers:     #容器配置
          - name: nginx-web
            image: nginx:1.20  #容器使用的镜像
    [root@k8s-master ~]#
    [root@k8s-master ~]# cat nginx-svc.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-web
      namespace: default
    spec:
      ports:
      - port: 80          #Service端口,通过ClusterIP访问
        protocol: TCP
        targetPort: 80    #Pod内部署应用的服务端口,比如nginx是80
      selector:           #标签选择器,与deployment中保持一致
        app: nginx-web
      type: NodePort      #Service类型
    [root@k8s-master ~]#
    [root@k8s-master ~]# kubectl apply -f nginx-deploy.yaml
    [root@k8s-master ~]# kubectl apply -f nginx-svc.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    查看iptables转发规则:

    [root@k8s-master ~]# iptables-save | grep nginx
    -A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx-web" -m tcp --dport 32379 -j KUBE-SVC-CJUJYHIKVPA67ZFN
    -A KUBE-SERVICES -d 10.111.222.10/32 -p tcp -m comment --comment "default/nginx-web cluster IP" -m tcp --dport 80 -j KUBE-SVC-CJUJYHIKVPA67ZFN
    
    • 1
    • 2
    • 3

    上面有两条规则(流量入口):

    • 通过NodePort访问32379端口,命中KUBE-NODEPORTS,走KUBE-SVC-CJUJYHIKVPA67ZFN规则;
    • 通过ClusterIP访问80端口,命中KUBE-SERVICES,也会走KUBE-SVC-CJUJYHIKVPA67ZFN规则。

    继续查看KUBE-SVC-CJUJYHIKVPA67ZFN规则的转发模式:

    # KUBE-SVC-CJUJYHIKVPA67ZFN规则
    [root@k8s-master ~]# iptables-save | grep KUBE-SVC-CJUJYHIKVPA67ZFN
    ...
    -A KUBE-SVC-CJUJYHIKVPA67ZFN -m comment --comment "default/nginx-web" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-7JJLFLA7KUXKZONR
    -A KUBE-SVC-CJUJYHIKVPA67ZFN -m comment --comment "default/nginx-web" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-VGBWIIDSI463MCML
    -A KUBE-SVC-CJUJYHIKVPA67ZFN -m comment --comment "default/nginx-web" -j KUBE-SEP-VJTMVZQFXALRW5SX
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可见KUBE-SVC-CJUJYHIKVPA67ZFN规则后面还有三条转发规则(负载均衡):

    • KUBE-SEP-7JJLFLA7KUXKZONR,命中的概率为三分之一;
    • KUBE-SEP-VGBWIIDSI463MCML,命中的概率为(1-33%)*50%=33%
    • KUBE-SEP-VJTMVZQFXALRW5SX,命中的概率为1-33%-33%=33%
      可见,iptables通过KUBE-SVC-CJUJYHIKVPA67ZFN规则实现了简单的负载均衡。

    继续查看上面三种规则的转发模式:

    [root@k8s-master ~]# iptables-save | grep  KUBE-SEP-7JJLFLA7KUXKZONR
    -A KUBE-SEP-7JJLFLA7KUXKZONR -p tcp -m comment --comment "default/nginx-web" -m tcp -j DNAT --to-destination 10.244.169.160:80
    -A KUBE-SVC-CJUJYHIKVPA67ZFN -m comment --comment "default/nginx-web" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-7JJLFLA7KUXKZONR
    [root@k8s-master ~]#
    [root@k8s-master ~]# iptables-save | grep  KUBE-SEP-VGBWIIDSI463MCML
    -A KUBE-SEP-VGBWIIDSI463MCML -p tcp -m comment --comment "default/nginx-web" -m tcp -j DNAT --to-destination 10.244.36.83:80
    -A KUBE-SVC-CJUJYHIKVPA67ZFN -m comment --comment "default/nginx-web" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-VGBWIIDSI463MCML
    [root@k8s-master ~]#
    [root@k8s-master ~]# iptables-save | grep  KUBE-SEP-VJTMVZQFXALRW5SX
    -A KUBE-SEP-VJTMVZQFXALRW5SX -p tcp -m comment --comment "default/nginx-web" -m tcp -j DNAT --to-destination 10.244.36.84:80
    -A KUBE-SVC-CJUJYHIKVPA67ZFN -m comment --comment "default/nginx-web" -j KUBE-SEP-VJTMVZQFXALRW5SX
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可见三条规则都做了DNAT(网络目标地址转换),并准发到三个不同的目标IP的80端口。而这三个IP刚好对应了我们部署的nginx-web的三个Pod。

    [root@k8s-master ~]# kubectl get ep
    NAME         ENDPOINTS                                           AGE
    nginx-web    10.244.169.160:80,10.244.36.83:80,10.244.36.84:80   45m
    
    • 1
    • 2
    • 3

    IPVS

    在ipvs模式下,kube-proxy监视Kubernetes服务和端点,调用netlink接口相应地创建IPVS规则,并定期将IPVS规则与Kubernetes服务和端点同步。该控制循环可确保IPVS状态与所需状态匹配。访问服务时,IPVS将流量定向到后端Pod之一。

    IPVS代理模式基于类似于iptables模式的netfilter挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。这意味着,与iptables模式下的kube-proxy相比,IPVS模式下的kube-proxy重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS模式还支持更高的网络流量吞吐量。

    查看负载均衡规则:

    #安装ipvs管理工具
    yum install ipvsadm -y
    #查看代理规则
    ipvsadm -L -n
    
    • 1
    • 2
    • 3
    • 4

    Service默认使用iptables代理模式。通过kubeadm方式可以修改为ipvs模式。

    #将mode配置为"ipvs"
    [root@k8s-master ~]# kubectl edit configmap kube-proxy -n kube-system
    mode: "ipvs"
    
    • 1
    • 2
    • 3

    kube-proxy配置文件以configmap方式存储。如果要让所有节点生效,需要重建所有节点的kube-proxy的Pod。

    #删除Pod后会自动重建,配置才会生效
    kubectl delete pod kube-proxy-75z28 -n kube-system
    
    • 1
    • 2

    实验2

    #查看节点1的ipvs转发规则
    [root@k8s-node1 ~]# ipvsadm -L -n
    IP Virtual Server version 1.2.1 (size=4096)
    Prot LocalAddress:Port Scheduler Flags
      -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
    
    #删除node1的kube-proxy Pod
    [root@k8s-node1 ~]# kubectl delete pod kube-proxy-75z28 -n kube-system
    
    #查看节点1的ipvs转发规则
    [root@k8s-node1 ~]# 
    [root@k8s-node1 ~]# ipvsadm -L -n
    IP Virtual Server version 1.2.1 (size=4096)
    Prot LocalAddress:Port Scheduler Flags
      -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
    ...
    TCP  172.17.0.1:32042 rr
      -> 10.244.36.83:80              Masq    1      0          0
      -> 10.244.36.84:80              Masq    1      0          0
      -> 10.244.169.160:80            Masq    1      0          0
    TCP  192.168.124.140:32042 rr
      -> 10.244.36.83:80              Masq    1      0          0
      -> 10.244.36.84:80              Masq    1      0          0
      -> 10.244.169.160:80            Masq    1      0          0
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    对比总结

    • iptables
      • 灵活、功能强大;
      • 规则遍历匹配和更新,呈线性时延;
      • 适合节点数较少的情况,一般不超过100个。
    • IPVS
      • 工作在内核态,性能更佳;
      • 调度算法丰富,如轮询rr、最少连接lc、目标地址哈希dh、源地址哈希sh等。

    DNS域名

    k8s默认部署了CoreDNS组件。CoreDNS服务监视Kubernetes API,为每一个Service创建DNS记录用于域名解析。

    ClusterIP A记录格式为:

    ..svc.cluster.local
    示例:my-svc.my-namespace.svc.cluster.local
    
    • 1
    • 2

    实验3

    [root@k8s-master ~]# kubectl run bs2 --image=busybox:1.28.4 -- sleep 24h
    pod/bs2 created
    
    [root@k8s-master ~]# kubectl exec -it bs2 -- sh
    / # nslookup nginx-web
    Server:    10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      nginx-web
    Address 1: 10.111.222.10 nginx-web.default.svc.cluster.local
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    References
    【1】https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/
    【2】https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

  • 相关阅读:
    softmax函数与参数 (x, dim = -1,0,1,2)
    20220909编译ITX-3588J的Buildroot的系统0(SDK预处理)
    三角定位是什么
    Vue中methods实现原理
    Facebook聊单,SaleSmartly有妙招!
    Python循环写文件结尾不带换行
    【零散技术】10分钟学会odoo的原型继承
    mybatis批量插数据
    阿里巴巴、阿里云Java面试题、笔试题(含答案)
    从0开始python学习-29.selenium 通过cookie信息进行登录
  • 原文地址:https://blog.csdn.net/Sebastien23/article/details/126190897