kubernetes之Service与ingress
1、Service介绍
kubernetes提供了Service资源,对提供同一个服务的多个Pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。
当创建Service的时候会通过api-service向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变化,然后将最新的Service信息转换成对于的访问规则。
kube-proxy目前支持三种工作模式
1.1、userspace模式
userspace模式下,kube-proxy会为每一个service创建一个监听端口,发向Cluser IP的请求被iptabes规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并建立链接,以将请求转发到Pod上。
该模式下,kube-proxy充当了一个四层负责负载均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时,会增大内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。
1.2、iptables模式
iptables模式下,kube-proxy为service后端的每个pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP
该模式下kube-proxy不承担四层负载均衡的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试
1.3、ipvs模式
ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对于iptables转发效率更高。除此之外,ipvs支持更多的LB算法
修改kube-proxy的规则,改成ipvs模式
#查看ipvs,目前未生效
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
#修改配置文件,搜索mode,改成mode: "ipvs",使其生效
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
......
53 metricsBindAddress: ""
54 mode: "ipvs"
55 nodePortAddresses: null
56 oomScoreAdj: null
.......
#这里是采用按照标签的格式来,删除当前kube-proxy,重新创建
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
pod "kube-proxy-794jw" deleted
pod "kube-proxy-ck5c4" deleted
pod "kube-proxy-fnfvl" deleted
#查看规则,发送到TCP上的指定ip,就会按照rr轮询规则转发到对应的地址下面
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30858 rr
TCP 172.17.0.1:32311 rr
-> 10.244.104.62:80 Masq 1 0 0
-> 10.244.166.189:80 Masq 1 0 0
-> 10.244.166.190:80 Masq 1 0 0
TCP 192.168.80.10:30858 rr
TCP 192.168.80.10:32311 rr
-> 10.244.104.62:80 Masq 1 0 0
-> 10.244.166.189:80 Masq 1 0 0
-> 10.244.166.190:80 Masq 1 0 0
TCP 10.96.0.1:443 rr
-> 192.168.80.10:6443 Masq 1 1 0
TCP 10.96.0.10:53 rr
-> 10.244.219.74:53 Masq 1 0 0
-> 10.244.219.75:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 10.244.219.74:9153 Masq 1 0 0
-> 10.244.219.75:9153 Masq 1 0 0
TCP 10.109.23.222:80 rr
TCP 10.109.206.2:80 rr
-> 10.244.104.62:80 Masq 1 0 0
-> 10.244.166.189:80 Masq 1 0 0
-> 10.244.166.190:80 Masq 1 0 0
TCP 10.244.219.64:30858 rr
TCP 10.244.219.64:32311 rr
-> 10.244.104.62:80 Masq 1 0 0
-> 10.244.166.189:80 Masq 1 0 0
-> 10.244.166.190:80 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 10.244.219.74:53 Masq 1 0 0
-> 10.244.219.75:53 Masq 1 0 0
2、Service类型
Service的资源清单文件
kind:Service # 资源类型
apiVersion:v1 #资源版本
metadata: #元数据
name:service #资源名称
namespace:dev #命名空间
spec: #描述
selector: #标签选择器,用于确定当前service代理哪些pod
app:nginx
type: # Service类型,指定service的访问方式
clusterIP: #虚拟服务的ip地址
sessionAffinity: #session亲和性,支持ClientIP、None两个选项
ports: #端口信息
- protocol:TCP
port:3017 #service端囗
targetPort:5003 #pod端
nodePort:31122 #主机端口
- CluserIP:默认值,它是kubernetes系统自动分配的虚拟IP,只能在集群内部访问
- NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
- LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
- ExternalName:把集群外部的服务引入集群内部,直接使用
3、Service使用
实验准备:
#分别查看三台nginx的内部ip
[root@master ~]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7854ff8877-45jkc 1/1 Running 0 63m 10.244.104.62 node2 <none> <none>
nginx-7854ff8877-wg2tn 1/1 Running 2 (70m ago) 17h 10.244.166.189 node1 <none> <none>
nginx-7854ff8877-zzct2 1/1 Running 0 62m 10.244.166.190 node1 <none> <none>
#分别进入三台nginx内部,将ip写入到index.html页面,使得curl可以显示对应的IP信息
[root@master ~]# kubectl exec -it nginx-7854ff8877-45jkc -n test /bin/bash
echo "10.244.104.62" > /usr/share/nginx/html/index.html
3.1、ClusterIP类型的Service
#暴露service
【--name】指对service名称
【expose deploymnet 参数】暴露deployment指定service
【--type=ClusterIP】这里产生的是service的ip,在service生命周期中,这个地址不变
[root@master ~]# kubectl expose deployment nginx --name=svc-nginx --type=ClusterIP --port=80 --target-port=80 -n test
service/svc-nginx exposed
#查看service
[root@master ~]# kubectl get service -n test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx NodePort 10.109.206.2 <none> 80:32311/TCP 67m
#查看详细信息
[root@master ~]# kubectl describe service svc-nginx -n test
Name: svc-nginx
Namespace: test
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.109.206.2
IPs: 10.109.206.2
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 32311/TCP
Endpoints: 10.244.104.62:80,10.244.166.189:80,10.244.166.190:80 #####这里的endpoints也是一种资源
Session Affinity: None #会话亲和性:这里还是None
External Traffic Policy: Cluster
Events: <none>
Endpoint
Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod访问地址,它是根据service配置文件中selector描述产生的
一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。
换句话说,service和pod之间的关系是通过endpoints实现的
负载分发策略
对Service的访问被分发到后端的Pod上去,目前kubernetes提供了两种负载分发策略:
- 如果不定义,默认使用kube-proxy的策略,比如随机、轮询
- 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上。此模式可以使在spec中添加sessionAffinity:ClientIP选项
#查看ipvs规则
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.109.206.2:80 rr
-> 10.244.104.62:80 Masq 1 0 0
-> 10.244.166.189:80 Masq 1 0 0
-> 10.244.166.190:80 Masq 1 0 0
#执行请求命令,发现现在为轮询策略
[root@master ~]# while true;do curl 10.109.206.2:80;sleep 5;done;
10.244.166.190
10.244.166.189
10.244.104.62
10.244.166.190
10.244.166.189
10.244.104.62
.....
#修改轮询策略为sessionAffinity:ClientIP
[root@master ~]# kubectl edit service svc-nginx -n test
....
spec:
clusterIP: 10.109.206.2
clusterIPs:
- 10.109.206.2
externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- nodePort: 32311
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: ClientIP #这里更改Node为ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 100 #保持100s
type: NodePort
status:
....
#查看详细
[root@master ~]# kubectl describe service svc-nginx -n test
Name: svc-nginx
Namespace: test
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.109.206.2
IPs: 10.109.206.2
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 32311/TCP
Endpoints: 10.244.104.62:80,10.244.166.189:80,10.244.166.190:80
Session Affinity: ClientIP #发现已经更改
External Traffic Policy: Cluster
Events: <none>
#查看规则
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
.......
TCP 10.109.23.222:80 rr
TCP 10.109.206.2:80 rr persistent 100 #持久化persistent 100
-> 10.244.104.62:80 Masq 1 0 0
-> 10.244.166.189:80 Masq 1 0 0
-> 10.244.166.190:80 Masq 1 0 6
......
#测试,发现只转发到190这台上
[root@master ~]# while true;do curl 10.109.206.2:80;sleep 5;done;
10.244.166.190
10.244.166.190
10.244.166.190
10.244.166.190
3.2、HeadLiness类型的Service
基于自己来控制负载均衡策略,这类service不会分配Cluster IP,如果要访问service,只能通过域名访问查询
#将cluster IP更改为None,即可创建HeadLiness Service
[root@master ~]# kubectl edit service svc-nginx -n test
....
spec:
clusterIP: None #改成None
....
3.3、NodePort类型的Service
如果要将Service暴露给集群外部使用,则需要使用NodePort类型的Service,NodePort的工作原理是将service的端口映射到Node的一个端口上,然后通过NodeIp:NodePort来访问service
#需要修改配置
[root@master ~]# kubectl describe service svc-nginx -n test
........
ports:
- nodePort: 32311
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 100
type: NodePort #类型改成NodePort
.......
#将内部的node的ip 10.109.206.2::80映射到主机外部的32311端口
[root@master ~]# kubectl get service -n test -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx NodePort 10.109.206.2 <none> 80:32311/TCP 4h1m app=nginx
3.4、LoadBalance类型的Service
LoadBalance和NodePort相似,目的都是向外部暴露一个端口,区别在于:
LoadBalance会在集群外部再做一个负载均衡设备,而这个设备需要外部环境的支持,外部服务发送到这个设备上的请求,会被设备负载均之后转发到集群中
3.5、ExternalName类型的Service
ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务
#需要修改配置
[root@master ~]# kubectl describe service svc-nginx -n test
........
Spec:
type: ExternalName #类型改成 ExternalName
externalName: www.baidu.com #改成ip地址也可以
........
#可以发现EXTERNAL-IP 为www.baidu.com
.com
[root@master ~]# kubectl get service -n test -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc-nginx ExternalName <none> www.baidu.com 80/TCP 4h13m app=nginx
4、Ingress
Service对集群外暴露服务的主要方式为:NodePort和LoadBalance,但是缺点如下:
- NodePort的缺点是占用集群机器的端口
- LB方式,是每个service都需要一个LB,并且需要kubernetes外部设备支持
因此,基于现状,k8s提供了Ingress资源对象,Ingress只需要一个NodePort或者LB就可以满足暴露多个Service 的需求,工作机制如下图:
实际上,Ingress相当于一个7层的负载均衡,做反向代理,工作原理类似于Nginx,可以理解在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的配置,然后对外部提供服务。
核心概念:
- ingress:kubernetes中的一个资源对象,作用是定义请求如何转发到service的规则
- ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发
作者:做个超努力的小奚&kervin24
个人博客站:http://www.kervin24.top
CSDN博客:https://blog.csdn.net/qq_52914969?type=blog