K8S—service服务与负载均衡

1、kube-proxy简介

        kube-proxy负责为Service提供cluster内部的服务发现和负载均衡,它运行在每个Node计算节点上,负责Pod网络代理, 它会定时从etcd服务获取到service信息来做相应的策略,维护网络规则和四层负载均衡工作。在K8s集群中微服务的负载均衡是由Kube-proxy实现的,它是K8s集群内部的负载均衡器,也是一个分布式代理服务器,在K8s的每个节点上都有一个,这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。

        service是一组pod的服务抽象,相当于一组pod的负载均衡,负责将请求分发给对应的pod。service会为这个负载均衡提供一个IP,一般称为cluster IP。kube-proxy的作用主要是负责service的实现,具体来说,就是实现了内部从pod到service和外部的从node port向service的访问。

  • kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。
  • kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也成为Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。
  • service是通过Selector选择的一组Pods的服务抽象,其实就是一个微服务,提供了服务的LB和反向代理的能力,而kube-proxy的主要作用就是负责service的实现。
  • service另外一个重要作用是,一个服务后端的Pods可能会随着生存灭亡而发生IP的改变,service的出现,给服务提供了一个固定的IP,而无视后端Endpoint的变化。

2、Service 简介

        Kubernetes Service定义了这样一种抽象: Service是一种可以访问 Pod逻辑分组的策略, Service通常是通过 Label Selector访问 Pod组。

        Service能够提供负载均衡的能力,但是在使用上有以下限制:只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的。

2.1、service的类型

2.1.1、ClusterIp(集群内部使用)

默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP(VIP)。

2.1.2、NodePort(对外暴露应用)

        在ClusterIP基础上为Service在每台机器上绑定一个端口,这样就可以通过NodeIP:NodePort访问来访问该服务。
端口范围:30000~32767

2.1.3、LoadBalancer(对外暴露应用,适用于公有云)

        在NodePort的基础上,借助Cloud Provider创建一个外部负载均衡器,并将请求转发到NodePort。

2.1.4、ExternalName

        创建一个dns别名指到service name上,主要是防止service name发生变化,要配合dns插件使用。通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容。这只有 Kubernetes 1.7或更高版本的kube-dns才支持。

2.2、Service 工作流程

  1. 客户端访问节点时通过 iptables实现的

  2. iptables规则是通过 kube-proxy写入的

  3. apiserver通过监控 kube-proxy去进行对服务和端点的监控

  4. kube-proxy通过 pod的标签( lables)去判断这个断点信息是否写入到 Endpoints里

 3、Endpoints简介

        endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址。service配置selector,endpoint controller才会自动创建对应的endpoint对象;否则,不会生成endpoint对象。

3.1、工作流程

        一个 Service 由一组 backend Pod 组成。这些 Pod 通过 endpoints 暴露出来。 Service Selector 将持续评估,结果被 POST 到一个名称为 Service-hello 的 Endpoint 对象上。 当 Pod 终止后,它会自动从 Endpoint 中移除,新的能够匹配上 Service Selector 的 Pod 将自动地被添加到 Endpoint 中。 检查该 Endpoint,注意到 IP 地址与创建的 Pod 是相同的。现在,能够从集群中任意节点上使用 curl 命令请求 hello Service <CLUSTER-IP>:<PORT> 。

3.2、示例:service-hello.yaml

        【例如】k8s集群中创建一个名为hello的service,就会生成一个同名的endpoint对象,ENDPOINTS就是service关联的pod的ip地址和端口。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 3
  selector:
    matchLabels:
     run: hello
  template:
    metadata:
      labels:
        run: hello
    spec:
      containers:
      - name: nginx
        image: nginx:latest
---
apiVersion: v1
kind: Service
metadata:
  name: service-hello
  labels:
  name: service-hello
spec:
  type: NodePort      # 这里代表是NodePort类型的,另外还有ingress,LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    nodePort: 31111   # 所有的节点都会开放此端口30000--32767,此端口供外部调用。
  selector:
    run: hello

3.2.1、查看验证

        Kube-proxy进程获取每个Service的Endpoints,实现Service的负载均衡功能。

[root@master ~]# kubectl get pods -o wide 
NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
hello-688d6b8d4c-bzfj9   1/1     Running   0          21m   10.254.1.2   node01   <none>           <none>
hello-688d6b8d4c-nwgd6   1/1     Running   0          21m   10.254.2.2   node02   <none>           <none>
hello-688d6b8d4c-s5ff8   1/1     Running   0          21m   10.254.3.2   harbor   <none>           <none>

