0%

k8s 中的网络插件 -- flannel 和 Calico

关于 Kubernetes 系列其他文章的传送门

  1. 《 部署 Kubernetes 也能如此简单吗? 》

  2. 《 初识 k8s,新时代的宠儿 》

  3. 《 深度学习 Pod 》

  4. 《 学会这 5 种 Pod 控制器,搞定发布、更新、回滚,从此告别人肉运维!! 》

  5. 《 k8s 中的网络入口 – Service 资源管理 》

  6. 《 k8s 中的 7 层调度 – Ingress 》

  7. 《 k8s 中的存储系统 – Volumes 》

  8. 《 k8s 中强大的配置中心 – ConfigMap 》

  9. 《 k8s 中的部署神器 – Statefulset 控制器 》

  10. 《 k8s 中的安全守护神 – 你不知道不代表它不在 》

  11. 《 k8s 中的图形接口 – Dashboard 》

  12. 《 k8s 中的网络插件 – flannel 和 Calico 》     您当前所在位置

  13. 《 k8s 中的调度器 – 亲和性、污点和容忍性 》

  14. 《 k8s 中对资源的监控 – 资源指标、Prometheus、弹性伸缩器 HPA 》

  15. 《 k8s 中的云原生应用管理利器 – Helm 》

  16. 《 理解 k8s 高可用,让你的集群稳如泰山 》



logo

一、flannel 介绍

  • flannel 是 CoreOS 开发的轻量易用的网络插件,缺点是不支持网络策略。
  • Calico 是支持网络策略的插件,但是性能没有 flannel 好。

flannel 的跨节点协议

在 k8s 中,对接外部网络的解决方案,是使用基于插件的接口。插件的接口有两种,早期的解决方案是 kubenet,后来交给 CNCF 维护后,有了更开放的接口 CNI,现在建议使用 CNI。

CNI 会在主机上创建一个独立的网桥接口 cni0,而不使用 docker 默认的 docker0 桥。另外还有一个桥叫 flannel.1 这是隧道专用接口接口桥。任何 Pod 创建以后会自动关联到 cni0 上,因此获得的 IP 其实就是 cni0 网桥上的网段地址。

以 Node1 节点为例,cni0 网卡的地址和掩码是 10.244.1.0/24,所以在 Node1 上创建的 Pod 都是 10.244.1.... 开头的地址,如果通信目标是 10.244.2.... 网段的地址,那就不是本地 cni0 所在的子网,会通过内核级的路由,被送往 flannel.1,之后被打上隧道报文,并将掩码压缩至 /16 位,变成一个大二层的网络,从而送给另外一个节点,完成跨节点的隧道间协议转发。

flannel 支持多种跨节点协议,有 VxLANUDPhost-gw

  • 其中 UDP 是属于四层隧道,性能并不好,只是为了兼容早期时部分还不支持 VxLAN 的 Linux 内核所使用;
  • 而 host-gw 是直接把每一个物理机上的容器,通过物理网桥直接接入到物理网络中,大家直接通信,性能非常好,只是局限性是所以物理节点必须都在同一个物理网络中,不能跨路由器,那么彼此间网络的距离就变近了;
  • 如果想跨机房通信还是建议使用 VxLAN,其实 VxLAN 也支持在某些时候以 host-gw 的方式工作,并且采用一种很机智的方式。
    • 如果大家在同一网络中,就工作为 host-gw 类型;
    • 如果不在同一个网络中,就工作为 VxLAN 隧道类型,这种方式名为 DriectRouting

flannel 工作架构

flannel 在部署后,会以 DaemonSet 类型的运行为 Pod,有几个节点,就有几个 Pod。并且直接共享宿主机的网络名称空间,从而可以直接操纵物理机的网卡,并创建 cni0flannel.1 这样的网卡。所以 flannel 即便运行为 Pod 形式,但是却类似于在宿主机节点上运行了一个系统级的守护进程。

flannel 也使用 etcd 保存自己分配出去的网段和节点 IP 的映射关系,不过 k8s 已经有一个 ETCD 了,所以 flannel 有两个选择,要么使用自己专用的 ETCD,要么共享 k8s 的。不过让 flannel 能够直接操作 k8s 的 ETCD 从安全性上考虑是存在风险的,因此 k8s 虽然允许和 flannel 共享使用 ETCD,但是不能直接访问,要通过 apiserver 来访问。

