Kubernetes 网络

  1.   一、Kubernetes 自家解决方案
    1. 1   三个网络
    2. 2   服务发现
    3. 3   Service 网络
      1. 3.1   NodePort
        1. 3.1.1   原理
        2. 3.1.2   实现
        3. 3.1.3   分析
      2. 3.2   LoadBalancer
      3. 3.3   Ingress
        1. 2.3.1   Ingress Controller
        2. 3.3.2   Ingress 配置
  2.   二、Istio 解决方案
  3.   三、API Gateway 解决方案

微服务是多进程、多服务部署,无法通过 IPC 进程内调用,必然通过网络调用,这将带来很多问题:不可靠、有带宽、协议设计。无论是 TCP、HTTP、RPC,无论是东西流量还是南北流量,涉及限流、熔断、域名及路径上下文,都需要 Kubernetes 或者第三方产品给出解决方案。网络是 Kubernetes 的难点之一。

  一、Kubernetes 自家解决方案

1   三个网络

  1. Node
  2. Pod,同一Pod 可以和进程间 IPC 通信,可以直接用 localhost 访问同一 Pod 中的其它容器。但不同 Pods 采用了不同的虚拟 IP。共同点是都不需要 NAT 下通信,他们共享网络命名空间,他们之间的通信不需要宿主机的端口映射,Pod ip 对 Kubernetes 网络内部还是宿主机都是一样的。
  3. Service,服务发现与负载均衡。ClusterIP 虚拟网络只对 Kubernetes 内部可见

以上三种网络是互不相交的。Kubernetes 并没有原生内置某种 Pod 网络实现,而是开放给了第三方厂商依据 CNI(Container Network Interface)规则接口实现。这是不同的容器接口可以调用相同的网络组件实现通信。
安装 Docker 容器后会有 /opt/cni/bin 目录。一般有三种实现方式:

  • 二层交换
  • 三层路由
  • Overlay 网络,在原有的网络上设计虚拟网络,实现解耦,但传输性能二法与二层和三层网络方案相比,实现上是分成 underlay 和 overlay 实现数据包地分发。
    备注:via 是导向网络 Default via 192.168.1.1,选择下一跳;dev 是导向设备 10.1.0.0/16 dev bridge,进行分发。

以三层路由网络举例,有 node1 和 node2 节点,有以下两种方案:

  1. 方案一,网关路由:
  • Gateway(192.168.1.1): RouteTable(10.1.1.0/24 via 192.168.1.100, 10.1.2.0/24 via 192.168.1.101)
  • node1(192.168.1.100/24): Container bridge(10.1.1.1/24), RouteTable(Default via 192.168.1.1)
    • pod1: 10.1.1.2
    • pod2: 10.1.1.3
  • node2(192.168.1.101/24): Container bridge(10.1.2.1/24), RouteTable(Default via 192.168.1.1)
    • pod1: 10.1.2.2
    • pod2: 10.1.2.3
  1. 方案二,主机路由:
  • Gateway(192.168.1.1):
  • node1(192.168.1.100/24): Container bridge(10.1.1.1/24), RouteTable(Default via 192.168.1.1, 10.1.2.0/24 via 192.168.1.101)
    • pod1: 10.1.1.2
    • pod2: 10.1.1.3
  • node2(192.168.1.101/24): Container bridge(10.1.2.1/24), RouteTable(Default via 192.168.1.1, 10.1.1.0/24 via 192.168.1.100)
    • pod1: 10.1.2.2
    • pod2: 10.1.2.3

2   服务发现

为了承担起 DNS 解析任务,每个 Kubernetes 环境都会有一个 DNS 服务,其 selector 为 k8s-app: kube-dns,

[yhdodo19@instance-1 ~]$ kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 27d

[yhdodo19@instance-1 ~]$ kubectl get po -n kube-system --show-labels | grep k8s-app=kube-dns
coredns-fb8b8dccf-2rdns 1/1 Running 6 27d k8s-app=kube-dns,pod-template-hash=fb8b8dccf
coredns-fb8b8dccf-nkqtv 1/1 Running 6 27d k8s-app=kube-dns,pod-template-hash=fb8b8dccf

3   Service 网络

3.1   NodePort

3.1.1   原理

  • 创建 socket 对外监听,但要求监听端口大于 30000
  • 基于 iptables 的流量转换,也可以认为是 socket 转发,通过 iptables Chain 实现
  • 基于 iptables 的简单负载均衡,通过 iptables 概率实现

3.1.2   实现

