通常,为了访问kubernetes集群中的pod,我们会使用service
的形式。以下,就不同的 service
类型来探讨对source ip
的影响。
kubernetes的官网对此也有说明:
https://kubernetes.io/docs/tutorials/services/source-ip/
在此简单总结一下(网络插件使用calico, 其他插件可能有不同,在此不做讨论):
在创建service
的时候,如果不声明type
字段,则默认为 ClusterIP
. 其原理就是利用iptables 对发送到clusterip
的报文进行转发。
[root@walker-1 hehe]# kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Selector: app=nginx
Type: ClusterIP
IP: 10.96.9.42
Port: 80/TCP
TargetPort: 80/TCP
Endpoints: 192.168.187.212:80
Session Affinity: None
如果是ClusterIP
, 一般用于集群内pod之间的访问。
1. 如果client和server在同一个node上,那么从server观察到的source ip
为client的真实IP。
2. 如果client和server分别处于node A, node B上,那么从server段观察到source ip
的就是 node B的IP(pod外出的流量会被做SNAT)。
NodePort
可以认为是构建在 ClusterIP
之上的。如果指定service 类型为NodePort
,那么在每个 node 上都会监听同一个端口,并通过iptables对其进行转发。
请求有两种情况:
toplogy:
client
\ ^
\ \
v \
node 1 <--- node 2
| ^ SNAT
| | --->
v |
endpoint
(此时pod所见的source ip
为node 2的IP)
toplogy:
client
| ^
| |SNAT
v |
node 1
| ^
| |
v |
endpoint
node1:nodeport
(此时pod所见的source ip
为node 1的IP)
这就很烦了,怎么都获取不到正确的用户ip。好在NodePort模式还提供了{"spec":{"externalTrafficPolicy":"Local"}}
参数
加上以后效果是这样的:
toplogy:
client
^ / \
/ / \
/ v X
node 1 node 2
^ |
| |
| v
endpoint
node1:nodeport
(此时pod所见的source ip
为client ip)
注:发往node2的报文会被丢弃,因为报文不会再做SNAT,来将client ip替换为node2 ip了。
默认也会做SNAT,替换客户端IP。
However, if you’re running on Google Kubernetes Engine/GCE, setting the same
service.spec.externalTrafficPolicy
field toLocal
forces nodes without Service endpoints to remove themselves from the list of nodes eligible for loadbalanced traffic by deliberately failing health checks.
Visually:
client
|
lb VIP
/ ^
v /
health check ---> node 1 node 2 <--- health check
200 <--- ^ | ---> 500
| V
endpoint
要让 pod 能正常获取客户端ip大致有如下几种方式:
1. 可以使用 NodePort
+ {"spec":{"externalTrafficPolicy":"Local"}}
的配置来实现。
2. 还有个解决思路就是利用 INGERSS。INGRESS 本质上是监听物理机端口,然后直接将客户端请求转发至service
。可以在INGRESS 请求转发阶段将客户端IP 带到请求头中。
3. pod直接使用 HOST 网络模式。
第三种方式最便捷,但容易造成端口冲突。安全问题也有待考量,因此不推荐。
第二种方式最灵活,即使pod分布在不同node上也可以通过统一入口访问,官方INGRESS是由nginx实现的,这样一来花样就多了(甚至可以做流控,认证功能)。推荐使用。
第一种方式折中吧,因为还没试过当service
的多个endpoint
分布在不同节点上的情况。