k8s中pod是有生命周期的,如果podIP发生变化,跟他向关联的服务就会找不到pod。

service就是为了解决这个问题,每个service和一个或一组pod绑定,可以理解为 service是pod的代理,service中记录着pod的IP,pod发生变化会通知service,我们访问service它会把请求转发给pod

service IP是逻辑存在的记录在iptables或ipvs中。

网络简述

每一个pod会生成一个根容器,根容器创建网络命名空间,在调用cni配置网络接口,使用cni扩展插件calico或flannel为容器分配IP 完成网络通讯

flannel: vxlan 等技术实现二层通讯。

k8s创建nginx pod k8s创建service_k8s创建nginx pod

通信流程:

  1. pod-a访问pod-b 因为两者IP不在同一个子网,首先数据会先到默认网关也就是cni010.64.0.1
  2. 节点上面的静态路由可以看出来10.64.1.0/24 是指向flannel.1的
  3. flannel.1 会使用vxlan协议把原始IP包加上目的mac地址封装成二层数据帧
  4. 原始vxlan数据帧无法在物理二层网络中通信,flannel.1 (linux 内核支持vxlan,此步骤由内核完成)会把数据帧封装成UDP报文经过物理网络发送到node-02
  5. node-02收到UDP报文中带有vxlan头信息,会转交给flannel.1解封装得到原始数据
  6. flannel.1 根据直连路由转发到cni0 上面
  7. cni0 转发给pod-2

calico: 三层路由网络,支持复杂的网络策略,每一个节点类似一个路由

k8s创建nginx pod k8s创建service_IP_02

 k8s集群中有三类IP

Node NetWork(节点网络)

Pod NetWork(pod网络)  这两种网络是真是存在的

Cluster NetWork(集群网络) 存在于逻辑上的地址,只出现在Service的规则中。

Service 资源清单:

 kubectl explain service
 

apiVersion: v1
kind: Service
metadata:
  # svc 名称
  name: my-service
spec:
  # 通过选择器,绑定后端pod
  selector:
    dep: app1-pod
  ports:
    # svc port
  - port: 80
    # 绑定的pod端口
    targetPort: 80

其他常用字段:

allocateLoadBalancerNodePorts <boolean>
   clusterIP <string>
#动态分配的地址,也可以自己在创建的时候指定,创建之后就改不了了
   clusterIPs <[]string>
   externalIPs <[]string>
   externalName <string>
   externalTrafficPolicy <string>
   healthCheckNodePort <integer>
   ipFamilies <[]string>
   ipFamilyPolicy <string>
   loadBalancerIP <string>
   loadBalancerSourceRanges <[]string>
   publishNotReadyAddresses <boolean>
#通过标签选择器选择关联的pod有哪些
   sessionAffinity <string>
   sessionAffinityConfig <Object>
#service在实现负载均衡的时候还支持sessionAffinity,sessionAffinity

什么意思?会话联系,默认是none,随机调度的(基于iptables规则调度的);如果我们定义sessionAffinity的client ip,那就表示把来自同一客户端的IP请求调度到同一个pod上

   topologyKeys <[]string>

#定义service的类型

service的四种类型

ExternalName:适用于集群中不同命名空间中的访问。

示例:

apiVersion: v1
kind: Service
metadata:
  name: my-exter
  namespace: dev
spec:
  type: ExternalName
  # 其他命名空间中FQDN(完全合格域名)
  externalName: my-service.default.svc.cluster.local
---
apiVersion: v1
kind: Pod
metadata:
  name: test-pod5
  namespace: dev
spec:
  containers:
  - name: bixbuy
    # 最新版镜像与k8s不兼容会出现解析失败情况
    image: busybox:1.28.4
    command: ["/bin/sh","-c","sleep 36000"]

解析命令 nslookup   svc名

k8s创建nginx pod k8s创建service_k8s创建nginx pod_03

ClusterIP:通过k8s集群内部IP暴露服务,选择该值,服务只能够在集群内部访问(默认)

NodePort:通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    dep: app1-pod
  ports:
  - port: 80
    # 外部端口,创建集群时的端口范围
    nodePort: 30078
    targetPort: 80
  # 类型
  type: NodePort

LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。

service只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群dns中动态添加一个资源记录,添加完成后我们就可以解析了,资源记录格式是:

SVC_NAME.NS_NAME.DOMAIN.LTD.

服务名.命名空间.域名后缀

集群默认的域名后缀是svc.cluster.local.

映射外部服务示例:

创建svc时会创建个同名的Endpoints完成绑定操作,通过操作Endpoints实现绑定外部服务

apiVersion: v1
kind: Service
metadata:
  name: mysql-svc
spec:
  # 定义svc,只指定开放的端口
  ports:
  - port: 3306

此时查看详情发现没有绑定Endpoints

k8s创建nginx pod k8s创建service_docker_04

创建Endpoints

apiVersion: v1
kind: Endpoints
metadata:
  name: mysql-svc
subsets:
- addresses:
  - ip: 192.168.1.23
  ports:
  - port: 3306

k8s创建nginx pod k8s创建service_docker_05

kube-proxy

service 只是把应用对外提供服务的方式做了抽象,应用是跑在容器中,我们的请求是需要发送到个个node上的。kube-proxy部署在k8s的没一个节点上,是k8s的核心组件,我们创建一个service时会在iptables上追加一些规则,实现路由及负载均衡等功能。k8s 1.8之前默认使用iptables模式,但随着service越多,规则链的数量越多,其性能会明显下降。之后引入了ipvs规则来采用hash表。加快查询速度。小集群还是选择iptables

一组pod通过标签绑定到一个svc(Cluster  IP), 这个 IP 是个虚拟IP 由iptables管理通过Endpoints实现的,集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod.

三种工作方式:

Userspace方式

client 请求发送到 server iptables,  iptables转发请求给kube-proxy接口,kube-proxy处理完毕把请求发送给指定的pod,再把请求发送给iptabels 由iptabels在转发给各个节点的pod

iptables方式

client 直接请求本地server Ip 根据iptables 分发到个个节点的pod(nat转发)

ipvs方式

与iptables类似,ipvs为负载均衡算法提供了更多选项,例如:

rr:轮询调度

lc:最小连接数

dh:目标哈希

sh:源哈希

sed:最短期望延迟

nq:不排队调度

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