kubernetes之Service与ingress

1、Service介绍

kubernetes提供了Service资源,对提供同一个服务的多个Pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务

image-20250818143759737

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中,在进行转发处理时,会增大内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。

image-20250818150915127

1.2、iptables模式

iptables模式下,kube-proxy为service后端的每个pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP

该模式下kube-proxy不承担四层负载均衡的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试

image-20250818151452465

1.3、ipvs模式

ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对于iptables转发效率更高。除此之外,ipvs支持更多的LB算法

image-20250818151831344

修改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实现的

image-20250818161529091

负载分发策略

对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
 .....

image-20250818173131928

#修改轮询策略为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

image-20250818185438874

 #需要修改配置
 [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会在集群外部再做一个负载均衡设备,而这个设备需要外部环境的支持,外部服务发送到这个设备上的请求,会被设备负载均之后转发到集群中

image-20250818190210306

3.5、ExternalName类型的Service

ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务

image-20250818190615654

 #需要修改配置
 [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 的需求,工作机制如下图:

image-20250820100601733

实际上,Ingress相当于一个7层的负载均衡,做反向代理,工作原理类似于Nginx,可以理解在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的配置,然后对外部提供服务。

核心概念:

  • ingress:kubernetes中的一个资源对象,作用是定义请求如何转发到service的规则
  • ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发

image-20250820102538948

阅读剩余
THE END