[root@master ~]# kubectl get service service-hello -o wide 
NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service-hello   NodePort   10.244.143.216   <none>        80:31111/TCP   18h   run=hello

[root@master ~]# kubectl describe service service-hello 
Name:                     service-hello
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 run=hello
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.244.143.216
IPs:                      10.244.143.216
Port:                     <unset>  80/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  31111/TCP
Endpoints:                10.254.1.6:8080,10.254.2.3:8080,10.254.3.3:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

[root@master ~]# kubectl get endpoints service-hello 
NAME            ENDPOINTS                                         AGE
service-hello   10.254.1.6:8080,10.254.2.3:8080,10.254.3.3:8080   18h

        Service的负载均衡转发规则:访问Service的请求,不论是Cluster IP+TargetPort的方式;还是用Node节点IP+NodePort的方式,都被Node节点的Iptables规则重定向到Kube-proxy监听Service服务代理端口。kube-proxy接收到Service的访问请求后,根据负载策略,转发到后端的Pod。

3、kubernetes中的port

  • nodePort是外部访问k8s集群中service的端口,通过nodeIP: nodePort可以从外部访问到某个service。
  • port是k8s集群内部访问service的端口,即通过clusterIP: port可以访问到某个service。
  • targetPort是pod的端口,从port和nodePort来的流量经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
  • containerPort是pod内部容器的端口,targetPort映射到containerPort。

4、kubernetes服务发现

Kubernetes提供了两种方式进行服务发现, 即环境变量和DNS, 简单说明如下:

环境变量:当你创建一个Pod的时候,kubelet会在该Pod中注入集群内所有Service的相关环境变量。【注意】要想一个Pod中注入某个Service的环境变量,则必须Service要比该Pod先创建。这一点,几乎使得这种方式进行服务发现不可用。

DNS:这是k8s官方强烈推荐的方式!!! 可以通过cluster add-on方式轻松的创建KubeDNS来对集群内的Service进行服务发现。

5、Service代理模式

5.1、userspace 代理模式

        这种模式,kube-proxy 会监视 Kubernetes 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的后端 Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个后端 Pod,是 kube-proxy 基于 SessionAffinity 来确定的。

        最后,它配置 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP) 和 Port 的请求,并重定向到代理端口,代理端口再代理请求到后端Pod。

        默认情况下,用户空间模式下的 kube-proxy 通过轮转算法选择后端。

5.2、iptables 代理模式

        这种模式,kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。

        默认的策略是,kube-proxy 在 iptables 模式下随机选择一个后端。

        使用 iptables 处理流量具有较低的系统开销,因为流量由 Linux netfilter 处理, 而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。

        如果 kube-proxy 在 iptables 模式下运行,并且所选的第一个 Pod 没有响应, 则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败, 并会自动使用其他后端 Pod 重试。

        你可以使用 Pod 就绪探测器 验证后端 Pod 可以正常工作,以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端。 这样做意味着你避免将流量通过 kube-proxy 发送到已知已失败的 Pod。

[root@master ~]# vim mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    name: mysql
    role: service
  name: mysql-service
spec:
  ports:
    - port: 3306
      targetPort: 3306
      nodePort: 30964
  type: NodePort
  selector:
    mysql-service: "true"
    name: mysql

[root@master ~]# kubectl apply -f mysql-service.yaml 
service/mysql-service created

[root@master ~]# kubectl get service
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes      ClusterIP   10.244.0.1       <none>        443/TCP          29h
mysql-service   NodePort    10.244.140.208   <none>        3306:30964/TCP   19s
service-hello   NodePort    10.244.143.216   <none>        80:31111/TCP     20h

5.3、IPVS 代理模式

特性状态: Kubernetes v1.11 [stable]

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

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

IPVS 提供了更多选项来平衡后端 Pod 的流量。 这些是:

  • rr:轮替(Round-Robin)
  • lc:最少链接(Least Connection),即打开链接数量最少者优先
  • dh:目标地址哈希(Destination Hashing)
  • sh:源地址哈希(Source Hashing)
  • sed:最短预期延迟(Shortest Expected Delay)
  • nq:从不排队(Never Queue)

5.3.1、kube-proxy配置 ipvs模式(所有节点)

1)加载ip_vs相关内核模块

[root@master ~]# modprobe -- ip_vs
[root@master ~]# modprobe -- ip_vs_sh
[root@master ~]# modprobe -- ip_vs_rr
[root@master ~]# modprobe -- ip_vs_wrr
[root@master ~]# modprobe -- nf_conntrack_ipv4

