一、前言
kubernetes提供服务的实体(POD)具有内部网络空间的地址,外部无法直接访问,所以kubernetes提供了多种对外暴露POD服务的方法。这些方法都借助于kubernetes的service的概念,将一系列具有等同服务能力的POD组成一个抽象的服务实体,这个服务实体具有一个虚IP,这个虚IP可以是kubernetes cluster内部网络可达的IP(对应cluster service),也可以是外部可达的IP(对应node port 和external load balancer)。
kubernetes service借助iptables将一组POD抽象成可达的网络服务,并且由于kubernetes要保证service在任何node的可达性,所以使用iptables rule将所有后端POD组成一个基于概率访问的组合,使用iptables的SNAT和DNAT技术在不同POD之间进行基于概率的请求转发。这样的模式带来了一个副作用:经过SNAT操作之后,客户端的IP消失在了请求的接入node,应用程序POD只能看到node的IP,对于一些对源IP有需求的应用来讲,需要kubernetes提供解决这个问题的机制。而kubernetes的source preserve的功能(https://kubernetes.io/docs/tutorials/services/source-ip/)就是为解决这个问题而引入的。
转载自https://blog.csdn.net/cloudvtech
二、kubernetes的服务和SNAT
在文章《Kubernetes如何利用iptables对外暴露service》中,可以看到基于概率对后端POD组进行访问的时候,如果选择的POD不在本地的node上面,则需要进行一次DNAT,将请求的目标地址设置成所选择的POD的IP,之后会进行路由选择,确定这是一个egress的数据包。在路由之后,还有一个POST ROUTING的规则如下:
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE
对于所有出去的数据包,需要进行MASQUERADE(基于接口动态IP的SNAT)处理,在这种情况下,source IP就丢失了,POD里面的应用程序看到的就是node的IP或者CNI互联设备的IP(比如bridge或者vxlan设备)。
转载自https://blog.csdn.net/cloudvtech
三、源IP保持的实现
通过上面的分析可以发现,如果不存在从一个node向另外一个node的请求转发,只在本地选择POD服务请求,则不会存在SNAT,进而可以保持源地址IP,kubernetes正式借助这一思路来实现源IP的保持。在建立node port或者external load balancer service的时候,在1.7及以上版本中可以在service的定义的时候加入如下控制来保证只向属于这个service的本地的POD转发请求(如果本地没有POD能服务这个请求,请求将被iptables DROP掉,客户端会发现请求超时没有响应):
"externalTrafficPolicy": "Local"
在较老的v1.5 ~ v1.6版本中,可以使用annotation使能这个功能:
service.beta.kubernetes.io/external-traffic: OnlyLocal
in release notes:
* When switching from the service.beta.kubernetes.io/external-traffic annotation to the new ([#46716](https://github.com/kubernetes/kubernetes/pull/46716), [@thockin](https://github.com/thockin)) externalTrafficPolicy field, the values chnag as follows: * "OnlyLocal" becomes "Local" * "Global" becomes "Cluster".
相关的code如下:
https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/iptables/proxier.go
...
...
node port service的例子yaml如下:
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: myapp
labels:
run: myapp
app: myapp
spec:
type: NodePort
externalTrafficPolicy: Local
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 80
selector:
app: myapp
根据这样的yaml生成的node port service如果本地存在对应的POD,则会出现如下iptables规则:
-A KUBE-XLB-N6IVUAXVQRPD4SQY -m comment --comment "Balancing rule 0 for namespace/svc-name:port" -j KUBE-SEP-V4WM6D5AHKHRR3SB
-A KUBE-XLB-EYNESMNWSNIVJYVW -m comment --comment "Balancing rule 1 for namespace/svc-name:port" -j KUBE-SEP-V4WM6D5AHKHRR3SB
如果本地不存在对应的POD,则会出现如下iptables规则:
-A KUBE-XLB-SB3VYBOWAYZV3VAW -m comment --comment " namespace/svc-name:port has no local endpoints" -j KUBE-MARK-DROP转载自https://blog.csdn.net/cloudvtech