【转】干货,Kubernetes中的Source Ip机制。

准备工做

你必须拥有一个正常工做的 Kubernetes 1.5 集群,用来运行本文中的示例。该示例使用一个简单的 nginx webserver 回送它接收到的请求的 HTTP 头中的源 IP 地址。你能够像下面这样建立它:node

$ kubectl run ``source``-ip-app --image=k8s.gcr.io``/echoserver``:1.4``deployment ``"source-ip-app"` `created

Type=ClusterIP 类型 Services 的 Source IP

若是你的 kube-proxy 运行在 iptables 模式下,从集群内部发送到 ClusterIP 的包永远不会进行源地址 NAT,这从 Kubernetes 1.2 开始是默认选项。Kube-proxy 经过一个 proxyMode endpoint 暴露它的模式。nginx

$ kubectl get nodes``NAME                           STATUS     AGE     VERSION``kubernetes-minion-group-6jst   Ready      2h      v1.6.0+fff5156``kubernetes-minion-group-cx31   Ready      2h      v1.6.0+fff5156``kubernetes-minion-group-jj1t   Ready      2h      v1.6.0+fff5156``kubernetes-minion-group-6jst $ curl localhost:10249``/proxyMode``iptables

你能够经过在 source IP 应用上建立一个服务来测试源 IP 保留。web

$ kubectl expose deployment ``source``-ip-app --name=clusterip --port=80 --target-port=8080``service ``"clusterip"` `exposed
$ kubectl get svc clusterip``NAME         CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE``clusterip    10.0.170.92   <none>        80``/TCP`    `51s

从相同集群中的一个 pod 访问这个 ClusterIP:json

$ kubectl run busybox -it --image=busybox --restart=Never --``rm``Waiting ``for` `pod default``/busybox` `to be running, status is Pending, pod ready: ``false``If you don't see a ``command` `prompt, try pressing enter.``# ip addr``1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue``    ``link``/loopback` `00:00:00:00:00:00 brd 00:00:00:00:00:00``    ``inet 127.0.0.1``/8` `scope host lo``       ``valid_lft forever preferred_lft forever``    ``inet6 ::1``/128` `scope host``       ``valid_lft forever preferred_lft forever``3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue``    ``link``/ether` `0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff``    ``inet 10.244.3.8``/24` `scope global eth0``       ``valid_lft forever preferred_lft forever``    ``inet6 fe80::188a:84ff:feb0:26a5``/64` `scope link``       ``valid_lft forever preferred_lft forever
# wget -qO - 10.0.170.92``CLIENT VALUES:``client_address=10.244.3.8``command``=GET``...

若是客户端 pod 和 服务端 pod 在相同的节点上,client_address 就是客户端 pod 的 IP 地址。可是,若是它们在不一样的节点上, client_address 将会是客户端 pod 所在节点的 flannel IP 地址。后端

Type=NodePort 类型 Services 的 Source IP

​ 对于 Kubernetes 1.5,发送给类型为 Type=NodePort Services 的数据包默认进行源地址 NAT。你能够建立一个 NodePort Service 来进行测试:app

$ kubectl expose deployment ``source``-ip-app --name=nodeport --port=80 --target-port=8080 --``type``=NodePort``service ``"nodeport"` `exposed``$ NODEPORT=$(kubectl get -o jsonpath=``"{.spec.ports[0].nodePort}"` `services nodeport)``$ NODES=$(kubectl get nodes -o jsonpath=``'{ $.items[*].status.addresses[?(@.type=="ExternalIP")].address }'``)

若是你的集群运行在一个云服务上,你可能须要为上面报告的 nodes:nodeport 开启一条防火墙规则。 如今,你能够经过上面分配的节点端口从外部访问这个 Service。负载均衡

$ ``for` `node ``in` `$NODES; ``do` `curl -s $node:$NODEPORT | ``grep` `-i client_address; ``done``client_address=10.180.1.1``client_address=10.240.0.5``client_address=10.240.0.3

请注意,这些并非正确的客户端 IP,它们是集群的内部 IP。这是所发生的事情:curl

一、客户端发送数据包到 node2:nodePortide

二、node2 使用它本身的 IP 地址替换数据包的源 IP 地址(SNAT)oop

三、node2 使用 pod IP 地址替换数据包的目的 IP 地址

四、数据包被路由到 node 1,而后交给 endpoint

五、Pod 的回复被路由回 node2

六、Pod 的回复被发送回给客户端

形象的:

image.png

