码迷,mamicode.com
首页 > 其他好文 > 详细

k8s四层负载均衡--Service

时间:2021-07-12 17:50:41      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:进入   线性   x86   工作原理   main   cas   mariadb   ons   因此   

k8s四层负载均衡--Service

一、四层负载均衡Service概述

1.1、为什么要有Service

在kubernetes中,Pod是有生命周期的,如果Pod重启它的IP很有可能会发生变化。如果我们的服务都是将Pod的IP地址写死,Pod挂掉或者重启,和刚才重启的pod相关联的其他服务将会找不到它所关联的Pod,为了解决这个问题,在kubernetes中定义了service资源对象,Service 定义了一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service是一组Pod的逻辑集合,这一组Pod能够被Service访问到,通常是通过Label Selector实现的。

技术图片

1)pod ip经常变化,service是pod的代理,我们客户端访问,只需要访问service,就会把请求代理到Pod

2)pod ip在k8s集群之外无法访问,所以需要创建service,这个service可以在k8s集群外访问的。

1.2、Service概述

service是一个固定接入层,客户端可以通过访问service的ip和端口访问到service关联的后端pod,这个service工作依赖于在kubernetes集群之上部署的一个附件,就是kubernetes的dns服务(不同kubernetes版本的dns默认使用的也是不一样的,1.11之前的版本使用的是kubeDNs,较新的版本使用的是coredns),service的名称解析是依赖于dns附件的,因此在部署完k8s之后需要再部署dns附件,kubernetes要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel,calico等)。每个K8s节点上都有一个组件叫做kube-proxy,kube-proxy这个组件将始终监视着apiserver中有关service资源的变动信息,需要跟master之上的apiserver交互,随时连接到apiserver上获取任何一个与service资源相关的资源变动状态,这种是通过kubernetes中固有的一种请求方法watch(监视)来实现的,一旦有service资源的内容发生变动(如创建,删除),kube-proxy都会将它转化成当前节点之上的能够实现service资源调度,把我们请求调度到后端特定的pod资源之上的规则,这个规则可能是iptables,也可能是ipvs,取决于service的实现方式。

1.3、Service工作原理

k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,据此创建与Service同名的endpoint对象,当Pod 地址发生变化时,endpoint也会随之发生变化,service接收前端client请求的时候,就会通过endpoint,找到转发到哪个Pod进行访问的地址。(至于转发到哪个节点的Pod,由负载均衡kube-proxy决定)

[root@k8s-master1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   37h
[root@k8s-master1 ~]# kubectl get endpoints
NAME         ENDPOINTS             AGE
kubernetes   192.168.40.180:6443   37h

1.4、kubernets中三类IP地址

1)Node Network(节点网络):物理节点或者虚拟节点的网络,如eth0接口上的网路地址

[root@k8s-master1 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:29:52:bf:68 brd ff:ff:ff:ff:ff:ff
    inet 192.168.40.180/24 brd 192.168.40.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe52:bf68/64 scope link 
       valid_lft forever preferred_lft forever

2)Pod network(pod 网络),创建的Pod具有的IP地址

[root@k8s-master1 ~]# kubectl get pods -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
myapp-v1-75fb478d6c-89w27   1/1     Running   0          10s   10.244.36.105   k8s-node1   <none>           <none>
myapp-v1-75fb478d6c-dg4bh   1/1     Running   0          10s   10.244.36.104   k8s-node1   <none>           <none>

# Node Network和Pod network这两种网络地址是我们实实在在配置的,其中节点网络地址是配置在节点接口之上,而pod网络地址是配置在pod资源之上的,因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的

3)Cluster Network(集群地址,也称为service network),这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在service的规则当中

[root@k8s-master1 ~]# kubectl get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        
kubernetes         ClusterIP   10.96.0.1           <none>        443/TCP

1.5、Service的四种类型

1.5.1、ExternalName

适用于k8s集群内部容器访问外部资源,它没有selector,也没有定义任何的端口和Endpoint。以下Service 定义的是将prod名称空间中的my-service服务映射到my.database.example.com

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com
  
# 当查询主机 my-service.prod.svc.cluster.local 时,DNS将返回值为my.database.example.com的CNAME记录
# service的FQDN是: <service_name>.<namespace>.svc.cluster.local

1.5.2、ClusterIP

