6

一文搞懂什么是kubernetes Service - 梨花海棠

 2 years ago
source link: https://www.cnblogs.com/xunweidezui/p/16555854.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.什么是Service?

在kubernets中,Pod是应用程序的载体,Pod你可以想象成就是容器,为动态的一组Pod提供一个固定的访问入口,它是以一种叫ClusterIP地址来进行标识,而ClusterIP就位于我们集群网络(Cluster Network)当中,我们可以通过Pod的IP地址来进行访问,但是会遇到问题:

  1. 动态Pod的IP地址不是固定的,一旦Pod异常退出、节点故障,则会发生Pod重建,一旦发生重建客户端则会访问失败;
  2. Pod如果扩容多个,会造成客户端无法有效使用新增的Pod,如果Pod进行缩容则会造成客户端访问错误;
  3. 官方文档: https://kubernetes.io/zh-cn/docs/concepts/services-networking/service/

1.2 Service能干什么?

  1. 为了解决这个问题,K8s提供了Service资源,Service为动态的一组Pod提供一个固定的访问入口;这个固定的访问入口可以理解为是一组应用的前端的负载均衡器;就像LVS或Nginx为一组ReadyServer做负载均衡器是一样的,你是看不见的,可以理解为Service就是负载均衡器,但是这种负载均衡器比传统的负载均衡器要强大;Service资源通过"标签选择器Label Selector"把筛选出来的符合条件的一组Pod对象定义成一个逻辑集合,而后Service对外提供自己的IP和端口。

    image
  2. 当客户端请求Service的IP和端口时,Service将请求调度给标签匹配的所有的Pod,Service向客户端隐藏了真实处理请求的Pod资源,使得客户端的请求看上去是由Service直接处理并进行响应。

  3. Service对象的IP地址(Cluster IP或Service IP)是虚拟IP地址,由kubernetes系统在Service对象创建时在专有网络(Service Network)地址中自动分配或由用户手动指定,其次Service是基于端口过滤,并根据事先定义好的规则将请求转发至其后端Pod对应的端口上,因此这种代理机制也称为"端口代理"或"四层代理"工作在TCP/IP协议栈的传输层;

1.3 Service的作用?

  1. 暴露流量,让用户可以通过ServiceIP+ServicePort访问后端的Pod应用;
  2. 负载均衡: 提供基于4层的TCP/IP负载均衡,并不提供HTTP/HTTPS等负载均衡;
  3. 服务发现: 当发现新增Pod则自动加入至Service的后端,如发现Pod异常则会踢出Service后端;

1.4 Service的工作逻辑;

  1. Service持续监视API-Server,监视Service标签选择器所匹配的后端的Pod,并实时跟踪这些Pod对象的变动情况,例如IP地址的变化、Pod对象增加或减少;
  2. Service并不直接与Pod建立关联关系,他们之间还有一个中间层Endpoints,Endpoints对象是一个由IP地址和端口组成的列表,这些IP地址和端口来自于Service标签选择器所匹配到的Pod,默认情况下,创建Service资源时,关联的Endpoints对象会被自动创建;

1.5 Endpoint资源

  1. 创建一个Service的时候会自动创建一个与Service同名的Endpoints,事实上,Service不但能够把标签选择器选中的Pod识别为自己的后端端点,还能够对后端端口做就绪状态检测。如果后端Pod是就绪的就把它加入到后端可用端点列表中,反之踢出;
  2. 这个功能不是Service来做的,而是Service借助一个中间组件,Endpoints也是kubernetes一个标准的资源类型;
  3. Service会自动去管理Endpoints,Endpoints真正能发挥作用的是Endpoint控制器。一旦创建一个Service,需要为Service指定的基本属性是"Label Selector"随后Service控制器会根据这个标签选择器创建一个同名的Endpoints资源,是由Sercice控制器请求创建一个同名的Endpoints资源,随后Endpoints控制器就会介入,因为有一个自己的资源需要被创建。Endpoints控制器就会使用Endpoints的标签选择器与Service一模一样,继承Service的,Endpoints控制器会根据"标签选择器"去查找多少个符合筛选的Pod。最重要的是还会检查Pod的就绪状态,真正去绑定的不是由Service做的,而是由Endpoints做的。Service只负责调度,如果关联到了。Service只是把Endpoint帮查找到的所有处于就绪状态的后端Pod告诉Service,于是成了Service的后端端点;

