> 文档中心 > 【云原生丨Kubernetes系列14】深入学习抽象对象 Service

【云原生丨Kubernetes系列14】深入学习抽象对象 Service


写在前面

Service 是⼀种抽象的对象, 它定义了⼀组 Pod 的逻辑集合和⼀个⽤于访问它们的策略,其实这个概念和微服务⾮常类似。⼀ 个 Serivce 下⾯包含的 Pod 集合⼀般是由 Label Selector 来决定的。

在这里插入图片描述

文章目录

  • 写在前面
  • 三种IP
  • 定义 Service
  • kube-proxy
  • Service 类型
  • NodePort 类型
  • ExternalName

三种IP

在继续往下学习 Service 之前,我们需要先弄明⽩ Kubernetes 系统中的三种IP这个问题,因为经常有同学混乱。

  • Node IP: Node 节点的 IP 地址
  • Pod IP: Pod 的IP地址
  • Cluster IP: Service 的 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 来组成⼀个可以通信的服务。


定义 Service

定义 Service 的⽅式和我们前⾯定义的各种资源对象的⽅式类型,例如,假定我们有⼀组 Pod 服务, 它们对外暴露了 8080 端⼝,同时都被打上了 app=myapp 这样的标签,那么我们就可以像下⾯这样来 定义⼀个 Service 对象:

apiVersion: v1 kind: Service metadata: name: myservice spec: selector: app: myapp ports: - protocol: TCP port: 80 targetPort: 8080 name: myapp-http 

然后通过的使⽤ kubectl create -f myservice.yaml 就可以创建⼀个名为 myservice 的 Service 对 象,它会将请求代理到使⽤ TCP 端⼝为 8080,具有标签 app=myapp 的 Pod 上,这个 Service 会被 系统分配⼀个我们上⾯说的 Cluster IP ,该 Service 还会持续的监听 selector 下⾯的 Pod ,会把 这些 Pod 信息更新到⼀个名为 myservice 的 Endpoints 对象上去,这个对象就类似于我们上⾯说 的 Pod 集合了。

需要注意的是, Service 能够将⼀个接收端⼝映射到任意的 targetPort 。 默认情况 下, targetPort 将被设置为与 port 字段相同的值。 可能更有趣的是,targetPort 可以是⼀个字符串,引⽤了 backend Pod 的⼀个端⼝的名称。 因实际指派给该端⼝名称的端⼝号,在每个 backend Pod 中可能并不相同,所以对于部署和设计 Service ,这种⽅式会提供更⼤的灵活性。

另外 Service 能够⽀持 TCP 和 UDP 协议,默认是 TCP 协议。


kube-proxy

前⾯我们讲到过,在 Kubernetes 集群中,每个 Node 会运⾏⼀个 kube-proxy 进程, 负责 为 Service 实现⼀种 VIP(虚拟 IP,就是我们上⾯说的 clusterIP )的代理形式,现在 的 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”)。

另外需要了解的是如果最开始选择的 Pod 没有响应,iptables 代理能够⾃动地重试另⼀个 Pod,所以 它需要依赖 readiness probes。

【云原生丨Kubernetes系列14】深入学习抽象对象 Service

Service 类型

我们在定义 Service 的时候可以指定⼀个⾃⼰需要的类型的 Service ,如果不指定的话默认是 ClusterIP 类型。

我们可以使⽤的服务类型如下:

  • 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”,Kubernetes master 将从给定的配置范围内(默认:30000- 32767)分配端⼝,每个 Node 将从该端⼝(每个 Node 上的同⼀端⼝)代理到 Service。该端⼝将通 过 Service 的 spec.ports[*].nodePort 字段被指定,如果不指定的话会⾃动⽣成⼀个端⼝。

需要注意的是,Service 将能够通过 :spec.ports[].nodePortspec.clusterIp:spec.ports[].port ⽽对外可⻅。

接下来我们来给⼤家创建⼀个 NodePort 的服务来访问我们前⾯的 Nginx 服务:(保存为service- demo.yaml)

apiVersion: v1 kind: Service metadata: name: myservice spec: selector: app: myapp type: NodePort ports: - protocol: TCP port: 80 targetPort: 80 name: myapp-http

创建该 Service :

$ kubectl create -f service-demo.yaml

然后我们可以查看 Service 对象信息:

$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 27d myservice NodePort 10.104.57.198 <none> 80:32560/TCP 14h 

我们可以看到 myservice 的 TYPE 类型已经变成了 NodePort ,后⾯的 PORT(S) 部分也多了⼀个 32560 的映射端⼝。


ExternalName

ExternalName 是 Service 的特例,它没有 selector,也没有定义任何的端⼝和 Endpoint。 对于运⾏在集群外部的服务,它通过返回该外部服务的别名这种⽅式来提供服务。

kind: Service apiVersion: v1 metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com 

当查询主机 my-service.prod.svc.cluster.local (后⾯服务发现的时候我们会再深⼊讲解)时,集群的 DNS 服务将返回⼀个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的⼯作⽅式与 其它的相同,唯⼀不同的是重定向发⽣在 DNS 层,⽽且不会进⾏代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 SelectorEndpoint,修改 Service 的 type,完全不需要修改调⽤的代码,这样就完全解耦了。


在这里插入图片描述

组词