原本觉得一篇就能搞定,仍是低估了本身的废话,好吧,只能经过两篇文章向你们介绍K8s核心原理。node
kubernetes API server的和核心功能是提供了kubernetes各种资源对象(pod、RC 、service等)的增、删、改、查以及watch等HTTP Rest接口,是整个系统的数据总线和数据中心。有时候咱们使用kubectl建立或者查看pod等资源的时候,发现没有反应,可能就是你的kube-apiservice服务异常退出致使的。
Kubernetes API server经过一个名为kube-apiservice的进程提供服务,该进程运行与master节点上。默认状况下该进程的端口是本机的8080提供restful服务。(注意若是是HTTPS,则是6443端口)。
接下来的一些操做,介绍一些如何经过rest 与kubernetes API server交互,这有便于后各k8s各个组件之间通讯的理解:mysql
[root@zy ~]# kubectl cluster-info #查看主节点信息
[root@zy ~]# curl localhost:8080/api #查看kubernetes API的版本信息
[root@zy ~]# curl localhost:8080/api #查看kubernetes API支持的全部的资源对象
固然咱们也能够访问具体的资源web
[root@zy ~]# curl localhost:8080/api/v1/pods [root@zy ~]# curl localhost:8080/api/v1/services [root@zy ~]# curl localhost:8080/api/v1/replicationcontrollers
当咱们在运行kubectl get svc时会发现:
有一个以上话红框的服务,这个是什么呢,原来为了让pod中的进程可以知道kubernetes API server的访问地址,kubernetes API server自己也是一个service,名字就叫“kubernetes”,而且他的cluster IP 就是cluster IP地址池例的第一个地址,另外它服务的端口就是443。算法
kubernetes API server还提供了一类很特殊的rest接口—proxy接口,这个结构就是代理REST请求,即kubernetes API server把收到的rest请求转发到某个node上的kubelet守护进程的rest端口上,由该kubelet进程负责相应。
举例:sql
masterIP:8080/api/v1/proxy/nodes/{node_name}/pods #某个节点下全部pod信息 masterIP:8080/api/v1/proxy/nodes/{node_name}/stats #某个节点内物理资源的统计信息 masterIP:8080/api/v1/proxy/nodes/{node_name}/spec #某个节点的概要信息
#接下来讲一下比较重要的pod的相关接口docker
masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name}/{path:*} #访问pod的某个服务接口 masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name} #访问pod
假如这里有一个名为myweb的Tomcat的pod
咱们在浏览器中输入masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/myweb就能访问到该pod的http服务了。
若是这里是一个几个web的pod组成的service的话:后端
masterIP:8080/api/v1/proxy/namespaces/{namespace}/services/{service_name}
就能访问到其下面的服务,固然最终会经过kube-proxy被定位到相应的pod下。api
Kubernetes API Server做为集群的和核心,负责集群各功能模块之间的通讯。集群内的各个功能模块经过API server将信息存入etcd,一样的想要获取和操做这些数据时,也是经过API Server的REST接口(GET、LIST、WATCH)来实现,从而实现各个模块之间的交互。
接下来小编经过一张图,简单介绍一下几种典型的交互场景:浏览器
介绍:Controller Manager做为集群内部管理控制中心,负责集群内的Node、Pod副本、EndPoint、命名空间、服务帐号、资源定额等管理。当某个Node意外宕机了,Controller Manager会及时发现此故障并执行自动修复流程,确保集群始终处于预期的工做状态。
由上图所示,Controller Manager中包含不少个controller,每一种controller都负责一种具体的控制流程,而Controller Manager正是这些controller的核心管理者。通常来讲,智能系统和自动系统都被称为一个“操纵系统”的机构来不断修正系统的工做状态。在kubernetes集群中,每一个controller都有这样一个“操纵系统”,他们经过API Server提供的接口实时监控整个集群里的每个资源对象的当前状态,当发生各类故障致使系统状态发生变化,这些controller会尝试将系统从“现有装态”修正到“指望状态”。
接下来小编会介绍一些比较重要的Controller。缓存
在介绍replication controller时小编要强调一点的是,千万不要把资源对象的那个RC和这个replication controller弄混淆了,咱们这里介绍的replication controller是副本的控制器,RC只是一个资源对象,上层是replication controlle管理各个RC。(这里咱们统一的将replication controller叫副本的控制器,资源对象RC叫RC)。
副本的控制器的核心做用是确保任何使用集群中的一个RC所关联的Pod副本数量保持预设的值。若是发现Pod副本数超过预设的值,则副本的控制器会销毁一些Pod的副本,反之则建立一些新的Pod的副本以达到目标值。值得注意的是只有当Pod的重启策略是always时,副本的控制器才会管理该pod的操做。一般状况下,pod对象被成功的建立以后不会消失,惟一例外的是当pod处于success或者failed状态的时间过长(超时时间能够设定),该pod会被系统自动回收,管理该pod的副本的控制器将在其余的工做节点上从新建立、启动该pod。
RC中的Pod模板就像一个模具,模具制做出来的东西一旦离开模具,两者将毫无关系,一旦pod建立,不管模板如何变化都不会影响到已经建立的pod,而且删除一个RC 不会影响它所建立出来的Pod,固然若是想在RC控制下,删除全部的Pod,须要将RC中设置的pod的副本数该为0,这样才会自动删除全部的Pod。
replication controller(副本的控制器)的职责:
- 确保当前管理的Pod的数量为预设值
- 经过调用RC的spec.replicas属性实现系统扩容和缩容
- 经过改变RC中的Pod的模板中的image,来实现系统的滚动升级
replication controller(副本的控制器)的使用场景 :
- 从新调度:不管是否有节点宕机,仍是pod意外死亡,RC均可以保证本身所管理的正在运行Pod的数量为预设值
- 弹性伸缩:实现集群的扩容和缩容(根据集群的可用资源和负载压力)
- 滚动升级:应用服务升级新的版本,而且保证整个升级过程,应用服务仍可对外提供服务。
Kubelet进程在启动时会经过API Server注册自身的节点信息,并定时的向API Server汇报状态信息,API Server在接受到这些信息后,将这些信息更新到etcd中。Etcd中存储的节点信息包括:节点的健康状态、节点资源、节点名称、节点地址信息、操做系统版本、docker版本、kubelet版本等。而节点的健康状态有三种:就绪(True)、未就绪(False)、未知(Unknown)。
接下来小编经过图来介绍Node Controller的核心工做流程:
具体步骤
- 若是controller manager在启动时设置了--cluster-cidr,那么为每个没有设置spec.PodCIDR的节点生成一个CIDR地址,并用该地址设置节点的spec.PodCIDR属性。
- 逐个读取节点信息,此时node controller中有一个nodestatusMap,里面存储了信息,与新发送过来的节点信息作比较,并更新nodestatusMap中的节点信息。Kubelet发送过来的节点信息,有三种状况:未发送、发送但节点信息未变化、发送而且节点信息变化。此时node controller根据发送的节点信息,更新nodestatusMap,若是判断出在某段时间内没有接受到某个节点的信息,则设置节点状态为“未知”。
- 最后,将未就绪状态的节点加入到待删除队列中,待删除后,经过API Server将etcd中该节点的信息删除。若是节点为就绪状态,那么就向etcd中同步该节点信息。
Kubernetes提供了资源配额管理(resourceQuota controller)这里高级功能,资源配置管理确保了指定的资源对象在任什么时候候都不会超量占用系统物理资源,避免了因为某些业务进程的设计或者实现的缺陷致使整个系统运行紊乱设置意外宕机,对整个集群的稳定性有着相当重要的做用。
目前kubernetes支持以下三个层次的资源配额管理:
- 容器级别:对CPU 和 memory的限制
- Pod级别:能够对一个pod内全部容器的可用资源进行限制
- Namespace级别:为namespace(多租户)级别的资源限制,其中限制的资源包括:
△ Pod数量
△ RC数量
△ Service数量
△ ResourceQuota数量
△ Secret数量
△ 可持有的PV数量
Kubernetes的配额管理是经过admission control(准入控制)来控制的。admission control当前提供了两种方式的配额约束,分别是limitRanger和resourceQuota。其中limitRanger做用于pod和容器上。ResourceQuota做用于namespace上,用于限定一个namespace里的各种资源的使用总额。
Kubernetes的配额管理是经过admission control(准入控制)来控制的。admission control当前提供了两种方式的配额约束,分别是limitRanger和resourceQuota。其中limitRanger做用于pod和容器上。ResourceQuota做用于namespace上,用于限定一个namespace里的各种资源的使用总额。
从上图中,咱们能够看出,大概有三条路线,resourceQuota controller在这三条路线中都起着重要的做用:
用户经过API Server能够建立新的namespace并保存在etcd中,namespace controller定时经过API Server读取这些namespace信息。若是namespace被API标记为优雅删除(经过设置删除周期),则将该namespace的状态设置为“terminating”并保存到etcd中。同时namespace controller删除该namespace下的serviceAccount,RC,pod,secret,PV,listRange,resourceQuota和event等资源对象。
当namespace的状态为“terminating”后,由admission controller的namespaceLifecycle插件来阻止为该namespace建立新的资源。同时在namespace controller删除完该namespace中的全部资源对象后,namespace controller对该namespace 执行finalize操做,删除namespace的spec.finallizers域中的信息。
固然这里有一种特殊状况,当个namespace controller发现namespace设置了删除周期,而且该namespace 的spec.finalizers域值为空,那么namespace controller将经过API Server删除该namespace 的资源。
上图所示了service和endpoint与pod的关系,endpoints表示一个service对应的全部的pod副本的访问地址,而endpoints controller就是负责生成和维护全部endpoints对象的控制器。
它负责监听service和对应的pod副本的变化,若是检测到service被删除,则删除和该service同名的endpoints对象。若是检测到新的service被建立或者修改,则根据该service的信息获取到相关的pod列表,而后建立或者更新service对应的endpoints对象。若是检测到pod的事件,则更新它对应service的endpoints对象(增长或者删除或者修改对应的endpoint条目)。
kubernetes scheduler 在整个系统中承担了“承上启下”的做用,“承上”是指它负责接收controller manager建立的新的pod,为其安排一个落脚的“家”,“启下”是指安置工做完成之后,目标node上的kubelet服务进程接管后继工做,负责pod生命周期中的“下半生”。
咱们都知道将service和pod经过label关联以后,咱们访问service的clusterIP对应的服务,就能经过kube-proxy将路由转发到对应的后端的endpoint(pod IP +port)上,最终访问到容器中的服务,实现了service的负载均衡功能。
那么接下来讲一说service controller的做用,它实际上是属于kubernetes与外部的云平台之间的一个接口控制器。Service controller监听service的变化,若是是一个loadBalancer类型的service,则service controller确保外部的云平台上该service对应的loadbalance实例被相应的建立、删除以及更新路由转发表(根据endpoint的条目)。
kubernetes scheduler 在整个系统中承担了“承上启下”的做用,“承上”是指它负责接收controller manager建立的新的pod,为其安排一个落脚的“家”,“启下”是指安置工做完成之后,目标node上的kubelet服务进程接管后继工做,负责pod生命周期中的“下半生”。
具体的来讲,kubernetes scheduler的做用就是将待调度的pod(新建的、补足副本而建立的)按照特定的调度算法和调度策略绑定到集群中某个合适的node上,并将绑定信息写入到etcd中。整个调度过程分为三个对象,分别是:待调度的pod列表、可有的合适的node列表、调度算法和策略。一句话就是经过合适的调度算法和策略,将待调度的pod列表中的pod在合适的node上建立并启动。
接下来小编经过一幅图简单介绍一下scheduler的工做流程:
有图可知:
遍历全部目标node,筛选出符合要求的候选节点。为此,kubernetes内置了多种预选策略
肯定优先节点,在第1步的基础上,采用优选策略,计算出每个节点候选的积分,积分最高者胜出
最后经过API Server将待调度的Pod,通知给最优node上的kubelet,将其建立并运行
在scheduler中可用的预选算有不少:NoDiskconflict、PodFitsResources、PodSelectorMatches、PodFitsHost、CheckNodeLabelPresence、CheckServiceAffinity、PodFitsPorts等策略。其中的5个默认的预选策略:PodFitsPorts、PodFitsResources、NoDiskconflict、PodSelectorMatches、PodFitsHost每一个节点只有经过这5个预选策略后,才能初步被选中,进入下一个流程。
下面小编介绍几个经常使用的预选策略:
判断备选pod的gcePersistentDisk或者AWSElasticBlockStore和备选的节点中已存在的pod是否存在冲突具体检测过程以下:
- 首先,读取备选pod的全部的volume信息,对每个volume执行一下步骤的冲突检测
- 若是该volume是gcePersistentDisk,则将volume和备选节点上的全部pod的每一个volume进行比较,若是发现相同的gcePersistentDisk,则返回false,代表磁盘冲突,检测结束,反馈给调度器该备选节点不合适做为备选的pod,若是volume是AWSElasticBlockStore,则将volume和备选节点上的全部pod的每一个volume进行比较,若是发现相同的AWSElasticBlockStore,则返回false,代表磁盘冲突,检测结束,反馈给调度器该备选节点不合适做为备选的pod
- 最终,检查备选pod的全部的volume均为发现冲突,则返回true,代表不存在磁盘冲突,反馈给调度器该备选节点合适备选pod
判断备选节点资源是否知足备选pod的需求,检测过程以下:
- 计算备选pod和节点中已存在的pod的全部容器的需求资源(CPU 和内存)的总和
- 得到备选节点的状态信息,其中包括节点的资源信息
- 若是备选pod和节点中已存在pod的全部容器的需求资源(CPU和内存)的总和超出了备选节点拥有的资源,则返回false,代表备选节点不适合备选pod,不然返回true,代表备选节点适合备选pod
判断备选节点是否包含备选pod的标签选择器指定的标签:
- 若是pod没有指定spec.nodeSelector标签选择器,则返回true
- 若是得到备选节点的标签信息,判断节点是否包含备选pod的标签选择器所指的标签,若是包含返回true,不包含返回false
判断备选pod的spec.nodeName域所指定的节点名称和备选节点的名称是否一致,若是一致返回true,不然返回false。
判断备选pod所用的端口列表汇中的端口是否在备选节点中被占用,若是被占用,则返回false,不然返回true。
Scheduler中的优选策略有:leastRequestedPriority、CalculateNodeLabelPriority和BalancedResourceAllocation等。每一个节点经过优先策略时都会算出一个得分,计算各项得分,最终选出得分值最大的节点做为优选结果。
小编接下来就给你们介绍一下一些经常使用的优选策略:
该策略用于从备选节点列表中选出资源消耗最小的节点:
- 计算出全部备选节点上运行的pod和备选pod的CPU占用量
- 计算出全部备选节点上运行的pod和备选pod的memory占用量
- 根据特定的算法,计算每一个节点的得分
若是用户在配置中指定了该策略,则scheduler会经过registerCustomPriorityFunction方法注册该策略。该策略用于判断策略列出的标签在备选节点中存在时,是否选择该备选节点。若是备选节点的标签在优选策略的标签列表中且优选策略的presence值为true,或者备选节点的标签不在优选策略的标签列表中且优选策略的presence值为false,则备选节点score=10,不然等于0。
该优选策略用于从备选节点列表中选出各项资源使用率最均衡的节点:
- 计算出全部备选节点上运行的pod和备选pod的CPU占用量
- 计算出全部备选节点上运行的pod和备选pod的memory占用量
- 根据特定的算法,计算每一个节点的得分
在kubernetes集群中,每一个node上都会启动一个kubelet服务进程。该进程用于处理master节点下发到本节点的任务,管理Pod以及Pod中的容器。每一个kubelet进程会在API Server上注册节点信息,按期向master节点汇报节点资源的使用状况,并经过cAdvisor监控容器和节点的资源。
节点经过设置kubelet的启动参数“--register-node”来决定是否向API Server注册本身。若是该参数为true,那么kubelet将试着经过API Server注册本身。在自注册时,kubelet启动时还包括如下参数:
-api-servers:API Server的位置
--kubeconfing:kubeconfig文件,用于访问API Server的安全配置文件
--cloud-provider:云服务商地址,仅用于共有云环境
若是没有选择自注册模式,用户须要手动去配置node的资源信息,同时告知ndoe上的kubelet API Server的位置。Kubelet在启动时经过API Server注册节点信息,并定时向API Server发送节点新消息,API Server在接受到这些消息以后,将这些信息写入etcd中。经过kubelet的启动参数“--node-status-update-frequency”设置kubelet每一个多长时间向API Server报告节点状态,默认为10s。
kubelet经过如下几种方式获取自身node上所要运行的pod清单:
文件:kubelet启动参数“--config”指定的配置文件目录下的文件(默认为“/etc/Kubernetes/manifests”)经过--file-check-frequency设置检查该文件的时间间隔,默认为20s
HTTP端点:经过“--manifest-url”参数设置。经过“--http-check-frequency”设置检查该HTTP端点数据的时间间隔,默认为20s。
API Server:kubelet经过API server监听etcd目录,同步pod列表
注意:这里static pod,不是被API Server建立的,而是被kubelet建立,以前文章中提到了静态的pod是在kubelet的配置文件中编写,而且总在kubelet所在node上运行。
Kubelet监听etcd,全部针对pod的操做将会被kubelet监听到。若是是新的绑定到本节点的pod,则按照pod清单的要求建立pod,若是是删除pod,则kubelet经过docker client去删除pod中的容器,并删除该pod。
具体的针对建立和修改pod任务,流程为:
- 为该pod建立一个目录
- 从API Server读取该pod清单
- 为该pod挂载外部volume
- 下载pod用到的secret
- 检查已经运行在节点中的pod,若是该pod没有容器或者Pause容器没有启动,则先中止pod里的全部容器的进程。若是pod中有须要删除的容器,则删除这些容器
- 检查已经运行在节点中的pod,若是该pod没有容器或者Pause容器没有启动,则先中止pod里的全部容器的进程。若是pod中有须要删除的容器,则删除这些容器
- 为pod中的每一个容器作以下操做
△ 为容器计算一个hash值,而后用容器的名字去查询docker容器的hash值。若查找到容器,且二者获得hash不一样,则中止docker中的容器的进程,而且中止与之关联pause容器的进程;若两个相同,则不作任何处理
△ 若是容器被中止了,且容器没有指定restartPolicy(重启策略),则不作任何处理
△调用docker client 下载容器镜像,调用docker client 运行容器
Pod经过两类探针来检查容器的健康状态。一个是livenessProbe探针,用于判断容器是否健康,告诉kubelet一个容器何时处于不健康状态,若是livenessProbe探针探测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略作相应的处理;若是一个容器不包含livenessProbe探针,那么kubelet认为livenessProbe探针的返回值永远为“success”。另外一个探针为ReadinessProbe,用于判断容器是否启动完成,且准备接受请求。若是ReadinessProbe探针检测到失败,则pod的状态将被修改,endpoint controller将从service的endpoints中删除包含该容器所在pod的IP地址的endpoint条目。
Kubelet按期调用容器中的livenessProbe探针来诊断容器的健康状态。livenessProbe包括如下三种实现方式:
- Execaction:在容器内执行一个命令,若是该命令的退出状态码为0,表示容器健康
- TCPSocketAction:经过容器的IP地址和端口执行一个TCP检查,若是端口能被访问,则代表该容器正常
- TCPSocketAction:经过容器的IP地址和端口执行一个TCP检查,若是端口能被访问,则代表该容器正常
具体的配置小编以前的文章中有详细说明:http://www.javashuo.com/article/p-fbxjufex-dp.html
介绍kube-proxy,不得不说service,这里小编先带你们回顾一下service,因为pod每次建立时它的IP地址是不固定的,为了访问方便以及负载均衡,这里引入了service的概念,service在建立后有一个clusterIP,这个IP是固定的,经过labelselector与后端的pod关联,这样咱们若是想访问后端的应用服务,只须要经过service的clusterIP,而后就会将请求转发到后端的pod上,即便一个反向代理,又是一个负载均衡。
可是在不少状况下service只是一个概念,而真正将service的做用落实的这是背后的kube-proxy服务进程。那么接下来就具体的介绍kube-proxy。
在kubernetes集群中的每个node上都有一个kube-proxy进程,这个进程能够看作service的透明代理兼负载均衡,其核心功能就是将到某个service的访问请求转发到后端的多个pod实例上。对每个TCP类型的kubernetes service,kube-proxy都会在本地node上创建一个socketserver来负责接收请求,而后均匀发送到后端的某个pod的端口上,这个过程默认采用round robin负载均衡算法。另外,kubernetes也提供经过修改service的service.spec.sessionAffinity参数的值来实现会话保持特性的定向发送,若是设置的值为“clientIP”,那么则未来来自同一个clientIP的请求都转发到同一个后端的pod上。
此外,service的clusterIP和nodePort等概念是kube-proxy服务经过Iptables的NAT转换实现的,kube-proxy在运行过程当中动态建立于service相关的Iptable规则,这些规则实现了clusterIP以及nodePort的请求流量重定向到kube-proxy进程上对应的服务的代理端口的功能。因为Iptable机制针对的是本地的kube-proxy端口,全部每个node上都要运行kube-proxy组件,这样一来,在kubernetes集群内部,咱们能够在任意node上发起对service的访问。由此看来,因为kube-proxy的做用,在service的调用过程当中客户端无序关心后端有几个pod,中间过程的通讯,负载均衡以及故障恢复都是透明。
目前kube-proxy的负载均衡只支持round robin算法。round robin算法按照成员列表逐个选取成员,若是一轮循环结束,便从头开始下一轮循环,如此循环往复。Kube-proxy的负载均衡器在round robin算法获得基础上还支持session保持。若是service在定义中指定了session保持,则kube-proxy接受请求时会从本地内存中查找是否存在来自该请求IP的affinitystate对象,若是存在该对象,且session没有超时,则kube-proxy将请求转向该affinitystate所指向的后端的pod。若是本地存在没有来自该请求IP的affinitystate对象,则按照round robin算法算法为该请求挑选一个endpoint,并建立一个affinitystate对象,记录请求的IP和指向的endpoint。后面请求就会“黏连”到这个建立好的affinitystate对象上,这就实现了客户端IP会话保持的功能。
kube-proxy经过查询和监听API Server中service与endpoint的变换,为每个service都创建一个“服务代理对象“,并自动同步。服务代理对相关是kube-proxy程序内部的一种数据结构,它包括一个用于监听此务请求的socketServer, socketServer的端口是随机指定的是本地一个空闲端口。此外,kube-proxy内部也建立了一个负载均衡器—loadBalancer, loadBalancer上保存了service到对应的后端endpoint列表的动态路由转发表,而具体的路由选择则取决于round robin算法和service的session会话保持。
针对发生变化的service列表,kube-proxy会逐个处理,下面是具体的处理流程:
- 若是service没有设置集群IP,这不作任何处理,不然,获取该service的全部端口定义列表
- 逐个读取服务端口定义列表中的端口信息,根据端口名称、service名称和namespace判断本地是否已经存在对应的服务代理对象,若是不存在则建立,若是存在而且service端口被修改过,则先删除Iptables中和该service端口相关的规则,关闭服务代理对象,而后走新建流程并为该service建立相关的Iptables规则
- 更新负载均衡组件中对应service的转发地址列表,对于新建的service,肯定转发时的会话保持策略
- 对于已删除的service则进行清理
接下来小编经过一个具体的案例,实际的给你们介绍一下kube-proxy的原理:
#首先建立一个service:
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"
mysql-service对应的nodePort暴露出来的端口为30964,对应的cluster IP(10.254.162.44)的端口为3306,进一步对应于后端的pod的端口为3306。这里的暴露出来的30964也就是为mysql-service服务建立的代理对象在本地的端口,在ndoe上访问该端口,则会将路由转发到service上。
mysql-service后端代理了两个pod,ip分别是192.168.125.129和192.168.125.131。先来看一下iptables。
[root@localhost ~]# iptables -S -t nat
首先若是是经过node的30964端口访问,则会进入到如下链:
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/mysql-service:" -m tcp --dport 30964 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/mysql-service:" -m tcp --dport 30964 -j KUBE-SVC-67RL4FN6JRUPOJYM
而后进一步跳转到KUBE-SVC-67RL4FN6JRUPOJYM的链
-A KUBE-SVC-67RL4FN6JRUPOJYM -m comment --comment "default/mysql-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-ID6YWIT3F6WNZ47P -A KUBE-SVC-67RL4FN6JRUPOJYM -m comment --comment "default/mysql-service:" -j KUBE-SEP-IN2YML2VIFH5RO2T
这里利用了iptables的--probability的特性,使链接有50%的几率进入到KUBE-SEP-ID6YWIT3F6WNZ47P链,50%的几率进入到KUBE-SEP-IN2YML2VIFH5RO2T链。
KUBE-SEP-ID6YWIT3F6WNZ47P的链的具体做用就是将请求经过DNAT发送到192.168.125.129的3306端口。
-A KUBE-SEP-ID6YWIT3F6WNZ47P -s 192.168.125.129/32 -m comment --comment "default/mysql-service:" -j KUBE-MARK-MASQ -A KUBE-SEP-ID6YWIT3F6WNZ47P -p tcp -m comment --comment "default/mysql-service:" -m tcp -j DNAT --to-destination 192.168.125.129:3306
同理KUBE-SEP-IN2YML2VIFH5RO2T的做用是经过DNAT发送到192.168.125.131的3306端口。
-A KUBE-SEP-IN2YML2VIFH5RO2T -s 192.168.125.131/32 -m comment --comment "default/mysql-service:" -j KUBE-MARK-MASQ -A KUBE-SEP-IN2YML2VIFH5RO2T -p tcp -m comment --comment "default/mysql-service:" -m tcp -j DNAT --to-destination 192.168.125.131:3306
总的来讲就是:在建立service时,若是不指定nodePort则为其建立代理对象时代理对象再本地监听一个随机的空闲端口,若是设置了nodePort则以nodePort为本地代理对象的端口。客户端在访问本地代理对象的端口后此时会根据iptables转发规则,将请求转发到service的clusterIP+port上,而后根据负载均衡策略指定的转发规则,将请求再次转发到后端的endpoint的target Port上,最终访问到具体pod中容器的应用服务,而后将响应返回。
文章内容参考至《kubernetes权威指南》