k8s service

Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略——通常称为微服务。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 实现的。

举个例子,一个图片处理 backend 运行了3个副本,frontend 不需要关心它们调用了哪个 backend 副本。然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。 Service 定义的抽象能够解耦这种关联。

对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod 发生变更,应用程序就会被更新。 对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 Service,再由 Service 重定向到 backend Pod。

1. 没有 selector 的 Service

Servcie 抽象了该如何访问 Kubernetes Pod,但也能够抽象其它类型的 backend,例如:

希望在生产环境中使用外部的数据库集群,但测试环境使用自己的数据库。
希望服务指向另一个 Namespace 中或其它集群中的服务。
正在将工作负载转移到 Kubernetes 集群,和运行在 Kubernetes 集群之外的 backend。

在任何这些场景中,都能够定义没有 selector 的 Service :

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

由于这个 Service 没有 selector,就不会创建相关的 Endpoints 对象。可以手动将 Service 映射到指定的 Endpoints:

kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 9376

注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。

访问没有 selector 的 Service,与有 selector 的 Service 的原理相同。请求将被路由到用户定义的 Endpoint(该示例中为 1.2.3.4:9376)。

ExternalName Service 是 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时,集群的 DNS 服务将返回一个值为my.database.example.com 的 CNAME 记录。

访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type。

2. VIP 和 Service 代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。

在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是 iptables 代理。

在 Kubernetes v1.0 版本,Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用来表示 “7层”(HTTP)服务。

3. Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。

这个选项允许开发人员*寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。

对这类 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了 selector。

3.1 配置 Selector

对定义了 selector 的 Headless Service,Endpoint 控制器在 API 中创建了 Endpoints 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod上。

3.2 不配置 Selector

对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 Endpoints 记录。 然而 DNS 系统会查找和配置,无论是:

ExternalName 类型 Service 的 CNAME 记录
记录:与 Service 共享一个名称的任何 Endpoints,以及所有其它类型

4. 发布服务 —— 服务类型

对一些应用(如 Frontend)的某些部分,可能希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。

Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

Type 的取值以及行为如下:

ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。

NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。

LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。

ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。

4.1 ExternalName

需求:需要两个不同的namespace之间的不同pod可以通过name的形式访问

实现方式:

A:在其他pod内ping [svcname].[namespace] ping出来到结果就是svc的ip地址

B:通过externalname,把对方到[svcname].[namespace].svc.cluster.local,绑定到externalname定义到字符串上

例如:

[root@faith131 ~]# kubectl get ingresses. -n test test-213 -o yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  generation: 1
  labels:
    app: "213"
  name: test-213
  namespace: test
  resourceVersion: "1387931"
spec:
  rules:
  - host: faith.vue.manage.com
    http:
      paths:
      - backend:
          serviceName: webserver-transfer
          servicePort: 80
        path: /manage
status:
  loadBalancer: {}
         
[root@faith131 ~]# kubectl get svc -n test webserver-transfer -o yaml
apiVersion: v1
kind: Service
metadata:
  name: webserver-transfer
  namespace: test
  resourceVersion: "1151477"
spec:
  externalName: webserver.webserver.svc.cluster.local
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
    sessionAffinity: None
    type: ExternalName
status:
  loadBalancer: {}

test命名空间下,发现ingress代理的是名称为“webserver-transfer”的service,而这个service类型为ExternalName,外部名称为webserver.webserver.svc.cluster.local,表示这个svc用于连接到名为webserver的namespace的名为webserver的service,从而使得当前namespace的pod可以访问paas的paaswebserver代理的pod。

所以整个流程为:外部访问ingress,ingress找到ExternalName的service,ExternalName的service找到webserver的webserver service。

上一篇:Python编码错误处理


下一篇:DecimalFormat 的用法