外部请求 -> NodeIP:NodePort -> ClusterIP:Port -> PodIP:TargetPort,它是通过 kube-proxy 实现:

  • kube-proxy 是以 daemonset pod 的形式运行在集群内的每个节点上,监听服务(Service)和端点(Endpoint)的变化设定和更新 iptables 规则
  • kube-proxy 通过 iptables 实现 nodePort 到集群内部 Service Port 再到 Pod 中的 Container targetPort 的流量转发
  • kube-proxy 是通过 iptables 实现 Service 流量转发到 Pod 的负载均衡

由以上的处理方式可见 NodePort 暴露端口方案是一个 4 层网络方案,无法处理 7 层网络,不能设置 80 等常用端口,它只能用来进行一些开发、测试工作,比如映射 3306:30001 进行数据操作或者数据迁移。

3.1.3   分析

kube-proxy 是基于 iptable 来实现的,它是防火墙的一部分;Linux 防火墙可以对数据进行过滤、更改、转发操作,它由两个组件组成:核心层 netfilters 和用户层 iptables。 iptables 在用户层设置、变更和维护过滤规则,并最终由 netfilters 执行,iptables 主要由 一组 table 和 table 里的 Chain 组成,chain 有默认也可自定义。

接下来我们以 外部请求 -> NodeIP:NodePort(35.237.188.250:30001) -> ClusterIP:Port(10.96.191.193:81) -> PodIP:TargetPort(10.36.0.10:80, ···) 来分析 iptables 是怎样实现转发和负载均衡的。

