Service代理: 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的访问。
- kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。
- kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也可以称为是Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。
kube-proxy 2种工作模式
-
iptables模式:
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折。
iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型。 -
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:不排队调度
如果某个服务后端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规则。
kube-proxy生成的iptables规则分析
- service的type类型是ClusterIp,iptables规则分析
kube-proxy生成的ipvs规则分析
CluserIP 类型Service ipvs规则分析
[root@master1 service]# kubectl apply -f pod_test.yaml
deployment.apps/my-nginx created
[root@master1 service]# kubectl get pods -l run=my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-5898cf8d98-lsnhn 1/1 Running 0 2m35s 172.16.166.140 node1
my-nginx-5898cf8d98-zvrrx 1/1 Running 0 2m35s 172.16.166.139 node1
#创建type为ClusterIP的Service
[root@master1 service]# kubectl apply -f service_test.yaml
service/my-nginx created
[root@master1 service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 392d
my-nginx ClusterIP 10.111.178.161 80/TCP 5s
可以看到my-nginx pod是在node1上,查看node1上的ipvs转发规则
[root@node1 manifests]# ipvsadm -Ln | grep -A5 '10.111.178.161'
TCP 10.111.178.161:80 rr
-> 172.16.166.139:80 Masq 1 0 0
-> 172.16.166.140:80 Masq 1 0 0
NodePort类型Service
[root@master1 service]# kubectl apply -f pod_nodeport.yaml
deployment.apps/my-nginx-nodeport created
[root@master1 service]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-5898cf8d98-lsnhn 1/1 Running 0 3h18m 172.16.166.140 node1
my-nginx-5898cf8d98-zvrrx 1/1 Running 0 3h18m 172.16.166.139 node1
my-nginx-nodeport-5fccbb754b-4l85l 1/1 Running 0 23s 172.16.166.141 node1
my-nginx-nodeport-5fccbb754b-r9pq8 1/1 Running 0 23s 172.16.104.12 node2
[root@master1 service]# kubectl apply -f service_nodeport.yaml
service/my-nginx-nodeport created
[root@master1 service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 392d
my-nginx ClusterIP 10.111.178.161 80/TCP 3h17m
my-nginx-nodeport NodePort 10.100.210.10 80:30380/TCP 7s
看下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
到对应主机上查看对应ipvs转发规则
[root@node1 ~]# ipvsadm -Ln | grep -A 3 10.100.210.10
TCP 10.100.210.10:80 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
[root@node1 ~]# ipvsadm -Ln | grep -A 2 30380
TCP 172.17.0.1:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
TCP 10.3.23.222:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
--
TCP 127.0.0.1:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
--
TCP 172.16.166.128:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
[root@node2 ~]# ipvsadm -Ln | grep -A 2 30380
TCP 10.3.23.223:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
--
TCP 127.0.0.1:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
--
TCP 172.16.104.0:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
--
TCP 172.17.0.1:30380 rr
-> 172.16.104.12:80 Masq 1 0 0
-> 172.16.166.141:80 Masq 1 0 0
Service服务发现:coredns组件详解
DNS是什么?
DNS全称是Domain Name System:域名系统,是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解IP地址,使得互联网的使用者不再需要直接接触很难阅读和理解的IP地址。域名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,如果没有了 DNS,那么可能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,如果我们为 IP 地址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,那么我们就可以保证集群对外提供的服务能够相对稳定地被其他客户端访问。DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化的分布式数据库,存储着从域名到 IP 地址的映射。
CoreDNS?
CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目以及工程师都会使用 CoreDNS 为集群提供服务发现的功能,Kubernetes 就在集群中使用 CoreDNS 解决服务发现的问题。 作为一个加入 CNCF(Cloud Native Computing Foundation)的服务, CoreDNS 的实现非常简单。
[root@master1 service]# 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@master1 service]# kubectl exec -it dig -- nslookup my-nginx-nodeport
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: my-nginx-nodeport.default.svc.cluster.local