1. Service定义
Service定义文档:
-
https://kubernetes.io/zh/docs/concepts/services-networking/service/
-
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#service-v1-core
2. Service概念与原理
2.1. Service概念
Service用途:
-
给一组Pod提供一个抽象、稳定的网络地址
-
实现负载均衡/路由,透明化后端Pod地址(Endpoint)
例如,有2个Tomcat的Pod组成的Deployment,包含2个Pod的地址,需要直接和它们通信,负载均衡需要自己做。
之后抽象出1个Service,抽象出一个稳定地址,访问该地址,自动负载均衡且不需要知道Pod的地址。
2.2. 负载均衡机制
a. kube-proxy代理
通过每个Node等kube-proxy
进程代理。
提供三种模式:
-
user-space:代理在用户空间实现,效率最低
-
iptables:基于内核的
iptables
规则进行转发路由,需要通过readiness probe更新规则 -
ipvs:基于内核
netlink
接口的IPVS规则,效率最高,需启动IPVS模块,否则退回iptables模式
路由规则支持:round-robin, least connection, dst hashing, src hashing, shortest expected delay等
b. 会话保持机制
一段时间内(会话内),将某个源IP的请求路由到同个Pod上。
Pod定义中,使用sessionAffinity
设置(为ClientIP
),并设置会话过期时间。
2.3. 端口设置
Service可以指定多个端口,以暴露在外。端口的通信协议可不同。
端口列表中每个端口的3个不同字段:
-
port
:服务暴露给集群内部访问的端口,配合ClusterIP:port
使用 -
targetPort
:映射到Pod上的端口,默认和port
相同- 如:Service的
port
为80
,Pod服务的端口是8080
,则targetPort
为8080
- 如:Service的
-
nodePort
:绑定在Node IP上的端口,以便外部访问,配合NodeIP:nodePort
使用
2.4. 路由到外部服务
一般情况下,Service将一组Pod合起来,将请求直接转发到Pod上。
也可直接路由到外部服务,即Service -> Service,不需配置Label Selector(selector
)。
步骤:
-
定义外部Service的Endpoints对象,即指名外部Service的IP和端口列表
-
本服务引用步骤1的Endpoints对象
2.5. 发布服务
通过设置spec.type
,将Service暴露给外部使用。共4种方法:
-
ClusterIP
:默认,集群内部IP暴露,仅内部访问 -
NodePort
:通过物理节点Node的IP暴露,即通过nodeIP:nodePort
访问 -
LoadBalancer
:通过云服务商的负载均衡器,向外部暴露服务。服务上云后,负载均衡器会自动将外网IP请求路由到ClusterIP
或NodePort
服务上 -
ExternalName
:通过DNS到CNAME
机制,将Service的内部域名转换为externalName
外部域名来访问
也可以用Ingress暴露,见后文。
2.6. 支持的网络协议
-
TCP:默认,任意可用
-
UDP:大多可用,若
spec.type
为LoadBalancer
(见2.5节),取决于云服务商 -
SCTP:需要插件,若
spec.type
为LoadBalancer
,取决于云服务商 -
HTTP:取决于云服务商,也可以用Ingress直接暴露
-
PROXY:取决于云服务商
2.7. 服务发现
实现机制有2类:
-
环境变量:创建Pod后,自动设置Service的地址、端口等环境变量
- 格式类似
<servicename>_SERVICE_HOST
、<servicename>_SERVICE_PORT
- 格式类似
-
DNS(推荐):使用CoreDNS服务器提供域名解析
-
A/AAAA记录:
-
格式为
<servicename>.<namespace>.svc.<clusterdomain>
-
“普通”服务返回一个IP地址,“无头”服务返回1组Pod的IP地址列表
-
-
SRV记录:
-
设置命名端口后,每个端口都有一条SRV记录,解析为端口号和域名
-
格式为
_<portname>._<protocol>.<servicename>.<namespace>.svc.<clusterdomain>
-
“普通”服务返回1个结果,“无头”服务返回1组Pod的结果
-
-
2.8. Headless Service
不提供ClusterIP,需要设置clusterIP: None
。
外部应用从DNS获取一组Pod的Endpoint列表,需要自行实现负载均衡。
若指定了Label Selector,则扫描符合条件的Pod,加入Endpoint列表中。
若没指定Label Selector,则不会创建Endpoint列表,而是:
-
若
spec.type
为ExternalName
,则直接转换为externalName
外部域名 -
若Service定义中有同名的Endpoint定义,则转换为该Endpoint定义中的列表
2.9. Endpoint Slices
将一组完整Pod的Endpoint列表分成若干个分片,解决大集群扩展问题。
为某个Service所有,类似于将网络分成若干子网分开管理:Service -> Endpoint Slice -> Endpoint。
原始情况:
-
Service要存储每个Pod的Endpoint,这些存储于etcd中,成本高、
apiserver
压力大且有容量限制 -
kube-proxy
需要维护的iptables或ipv的规则增多 -
Pod的Endpoint更改时,都要传输到每个
kube-proxy
上,带宽浪费
Endpoint Slices下:
-
Pod的Endpoint更改时,只需更新一个分片,开销急剧变小
-
为基于Node拓扑的服务路由提供支持
Endpoint Slice分片:基于nodeName
和zone
Endpoint Slice分片平衡:
-
移除和修改已有Endpoint Slice的Endpoint
-
对1中已修改的Endpoint Slice,用新Endpoint填满
-
剩余的,添加到未修改的Endpoint Slice,或创建新的,优先考虑创建新的
3. DNS服务
3.1. CoreDNS服务器
目前使用CoreDNS实现,为一个Pod,里面为1个coredns
容器。架构图如下:
部署可参考:[使用 CoreDNS 进行服务发现 | Kubernetes](https://kubernetes.io/zh/docs/tasks/administer-cluster/coredns/) |
3.2. Node本地DNS缓存
Node节点本地拥有NodeLocal DNSCache,优先使用本地,然后使用CoreDNS查询。可缓解CoreDNS服务器压力,降低查询延迟、网络竞争,提升性能。
部署可参考:[在 Kubernetes 集群中使用 NodeLocal DNSCache | Kubernetes](https://kubernetes.io/zh/docs/tasks/administer-cluster/nodelocaldns/) |
3.3. Pod DNS
a. 域名规则
规则为:<pod-ip>.<servicename>.<namespace>.pod.<clusterdomain>
若Pod没有由Service暴露出来,则<servicename>
省略。
b. 自定义hostname和subdomain
Pod的定义文件中,可设置hostname
,替换掉规则中的pod-ip
。
也可设置subdomain
,替换掉<servicename>
。若需要将其暴露在外,需要创建Headless Service,其name
要设置为对应的subdomain
。
c. DNS策略
配置dnsPolicy
设置,有4种:
-
Default
:继承宿主机 -
ClusterFirst
:优先使用k8s集群的DNS服务(如CoreDNS) -
ClusterFirstWithHostNet
:若Pod开启hostNetwork
直接使用宿主机网卡通信,开启它可使用k8s集群的DNS服务 -
None
:忽略,需要手动配置DNS,见下d
d. 自定义DNS配置
当dnsPolicy
为None
时,在dnsConfig
设置,包含下面3个参数:
-
nameservers
:DNS服务器列表 -
searches
:用于搜索的域名后缀 -
options
:其它可选参数,如ndot
,timeout
等
4. Ingress
Ingress将集群内服务暴露到集群外,并进行服务到流量路由,规则由Ingress对象控制。
其包含了:
-
Ingress Controller:进行服务暴露和流量路由
-
Ingress:定义路由规则
外部访问Ingress Controller暴露的端口即可。
4.1. Ingress Controller
作用:
-
进行服务暴露和流量路由
-
自动监听
apiserver
并更新路由规则
可选nginx、HAProxy、云服务提供等方案。
部署:选择一个方案,如nginx-ingress
,构建Deployment或DaemonSet
nginx-ingress
: https://kubernetes.github.io/ingress-nginx/deploy/
4.2. Ingress
用于定义路由规则。
包含2种设置:
-
路由规则(
spec.rules
):包括host
域名、http.paths
服务路径,每个路径还需要设置backend
,即对应的服务名和端口号- 路径需要设置路径类型(
pathType
),可选精确、前缀,或由哪个IngressClass控制,通配符也可匹配
- 路径需要设置路径类型(
-
默认后端设置(
defaultBackend
):在不匹配任何服务时,路由到此,也可在Ingress Controller中设置
4.3. IngressClass
k8s可部署多个Ingress Controller,而Ingress归哪个Ingress Controller管,需要IngressClass。
IngressClass指定了Ingress Controller的名字,Ingress应用它即可归对应的Ingress Controller管。
系统有一个默认的IngressClass,当Ingress没指定IngressClass时使用。