k8s-service

说明

服务内部的访问

 解决ip不固定的问题,当Pod宕机后重新生成时,其IP等状态信息可能会变动,Service会根据Pod的Label对这些状态信息进行监控和变更,保证上游服务不受Pod的变动而影响

kube-proxy

Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,

而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。

 

 

kube-proxy支持的工作模式

user space

userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。

​ 该模式下,kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。

clusterip重定向到kube-proxy服务的过程存在内核态到用户态的切换,开销很大,因此有了iptables模式,而userspace模式也被废弃了。

工作步骤:

  • kube-proxy为每个service在node上打开一个随机端口作为代理端口
  • 建立iptables规则,将clusterip的请求重定向到代理端口(用户空间)
  • 到达代理端口的请求再由kubeproxy转发到后端

 

 

iptables

kubernets从1.2版本开始将iptabels模式作为默认模式,这种模式下kube-proxy不再起到proxy的作用。其核心功能:通过API Server的Watch接口实时跟踪Service和Endpoint的变更信息,并更新对应的iptables规则,Client的请求流量通过iptables的NAT机制“直接路由”到目标Pod。

对于每一个Service,Kube-proxy创建相应的IPtables规则,并将发送到Service cluseter IP的流量转发到Service后端提供服务的Pod的相应端口上。并且流量的转发都是在内核态,所以性能高。
在这种模式下缺点是在大规模集群中,iptables添加规则会很大的延迟,因为使用iptables,每增加一个Service都会增加一个iptables的chain。并且iptables修改了规则后必须全部刷新才可以生效。

 

 

ipvs

从kubernetes 1.8版本开始引入第三代的IPVS模式,它也是基于netfilter实现的,但定位不同:iptables是为防火墙设计的,IPVS则专门用于高性能

负载均衡,并使用高效的数据结构Hash表,允许几乎无限的规模扩张。

一句话说明:ipvs使用ipset存储iptables规则,在查找时类似hash表查找,时间复杂度为O(1),而iptables时间复杂度则为O(n)。

