深入理解 kubernetes iptables proxy 模式

概述

最近在面试的时候问了不少 network request 如何到 k8s service backend 的问题,觉得可以整合一下网络上的资料,这篇主要讨论 iptables proxy mode。大部分的情况没有在使用 userspace proxy modesipvs proxy mode 可能要等到下一次讨论。

事先准备

要先了解 iptable 工作机制,建议可以看这一篇:https://phoenixnap.com/kb/iptables-tutorial-linux-firewall,当然 wikipedia 也是写的不错,我下面的文字也大多数引用:https://zh.wikipedia.org/wiki/Iptables

快速带过 iptable

说到 iptable 要先了解 Tables, ChainsRueles

  • Table 指不同类型的封包处理流程,总共有五种,不同的 Tables 处理不同的行为
    • raw:处理异常,追踪状态 -> /proc/net/nf_conntrack
    • mangle:处理封包,修改 headler 之类的
    • nat:进行位址转换操作
    • filter:进行封包过滤
    • security:SElinux 相关
  • Chains 来对应进行不同的行为。像是 “filter” Tables 进行封包过滤的流程,而 “nat” 针对连接进行位址转换操作。Chains 里面包含许多规则,主要有五种类型的 Chains
    • PREROUTING:处理路由规则前通过此 Chains,通常用于目的位址转换(DNAT)
    • INPUT:发往本机的封包通过此 Chains
    • FORWARD:本机转发的封包通过此 Chains
    • OUTPUT:处理本机发出的封包。
    • POSTROUTING:完成路由规则后通过此 Chains,通常用于源位址转换(SNAT)
  • Rules 规则会被逐一进行匹配,如果匹配,可以执行相应的动作

大致的工作流向情况分两种:

  1. backend 为本机

    1
    2
    NIC → PREROUTING → INPUT → Local process 
    Local process → OUTPUT → POSTROUTING → NIC
  2. backend 目的地非本机

    1
    NIC→PREROUTING → FORWARD → POSTROUTING→NIC

Iptables Basics

下面是比较详细的流程,有包含 EBTABLES,但这个看久头会昏,我这次会主要讨论 Network Layer 这一部分,然后用上面这张比较精简的图
Netfilter pic of wikipedia

Kube-proxy 修改了 filter,nat 两个表,自定义了
KUBE-SERVICESKUBE-NODEPORTSKUBE-POSTROUTINGKUBE-FORWARDKUBE-MARK-MASQKUBE-MARK-DROP,所以我这次会 focus on filter ,nat 两个 Table

1. filter table 有三个 Chain “INPUT” “OUTPUT” “FORWARD

iptables-routing

kube-proxy 在 filter table 的 “INPUT” “OUTPUT” chain 增加了 KUBE-FIREWALL 在 “INPUT” “OUTPUT” “FORWARD” chain 增加了 KUBE-SERVICES

KUBE_FIREWALL 会丢弃所有被 KUBE-MARK-DROP 标记 0x8000 的封包,而标记的动作可以在其他的 table 中(像是第二部分提到的 NAT table 中)
proxy iptable
proxy iptable

而 filter table 的 KUBE-SERVICES 可以过滤封包,假如一个 service 没有对应的 endpoint,就会被 reject,这里我先要建立一个 service 和没有正确设定 endpoint。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kind: Service 
apiVersion: v1
metadata:
name: test-error-endpoint
namespace: default
spec:
ports:
- protocol: TCP
port: 7777
targetPort: 7777
---
kind: Endpoints
apiVersion: v1
metadata:
name: test-error-endpoint
namespace: default

service cluster ip 为 10.95.58.92

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kind: Service 
apiVersion: v1
metadata:
name: test-error-endpoint
namespace: default
selfLink: /api/v1/namespaces/default/services/test-error-endpoint
uid: 5d415d63-6fc3-444e-8b5a-29015b436a83
resourceVersion: ' 73026369'
creationTimestamp: '2020-11-17T05:48:52Z'
spec:
ports:
- protocol: TCP
port: 7777
targetPort: 7777
clusterIP: 10.95.58.92
type: ClusterIP
sessionAffinity: None
status:
loadBalancer: {}

再次检查 iptable,就可以看到 default/test-error-endpoint: has no endpoints -> tcp dpt:7777 reject-with icmp-port-unreachable
proxy iptable

2. nat table 有三个 Chain “PREROUTING” “OUTPUT” “POSTROUTING