# 通过k8s集群内部IP暴露服务,选择该值,服务只能够在集群内部访问,这也是默认的ServiceType。
[root@k8s-master1 ~]# kubectl get svc -n kube-system 
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                  AGE
kube-dns         ClusterIP   10.96.0.10    <none>        53/UDP,53/TCP,9153/TCP   37h
metrics-server   ClusterIP   10.100.13.0   <none>        443/TCP                  35h

1.5.3、NodePort

通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务。通过请求<NodeIP>:<NodePort>可以把请求代理到内部的pod。

访问流量:Client ==> NodeIP:NodePort ==> Service Ip:ServicePort ==> PodIP:ContainerPort

1.5.4、LoadBalancer

使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP 服务

二、Service创建

2.1、创建类型ClusterIP的Service

1)创建deployment

[root@k8s-master1 ~]# cat pod_test.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80  #pod中的容器需要暴露的端口

# 更新资源清单文件
[root@k8s-master1 ~]# kubectl apply -f pod_test.yaml

# 查看刚才创建的Pod ip地址
[root@k8s-master1 ~]# kubectl get pods -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
my-nginx-69f769d56f-gr5jg   1/1     Running   0          6s    10.244.169.149   k8s-node2   <none>           <none>
my-nginx-69f769d56f-h52dw   1/1     Running   0          6s    10.244.36.108    k8s-node1   <none>           <none>
[root@k8s-master1 ~]# kubectl get pods --show-labels
NAME                        READY   STATUS    RESTARTS   AGE   LABELS
my-nginx-69f769d56f-gr5jg   1/1     Running   0          52s   pod-template-hash=69f769d56f,run=my-nginx
my-nginx-69f769d56f-h52dw   1/1     Running   0          52s   pod-template-hash=69f769d56f,run=my-nginx

2)创建service

[root@k8s-master1 ~]# cat service_test.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80   #service的端口,暴露给k8s集群内部服务访问
    protocol: TCP
    targetPort: 80    #pod容器中定义的端口
  selector:
    run: my-nginx  #选择拥有run=my-nginx标签的pod
    
[root@k8s-master1 ~]# kubectl apply -f service_test.yaml
service/my-nginx created
[root@k8s-master1 ~]# kubectl get svc -l run=my-nginx
NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.107.97.200   <none>        80/TCP    14s
[root@k8s-master1 ~]# curl 10.107.97.200
<!DOCTYPE html>
<html>
<title>Welcome to nginx!</title>
</html>
.....

# 查看service详细信息
[root@k8s-master1 ~]# kubectl describe svc my-nginx
Name:              my-nginx
Namespace:         default
Labels:            run=my-nginx
Annotations:       <none>
Selector:          run=my-nginx
Type:              ClusterIP
IP Families:       <none>
IP:                10.107.97.200
IPs:               10.107.97.200
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.169.149:80,10.244.36.108:80
Session Affinity:  None
Events:            <none>

# 查看endpoints详细信息
[root@k8s-master1 ~]# kubectl get ep my-nginx
NAME       ENDPOINTS                            AGE
my-nginx   10.244.169.149:80,10.244.36.108:80   2m21s

# service可以对外提供统一固定的ip地址,并将请求重定向至集群中的pod。其中“将请求重定向至集群中的pod”就是通过endpoint与selector协同工作实现。selector是用于选择pod,由selector选择出来的pod的ip地址和端口号,将会被记录在endpoint中。endpoint便记录了所有pod的ip地址和端口号。当一个请求访问到service的ip地址时,就会从endpoint中选择出一个ip地址和端口号,然后将请求重定向至pod中。具体把请求代理到哪个pod,需要的就是kube-proxy的轮询实现的。service不会直接到pod,service是直接到endpoint资源,就是地址加端口,再由endpoint再关联到pod。

3)service解析FQDN

# service解析
service只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群dns中动态添加一个资源记录,添加完成后我们就可以解析了
资源记录格式是:
    SVC_NAME.NS_NAME.DOMAIN.LTD.
    服务名.命名空间.域名后缀
集群默认的域名后缀是svc.cluster.local.
就像我们上面创建的my-nginx这个服务,它的完整名称解析就是:my-nginx.default.svc.cluster.local
[root@k8s-master1 ~]# kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
my-nginx-69f769d56f-gr5jg   1/1     Running   0          9m55s
my-nginx-69f769d56f-h52dw   1/1     Running   0          9m55s
[root@k8s-master1 ~]# kubectl exec -it my-nginx-69f769d56f-gr5jg -- /bin/bash
root@my-nginx-69f769d56f-gr5jg:/# cat /etc/resolv.conf                   
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
root@my-nginx-69f769d56f-gr5jg:/# curl my-nginx.default.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
root@my-nginx-69f769d56f-gr5jg:/#

