本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
使用 KPNG 编写专门的 kube-proxier
本文将向您展示如何使用 Kubernetes Proxy NG kpng 创建一个专业的服务 kube-proxy 风格的网络代理器,而不会干扰现有的 kube-proxy。kpng 项目旨在更新默认的 Kubernetes Service 实现,即“kube-proxy”。kpng 的一个重要特性是它可以作为一个库在 K8s 之外创建代理器。虽然这对于取代 kube-proxy 的 CNI 插件很有用,但它也为任何人创建特殊用途的代理器提供了可能性。
定义一个使用专用代理器的服务
apiVersion: v1
kind: Service
metadata:
name: kpng-example
labels:
service.kubernetes.io/service-proxy-name: kpng-example
spec:
clusterIP: None
ipFamilyPolicy: RequireDualStack
externalIPs:
- 10.0.0.55
- 1000::55
selector:
app: kpng-alpine
ports:
- port: 6000
如果定义了 service.kubernetes.io/service-proxy-name
标签,kube-proxy
将忽略该服务。自定义控制器可以监视带有设置为其自身名称(在此示例中为“kpng-example”)的标签的服务,并设置专门的负载均衡。
service.kubernetes.io/service-proxy-name
标签并非新功能,但迄今为止,编写专用代理器一直相当困难。
专用代理器的常见用途被认为是处理 K8s 不支持的某些用例的外部流量。在这种情况下,不需要 ClusterIP
,因此在此示例中我们使用“无头”服务。
使用 kpng 的专用代理器
基于 kpng 的代理器由处理所有 K8s API 相关功能的 kpng
控制器和一个实现负载均衡的“后端”组成。后端可以与 kpng
控制器二进制文件链接,也可以是使用 gRPC 与控制器通信的单独程序。
kpng kube --service-proxy-name=kpng-example to-api
这将启动 kpng
控制器,并告诉它只监视带有“kpng-example”服务代理名称的服务。“to-api”参数将打开一个 gRPC 服务器用于后端。
您可以在集群外部自行测试。请参阅下面的示例。
现在我们启动一个后端,它只打印来自控制器的更新。
$ kubectl apply -f kpng-example.yaml
$ kpng-json | jq # (this is the backend)
{
"Service": {
"Namespace": "default",
"Name": "kpng-example",
"Type": "ClusterIP",
"IPs": {
"ClusterIPs": {},
"ExternalIPs": {
"V4": [
"10.0.0.55"
],
"V6": [
"1000::55"
]
},
"Headless": true
},
"Ports": [
{
"Protocol": 1,
"Port": 6000,
"TargetPort": 6000
}
]
},
"Endpoints": [
{
"IPs": {
"V6": [
"1100::202"
]
},
"Local": true
},
{
"IPs": {
"V4": [
"11.0.2.2"
]
},
"Local": true
},
{
"IPs": {
"V4": [
"11.0.1.2"
]
}
},
{
"IPs": {
"V6": [
"1100::102"
]
}
}
]
}
一个真正的后端将使用某种机制来平衡从外部 IP 到端点的流量。
编写后端
kpng-json
后端如下所示:
package main
import (
"os"
"encoding/json"
"sigs.k8s.io/kpng/client"
)
func main() {
client.Run(jsonPrint)
}
func jsonPrint(items []*client.ServiceEndpoints) {
enc := json.NewEncoder(os.Stdout)
for _, item := range items {
_ = enc.Encode(item)
}
}
(是的,这就是整个程序)
当然,一个真正的后端会复杂得多,但这说明了 kpng
如何让您专注于负载均衡。
您可以将多个后端连接到 kpng
控制器,因此在开发或调试期间,让 kpng-json
后端之类的东西与您的真实后端并行运行可能很有用。
示例
完整示例可在此处找到。
作为示例,我们实现了一个“all-ip”后端。它将所有外部 IP 的流量定向到本地端点,无论端口和上层协议如何。针对此功能有一个 KEP,本示例是一个大大简化的版本。
要将所有来自外部地址的流量定向到本地 POD,只需一个 iptables 规则,例如:
ip6tables -t nat -A PREROUTING -d 1000::55/128 -j DNAT --to-destination 1100::202
如您所见,地址在对后端的调用中,它所要做的就是
- 提取
Local: true
的地址 - 为
ExternalIPs
设置 iptables 规则
执行此操作的脚本可能如下所示
xip=$(cat /tmp/out | jq -r .Service.IPs.ExternalIPs.V6[0])
podip=$(cat /tmp/out | jq -r '.Endpoints[]|select(.Local == true)|select(.IPs.V6 != null)|.IPs.V6[0]')
ip6tables -t nat -A PREROUTING -d $xip/128 -j DNAT --to-destination $podip
假设上述 JSON 输出存储在 /tmp/out
中(jq 是一个很棒的程序!)。
由于这是一个示例,我们通过使用上述 kpng-json
后端的一个小变体来简化自身。不是只打印,而是调用一个程序,并将 JSON 输出作为 stdin
传递给该程序。后端可以独立测试
CALLOUT=jq kpng-callout
其中 jq
可以替换为您自己的程序或脚本。脚本可能看起来像上面的示例。有关更多信息和完整示例,请参阅 https://github.com/kubernetes-sigs/kpng/tree/master/examples/pipe-exec。
总结
虽然 kpng 仍处于早期开发阶段,但本文旨在展示您将来如何构建自己的专用 K8s 代理器。您的应用程序唯一需要做的就是在 Service manifest 中添加 service.kubernetes.io/service-proxy-name
标签。
将新功能引入 kube-proxy
是一个繁琐的过程,而且它们很可能被拒绝,因此编写专用代理器可能是唯一的选择。