Service
This commit is contained in:
@ -307,6 +307,73 @@ DNS 的配置方式取决于该 Service 是否配置了 selector:
|
||||
* 对 ExternalName 类型的 Service,返回 CNAME 记录
|
||||
* 对于其他类型的 Service,返回与 Service 同名的 `Endpoints` 的 A 记录
|
||||
|
||||
## 虚拟 IP 的实现
|
||||
|
||||
如果只是想要正确使用 Service,不急于理解 Service 的实现细节,您无需阅读本章节。
|
||||
|
||||
### 避免冲突
|
||||
|
||||
Kubernetes 的一个设计哲学是:尽量避免非人为错误产生的可能性。就设计 Service 而言,Kubernetes 应该将您选择的端口号与其他人选择的端口号隔离开。为此,Kubernetes 为每一个 Service 分配一个该 Service 专属的 IP 地址。
|
||||
|
||||
为了确保每个 Service 都有一个唯一的 IP 地址,kubernetes 在创建 Service 之前,先更新 etcd 中的一个全局分配表,如果更新失败(例如 IP 地址已被其他 Service 占用),则 Service 不能成功创建。
|
||||
|
||||
Kubernetes 使用一个后台控制器检查该全局分配表中的 IP 地址的分配是否仍然有效,并且自动清理不再被 Service 使用的 IP 地址。
|
||||
|
||||
### Service 的 IP 地址
|
||||
|
||||
Pod 的 IP 地址路由到一个确定的目标,然而 Service 的 IP 地址则不同,通常背后并不对应一个唯一的目标。 kube-proxy 使用 iptables (Linux 中的报文处理逻辑)来定义虚拟 IP 地址。当客户端连接到该虚拟 IP 地址时,它们的网络请求将自动发送到一个合适的 Endpoint。Service 对应的环境变量和 DNS 实际上反应的是 Service 的虚拟 IP 地址(和端口)。
|
||||
|
||||
#### Userspace
|
||||
|
||||
以上面提到的图像处理程序为例。当后端 Service 被创建时,Kubernetes master 为其分配一个虚拟 IP 地址(假设是 10.0.0.1),并假设 Service 的端口是 1234。集群中所有的 kube-proxy 都实时监听者 Service 的创建和删除。Service 创建后,kube-proxy 将打开一个新的随机端口,并设定 iptables 的转发规则(以便将该 Service 虚拟 IP 的网络请求全都转发到这个新的随机端口上),并且 kube-proxy 将开始接受该端口上的连接。
|
||||
|
||||
当一个客户端连接到该 Service 的虚拟 IP 地址时,iptables 的规则被触发,并且将网络报文重定向到 kube-proxy 自己的随机端口上。kube-proxy 接收到请求后,选择一个后端 Pod,再将请求以代理的形式转发到该后端 Pod。
|
||||
|
||||
这意味着 Service 可以选择任意端口号,而无需担心端口冲突。客户端可以直接连接到一个 IP:port,无需关心最终在使用哪个 Pod 提供服务。
|
||||
|
||||
#### iptables
|
||||
|
||||
仍然以上面提到的图像处理程序为例。当后端 Service 被创建时,Kubernetes master 为其分配一个虚拟 IP 地址(假设是 10.0.0.1),并假设 Service 的端口是 1234。集群中所有的 kube-proxy 都实时监听者 Service 的创建和删除。Service 创建后,kube-proxy 设定了一系列的 iptables 规则(这些规则可将虚拟 IP 地址映射到 per-Service 的规则)。per-Service 规则进一步链接到 per-Endpoint 规则,并最终将网络请求重定向(使用 destination-NAT)到后端 Pod。
|
||||
|
||||
当一个客户端连接到该 Service 的虚拟 IP 地址时,iptables 的规则被触发。一个后端 Pod 将被选中(基于 session affinity 或者随机选择),且网络报文被重定向到该后端 Pod。与 userspace proxy 不同,网络报文不再被复制到 userspace,kube-proxy 也无需处理这些报文,且报文被直接转发到后端 Pod。
|
||||
|
||||
在使用 node-port 或 load-balancer 类型的 Service 时,以上的代理处理过程是相同的。
|
||||
|
||||
#### IPVS <Badge text="Kuboard 已支持" type="success"/>
|
||||
|
||||
在一个大型集群中(例如,存在 10000 个 Service)iptables 的操作将显著变慢。IPVS 的设计是基于 in-kernel hash table 执行负载均衡。因此,使用 IPVS 的 kube-proxy 在 Service 数量较多的情况下仍然能够保持好的性能。同时,基于 IPVS 的 kube-proxy 可以使用更复杂的负载均衡算法(最少连接数、基于地址的、基于权重的等)
|
||||
|
||||
## 支持的传输协议
|
||||
|
||||
未完,待续,【2019年9月18日 22:33】
|
||||
#### TCP <Badge text="Kuboard 已支持" type="success"/>
|
||||
|
||||
默认值。任何类型的 Service 都支持 TCP 协议。
|
||||
|
||||
#### UDP
|
||||
|
||||
大多数 Service 都支持 UDP 协议。对于 LoadBalancer 类型的 Service,是否支持 UDP 取决于云供应商是否支持该特性。
|
||||
|
||||
#### HTTP <Badge text="Kuboard 不支持" type="error"/>
|
||||
|
||||
如果您的云服务商支持,您可以使用 LoadBalancer 类型的 Service 设定一个 Kubernetes 外部的 HTTP/HTTPS 反向代理,将请求转发到 Service 的 Endpoints。
|
||||
|
||||
::: tip
|
||||
使用 Ingress
|
||||
:::
|
||||
|
||||
#### Proxy Protocol <Badge text="Kuboard 不支持" type="error"/>
|
||||
|
||||
如果您的云服务上支持(例如 AWS),您可以使用 LoadBalancer 类型的 Service 设定一个 Kubernetes 外部的负载均衡器,并将连接已 PROXY 协议转发到 Service 的 Endpoints。
|
||||
|
||||
负载均衡器将先发送描述该 incoming 连接的字节串,如下所示:
|
||||
|
||||
```
|
||||
PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n
|
||||
|
||||
```
|
||||
|
||||
然后在发送来自于客户端的数据
|
||||
|
||||
#### SCTP <Badge text="Kuboard 不支持" type="error"/>
|
||||
|
||||
尚处于 `alpha` 阶段,暂不推荐使用。如需了解,请参考 [SCTP](https://kubernetes.io/docs/concepts/services-networking/service/#sctp)
|
||||
|
||||
@ -3,7 +3,11 @@ layout: LearningLayout
|
||||
description: Kubernetes 中发布 Service 的方式,ServiceType
|
||||
---
|
||||
|
||||
# Service 类型
|
||||
# 发布 Service
|
||||
|
||||
Kubernetes Service 支持的不同访问方式。
|
||||
|
||||
## Service 类型
|
||||
|
||||
Kubernetes 中可以通过不同方式发布 Service,通过 `ServiceType` 字段指定,该字段的默认值是 `ClusterIP`,可选值有:
|
||||
|
||||
@ -31,10 +35,104 @@ Kubernetes 中可以通过不同方式发布 Service,通过 `ServiceType` 字
|
||||
|
||||
## ClusterIP
|
||||
|
||||
未完,待续,【2019年9月18日 22:56】
|
||||
ClusterIP 是 ServiceType 的默认值。在 [Iptables 代理模式](service-details.html#iptables-代理模式) 中,详细讲述了 ClusterIP 类型 Service 的工作原理。
|
||||
|
||||
## NodePort
|
||||
|
||||
对于 `NodePort` 类型的 Service,Kubernetes 为其分配一个节点端口(对于同一 Service,在每个节点上的节点端口都相同),该端口的范围在初始化 apiserver 时可通过参数 `--service-node-port-range` 指定(默认是:30000-32767)。节点将该端口上的网络请求转发到对应的 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` 字段。如下所示:
|
||||
|
||||
``` yaml
|
||||
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
|
||||
```
|
||||
|
||||
发送到外部负载均衡器的网络请求就像被转发到 Kubernetes 中的后端 Pod 上。负载均衡的实现细节由各云服务上确定。
|
||||
|
||||
由于 Kuboard 不限定 Kubernetes 是运行在裸机上、私有云上或者是公有云上,因此 Kuboard 暂不支持 LoadBalancer 类型的 Service。关于更多 LoadBalancer Service 相关的描述,请参考 [Type LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) 和您所使用的云供应商的文档
|
||||
|
||||
## ExternalName
|
||||
|
||||
ExternalName 类型的 Service 映射到一个外部的 DNS name,而不是一个 pod label selector。可通过 `spec.externalName` 字段指定外部 DNS name。
|
||||
|
||||
下面的例子中,名称空间 `prod` 中的 Service `my-service` 将映射到 `my.database.example.com`:
|
||||
|
||||
``` yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-service
|
||||
namespace: prod
|
||||
spec:
|
||||
type: ExternalName
|
||||
externalName: my.database.example.com
|
||||
```
|
||||
|
||||
执行 `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 的类型
|
||||
|
||||
::: tip 注意事项
|
||||
* ExternalName 可以接受一个 IPv4 地址型的字符串作为 `.spec.externalName` 的值,但是这个字符串将被认为是一个由数字组成的 DNS name,而不是一个 IP 地址。
|
||||
* 如果要 hardcode 一个 IP 地址,请考虑使用 [headless Service](./service-details.html#headless-services)
|
||||
:::
|
||||
|
||||
## External IP
|
||||
|
||||
如果有外部 IP 路由到 Kubernetes 集群的一个或多个节点,Kubernetes Service 可以通过这些 `externalIPs` 进行访问。`externalIP` 需要由集群管理员在 Kubernetes 之外配置。
|
||||
|
||||
在 Service 的定义中, `externalIPs` 可以和任何类型的 `.spec.type` 一通使用。在下面的例子中,客户端可通过 `80.11.12.10:80` (externalIP:port) 访问`my-service`
|
||||
|
||||
``` yaml
|
||||
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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user