2.2、创建类型NodePort的Service

1)创建deployment

# 创建一个pod资源
[root@k8s-master1 ~]# cat pod_nodeport.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-nodeport
spec:
  selector:
    matchLabels:
      run: my-nginx-nodeport
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx-nodeport
    spec:
      containers:
      - name: my-nginx-nodeport-container
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

# 更新资源清单文件
[root@k8s-master1 ~]# kubectl apply -f pod_nodeport.yaml 
deployment.apps/my-nginx-nodeport created
# 查看pod是否创建成功
[root@k8s-master1 ~]# kubectl get pods -l run=my-nginx-nodeport
NAME                                 READY   STATUS    RESTARTS   AGE
my-nginx-nodeport-649c945f85-7nngb   1/1     Running   0          9s
my-nginx-nodeport-649c945f85-cfgjk   1/1     Running   0          10s

2)创建service

[root@k8s-master1 ~]# cat service_nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-nodeport
  labels:
    run: my-nginx-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30380
  selector:
    run: my-nginx-nodeport

# 更新资源清单文件
[root@k8s-master1 ~]# kubectl apply -f service_nodeport.yaml 
service/my-nginx-nodeport created

# 查看刚才创建的service
[root@k8s-master1 ~]# kubectl get svc -l run=my-nginx-nodeport
NAME                TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
my-nginx-nodeport   NodePort   10.109.246.208   <none>        80:30380/TCP   11s

# 查看详细信息
[root@k8s-master1 ~]# kubectl describe svc my-nginx-nodeport 
Name:                     my-nginx-nodeport
Namespace:                default
Labels:                   run=my-nginx-nodeport
Annotations:              <none>
Selector:                 run=my-nginx-nodeport
Type:                     NodePort
IP Families:              <none>
IP:                       10.109.246.208	# 只能在集群内访问
IPs:                      10.109.246.208
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30380/TCP
Endpoints:                10.244.36.109:80,10.244.36.110:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

# 在集群外访问:http://nodeIP:30380
# 服务请求走向:Client-node ip:30380 -> service ip:80 -> pod ip:container port

技术图片

2.3、创建类型ExternalName的Service

应用场景:跨名称空间访问
需求:default名称空间下的client 服务想要访问nginx-ns名称空间下的nginx-svc服务

1)创建client的deployment及service

[root@k8s-master1 ~]# cat client.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: client
spec: 
  replicas: 1
  selector:
    matchLabels:
      app: busybox
  template:
   metadata:
    labels:
      app: busybox
   spec:
     containers:
     - name: busybox
       image: busybox
       command: ["/bin/sh","-c","sleep 36000"]

[root@k8s-master1 ~]# kubectl apply -f client.yaml

[root@k8s-master1 ~]# cat client_svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: client-svc
spec:
  type: ExternalName
  externalName: nginx-svc.nginx-ns.svc.cluster.local
  ports:
  - name: http
    port: 80
    targetPort: 80

# 该文件中指定了到 nginx-svc 的软链,让使用者感觉就好像调用自己命名空间的服务一样。
# 查看pod是否正常运行
[root@k8s-master1 ~]# kubectl get pods
NAME                                 READY   STATUS    RESTARTS   AGE
client-76b6556d97-zx77m              1/1     Running   0          90s      
[root@k8s-master1 ~]# kubectl apply -f client_svc.yaml
[root@k8s-master1 ~]# kubectl get svc
NAME                TYPE           CLUSTER-IP       EXTERNAL-IP                            PORT(S)        AGE
client-svc          ExternalName   <none>           nginx-svc.nginx-ns.svc.cluster.local   80/TCP         4s
[root@k8s-master1 ~]# kubectl describe svc client-svc
Name:              client-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ExternalName
IP Families:       <none>
IP:                
IPs:               <none>
External Name:     nginx-svc.nginx-ns.svc.cluster.local
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

2)创建server的deployment及service

[root@k8s-master1 ~]# kubectl create ns nginx-ns
[root@k8s-master1 ~]# cat server_nginx.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: nginx-ns
spec: 
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
   metadata:
    labels:
      app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx
       imagePullPolicy: IfNotPresent
       
[root@k8s-master1 ~]# kubectl apply -f server_nginx.yaml
#查看pod是否创建成功
[root@k8s-master1 ~]# kubectl get pods -n nginx-ns
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7cf7d6dbc8-slmv4   1/1     Running   0          8s