。除此以外,ipvs支持更多的LB算法。

 

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的映射规则【rr 轮询】
[root@master ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0
 
# 循环访问测试
[root@master ~]# while true;do curl 10.97.97.97:80; sleep 5; done;
10.244.1.40
10.244.1.39
10.244.2.33
10.244.1.40
10.244.1.39
10.244.2.33
 
# 修改分发策略----sessionAffinity:ClientIP
 
# 查看ipvs规则【persistent 代表持久】
[root@master ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr persistent 10800
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0
 
# 循环访问测试
[root@master ~]# while true;do curl 10.97.97.97; sleep 5; done;
10.244.2.33
10.244.2.33
10.244.2.33
  
# 删除service
[root@master ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted

 

几种类型的service

ClusterIP类型的Service

默认类型,Service将会被分配一个Cluster IP,只在集群内部可用。比如redis mq等ip访问

创建service-clusterip.yaml文件

apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.97.97.97 # service的ip地址,如果不写,默认会生成一个  一般不指定
  type: ClusterIP
  ports:
  - port: 80  # Service端口       
    targetPort: 80 # pod端口

测试

# 创建service
[root@master ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
 
# 查看service
[root@master ~]# kubectl get svc -n dev -o wide
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    13s   app=nginx-pod
 
# 查看service的详细信息
# 在这里有一个Endpoints列表,里面就是当前service可以负载到的服务入口
[root@master ~]# kubectl describe svc service-clusterip -n dev
Name:              service-clusterip
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                10.97.97.97
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>
 
# 查看ipvs的映射规则
[root@master ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0
 
# 访问10.97.97.97:80观察效果
[root@master ~]# curl 10.97.97.97:80
10.244.2.33

HeadLiness类型的Service

在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。
service定义

apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
  type: ClusterIP
  ports:
  - port: 80    
    targetPort: 80

 

# 创建service
[root@master ~]# kubectl create -f service-headliness.yaml
service/service-headliness created
 
# 获取service, 发现CLUSTER-IP未分配
[root@master ~]# kubectl get svc service-headliness -n dev -o wide
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-headliness   ClusterIP   None         <none>        80/TCP    11s   app=nginx-pod
 
# 查看service详情
[root@master ~]# kubectl describe svc service-headliness  -n dev
Name:              service-headliness
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                None
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>
 
# 查看域名的解析情况
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
 
[root@master ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33

NodePort类型的Service

 

创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将service的端口映射到Node的一个端口上(在每个Node上都会公开一个固定的端口,通过该端口可以访问Service。在Node的IP地址上可以通过该端口直接访问Service),然后就可以通过NodeIp:NodePort来访问service了
定义

比如测试的时候本机想访问测试环境redis

apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # service类型
  ports:
  - port: 80
    nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配
    targetPort: 80

 

# 创建service
[root@master ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created
 
# 查看service
[root@master ~]# kubectl get svc -n dev -o wide
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)       SELECTOR
service-nodeport   NodePort   10.105.64.191   <none>        80:30002/TCP  app=nginx-pod
 
# 接下来可以通过电脑主机的浏览器去访问集群中任意一个nodeip的30002端口,即可访问到pod

LoadBalancer类型的Service

 LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。

LoadBalancer 类型的 Service 在 Kubernetes 中主要用于将服务公开到云平台的外部,并通过云提供商的负载均衡器来处理流量。这个类型的 Service 通常在云环境中使用,例如 AWS、GCP、Azure 等,因为这些云提供商可以提供负载均衡器服务。

使用 LoadBalancer 类型的 Service 时,确保你的云平台支持该功能,并且你有相应的权限。不同的云提供商可能有一些细微的差异,因此需要查阅相应的文档以确保正确配置。

 

apiVersion: v1
kind: Service
metadata:
  name: my-loadbalancer-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

 

ExternalName类型的Service

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

yml定义

apiVersion: v1
kind: Service
metadata:
  name: service-externalname
  namespace: dev
spec:
  type: ExternalName # service类型
  externalName: www.baidu.com  #改成ip地址也可以
# 创建service
[root@master ~]# kubectl  create -f service-externalname.yaml
service/service-externalname created
 
# 域名解析
[root@master ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com.          30      IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       30      IN      A       39.156.66.18
www.a.shifen.com.       30      IN      A       39.156.66.14

 

service如何访问到pod

1.访问到service(创建service如果指定了selector会自动创建对应的endpoint)

2.找到对应的endpoint

3.通过iptables找到路由到对应节点的kube-proxy

4.通过kube-proxy路由到对应的pod

 

 

如何定义一个sevice

apiVersion: v1
kind: Service
metadata:
  name: "order"
  namespace: "test"
spec:
  selector:
    app: "order"
  type: ClusterIP
  ports:
    - name: order-http
      port: 8000
      targetPort: 8000
    - name: order-grpc
      port: 8001
      targetPort: 8001

 

apiVersion: v1
kind: Service
metadata:
  name: my-service  # 服务的名称
spec:
  selector:
    app: my-app  # 选择器,指定服务所匹配的Pod
  ports:
    - protocol: TCP  # 服务使用的协议
      port: 80  # 服务暴露的端口
      targetPort: 8080  # 服务转发到Pod的端口
  type: NodePort  # 服务的类型,可以是NodePort、ClusterIP、LoadBalancer等
  # 以下是可选的配置
  # loadBalancerIP: 192.168.1.100  # 如果服务类型为LoadBalancer,可以指定负载均衡器的IP
  # externalTrafficPolicy: Local  # 控制流量的策略,可以是Local或者Cluster

相关命令

根据yml创建service
kubectl apply -f service.yaml
查看所有service: kubectl get services 查看特定service的详细信息: kubectl describe service
<service-name> 删除一个service: kubectl delete service <service-name> 编辑一个service: kubectl edit service <service-name> 替换一个service,通过新的YAML文件: kubectl replace -f <new-service-definition.yaml>

如何跨命名空间访问

wget http://nginx-svc.{namespace}

如果自定义endpint 

应用场景,转发请求到外部、比如迁移应用到k8s 一部分应用是k8s 一部分非k8s

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

 

apiVersion: v1
kind: Endpoints
metadata:
  name: my-service 与service一致
subsets:
  - addresses:
      - ip: 192.168.1.1  # 转发的地址
    ports:
      - port: 8080  # 替换为你的Pod的端口
kubectl apply -f service.yaml
kubectl apply -f endpoints.yaml

service如何代理到域名 

apiVersion: v1
kind: Service
metadata:
  name: my-external-service
spec:
  type: ExternalName
  externalName: example.com  # 将此处替换为你的域名
  ports:
    - port: 80

属性介绍

type

ClusterIp 集群内部使用 转发ip(默认)

ExternalName 域名方式

NodePort

LoadBalancer

posted @ 2023-12-13 15:29  意犹未尽  阅读(43)  评论(0编辑  收藏  举报