在前两个封包处理流程是比较相似和复杂的,大体来说是藉由客制化的规则,来处理符合条件封包,帮它们找到正确的 k8s endpoint (后面会细讲),在 POSTROUTING 主要是针对 k8s 处理的封包(标记 0x4000 的封包),在离开 node 的时候做 SNAT

  • (inbound) 在 “PREROUTING” 将所有封包转发到 KUBE-SERVICES
  • (outbound) 在 “OUTPUT” 将所有封包转发到 KUBE-SERVICES
  • (outbound) 在 “POSTROUTING” 将所有封包转发到 KUBE-POSTROUTING

iptables-routing
当封包进入 “PREROUTING” 和 “OUTPUT”,会整个被 KUBE-SERVICES Chain 整个绑架走,开始逐一匹配 KUBE-SERVICES 中的 rule 和打上标签。
nat tables

kube-proxy 的用法是一种 O(n) 算法,其中的 n 随 k8s cluster 的规模同步增加,更简单的说就是 service 和 endpoint 的数量。
kube-services

我这里会准备三个最常见的 service type 的 kube-proxy 路由流程

  • cluster IP
  • nodePort
  • load balancer

clusterIP 流程

这里我使用 default/jeff-api(clusterIP: 10.95.57.19) 举例,我下面图过滤掉不必要的资讯
kube-services

最后会到实际 pod 的位置,podIP: 10.95.35.31,hostIP: 10.20.0.128 是该 pod 所在 node 的 ip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kind: Pod 
apiVersion: v1
metadata:
name: jeff-api-746f4c9985-5qmw6
generateName: jeff-api-746f4c9985-
namespace: default
spec:
containers:
- name: promotion-api
image: 'gcr.io/jeff-project/jeff /jeff-api:202011161901'
ports:
- name: 80tcp02
containerPort: 80
protocol: TCP
nodeName: gke-sit-jeff-k8s-tw-01-default-pool-7983af35-ug91
status:
phase: Running
hostIP: 10.20.0.128
podIP: 10.95.35.31

nodePort 流程

这里有一个关键就是 KUBE-NODEPORTS 一定是在 KUBE-SERVICES 最后一项,iptables 在处理 packet 会先处理 ip 为 cluster ip 的 service,当全部的 KUBE-SVC-XXXXXX 都对应不到的时候就会使用 nodePort 去匹配。
kube-services

我们看实际 pod 的资讯,podIP: 10.95.32.17,hostIP: 10.20.0.124 是其中一台 node 的 ip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
kind: Service 
apiVersion: v1
metadata:
name: jeff-frontend
namespace: jeff-frontend
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 31929
selector:
app: jeff-frontend
clusterIP: 10.95.58.51
type: NodePort
externalTrafficPolicy: Cluster
---
kind: Pod
apiVersion: v1
metadata:
name: jeff-frontend-c94bf68d9-bbmp8
generateName: jeff-frontend-c94bf68d9-
namespace: jeff-frontend
spec:
containers:
- name: jeff-frontend
image: 'gcr.io/jeff-project/jeff/jeff-image:jeff-1.0.6.5'
ports:
- name: http
containerPort: 80
protocol: TCP
nodeName: gke-sit-jeff-k8s-tw-01-default -pool-b5692f8d-enk7
status:
phase: Running
hostIP: 10.20.0.124
podIP: 10.95.32.17

load balancer流程

假如目的地 IP 是 load balancer 就会使用 KUBE-FW-XXXXXX,我建立一个 internal load balancer service 和 endpoint 指到 google postgresql DB(10.28.193.9)
kube-services

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1 
kind: Service
metadata:
annotations:
cloud.google.com/load-balancer-type: Internal
networking.gke.io/internal-load-balancer-allow-global-access: 'true'
name: external-postgresql
spec :
ports:
- protocol: TCP
port: 5432
targetPort: 5432
type: LoadBalancer
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-postgresql
subsets:
- addresses:
- ip: 10.28.193.9
ports:
- port: 5432
protocol : TCP

kube-services
在 NAT table 看到 KUBE-MARK-MASQKUBE-MARK-DROP 这两个规则主要是经过的封包打上标签,打上标签的封包会做相应的处理。KUBE-MARK-DROPKUBE-MARK-MASQ 本质上就是使用 iptables 的 MARK 指令

1
2
-A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000

如果打上了 0x8000 到后面 filter table (上面提到 KUBE_FIREWALL )就会丢弃。

如果打上了 0x4000 k8s 将会在 PREROUTING table 的 KUBE-POSTROUTING chain 对它进行 SNAT 转换。
kube-services
POSTROUTING table
KUBE-POSTROUTING Chain
KUBE-SERVICES

参考:

来源:https://jeff-yen.medium.com/iptables-proxy-mode-in-kube-proxy-6862bb4b329

微信订阅号

-------------本文结束感谢您的阅读-------------