[root@k8s-master1 ~]# cat nginx_svc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: nginx-ns
spec:
  selector:
    app: nginx
  ports:
   - name: http
     protocol: TCP
     port: 80
     targetPort: 80
[root@k8s-master1 exter]# kubectl apply -f nginx_svc.yaml
[root@k8s-master1 ~]# kubectl get svc -n nginx-ns 
NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx-svc   ClusterIP   10.103.192.106   <none>        80/TCP    46s

3)访问测试

# 登录到client pod,访问的结果一样
[root@k8s-master1 ~]# kubectl exec -it  client-76b6556d97-zx77m -- /bin/sh
/ # wget -q -O - client-svc.default.svc.cluster.local
/ # wget -q -O - nginx-svc.nginx-ns.svc.cluster.local

2.4、自定义endpoint实现映射外部服务

需求:k8s集群引用外部的mysql数据库

# 1.在k8s-node2上安装mysql数据库:
[root@k8s-node2 ~]# yum install mariadb-server.x86_64 -y
[root@k8s-node2 ~]# systemctl start mariadb
[root@k8s-node2 ~]# netstat -lntp|grep 3306
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      36719/mysqld 

# 2.创建mysql_service
[root@k8s-master1 mysql]# cat mysql_service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  type: ClusterIP
  ports:
  - port: 3306
[root@k8s-master1 mysql]# kubectl apply -f mysql_service.yaml 
service/mysql created
[root@k8s-master1 mysql]# kubectl get svc | grep mysql
mysql               ClusterIP      10.103.151.40    <none>                                 3306/TCP       7s 

root@k8s-master1 mysql]# kubectl describe svc mysql
[root@k8s-master1 mysql]# kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Families:       <none>
IP:                10.103.151.40
IPs:               10.103.151.40
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         <none>	# 还没有endpoint
Session Affinity:  None
Events:            <none>

# 3.创建自定义endpoint
[root@k8s-master1 mysql]# cat mysql_endpoint.yaml 
apiVersion: v1
kind: Endpoints
metadata:
  name: mysql
subsets:
- addresses:
  - ip: 192.168.40.182
  ports:
  - port: 3306

[root@k8s-master1 mysql]# kubectl apply -f mysql_endpoint.yaml 
endpoints/mysql created

[root@k8s-master1 mysql]# kubectl describe svc mysql
Name:              mysql
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Families:       <none>
IP:                10.103.151.40
IPs:               10.103.151.40
Port:              <unset>  3306/TCP
TargetPort:        3306/TCP
Endpoints:         192.168.40.182:3306	# 自定义的endpoint
Session Affinity:  None
Events:            <none>
# 上面配置就是将外部IP地址和服务引入到k8s集群内部,由service作为一个代理来达到能够访问外部服务的目的。

三、Service代理:kube-proxy组件

3.1、kube-proxy组件介绍

Kubernetes service只是把应用对外提供服务的方式做了抽象,真正的应用跑在Pod中的container里,我们的请求转到kubernetes nodes对应的nodePort上,那么nodePort上的请求是如何进一步转到提供后台服务的Pod的呢? 就是通过kube-proxy实现的:

kube-proxy部署在k8s的每一个Node节点上,是Kubernetes的核心组件,我们创建一个 service 的时候,kube-proxy 会在iptables中追加一些规则,为我们实现路由与负载均衡的功能。在k8s1.8之前,kube-proxy默认使用的是iptables模式,通过各个node节点上的iptables规则来实现service的负载均衡,但是随着service数量的增大,iptables模式由于线性查找匹配、全量更新等特点,其性能会显著下降。从k8s的1.8版本开始,kube-proxy引入了IPVS模式,IPVS模式与iptables同样基于Netfilter,但是采用的hash表,因此当service数量达到一定规模时,hash查表的速度优势就会显现出来,从而提高service的服务性能。

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

1、kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。

2、kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也可以称为是Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。

3.2、kube-proxy三种工作模式

3.2.1、Userspace方式

技术图片

Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,由service iptables将请求转给各个节点中的Server Pod。

这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在Kubernetes 1.1版本之前,userspace是默认的代理模型。

3.2.2、iptables方式

技术图片

客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折

iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型

3.2.3、ipvs方式

技术图片

Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项,例如:

rr:轮询调度
lc:最小连接数
dh:目标哈希
sh:源哈希
sed:最短期望延迟
nq:不排队调度