​ 为了防止这种状况发生,Kubernetes 提供了一个特性来保留客户端的源 IP 地址(点击此处查看可用特性)。设置 service.spec.externalTrafficPolicy 的值为 Local,请求就只会被代理到本地 endpoints 而不会被转发到其它节点。这样就保留了最初的源 IP 地址。若是没有本地 endpoints,发送到这个节点的数据包将会被丢弃。这样在应用到数据包的任何包处理规则下,你都能依赖这个正确的 source-ip 使数据包经过并到达 endpoint。

设置 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_address=104.132.1.79

请注意,你只从 endpoint pod 运行的那个节点获得了一个回复,这个回复有正确的客户端 IP。

这是发生的事情:

一、客户端发送数据包到 node2:nodePort,它没有任何 endpoints

二、数据包被丢弃

三、客户端发送数据包到 node1:nodePort,它endpoints

四、node1 使用正确的源 IP 地址将数据包路由到 endpoint

形象的:

image.png

Type=LoadBalancer 类型 Services 的 Source IP

​ 对于 Kubernetes 1.5,发送给类型为 Type=LoadBalancer Services 的数据包默认进行源地址 NAT,这是因为全部处于 Ready 状态的 Kubernetes 节点对于负载均衡的流量都是符合条件的。因此若是数据包到达一个没有 endpoint 的节点,系统将把这个包代理到有 endpoint 的节点,并替换数据包的源 IP 为节点的 IP(如前面章节所述)。

​ 你能够经过在一个 loadbalancer 上暴露这个 source-ip-app 来进行测试。

$ kubectl expose deployment ``source``-ip-app --name=loadbalancer --port=80 --target-port=8080 --``type``=LoadBalancer``service ``"loadbalancer"` `exposed``$ kubectl get svc loadbalancer``NAME           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE``loadbalancer   10.0.65.118   104.198.149.140   80``/TCP`    `5m``$ curl 104.198.149.140``CLIENT VALUES:``client_address=10.240.0.5``...

​ 然而,若是你的集群运行在 Google Kubernetes Engine/GCE 上,设置 service.spec.externalTrafficPolicy 字段值为 Local 能够强制使没有 endpoints 的节点把他们本身从负载均衡流量的可选节点名单中删除。这是经过故意使它们健康检查失败达到的。

形象的:

image.png

你能够设置 annotation 来进行测试:

$ kubectl patch svc loadbalancer -p ``'{"spec":{"externalTrafficPolicy":"Local"}}'

你应该可以当即看到 Kubernetes 分配的 service.spec.healthCheckNodePort 字段:

$ kubectl get svc loadbalancer -o yaml | ``grep` `-i healthCheckNodePort``  ``healthCheckNodePort: 32122

service.spec.healthCheckNodePort 字段指向每一个节点在 /healthz 路径上提供的用于健康检查的端口。你能够这样测试:

$ kubectl get pod -o wide -l run=``source``-ip-app``NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE``source``-ip-app-826191075-qehz4   1``/1`       `Running   0          20h       10.180.1.136   kubernetes-minion-group-6jst``kubernetes-minion-group-6jst $ curl localhost:32122``/healthz``1 Service Endpoints found``kubernetes-minion-group-jj1t $ curl localhost:32122``/healthz``No Service Endpoints Found

​ 主节点运行的 service 控制器负责分配 cloud loadbalancer。在这样作的同时,它也会分配指向每一个节点的 HTTP 健康检查的 port/path。等待大约 10 秒钟以后,没有 endpoints 的两个节点的健康检查会失败,而后 curl 负载均衡器的 ip:

$ curl 104.198.149.140``CLIENT VALUES:``client_address=104.132.1.79``...

跨平台支持

​ 因为 Kubernetes 1.5 在类型为 Type=LoadBalancer 的 Services 中支持源 IP 保存的特性仅在 cloudproviders 的子集中实现(GCP and Azure)。你的集群运行的 cloudprovider 可能以某些不一样的方式知足 loadbalancer 的要求:

一、使用一个代理终止客户端链接并打开一个到你的 nodes/endpoints 的新链接。在这种状况下,源 IP 地址将永远是云负载均衡器的地址而不是客户端的。

二、使用一个包转发器,所以从客户端发送到负载均衡器 VIP 的请求在拥有客户端源 IP 地址的节点终止,而不被中间代理。

​ 第一类负载均衡器必须使用一种它和后端之间约定的协议来和真实的客户端 IP 通讯,例如 HTTP X-FORWARDED-FOR 头,或者 proxy 协议。 第二类负载均衡器能够经过简单的在保存于 Service 的 service.spec.healthCheckNodePort 字段上建立一个 HTTP 健康检查点来使用上面描述的特性。

原文地址:

http://www.damonyi.cc/kubernetes%e4%b8%ad%e7%9a%84source-ip%e6%9c%ba%e5%88%b6/

相关文章
相关标签/搜索