AIOps 一场颠覆传统运维的盛筵
686
2022-10-12
Kubernetes服务发现Service(一)
1. Service 为何存在
Kubernetes集群就为我们提供了这样的一个对象 - Service,Service是一种抽象的对象,它定义了一组Pod的逻辑集合和一个用于访问它们的策略,其实这个概念和微服务非常类似。一个Serivce下面包含的Pod集合一般是由Label Selector来决定的。
我们这样就可以不用去管后端的Pod如何变化,只需要指定Service的地址就可以了,因为我们在中间添加了一层服务发现的中间件,Pod销毁或者重启后,把这个Pod的地址注册到这个服务发现中心去。Service的这种抽象就可以帮我们达到这种解耦的目的。
2. Kubernetes的三种IP
Node IP: Node节点的IP地址Cluster IP: Service的IP地址Pod IP: Pod的IP地址
Node IP是Kubernetes集群中节点的物理网卡IP地址(一般为内网),属于这个网络的服务器之间都可以直接通信,所以Kubernetes集群外要想访问Kubernetes集群内部的某个节点或者服务,肯定得通过Node IP进行通信(这个时候一般是通过外网IP了)Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的(我们这里使用的是flannel这种网络插件保证所有节点的Pod IP不会冲突)Cluster IP是一个虚拟的IP,仅仅作用于Kubernetes Service这个对象,由Kubernetes自己来进行管理和分配地址,当然我们也无法ping这个地址,他没有一个真正的实体对象来响应,他只能结合Service Port来组成一个可以通信的服务,相当于VIP地址,来代理后端服务。
3. 创建Service
现在我们先创建一组Pod
---apiVersion: apps/v1beta1kind: Deploymentmetadata: name: httpd labels: app: httpd-demospec: replicas: 3 template: metadata: labels: app: httpd spec: containers: - name: httpd image: httpd ports: - containerPort: 80
上面这个Pod 我们定义了三个副本,标签为 app=httpd,下面我们来创建这个Pod的Service,通过spec.selector来指定标签为httpd的Pod。
其中被selector 选中的 Pod,就称为 Service 的Endpoints,可以使用 kubectlgetep 命令查看。
[root@master testyml]# kubectl get epNAME ENDPOINTS AGEfuseim.pri-ifs
我们可以看到 httpd-svc的后端端点IP 10.244.0.24:80,10.244.1.112:80,10.244.2.2:80,也就是httpd应用Pod的IP地址。需要注意的是,只有处于Running状态且readlinessProbe检查通过的Pod,才会出现在Service的Endpoints列表里,同样当某个Pod出现问题时,Service也会把这个Pod从Endpoints中摘掉。
现在我们创建上面Pod集合所对应的Service
apiVersion: v1kind: Servicemetadata: name: httpd-svcspec: selector: app: httpd ports: - protocol: TCP port: 8080 targetPort: 80
[root@master testyml]# kubectl create -f svc.yml service "httpd-svc" created[root@master testyml]# kubectl get svc --all-namespacesNAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEblog mysql ClusterIP 10.101.134.172
现在我们看到已经创建了名为 httpd-svc的Service,分配的Cluster IP 为 10.108.195.47,该Service还会持续的监听selector下面的Pod,会把这些Pod信息更新到一个名为httpd-svc的Endpoints对象上去,这个对象就类似于我们上面说的Pod集合了。
需要注意的是,Service能够将一个接收端口映射到任意的targetPort。 默认情况下,targetPort将被设置为与port字段相同的值。 可能更有趣的是,targetPort 可以是一个字符串,引用了 backend Pod 的一个端口的名称。 因实际指派给该端口名称的端口号,在每个 backend Pod 中可能并不相同,所以对于部署和设计 Service ,这种方式会提供更大的灵活性。
另外Service能够支持 TCP 和 UDP 协议,默认是 TCP 协议。
4. kube-proxy
在Kubernetes集群中,每个Node会运行一个kube-proxy进程, 负责为Service实现一种 VIP(虚拟 IP,就是clusterIP)的代理形式。Service其实是由kube-proxy组件和Iptables来实现的。
现在的Kubernetes中默认是使用的iptables这种模式来代理这种模式,kube-proxy会监视Kubernetes master对 Service 对象和 Endpoints (端点)对象的添加和移除。 对每个 Service,它会添加上 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend(后端) 中的某一个个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod。
默认的策略是,随机选择一个 backend。 我们也可以实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。
5. Service 的类型
ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的ServiceType。NodePort:通过每个 Node 节点上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 :,可以从集群的外部访问一个 NodePort 服务。LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务,这个需要结合具体的云厂商进行操作。ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
NodePort
如果设置 type 的值为 "NodePort",端口可以通过 Service 的 spec.ports[*].nodePort 字段来指定,如果不指定的话会自动生成一个端口,Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 Service。
需要注意的是,Service 将能够通过 :spec.ports[].nodePort 和 spec.clusterIp:spec.ports[].port 而对外可见。
现在我们修改上面的Service,将Cluster IP 改为NodePort,指定nodeport为30088
apiVersion: v1kind: Servicemetadata: name: httpd-svcspec: selector: app: httpd type: NodePort ports: - protocol: TCP port: 80 targetPort: 80 nodePort: 30088
现在我们更新这个Service
[root@master testyml]# kubectl apply -f svc.yml Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl applyservice "httpd-svc" configured[root@master testyml]# kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEhttpd-svc NodePort 10.108.195.47
ExternalName
ExternalName 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
kind: ServiceapiVersion: v1metadata: name: my-service namespace: prodspec: type: ExternalName externalName: my.database.example.com
当查询主机 my-service.prod.svc.cluster.local ,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type,完全不需要修改调用的代码,这样就完全解耦了。
发表评论
暂时没有评论,来抢沙发吧~