本文介绍了 Kubernetes Service 的概念、原理和具体应用。
作者:沈亚军
爱可生研发团队成员,负责公司 DMP 产品的后端开发,喜好太广,三天三夜都说不完,低调低调…
本文起源:原创投稿
- 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。
Service 是什么?
Service 是 Kubernetes 一种资源,用于实现恒定的入口拜访一组提供雷同服务的 Pod。每个 Service 在其生命周期内领有固定的 IP 和 Port,客户端能够通过拜访该 IP 和端口拜访到和其关联的所有 Pod。这样服务的客户端不须要晓得提供服务的各个 Pod 的地位,从而容许这些 Pod 在集群中挪动。
首先咱们应用 Deployment 创立标签为 app=nginx
的三个 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
如下应用 Deployment 创立了三个 Pod。
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-6b474476c4-hc6k4 1/1 Running 0 7d2h 10.42.1.3 node8 <none> <none>
nginx-deployment-6b474476c4-mp8vw 1/1 Running 0 7d2h 10.42.0.7 node10 <none> <none>
nginx-deployment-6b474476c4-wh8xd 1/1 Running 0 7d2h 10.42.1.4 node8 <none> <none>
接下来咱们定义一个名为 my-service
的 Service 资源并指定选择器为 app=nginx
,其中 selector 定义了能够通过 Label selector 拜访的 Pod 组。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
- name: default
protocol: TCP
port: 80 #service port
targetPort: 80
如下咱们胜利创立了一个默认类型为 ClusterIP 的 Service 对象,并随机调配了一个 ClusterIP 10.109.163.26 尔后咱们就能够通过该 IP 和 Port 拜访到之前创立的 Pod 了,Service 的默认方拜访模式为轮询模式,即轮询将申请转发到后端的各个 Pod。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-service ClusterIP 10.109.163.26 <none> 80/TCP 4d1h app=nginx
为什么须要 Service?
Service 在咱们应用 Kubernetes 简直必不可少的一个资源对象次要包含以下起因
- 集群中的每一个 Pod 都能够通过 PodIP 被间接拜访的,然而 Kubernetes 中的 Pod 是有生命周期的对象,尤其是被 ReplicaSet、Deployment 等对象治理的 Pod,随时都有可能因为集群的状态变动被销毁和创立,导致 Pod 的 IP 发生变化。
- Pod 被 Kubernetes 调度到确定的节点后,才会为 Pod 调配 IP 地址,在启动之前客户端无奈晓得服务器 Pod 的 IP 地址。
- 程度扩大意味着多个 Pod 能够提供雷同的服务,每个 Pod 都能够提供雷同的服务 Pod 有本人的 IP 地址。客户端不应该关怀有多少 Pod 反对服务及其 IP 是什么而且不应该保留所有 Pod 的 IP 列表。相同所有这些 Pod 都应该能够通过单个 IP 地址提供服务。
Service 工作原理
在 Kubernetes 中创立一个新的 Service 对象会波及到两大模块,其中一个模块是控制器,它须要在每次客户端创立新的 Service 对象时,生成用于裸露一组 Pod 的 Kubernetes 对象,也就是 Endpoint 对象;另一个模块是 kube-proxy,它运行在 Kubernetes 集群中的每一个节点上,两大模块同时合作实现了 Service 性能
- 当咱们发动创立 Service 申请时 kube-apiserver 会生成一个 Service 对象并将其保留到 ETCD。
- Endpoint Controller 在订阅到 Service 创立时会创立对应的 Endpoint 对象。
- kube-proxy 会订阅 Service 和 Endpoint 变动,以此扭转节点上 iptables/ipvs 中保留的规定。
接下来会别离介绍这两大模块是何合作实现 Service 性能
Endpoint
如上文所述当咱们创立 Service 时,同时也会生成如下所示的 Endpoint。
NAME ENDPOINTS AGE
my-service 10.42.0.7:80,10.42.1.3:80,10.42.1.4:80 7d3h
Endpoint 是 Kubernetes 集群中的一个资源对象存储在 Etcd 中,从上文能够看到 Endpoint 中保留了一个 Service 对应的所有 Pod 的拜访地址,Endpoint 的创立和更新是通过 Endpoint Controller,当咱们通过 API 创立 / 批改 Service 对象时,Endpoints Controller 的 informer 机制监听到 Service 对象更新,而后依据 Service 的配置的 selector 创立对应 Endpoint 对象,此对象将 Pod 的 IP、容器端口做记录并存储到 Etcd,这样 Service 只有看一下本人名下的 Endpoint 就能够晓得所对应 Pod 信息了。
Endpoint Controller 是 kube-controller-manager 组件中泛滥控制器中的一个,是 Endpoint 资源对象的控制器,其通过对 Service、Pod 两种资源的监听实现对的 Endpoint 资源进行治理,次要包含以下性能:
- 负责生成和保护所有 Endpoint 对象
- 负责监听 Service 和对应 Pod 的变动
- 监听到 Service 被删除,则删除和该 Service 同名的 Endpoint 对象
- 监听到新的 Service 被创立,则依据新建 Service 信息获取相干 Pod 列表,而后创立对应 Endpoint 对象
- 监听到 Service 被更新,则依据更新后的 Service 信息获取相干 Pod 列表,而后更新对应 Endpoint 对象
- 监听到 Pod 事件,则更新 endpoint 对象保留的 Pod IP 列表, 如 Pod 处于非衰弱状态时则把 Pod 从保留的 IP 列表中移除,复原时再重新加入
kube-proxy
kube-proxy 是 Kubernetes 的一个网络代理组件,运行在每个 worker 节点上。kube-proxy 保护节点上的网络规定,实现了 Kubernetes Service 概念的一部分,它的作用是使发往 Service 的流量(通过 ClusterIP 和端口)负载平衡到正确的后端 Pod,kube-proxy 反对多种配置模式次要包含 iptable 和 ipvs 模式,本文则基于 iptable 模式形容 kube-proxy 的工作原理,kube-proxy 的主要职责包含两大块:
- 监听 Service 更新事件,并更新 Service 相干的 iptables 规定。
- 监听 Endpoint 更新事件,更新 Endpoint 相干的 iptables 规定, 如 KUBE-SVC- 链中的规定会把包申请转入 Endpoint 对应的 Pod。如果某个 Service 尚没有 Pod 创立,那么针对此 Service 的申请将会被 drop 掉。
在 iptables 模式下, 创立 Service 会创立一系列的 iptable 规定,
首先咱们在任意一台 worker 节点执行 iptables -nvL OUTPUT -t nat
查看 iptables net 表的 output 链,存在 kube-proxy 创立的 KUBE-SERVICE 链
Chain OUTPUT (policy ACCEPT 40606 packets, 2435K bytes)
pkts bytes target prot opt in out source destination
15M 929M KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
执行 iptables -nvL KUBE-SERVICES -t nat
查看 KUBE-SERVICES 链,能够看到 clusterIP 10.43.7.11 跳转到 KUBE-SVC-KEAUNL7HVWWSEZA6
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SVC-KEAUNL7HVWWSEZA6 tcp -- * * 0.0.0.0/0 10.43.7.11 /* default/my-service: cluster IP */ tcp dpt:80
而后咱们执行 iptables -nvL KUBE-SVC-KEAUNL7HVWWSEZA6 -t nat
查看 KUBE-SVC-KEAUNL7HVWWSEZA6 能够看到以不同的概率进行了跳转
Chain KUBE-SVC-KEAUNL7HVWWSEZA6 (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-SKMF2UJJQ24AYOPG all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-service: */ statistic mode random probability 0.33333333349
0 0 KUBE-SEP-BAKPXLXOJZJDGFKA all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-service: */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-M4RM3QHTJOBNSPNE all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-service: */
最初执行 iptables -nvL KUBE-SEP-SKMF2UJJQ24AYOPG -t nat
查看其中一条通过 DNAT 发送到其中一个 Pod 地址 10.42.0.7:80
Chain KUBE-SEP-SKMF2UJJQ24AYOPG (1 references)
pkts bytes target prot opt in out source destination
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/my-service: */ tcp to:10.42.0.7:80
从上问所述可知在 Service 创立时会生成大量的 iptalbes 规定,当服务数量十分宏大时 iptables 规定也会成倍增长,带来的问题是路由提早和服务拜访提早,而且因为 iptables 应用非增量式更新当规定数量宏大时增加或删除一条规定也有较大提早, 为了解决该问题所以 kubernetes 在 1.11 之后版本推出了 ipvs 模式。