Kubernetes 的 Service 对 externalTrafficPolicy 属性设置,以及和获取客户端IP相关知识点
目录
[TOC]
1、先决条件
2、具有 Type = ClusterIP 服务的客户端IP
3、通过 Type=NodePort 获取客户端IP
4、通过 Type = LoadBalancer 获取客户端IP
5、删除服务
6、参考文章
本文主要内容翻译自:source-ip
1、先决条件
您必须具有可用的 Kubernetes 1.5
集群才能运行本文档中的示例。这些示例使用一个小的 Nginx Web
服务器,它会显示通过HTTP头接收的请求的一切信息,包括客户端IP。您可以按如下方式创建它:
$ kubectl create deployment source-ip-app --image=idoall/echoserver:1.10
deployment.apps/source-ip-app created
2、具有 Type = ClusterIP 服务的客户端IP
如果您在 iptables
模式下运行 kube-proxy
,则从群集内发送到 ClusterIP
的数据包永远不会来自 DNAT
,这是Kubernetes 1.2
以来的默认设置。Kube-proxy
通过访问proxyMode endpoint 可以查看公开模式:
$ curl localhost:10249/proxyMode
iptables
接下来我们创建一个服务来测试客户端IP:
$ kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080
service/clusterip exposed
$ kubectl get svc clusterip
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
clusterip ClusterIP 10.245.180.210 <none> 80/TCP 8s
刚才我们只是部署了一个名为
source-ip-app
的Pod ,通过kubectl expose
将资源暴露为新的Kubernetes Service
,名称为clusterip
。
接下来创建一个 pod
尝试获取 clusterip
测试
$ kubectl run busybox -it --image=busybox --restart=Never --rm
If you don't see a command prompt, try pressing enter.
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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
3: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue
link/ether 5e:69:18:64:dd:94 brd ff:ff:ff:ff:ff:ff
inet 10.244.5.27/24 scope global eth0
valid_lft forever preferred_lft forever
/ # wget -qO - 10.245.180.210
Hostname: source-ip-app
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.3 - lua: 10008
Request Information:
client_address=10.244.5.27
method=GET
real path=/
query=
request_version=1.1
request_scheme=http
request_uri=http://10.245.180.210:8080/
Request Headers:
connection=close
host=10.245.180.210
user-agent=Wget
Request Body:
-no body in request-
/ #
无论何种访问方式,可以发现获取到的 client_address
地址,永远都是客户端所在容器的IP 10.244.5.27
。
3、通过 Type=NodePort 获取客户端IP
从 Kubernetes 1.5
开始, 默认情况下,发送到 Type = NodePort
的服务数据包是来自 DNAT
。接下来我们再创建一个类型是 NodePort
的服务,来对应刚才创建的部署 source-ip-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=="InternalIP")].address}')
$ for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done
client_address=10.244.3.0
client_address=10.244.4.0
client_address=10.244.5.1
client_address=10.244.0.0
client_address=10.244.1.0
client_address=10.244.2.0
通过上面的方法能够看到,我们从不同节点去访问
NodePort
,得到的也并不是真正的客户端IP,而是集群IP
处理流程如下:
* 客户端发送数据包 node2:nodePort
* node2
用自己的IP地址替换数据包中的源IP地址(SNAT)
* node2
使用 pod IP 替换数据包上的目标 IP
* 数据包路由到 node1
,然后路由到 endpoint
* pod的回复被路由回 node2
* pod的回复被发送回客户端
client
\ ^
\ \
v \
node 1 <--- node 2
| ^ SNAT
| | --->
v |
endpoint
为避免这种情况,Kubernetes
具有保留客户端IP 的功能。设置service.spec.externalTrafficPolicy
为 Local
会将请求代理到本地端点,不将流量转发到其他节点,从而保留原始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_address=10.0.0.100
client_address=10.0.0.100
这时获取到的正确IP地址,只能是在当前节点的IP地址。
处理流程如下:
* 客户端发送数据包 node2:nodePort
,没有任何 endpoint
* 数据包被丢
* 客户端发送数据包 node1:nodePort
,存在 endpoint
* node1
使用正确的源IP将数据包路由到 endpoint
client
^ / \
/ / \
/ v X
node 1 node 2
^ |
| |
| v
endpoint
4、通过 Type = LoadBalancer 获取客户端IP
从 Kubernetes 1.5
开始,默认情况下,发送到具有 Type = LoadBalancer
的服务的数据包来源是 DNAT,因为该 Ready
状态中的所有可调度 Kubernetes
节点都能获得负载平衡流量。因此,如果数据包到达没有 endpoint
的 Node
,系统会将其代理到具有 endpoint
的 Node
,用这个 Node
的IP替换数据包上的源IP(如上一节所述)。
您可以通过负载均衡器公开 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 TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
loadbalancer LoadBalancer 10.245.176.219 192.168.11.221 80:30821/TCP 57s
$ curl -s 192.168.11.221 | grep -i client_address
client_address=10.244.0.0
通过上面的实例可以看到,获取到的
client_address
还是集群内的IP。
通过设置相同的 service.spec.externalTrafficPolicy
字段以 Local
强制没有服务端点的节点通过故意失败的运行状况检查将自己从符合加载平衡流量的节点列表中删除。
client
|
lb VIP
/ ^
v /
health check ---> node 1 node 2 <--- health check
200 <--- ^ | ---> 500
| V
endpoint
您可以通过下面的代码来测试:
$ kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'
service/loadbalancer patched
你应该立即看到service.spec.healthCheckNodePortKubernetes
分配的字段
$ kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort
healthCheckNodePort: 32131
该 service.spec.healthCheckNodePort
字段指向服务于运行状况检查的每个节点上的端口 /healthz
。可以通过以下字段测试:
$ kubectl get pod -o wide -l run=source-ip-app
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
source-ip-app 1/1 Running 0 47m 10.244.4.179 k8s-dev-datanode-02 <none> <none>
source-ip-app-784589f58-rk95c 1/1 Running 0 47m 10.244.5.26 k8s-dev-datanode-03 <none> <none>
[root@k8s-dev-datanode-01 ~]# curl localhost:32131
{
"service": {
"namespace": "default",
"name": "loadbalancer"
},
"localEndpoints": 0
[root@k8s-dev-datanode-02 ~]# curl localhost:32131
{
"service": {
"namespace": "default",
"name": "loadbalancer"
},
"localEndpoints": 1
在 master
服务器上会进行负载均衡分配 ,当执行此操作时,它会分配指向每个节点上的端口/路径的HTTP运行状况检查。对于没有端点的2个节点等待大约10秒钟的健康检查失败,然后会指向正确的IP地址:
$ curl -s 192.168.11.221 | grep -i client_address
client_address=192.168.11.175
只有在所在节点 datanode-02 上运行,才可以访问
5、删除服务
$ kubectl delete svc -l app=source-ip-app
删除标签是
app: source-ip-app
的服务
删除 Deployment
,ReplicaSet
和 Pod
:
$ kubectl delete deployment source-ip-app
6、参考文章
希望本文对您有帮助,感谢您的支持和阅读我的博客。
博文作者:迦壹
博客地址:Kubernetes 的 Service 对 externalTrafficPolicy 属性设置,以及和获取客户端IP相关知识点
转载声明:可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明,谢谢合作!