河南省水利建设厅网站,清溪镇网站仿做,湛江建站价格,免费注册个人网站使用源 IP
运行在 Kubernetes 集群中的应用程序通过 Service 抽象发现彼此并相互通信#xff0c;它们也用 Service 与外部世界通信。 本文解释了发送到不同类型 Service 的数据包的源 IP 会发生什么情况#xff0c;以及如何根据需要切换此行为。
准备开始
术语表
本文使用…使用源 IP
运行在 Kubernetes 集群中的应用程序通过 Service 抽象发现彼此并相互通信它们也用 Service 与外部世界通信。 本文解释了发送到不同类型 Service 的数据包的源 IP 会发生什么情况以及如何根据需要切换此行为。
准备开始
术语表
本文使用了下列术语
NAT网络地址转换Source NAT替换数据包上的源 IP在本页面中这通常意味着替换为节点的 IP 地址Destination NAT替换数据包上的目标 IP在本页面中这通常意味着替换为 Pod 的 IP 地址VIP一个虚拟 IP 地址例如分配给 Kubernetes 中每个 Service 的 IP 地址Kube-proxy一个网络守护程序在每个节点上协调 Service VIP 管理
先决条件
你必须拥有一个 Kubernetes 的集群同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 如果你还没有集群你可以通过 Minikube 构建一个你自己的集群或者你可以使用下面的 Kubernetes 练习环境之一
Killercoda玩转 Kubernetes
示例使用一个小型 nginx Web 服务器服务器通过 HTTP 标头返回它接收到的请求的源 IP。 你可以按如下方式创建它
kubectl create deployment source-ip-app --imageregistry.k8s.io/echoserver:1.4输出为
deployment.apps/source-ip-app created教程目标
通过多种类型的 Service 暴露一个简单应用了解每种 Service 类型如何处理源 IP NAT了解保留源 IP 所涉及的权衡
TypeClusterIP类型 Service 的源 IP
如果你在 iptables 模式默认下运行 kube-proxy则从集群内发送到 ClusterIP 的数据包永远不会进行源 NAT。 你可以通过在运行 kube-proxy 的节点上获取 http://localhost:10249/proxyMode 来查询 kube-proxy 模式。
kubectl get nodes输出类似于
NAME STATUS ROLES AGE VERSION
kubernetes-node-6jst Ready none 2h v1.13.0
kubernetes-node-cx31 Ready none 2h v1.13.0
kubernetes-node-jj1t Ready none 2h v1.13.0在其中一个节点上获取代理模式kube-proxy 监听 10249 端口
# 在要查询的节点上的 Shell 中运行
curl http://localhost:10249/proxyMode输出为
iptables你可以通过在源 IP 应用程序上创建 Service 来测试源 IP 保留
kubectl expose deployment source-ip-app --nameclusterip --port80 --target-port8080输出为
service/clusterip exposedkubectl get svc clusterip输出类似于
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterip ClusterIP 10.0.170.92 none 80/TCP 51s并从同一集群中的 Pod 中访问 ClusterIP
kubectl run busybox -it --imagebusybox:1.28 --restartNever --rm输出类似于
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you dont see a command prompt, try pressing enter.然后你可以在该 Pod 中运行命令
# 从 “kubectl run” 的终端中运行
ip addr1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueuelink/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope hostvalid_lft forever preferred_lft forever
3: eth0: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1460 qdisc noqueuelink/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ffinet 10.244.3.8/24 scope global eth0valid_lft forever preferred_lft foreverinet6 fe80::188a:84ff:feb0:26a5/64 scope linkvalid_lft forever preferred_lft forever然后使用 wget 查询本地 Web 服务器
# 将 “10.0.170.92” 替换为 Service 中名为 “clusterip” 的 IPv4 地址
wget -qO - 10.0.170.92CLIENT VALUES:
client_address10.244.3.8
commandGET
...不管客户端 Pod 和服务器 Pod 位于同一节点还是不同节点client_address 始终是客户端 Pod 的 IP 地址。
TypeNodePort类型 Service 的源 IP
默认情况下发送到 TypeNodePort 的 Service 的数据包会经过源 NAT 处理。你可以通过创建一个 NodePort 的 Service 来测试这点
kubectl expose deployment source-ip-app --namenodeport --port80 --target-port8080 --typeNodePort输出为
service/nodeport exposedNODEPORT$(kubectl get -o jsonpath{.spec.ports[0].nodePort} services nodeport)
NODES$(kubectl get nodes -o jsonpath{ $.items[*].status.addresses[?(.typeInternalIP)].address })如果你在云供应商上运行你可能需要为上面报告的 nodes:nodeport 打开防火墙规则。 现在你可以尝试通过上面分配的节点端口从集群外部访问 Service。
for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done输出类似于
client_address10.180.1.1
client_address10.240.0.5
client_address10.240.0.3请注意这些并不是正确的客户端 IP它们是集群的内部 IP。这是所发生的事情
客户端发送数据包到 node2:nodePortnode2 使用它自己的 IP 地址替换数据包的源 IP 地址SNATnode2 将数据包上的目标 IP 替换为 Pod IP数据包被路由到 node1然后到端点Pod 的回复被路由回 node2Pod 的回复被发送回给客户端
用图表示 如图。使用 SNAT 的源 IPTypeNodePort
为避免这种情况Kubernetes 有一个特性可以保留客户端源 IP。 如果将 service.spec.externalTrafficPolicy 设置为 Local kube-proxy 只会将代理请求代理到本地端点而不会将流量转发到其他节点。 这种方法保留了原始源 IP 地址。如果没有本地端点则发送到该节点的数据包将被丢弃 因此你可以在任何数据包处理规则中依赖正确的源 IP你可能会应用一个数据包使其通过该端点。
设置 service.spec.externalTrafficPolicy 字段如下
kubectl patch svc nodeport -p {spec:{externalTrafficPolicy:Local}}输出为
service/nodeport patched现在重新运行测试
for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done输出类似于
client_address198.51.100.79请注意你只从运行端点 Pod 的节点得到了回复这个回复有正确的客户端 IP。
这是发生的事情
客户端将数据包发送到没有任何端点的 node2:nodePort数据包被丢弃客户端发送数据包到必有端点的 node1:nodePortnode1 使用正确的源 IP 地址将数据包路由到端点
用图表示 如图。源 IPTypeNodePort保存客户端源 IP 地址
TypeLoadBalancer类型 Service 的源 IP
默认情况下发送到 TypeLoadBalancer 的 Service 的数据包经过源 NAT处理因为所有处于 Ready 状态的可调度 Kubernetes 节点对于负载均衡的流量都是符合条件的。 因此如果数据包到达一个没有端点的节点系统会将其代理到一个带有端点的节点用该节点的 IP 替换数据包上的源 IP如上一节所述。
你可以通过负载均衡器上暴露 source-ip-app 进行测试
kubectl expose deployment source-ip-app --nameloadbalancer --port80 --target-port8080 --typeLoadBalancer输出为
service/loadbalancer exposed打印 Service 的 IP 地址
kubectl get svc loadbalancer输出类似于
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
loadbalancer LoadBalancer 10.0.65.118 203.0.113.140 80/TCP 5m接下来发送请求到 Service 的 的外部 IPExternal-IP
curl 203.0.113.140输出类似于
CLIENT VALUES:
client_address10.240.0.5
...然而如果你在 Google Kubernetes Engine/GCE 上运行 将相同的 service.spec.externalTrafficPolicy 字段设置为 Local 故意导致健康检查失败从而强制没有端点的节点把自己从负载均衡流量的可选节点列表中删除。
用图表示 你可以通过设置注解进行测试
kubectl patch svc loadbalancer -p {spec:{externalTrafficPolicy:Local}}你应该能够立即看到 Kubernetes 分配的 service.spec.healthCheckNodePort 字段
kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort输出类似于 healthCheckNodePort: 32122service.spec.healthCheckNodePort 字段指向每个在 /healthz 路径上提供健康检查的节点的端口。你可以这样测试
kubectl get pod -o wide -l appsource-ip-app输出类似于
NAME READY STATUS RESTARTS AGE IP NODE
source-ip-app-826191075-qehz4 1/1 Running 0 20h 10.180.1.136 kubernetes-node-6jst使用 curl 获取各个节点上的 /healthz 端点
# 在你选择的节点上本地运行
curl localhost:32122/healthz1 Service Endpoints found在不同的节点上你可能会得到不同的结果
# 在你选择的节点上本地运行
curl localhost:32122/healthzNo Service Endpoints Found在控制平面上运行的控制器负责分配云负载均衡器。 同一个控制器还在每个节点上分配指向此端口/路径的 HTTP 健康检查。 等待大约 10 秒让 2 个没有端点的节点健康检查失败然后使用 curl 查询负载均衡器的 IPv4 地址
curl 203.0.113.140输出类似于
CLIENT VALUES:
client_address198.51.100.79
...跨平台支持
只有部分云提供商为 TypeLoadBalancer 的 Service 提供保存源 IP 的支持。 你正在运行的云提供商可能会以几种不同的方式满足对负载均衡器的请求
使用终止客户端连接并打开到你的节点/端点的新连接的代理。 在这种情况下源 IP 将始终是云 LB 的源 IP而不是客户端的源 IP。使用数据包转发器这样客户端发送到负载均衡器 VIP 的请求最终会到达具有客户端源 IP 的节点而不是中间代理。
第一类负载均衡器必须使用负载均衡器和后端之间商定的协议来传达真实的客户端 IP 例如 HTTP 转发或 X-FORWARDED-FOR 标头或代理协议。 第二类负载均衡器可以通过创建指向存储在 Service 上的 service.spec.healthCheckNodePort 字段中的端口的 HTTP 健康检查来利用上述功能。
清理现场
删除 Service
kubectl delete svc -l appsource-ip-app删除 Deployment、ReplicaSet 和 Pod
kubectl delete deployment source-ip-app