3.3、kube-proxy的watch机制

如果某个服务后端pod发生变化,标签选择器适应的pod又多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etcd中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理

技术图片

以上不论哪种,kube-proxy都通过watch的方式监控着apiserver写入etcd中关于Pod的最新状态信息,它一旦检查到一个Pod资源被删除了或新建了,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。

自k8s1.11以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则

3.4、kube-proxy生成的iptables规则分析

3.4.1、ClusterIp类型Service的iptables规则分析

在k8s创建的service,虽然有ip地址,但是service的ip是虚拟的,不存在物理机上的,是在iptables或者ipvs规则里的

# 1.创建pod及clusterIp类型的service
[root@k8s-master1 ~]# cat pod_test.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80  #pod中的容器需要暴露的端口
[root@k8s-master1 ~]# cat service_test.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: ClusterIP
  ports:
  - port: 80   #service的端口,暴露给k8s集群内部服务访问
    protocol: TCP
    targetPort: 80    #pod容器中定义的端口
  selector:
    run: my-nginx  #选择拥有run=my-nginx标签的pod
    
[root@k8s-master1 ~]# kubectl apply -f pod_test.yaml 
deployment.apps/my-nginx created
[root@k8s-master1 ~]# kubectl apply -f service_test.yaml 
service/my-nginx created

# 2.查看svc和pod的ip
[root@k8s-master1 ~]# kubectl get svc -l run=my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.103.178.2   <none>        80/TCP    107s
[root@k8s-master1 ~]# kubectl get pods -l run=my-nginx -o wide
NAME                        READY   STATUS    RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
my-nginx-69f769d56f-drwh7   1/1     Running   0          2m10s   10.244.36.112   k8s-node1   <none>           <none>
my-nginx-69f769d56f-phqq8   1/1     Running   0          2m10s   10.244.36.113   k8s-node1   <none>           <none>

# 3.查看svc ip的iptables规则
[root@k8s-master1 ~]# iptables -t nat -L | grep 10.103.178.2
KUBE-MARK-MASQ  tcp  -- !10.244.0.0/16        10.103.178.2         /* default/my-nginx cluster IP */ tcp dpt:http
KUBE-SVC-L65ENXXZWWSAPRCR  tcp  --  anywhere             10.103.178.2         /* default/my-nginx cluster IP */ tcp dpt:http
[root@k8s-master1 ~]# iptables -t nat -L | grep KUBE-SVC-L65ENXXZWWSAPRCR
KUBE-SVC-L65ENXXZWWSAPRCR  tcp  --  anywhere             10.103.178.2         /* default/my-nginx cluster IP */ tcp dpt:http
Chain KUBE-SVC-L65ENXXZWWSAPRCR (1 references)

# 4、查看pod ip的iptables规则
[root@k8s-master1 ~]# iptables -t nat -L | grep 10.244.36.112
KUBE-MARK-MASQ  all  --  10.244.36.112        anywhere             /* default/my-nginx */
DNAT       tcp  --  anywhere             anywhere             /* default/my-nginx */ tcp to:10.244.36.112:80
[root@k8s-master1 ~]# iptables -t nat -L | grep 10.244.36.113
KUBE-MARK-MASQ  all  --  10.244.36.113        anywhere             /* default/my-nginx */
DNAT       tcp  --  anywhere             anywhere             /* default/my-nginx */ tcp to:10.244.36.113:80

总结:通过上面可以看到之前创建的service,会通过kube-proxy在iptables中生成一个规则,来实现流量路由,有一系列目标为 KUBE-SVC-xxx 链的规则,每条规则都会匹配某个目标 ip 与端口。也就是说访问某个 ip:port 的请求会由 KUBE-SVC-xxx 链来处理。这个目标 IP 其实就是service ip。

3.4.2、nodePort类型Service的iptables规则分析

# 1.创建pod及nodePort类型的service
[root@k8s-master1 ~]# cat pod_nodeport.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-nodeport
spec:
  selector:
    matchLabels:
      run: my-nginx-nodeport
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx-nodeport
    spec:
      containers:
      - name: my-nginx-nodeport-container
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
[root@k8s-master1 ~]# cat service_nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: my-nginx-nodeport
  labels:
    run: my-nginx-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30380
  selector:
    run: my-nginx-nodeport
    
[root@k8s-master1 ~]# kubectl apply -f pod_nodeport.yaml 
deployment.apps/my-nginx-nodeport created
[root@k8s-master1 ~]# kubectl apply -f service_nodeport.yaml
service/my-nginx-nodeport created