[root@master ~]# lsmod | grep ip_vs
ip_vs_sh               12688  0 
ip_vs_wrr              12697  0 
ip_vs_rr               12600  0 
ip_vs                 141432  6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack          133053  10 ip_vs,nf_nat,nf_nat_ipv4,nf_nat_ipv6,xt_conntrack,nf_nat_masquerade_ipv4,nf_nat_masquerade_ipv6,nf_conntrack_netlink,nf_conntrack_ipv4,nf_conntrack_ipv6
libcrc32c              12644  4 xfs,ip_vs,nf_nat,nf_conntrack

2)安装ipvsadm工具

[root@master ~]# yum -y install ipset ipvsadm

3)查看kube-proxy 

[root@master ~]# kubectl get pod -n kube-system | grep kube-proxy
kube-proxy-2f9x9                 1/1     Running   2 (18h ago)   29h
kube-proxy-8xjdp                 1/1     Running   2 (18h ago)   29h
kube-proxy-f425c                 1/1     Running   2 (18h ago)   29h
kube-proxy-t4pq8                 1/1     Running   2 (18h ago)   29h

3)编辑kube-proxy配置文件,mode修改成ipvs 


[root@master ~]# kubectl edit configmaps -n kube-system kube-proxy 
mode: "ipvs" 

[root@master ~]# kubectl get pod -n kube-system | grep kube-proxy |awk '{system("kubectl delete pod "$1" -n kube-system")}'
pod "kube-proxy-2f9x9" deleted
pod "kube-proxy-8xjdp" deleted
pod "kube-proxy-f425c" deleted
pod "kube-proxy-t4pq8" deleted

[root@master ~]# kubectl get pod -n kube-system | grep kube-proxy       # 再次查看
kube-proxy-5p5tb                 1/1     Running   0              10s
kube-proxy-c2vxk                 1/1     Running   0              13s
kube-proxy-z2hjm                 1/1     Running   0              12s
kube-proxy-zgjc7                 1/1     Running   0              9s

[root@master ~]# ipvsadm -Ln

 说明:要在 IPVS 模式下运行 kube-proxy,必须在启动 kube-proxy 之前使 IPVS 在节点上可用。当 kube-proxy 以 IPVS 代理模式启动时,它将验证 IPVS 内核模块是否可用。 如果未检测到 IPVS 内核模块,则 kube-proxy 将退回到以 iptables 代理模式运行。

6、服务状态

        从数据层面看状态,数据的状态往往受2个维度有关,一是与时间相关或者顺序相关的,不同的操作顺序可能导致同一个时间点上的数据状态大于1个,二是与数据的副本状态相关的。也就是数据的位置,数据落在多个副本上,可能出现多种数据状态的组合。

        从服务层面看,服务层面的状态取决于实例是单独维护数据还是共享数据,或者说是否存在多个数据闭环让数据的流向产生了多条路径。有状态的服务往往比较难进行水平拓展,在现在容器盛行的环境,把服务设计成无状态的更加高效,即便是有状态的服务,也要将状态内敛在系统的某个范围,比如分布式的存储,对于业务服务,我不需要关心数据在多个副本的状态,数据的状态由分布式存储这个服务本身解决。

差异维度有状态服务无状态服务
服务本身服务本身依赖或者存在局部的状态数据,这些数据需要自身持久化或者可以通过其他节点恢复。服务不依赖自身的状态,实例的状态数据可以维护在内存中。
节   点一个请求只能被某个节点(或者同等状态下的节点)处理。任何一个请求都可以被任意一个实例处理。
数据状态存储状态数据,实例的拓展需要整个系统参与状态的迁移。不存储状态数据,实例可以水平拓展,通过负载均衡将请求分发到各个节点。
系统中在一个封闭的系统中,存在多个数据闭环,需要考虑这些闭环的数据一致性问题。在一个封闭的系统中,只存在一个数据闭环。
架构中通常存在于分布式架构中。通常存在于单体架构的集群中。
相关资源

statefulSet,由于是有状态的服务,所以每个pod都有特定的名称和网络标识。比如pod名是由statefulSet名+有序的数字组成(0、1、2..)

ReplicaSet、ReplicationController、Deployment等,由于是无状态服务,所以这些控制器创建的pod序号都是随机值。并且在缩容的时候并不会明确缩容某一个pod,而是随机的,因为所有实例得到的返回值都是一样,所以缩容任何一个pod都可以。

相关服务有状态服务 可以说是 需要数据存储功能的服务、或者指多线程类型的服务,队列等。(mysql数据库、kafka、zookeeper等)多个实例可以共享相同的持久化数据。例如:nginx实例,tomcat实例等

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值