flannel 从 kube-system 名称空间下的 ConfigMap 加载配置文件,使用 kubectl get configmap -n kube-system kube-flannel-cfg -o yaml 可以看到对应的 ConfigMap 信息。

  • 其中 net-conf.jsonBackend 字段,定义了使用哪种类型的网络模型,比如显示为 "Type": "vxlan" 时则代表使用 VxLAN 模型;
  • "Network": "10.244.0.0/16" 字段则定义了 flannel 使用的网段地址。

flannel 配置中 backend 字段的参数解析:

  • Type:指定为 vxlan 时使用 VXLAN;
  • DirectRouting:定义为 true 时会工作为类似于 host-gw 的大二层网络模式。各同网段的 Pod 之间可以直接通信,同网段的 Node 之间也能直接通信。但是 Node 和 Pod 属于不同网段,所以 Pod 不能和 Node 通信。

二、NetworkPolicy(网络策略)

k8s 中跨名称空间的 Pod 是可以直接通信的,因为默认情况下,名称空间仅隔离名称,不隔离网络通信。

所以在 k8s 中提供了一种标准的资源叫 NetworkPolicy,简称 netpol,可以由用户定义网络的访问控制。

安装 Calico

之前说过,flannel 不支持网络策略功能,要使用网络策略需要部署 Calico。

官方 V3.10 版的 Calico 官方地址

使用和 k8s 同一个 ETCD,并通过 Kubernetes API Server 存储数据,部署方式为:

1、下载部署配置文件。

1
curl https://docs.projectcalico.org/v3.10/manifests/canal.yaml -O

2、如果你使用的 Pod 网络是 CIDR: 10.244.0.0/16,请跳至下一步。如果使用 Pod 网络是其他的 CIDR,请使用以下示例来设置一个包含实际 Pod 网络的环境变量,并将示例中的 10.244.0.0/16 替换为实际 Pod 网络。

1
2
POD_CIDR="<your-pod-cidr>" \
sed -i -e "s?10.244.0.0/16?$POD_CIDR?g" canal.yaml

3、使用以下命令来安装 Calico。

1
kubectl apply -f canal.yaml

定义策略

定义访问控制时,可以:

  • 使用 podSelector 选择对哪个名称空间下的哪个 Pod 生效;
  • 使用 ingress 定义入站规则;
  • 使用 egress 定义出站规则;
  • 使用 policyTypes 控制生效哪个规则,默认只启用 ingress

注意:如果先创建的 Pod,后创建的规则,规则可能不会生效,或生效非常慢。

所有规则需要显式定义,没有明确说明允许的,都为拒绝。

策略示例:拒绝所有入站

拒绝所有入站请求,包括同一个名称空间下的其他 Pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
-> # vim deny-all-ingress.yaml 

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress

# 没有使用 ingress 明确指明入站规则,则为全部拒绝

策略示例:允许所有入站

允许所有入站请求:

1
2
3
4
5
6
7
8
9
10
11
12
-> # vim allow-all-ingress.yaml 

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {} # 指明入站规则,{} 为全部,则为全部放行

策略示例:只允许指定名称空间的入站

只允许来自 dev 名称空间内的所有 Pod 访问。

因为是通过标签来匹配的,如果没有标签,记得打标签:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-> # vim allow-dev-ingress.yaml 

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from: # 指明入站规则
- namespaceSelecrot: # 允许某个名称空间的访问
matchLabels: # 使用标签匹配名称空间
name: dev # 定义匹配的标签
ports:
- port: 80 # 定义允许访问哪个端口,不指定则为全部允许

策略示例:只允许自己名称空间的入站

只允许自己名称空间内的所有 Pod 访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-> # vim allow-my-ingress.yaml 

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from: # 指明入站规则
- namespaceSelecrot: # 允许某个名称空间的访问
matchLabels: # 使用标签匹配名称空间
name: default # 定义自己名称空间的标签
ports:
- port: 80 # 定义允许访问哪个端口,不指定则为全部允许

策略示例:允许多个名称空间的入站

允许多个名称空间的访问,比如 dev 和 pord:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-> # vim allow-other-ingress.yaml 

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from: # 指明入站规则
- namespaceSelecrot:
matchExpressions: # 使用表达式匹配多个名称空间
- key: name
operator: In # In 内部的值为或关系
values: ["dev","pord"]