# 发布Service

Kubernetes Service 支持的不同访问方式。

# Service 类型

Kubernetes 中可以通过不同方式发布 Service,通过 ServiceType 字段指定,该字段的默认值是 ClusterIP,可选值有:

  • ClusterIP: Kuboard 已支持 默认值。通过集群内部的一个 IP 地址暴露 Service,只在集群内部可以访问

  • NodePort: Kuboard 已支持 通过每一个节点上的的静态端口(NodePort)暴露 Service,同时自动创建 ClusterIP 类型的访问方式

    • 在集群内部通过 $(ClusterIP): $(Port) 访问
    • 在集群外部通过 $(NodeIP): $(NodePort) 访问
  • LoadBalancer: Kuboard 不支持 通过云服务供应商(AWS、Azure、GCE 等)的负载均衡器在集群外部暴露 Service,同时自动创建 NodePort 和 ClusterIP 类型的访问方式

    • 在集群内部通过 $(ClusterIP): $(Port) 访问
    • 在集群外部通过 $(NodeIP): $(NodePort) 访问
    • 在集群外部通过 $(LoadBalancerIP): $(Port) 访问

    替代方案

    • LoadBalancer 类型的 Service,可以自动调用云服务商在 IaaS 层面的接口,并自动创建 LoadBalancer,将其指向该 Service。
    • 由于 Kuboard 不限定云服商,因此不能实现此类型的 Service,建议用户先创建 NodePort 类型的 Service,再手工创建 LoadBalancer,将其配置到各节点上对应的 Service 的 NodePort。此操作最终效果与 LoadBalancer 类型 Service 的效果相同
  • ExternalName: Kuboard 暂不支持 将 Service 映射到 externalName 指定的地址(例如:foo.bar.example.com),返回值是一个 CNAME 记录。不使用任何代理机制。

    TIP

    如使用 ExternalName 类型的 Service,CoreDNS 版本不能低于 1.7

# ClusterIP

ClusterIP 是 ServiceType 的默认值。在 Iptables 代理模式 中,详细讲述了 ClusterIP 类型 Service 的工作原理。

# NodePort

对于 NodePort 类型的 Service,Kubernetes 为其分配一个节点端口(对于同一 Service,在每个节点上的节点端口都相同),该端口的范围在初始化 apiserver 时可通过参数 --service-node-port-range 指定(默认是:30000-32767),参考 修改NodePort的范围。节点将该端口上的网络请求转发到对应的 Service 上。可通过 Service 的 .spec.ports[*].nodePort 字段查看该 Service 分配到的节点端口号。

在启动 kube-proxy 时使用参数 --nodeport-address 可指定阶段端口可以绑定的 IP 地址段。该参数接收以逗号分隔的 CIDR 作为参数值(例如:10.0.0.0/8,192.0.2.0/25),kube-proxy 将查找本机符合该 CIDR 的 IP 地址,并将节点端口绑定到符合的 IP 地址上。

例如,

  • 如果启动 kube-proxy 时指定了参数 --nodeport-address=127.0.0.0/8,则 kube-proxy 只将阶段端口绑定到 loopback 地址上。
  • --nodeport-address 的默认值是一个空列表。则 kube-proxy 将节点端口绑定到该节点所有的网络 IP 地址上。

您可以通过 nodePort 字段指定节点端口号(必须在 --service-node-port-range 指定的范围内)。Kubernetes 在创建 Service 时将使用该节点端口,如果该端口已被占用,则创建 Service 将不能成功。在这种情况下,您必须自己规划好端口使用,以避免端口冲突。

使用 NodePort,您可以:

  • 根据自己的需要配置负载均衡器
  • 配置 Kubernetes / 非 Kubernetes 的混合环境
  • 直接暴露一到多个节点的 IP 地址,以便客户端可访问 Kubernetes 中的 Service

NodePort 类型的 Service 可通过如下方式访问:

  • 在集群内部通过 $(ClusterIP): $(Port) 访问
  • 在集群外部通过 $(NodeIP): $(NodePort) 访问

# LoadBalancer

在支持外部负载均衡器的云环境中(例如 GCE、AWS、Azure 等),将 .spec.type 字段设置为 LoadBalancer,Kubernetes 将为该Service 自动创建一个负载均衡器。负载均衡器的创建操作异步完成,您可能要稍等片刻才能真正完成创建,负载均衡器的信息将被回写到 Service 的 .status.loadBalancer 字段。如下所示:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 146.148.47.155
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

发送到外部负载均衡器的网络请求就像被转发到 Kubernetes 中的后端 Pod 上。负载均衡的实现细节由各云服务上确定。

由于 Kuboard 不限定 Kubernetes 是运行在裸机上、私有云上或者是公有云上,因此 Kuboard 暂不支持 LoadBalancer 类型的 Service。关于更多 LoadBalancer Service 相关的描述,请参考 Type LoadBalancer 和您所使用的云供应商的文档

# ExternalName

ExternalName 类型的 Service 映射到一个外部的 DNS name,而不是一个 pod label selector。可通过 spec.externalName 字段指定外部 DNS name。

下面的例子中,名称空间 prod 中的 Service my-service 将映射到 my.database.example.com

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com
1
2
3
4
5
6
7
8

执行 nslookup my-service.prod.svc.cluster.local 指令时,集群的 DNS 服务将返回一个 CNAME 记录,其对应的值为 my.database.example.com。访问 my-service 与访问其他类型的 Service 相比,网络请求的转发发生在 DNS level,而不是使用 proxy。如果您在后续想要将 my.database.example.com 对应的数据库迁移到集群内部来,您可以按如下步骤进行:

  1. 在 Kubernetes 中部署数据库(并启动数据库的 Pod)
  2. 为 Service 添加合适的 selector 和 endpoint
  3. 修改 Service 的类型

注意事项

  • ExternalName 可以接受一个 IPv4 地址型的字符串作为 .spec.externalName 的值,但是这个字符串将被认为是一个由数字组成的 DNS name,而不是一个 IP 地址。
  • 如果要 hardcode 一个 IP 地址,请考虑使用 headless Service

# External IP

如果有外部 IP 路由到 Kubernetes 集群的一个或多个节点,Kubernetes Service 可以通过这些 externalIPs 进行访问。externalIP 需要由集群管理员在 Kubernetes 之外配置。

在 Service 的定义中, externalIPs 可以和任何类型的 .spec.type 一通使用。在下面的例子中,客户端可通过 80.11.12.10:80 (externalIP:port) 访问my-service

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
  externalIPs:
    - 80.11.12.10
1
2
3
4
5
6
7
8
9
10
11
12
13
14