1.6 Servcie的实现;

  1. 在kubernetes中,Service只是一个抽象的概念,真正起作用实现负载均衡规则的其实是kube-proxy这个进程,它在每一个节点都需要运行一个kube-proxy,用来完成负载均衡规则的创建;

1.7 Kube-Proxy代理模式

1.7.1UserSpace

  1. UserSpace模式下,kube-proxy为ServiceIP创建一个监听端口,当用户向ServiceIP发起请求;首先按请求会被Iptables规则拦截,然后重定向到kube-proxy对应的端口上,然后kube-proxy根据调度算法挑选一个Pod,将请求调度到该Pod上;
  2. 该模式流量经过内核空间后,会送往用户空间Kube-Proxy进程,而后又被送回内核空间,发往调度分配的目标后端Pod;效率太差。报文先到内核空间再回到用户空间,因为报文在用户空间来回切换两次以上,
    1.7.2iptables
  3. iptables模式下,kube-proxy为Service后端的所有Pod创建对应的iptables规则,当用户向ServiceIP发起请求;首先iptables会拦截用户请求,然后直接将请求调度给后端的Pod;问题是一个Service会创建大量的Iptables规则,且不支持更高级的调度算法;
    1.7.3IPVS
    ipvs模式和iptables类似,kube-proxy为Service后端所有的Pod创建对应的IPVS规则, 一个Service只生成一条规则,所以规模较大的场景应使用IPVS。其次IPVS支持更多的高级算法;

2.Service的类型

2.1 Service资源规范

apiVersion: v1       # API的版本
kind: Service        # 资源类型定义为Service
metadata:           
  name: ...          # Serivce的名称
  namespace: ...     # 默认的default
  labels:
     key1: value1   # 标签 key:value格式;
     key2: value2
spec:
  type <string>      # Service类型,默认为ClusterIP;
  selector <map[string]string> #  标签选择器
  ports:                       #  ClusterIP:ServicePort
  targetPort: <string>  #后端目标进程的端口号或名称。
  nodePort: <integer>  # 节点端口号,仅适用于NodePort和loadbalancer类型。  "建议动态选择30000-32767" 
 clusterIP <string> # Service的集群IP,建议由系统自动分配
 externalTrafficPolicy <string> # 外部流量策略处理方式,local表示由当前节点处理,cluster表示向集群范围调度
 loadBalancerIP <string> # 外部负载均衡器使用的IP地址,仅适用于loadbalancer,前提是你的公有云得支持你自己指定;
 externalName <string> # 外部服务名称,该名称作为Service的DNS CNAME值

2.2 ClusterIP

ClusterIP: 通过集群内部IP暴露服务,选择ServiceIP只能够在集群内部访问,这也是默认的Service类型;该地址仅在集群内部可见、可达。无法被集群外部客户端访问;而且是默认类型,创建的任何Service默认就是ClusterIP类型,而且只能接受集群内部客户端的访问。

2.2.1 ClusterIP示例;

root@kubernetes-master01:~# cat services-clusterip-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-clusterip
  namespace: default
  labels:
     app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:alpine
    imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default
spec:
  clusterIP:
  selector:     # 标签选择器 
    app: nginx   
  ports:
  - name: http  # 端口名称
    protocol: TCP   # 协议类型,目前支持TCP、UDP、SCTP默认为TCP
    port: 80   # Service的端口号
    targetPort: 80  # 后端目标进程的端口号
root@kubernetes-master01:~# kubectl apply -f services-clusterip-nginx.yaml 
pod/nginx-clusterip created
service/nginx-svc created

2.2.1.1查看Service;

root@kubernetes-master01:~# kubectl get svc
nginx-svc    ClusterIP   10.106.70.164    <none>        80/TCP    3m

2.2.1.2 可以看到后端就一个Pod;