[yhdodo19@instance-1 ~]$ kubectl describe svc goapi
Name: goapi
Namespace: default
Labels: env=goapi
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"env":"goapi"},"name":"goapi","namespace":"default"},"spec":{"p...
Selector: app=goweb
Type: NodePort
IP: 10.96.191.193
Port: http 81/TCP
TargetPort: 80/TCP
NodePort: http 30001/TCP
Endpoints: 10.36.0.10:80,10.36.0.11:80,10.36.0.5:80 + 2 more...
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>

[yhdodo19@instance-1 ~]$ sudo lsof -i tcp:30001
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-prox 5307 root 21u IPv6 9386976 0t0 TCP *:pago-services1 (LISTEN)

可以看到 NodePort 就是 kube-proxy 创建并监听的,该服务一样有 5 个 Endpoints。接下执行命名 iptables -t nat -vnL --line-number > iptables-nat.txt 导出查看:

Chain PREROUTING (policy ACCEPT 34 packets, 3918 bytes)
num pkts bytes target prot opt in out source destination
1 2796K 327M KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */

Chain KUBE-SERVICES (2 references)
47 23 2744 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL

Chain KUBE-NODEPORTS (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/goapi:http */ tcp dpt:30001
2 0 0 KUBE-SVC-4MT5SRJPZGU2FACQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/goapi:http */ tcp dpt:30001
3 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:tls */ tcp dpt:30614
4 0 0 KUBE-SVC-S4S242M2WNFIAT6Y tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:tls */ tcp dpt:30614
5 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/web: */ tcp dpt:32063
6 0 0 KUBE-SVC-BIJGBSD4RZCCZX5R tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/web: */ tcp dpt:32063
7 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-prometheus */ tcp dpt:32105
8 0 0 KUBE-SVC-VCO3RXEEJXVGNRLL tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-prometheus */ tcp dpt:32105
9 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-grafana */ tcp dpt:30523
10 0 0 KUBE-SVC-MZX34IYCYJRMNTMQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-grafana */ tcp dpt:30523
11 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:http2 */ tcp dpt:31380
12 0 0 KUBE-SVC-G6D3V5KS3PXPUEDS tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:http2 */ tcp dpt:31380
13 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https */ tcp dpt:31390
14 0 0 KUBE-SVC-7N6LHPYFOVFT454K tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https */ tcp dpt:31390
15 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:tcp */ tcp dpt:31400
16 0 0 KUBE-SVC-62L5C2KEOX6ICGVJ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:tcp */ tcp dpt:31400
17 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-kiali */ tcp dpt:32227
18 0 0 KUBE-SVC-Y4Y3QMSBONEWNEDG tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-kiali */ tcp dpt:32227
19 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:status-port */ tcp dpt:30642
20 0 0 KUBE-SVC-TFRZ6Y6WOLX5SOWZ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:status-port */ tcp dpt:30642
21 0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-tracing */ tcp dpt:32308
22 0 0 KUBE-SVC-U67ZB3ILROLSW2OD tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* istio-system/istio-ingressgateway:https-tracing */ tcp dpt:32308

可以找到 KUBE-SVC-4MT5SRJPZGU2FACQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/goapi:http */ tcp dpt:30001, 继续找 KUBE-SVC-4MT5SRJPZGU2FACQ Chain

Chain KUBE-SERVICES (2 references)
num pkts bytes target prot opt in out source destination
2 0 0 KUBE-SVC-4MT5SRJPZGU2FACQ tcp -- * * 0.0.0.0/0 10.96.191.193 /* default/goapi:http cluster IP */ tcp dpt:81

Chain KUBE-SVC-4MT5SRJPZGU2FACQ (2 references)
num pkts bytes target prot opt in out source destination
1 0 0 KUBE-SEP-2GRZXFB4YOVSQMTA all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.20000000019
2 0 0 KUBE-SEP-FHKPE4W4K4BU4T6A all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.25000000000
3 0 0 KUBE-SEP-X6ZXAKDCXPAUMTPF all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.33332999982
4 0 0 KUBE-SEP-GXKTNAJ66D3R7K2Y all -- * * 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.50000000000
5 0 0 KUBE-SEP-2JFIICOQDPEEFOFI all -- * * 0.0.0.0/0 0.0.0.0/0

以上 5 条规则对应了 5 个 Endpoints,并且每一条都有概率设置实现的负载均衡。到此分析结束了。

3.2   LoadBalancer

需要第三方的负载均衡支持,以 GCE 为例,在“网络服务 -> 负载均衡”提供了三种类型的负载均衡:

  • HTTP(S) 负载平衡:适用于 HTTP 和 HTTPS 应用的第 7 层负载平衡
  • TCP 负载平衡:适用于依赖 TCP/SSL 协议的应用的第 4 层负载平衡或代理
  • UDP 负载平衡:适用于依赖 UDP 协议的应用的第 4 层负载平衡

3.3   Ingress

我们知道 Service 的表现形式为 IP:Port,即工作在 TCP/IP 层,仅适用于依赖 TCP/SSL 协议的应用的第 4 层负载平衡或代理。而对于基于 HTTP 的服务来说,不同的 URL 地址经常对应到不同的后端服务或者虚拟服务器,这些应用的转发机制仅通过 Kubernetes 的 Service 机制是无法实现的。Kubernetes 提供了 Ingress 适用于 HTTP 和 HTTPS 应用的第 7 层负载平衡:

  • 外部可访问 URL
  • 负载均衡
  • SSL / TLS
  • 基于域名的虚拟主机

其实就是实现了 nginx 的功能,更重要的是可以通过 ingress 配置文件直接控制 nginx。

2.3.1   Ingress Controller

控制器监听 Kubernetes API Ingress 资源的变化(反向代理和负载均衡),并实时的感知后端 service、pod 等变化(服务发现),对内置的反向代理进行设置和更新。
这里我们使用官方提供的 ingress-nginx-controller 进行实践,首先是安装 ingress-nginx-controller。只需要运行
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
但因为上面的配置文件没有开放 80、443 端口,所以必须得下载下来进行修改,
添加以下信息,添加 nodeSelector 是因为它是 deployment 部署 1 个副本,为了配合域名解析的稳定性所以固定某一个 node 节点上运行 ingress-nginx。

...
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/hostname: instance-3
...

kubectl apply -f mandatory.yaml,等待 pod 启动完成。

3.3.2   Ingress 配置

  • ingress 配置的命名空间必须得在服务所在的空间,毕竟 ingress 的 backend 无法指定服务的命名空间。
  • 如果域名和路径相同,则先部署的 ingress 优先。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-nginx
spec:
rules:
- host: kube.jemper.cn
http:
paths:
- backend:
serviceName: goapi
servicePort: 81
path: /
---

  二、Istio 解决方案

Istio社区意识到了Ingress和Mesh内部配置割裂的问题,因此从0.8版本开始,社区采用了 Gateway 资源代替K8s Ingress来表示流量入口。

Istio Gateway资源本身只能配置L4-L6的功能,例如暴露的端口,TLS设置等;但Gateway可以和绑定一个VirtualService,在VirtualService 中可以配置七层路由规则,这些七层路由规则包括根据按照服务版本对请求进行导流,故障注入,HTTP重定向,HTTP重写等所有Mesh内部支持的路由规则。

  三、API Gateway 解决方案




参考文献
[1] Kubernetes Ingress with Nginx Example. https://matthewpalmer.net/kubernetes-app-developer/articles/kubernetes-ingress-guide-nginx-example.html