# 2.查看svc和pod的ip
[root@k8s-master1 ~]# kubectl get svc -l run=my-nginx-nodeport
NAME                TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
my-nginx-nodeport   NodePort   10.109.183.22   <none>        80:30380/TCP   41s
[root@k8s-master1 ~]# kubectl get pods -l  run=my-nginx-nodeport -o wide
NAME                                 READY   STATUS    RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
my-nginx-nodeport-649c945f85-4zk46   1/1     Running   0          9m27s   10.244.36.115   k8s-node1   <none>           <none>
my-nginx-nodeport-649c945f85-xnwks   1/1     Running   0          9m27s   10.244.36.114   k8s-node1   <none>           <none>

# 3.查看关于nodeport=30380的iptables规则
[root@k8s-master1 ~]# iptables -t nat -S | grep 30380
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q

# 4.查看KUBE-SVC-J5QV2XWG4FEBPH3Q链
[root@k8s-master1 ~]# iptables -t nat -S | grep KUBE-SVC-J5QV2XWG4FEBPH3Q
-N KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-SERVICES -d 10.109.183.22/32 -p tcp -m comment --comment "default/my-nginx-nodeport cluster IP" -m tcp --dport 80 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-EBCJC5WP2H42KJXA
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -j KUBE-SEP-BGGUKAMS6QUZEUGX

# 5、查看KUBE-SEP-EBCJC5WP2H42KJXA链及KUBE-SEP-BGGUKAMS6QUZEUGX
[root@k8s-master1 ~]# iptables -t nat -S | grep KUBE-SEP-EBCJC5WP2H42KJXA
-N KUBE-SEP-EBCJC5WP2H42KJXA
-A KUBE-SEP-EBCJC5WP2H42KJXA -s 10.244.36.114/32 -m comment --comment "default/my-nginx-nodeport" -j KUBE-MARK-MASQ
-A KUBE-SEP-EBCJC5WP2H42KJXA -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp -j DNAT --to-destination 10.244.36.114:80
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-EBCJC5WP2H42KJXA

[root@k8s-master1 ~]# iptables -t nat -S | grep KUBE-SEP-BGGUKAMS6QUZEUGX
-N KUBE-SEP-BGGUKAMS6QUZEUGX
-A KUBE-SEP-BGGUKAMS6QUZEUGX -s 10.244.36.115/32 -m comment --comment "default/my-nginx-nodeport" -j KUBE-MARK-MASQ
-A KUBE-SEP-BGGUKAMS6QUZEUGX -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp -j DNAT --to-destination 10.244.36.115:80
-A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -j KUBE-SEP-BGGUKAMS6QUZEUGX

四、Service服务发现:coredns组件

CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目以及工程师都会使用 CoreDNS 为集群提供服务发现的功能,Kubernetes 就在集群中使用 CoreDNS 解决服务发现的问题。 作为一个加入 CNCF(Cloud Native Computing Foundation)的服务, CoreDNS 的实现非常简单。

[root@k8s-master1 ~]# cat dig.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: dig
  namespace: default
spec:
  containers:
  - name: dig
    image:  xianchao/dig:latest
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

# 更新资源清单文件
[root@k8s-master1 ~]# kubectl apply -f dig.yaml 
pod/dig configured

# 查看默认名称空间的kubernetes服务
[root@k8s-master1 ~]# kubectl get svc | grep kubernetes
kubernetes          ClusterIP   10.96.0.1       <none>        443/TCP        43h
[root@k8s-master1 ~]# kubectl get pods -o wide
NAME                                 READY   STATUS    RESTARTS   AGE    IP              NODE        NOMINATED NODE   READINESS GATES
dig                                  1/1     Running   0          2m5s   10.244.36.116   k8s-node1   <none>           <none>

# 解析dns,如有以下返回说明dns安装成功
[root@k8s-master1 ~]# kubectl exec -it dig -- nslookup kubernetes
Server:		10.96.0.10
Address:	10.96.0.10#53
Name:	kubernetes.default.svc.cluster.local
Address: 10.96.0.1

[root@k8s-master1 ~]# kubectl exec -it dig -- cat /etc/resolv.conf 
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

k8s四层负载均衡--Service

标签:进入   线性   x86   工作原理   main   cas   mariadb   ons   因此   

原文地址:https://www.cnblogs.com/hujinzhong/p/14994030.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!