# k8s 创建一个外部负载均衡器
本文描述如何创建一个外部负载均衡设备。
当创建一个服务时,你可以选择自动创建一个云端的网路负载均衡器。这能为外部提供一个可达的 IP 地址,这个地址会将数据传递到集群节点上正确的服务端口,但前提是,集群运行在一个可支持负载均衡的环境中,并且对云端但负载均衡正确地进行了配置。
# 开始之前
- 您需要有一个 k8s 集群,并且必须配置 kubectl 命令行工具以与集群通信。如果您还没有集群,可以使用 Minikube 创建一个集群。
# 配置文件
创建外部负载均衡器时,请把下面几行添加到 服务配置文件中:
"type": "LoadBalancer"
配置文件类似于:
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "example-service"
},
"spec": {
"ports": [{
"port": 8765,
"targetPort": 9376
}],
"selector": {
"app": "example"
},
"type": "LoadBalancer"
}
}
# 使用 kubectl
或者也可以使用 kubectl expose 命令和 --type=LoadBalancer 参数创建服务:
kubectl expose rc example --port=8765 --target-port=9376 \
--name=example-service --type=LoadBalancer
这个命令使用了相同的 selector 作为关联资源来创建服务(在上面这个例子中,replication controller 的名字为 example):
# 查看 IP 地址
通过 kubectl 可以查看到所创建服务的 IP 地址:
kubectl describe services example-service
输出应该是这样的:
Name: example-service
Selector: app=example
Type: LoadBalancer
IP: 10.67.252.103
LoadBalancer Ingress: 123.45.678.9
Port: <unnamed> 80/TCP
NodePort: <unnamed> 32445/TCP
Endpoints: 10.64.0.4:80,10.64.1.5:80,10.64.2.4:80
Session Affinity: None
No events.
IP 地址会显示在 LoadBalancer Ingress 右侧。
# 保留客户端源 IP
因为对于这个特性的代码实现的关系,目标容器看到的源 IP 不是客户端的源 IP。如需要保留客户端源 IP,可以配置一下这些服务的 spec 字段(GCE/GKE 环境中可支持):
- service.spec.externalTrafficPolicy - 如果这个服务需要将外部流量路由到 本地节点或者集群级别的端点,那么需要指明该参数。存在两种选项:”Cluster”(默认)和 “Local”。 “Cluster” 隐藏源 IP 地址,可能会导致第二跳(second hop)到其他节点,但是全局负载效果较好。”Local” 保留客户端源 IP 地址,避免 LoadBalancer 和 NodePort 类型服务的第二跳,但是可能会导致负载不平衡。
- service.spec.healthCheckNodePort - 定义服务的 healthCheckNodePort (数字端口号)。 如果没有声明,服务 API 后端会用分配的 nodePort 创建 healthCheckNodePort。如果客户端 指定了 nodePort,则会使用用户自定义值。这只有当类型被设置成 “LoadBalancer” 并且 externalTrafficPolicy 被设置成 “Local” 时,才会生效。
可用通过将服务的配置文件中的 externalTrafficPolicy 参数设置为 “Local” 来激活这个特性。
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "example-service",
},
"spec": {
"ports": [{
"port": 8765,
"targetPort": 9376
}],
"selector": {
"app": "example"
},
"type": "LoadBalancer",
"externalTrafficPolicy": "Local"
}
}
# 功能特性可用性
k8s 版本 | 特性功能 |
---|---|
1.7+ | 支持全 API 字段 |
1.5 - 1.6 | 支持 Beta Annotations |
<1.5 | 不支持 |
可以在下面找到已经被废弃的 Beta 注释(annotation),它们用于在老版本中开启这个功能。 新版本的 k8s 将会在 v1.7 后停止支持这些注释。请更新现有应用直接使用字段(field)。
- service.beta.kubernetes.io/external-traffic 注释
<-> service.spec.externalTrafficPolicy
字段 - service.beta.kubernetes.io/healthcheck-nodeport 注释
<-> service.spec.healthCheckNodePort
字段
service.beta.kubernetes.io/external-traffic 注释相比 service.spec.externalTrafficPolicy 字段而言有一套不同的值。值的对应关系如下:
- “OnlyLocal” 用于注释 <-> “Local” 用于字段
- “Global” 用于注释 <-> “Cluster” 用于字段
注意这个特性目前并未在所有云平台/环境中实现。
# 外部负载均衡提供者
非常需要注意的是,这个功能的数据路径(datapath)是由 k8s 集群的外部负载均衡器提供的。
当服务类型被设置为 LoadBalancer 时,k8s 提供的功能等同于 type=<ClusterIP>
在集群中对于 pod 的功能,并且通过使用 k8s 虚拟机入口对负载均衡器(k8s 外部的)编程来对其进行扩展。 k8s 服务控制器自动创建外部负载均衡器,健康检查(如果需要的话),防火墙规则(如果需要的话)并且获取云服务提供商分配的外部 IP 并将其存入服务对象中。
# 使用保留源 IP 的警告和限制
GCE/AWS 负载均衡器不为目标池提供权重。这对于旧的负载均衡 kube-proxy 规则而言不是问题,旧的负载均衡规则依然能正确地平衡所有端点的流量。
新功能中,外部的流量不会按照 pod 平均分配,而是在节点(node)层面平均分配(因为 GCE/AWS 和其他外部负载均衡实现没有能力做节点权重,而是平均地分配给所有目标节点,忽略每个节点上所拥有的 pod 数量)。
然而,在 pod 数量(NumServicePods) « 节点数(NumNodes)或者 pod 数量(NumServicePods) » 节点数(NumNodes)的情况下,即使没有权重策略,我们也可以看到非常接近公平分发的场景。
一旦外部负载均衡提供了权重,这个功能可以添加到负载均衡的编程路径中。 未来的工作:1.4 版本对于权重没有支持,但是可能会在未来加入这个功能
内部 pod 对 pod 的流量应该与 ClusterIP 服务类似,流量对于所有 pod 是均分的。