root@kubernetes-master01:~# kubectl get pods --show-labels -l app=nginx -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP            NODE                NOMINATED NODE   READINESS GATES   LABELS
nginx-clusterip   1/1     Running   0          8m58s   10.244.4.49   kubernetes-node01   <none>           <none>            app=nginx

2.2.1.3查看Endpoint资源,Endpoints可以简写为ep;

root@kubernetes-master01:~# kubectl get endpoints
nginx-svc    10.244.4.49:80                                10m

2.2.1.4测试访问;只能在集群内部访问,外部无法访问;

root@kubernetes-master01:~# curl -I  10.106.70.164
HTTP/1.1 200 OK
Server: nginx/1.21.5
Content-Type: text/html
Content-Length: 615
Connection: keep-alive
ETag: "61cb5be0-267"
Accept-Ranges: bytes

2.3 NodePort

NodePort:首先是一种ClusterIP类型,也就是说,NodePort是ClusterIP的扩展类型。NodePort类型的Service不仅仅能够被集群内部的客户端可见,还能对外部客户端可见。怎么可见呢?它会与ClusterIP的功能之外在每个节点上使用一个相同的端口号"注意是在每个节点上使用一个相同的端口号"将外部流量引入到该Service上来。

2.3.1 NodePort示例;

root@kubernetes-master01:~# cat services-nodeport-nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport-svc
  namespace: default
spec:
  type: NodePort
  clusterIP:
  selector:
    app: nginx
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 80   # 后端Pod监听什么端口就写什么端口。要不然到达Service的请求转发给Pod,Pod没有那个端口也没用。一定真正转发到后端程序监听的端口。如果没有特殊情况的话,ServicePort和TargetPort保持一致。NodePort可以不用指定。
    nodePort:        # 正常情况下应由系统自己分配,默认是3000~32767
root@kubernetes-master01:~# kubectl apply -f services-nodeport-nginx.yaml 
service/nginx-nodeport-svc created

2.3.1.1查看Service,意味着访问宿主机IP+nodeport端口就可以访问服务;

root@kubernetes-master01:~# kubectl get svc
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes           ClusterIP   10.96.0.1        <none>        443/TCP        36d
nginx-nodeport-svc   NodePort    10.111.124.121   <none>        80:32049/TCP   5s
nginx-svc            ClusterIP   10.106.70.164    <none>        80/TCP         34m

2.3.1.2 测试,这是windows的命令行,也是没有问题;

C:\Users\海棠>curl -I  10.0.0.1xx:30824
HTTP/1.1 200 OK
Server: nginx/1.21.5
Content-Type: text/html
Content-Length: 615
Connection: keep-alive
ETag: "61cb5be0-267"
Accept-Ranges: bytes

2.4 LoadBalancer

loadBalancer: 这类Service依赖云厂商,需要通过云厂商调用API接口创建软件负载均衡将服务暴露到集群外部,当创建LoadBalancer类型的Service对象时,它会在集群上自动创建一个NodePort类型的Service,集群外部的请求流量会先路由至该负载均衡,并由该负载均衡调度至各个节点的NodePort;

2.4.1 LoadBalancer示例;

root@kubernetes-master01:~# cat  services-loadbalancer-nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer-svc
  namespace: default
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 80
  loadBalancerIP: 1.2.3.4

2.4.1.2测试访问;

# 我们还是只能是通过NodePort来访问,因为没有LoadBalancer的IP;
# LoadBalancer其实就是一个增强的NodePort。而LoadBalancer没有限制流量调度策略的。外部流量策略对loadbalancer依然使用,因为LoadBalancer首先是一个NodePort的Service。
C:\Users\冷雨夜>curl -I 10.0.0.1XX:31943
HTTP/1.1 200 OK
Server: nginx/1.21.5
Content-Type: text/html
Content-Length: 615
Connection: kep-alive
ETag: "61cb5be0-267"
Accept-Ranges: bytes

2.5 ExternalName

此类型不是用来定义如何访问集群内服务的,而是把集群外部的某些服务以DNS CANME方式映射到集群内,从而让集群内的Pod资源能够访问外部服务的一种实现方式。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK