一、k8s网络通信模型
k8s的网络中主要存在四种类型的通信:
1.同一pod内的容器间通信
2.pod与pod之间的通信
3.pod与service间的通信
4.Internet同service之间的通信
1, 同一pod内的容器间通信
同一pod内的容器共享同一个网络命名空间,所以可以直接通过lo直接通信
2, pod与pod之间的通信
1.同node上pod之间通信
2.不同node上的pod之间通信
同node上pod之间通信
不同pod都有独立的IP,可以直接通信
它们在同一个网段上,通过Docker0网桥实现通信
不同node上pod之间的通信
可以使用Overlay Network(叠加网络,二层报文或三层隧道报文)进行通信
3, pod与service间的通信
Service都会生成一个虚拟IP,称为Service-IP
Kube-porxy组件负责实现Service-IP路由和转发,在overlay network之上又实现了虚拟转发网络
Kube-porxy实现了以下功能:
- 转发访问Service的Service-IP的请求到Endpoints(即Pod-IP)。
- 监控Service和Endpoints的变化,实时刷新转发规则。
- 负载均衡能力。
iptables实现
IPVS实现
4, Internet同service之间的通信
分为以下两种情况:
- 从k8s的service访问Internet
- 从Internet访问k8s的service
步骤:
1.将pod-IP进行SNAT转换为node-IP
2.node-IP通过网关指向路由器将包给路由器
3.路由器SNAT成路由器公网IP出去访问Internet
4.包回来给路由器公网IP
5.由路由器自动DNAT给node-IP
6.由node自动DNAT给pod-IP
说明: 和以前学习的虚拟机NAT上外网,云主机NAT上外网过程一致。
让Internet流量进入k8s集群,可分为:
- NodePort
- LoadBalancer
- Ingress控制器
- pod的hostnetwork: True或hostPort
二、k8s网络分层
将k8s网络分层为:
1, Internet : 外部网络 ( 严格来说,internet不属于k8s集群网络 )
2, node网络: 各主机(master、node、etcd等)自身所属的网络,物理网卡配置的网络
3, service网络: 虚拟网络
4, pod网络: 虚拟网络
三、k8s网络解决方案
1.CNI
CNI(container network interface)是容器网络接口
- 是K8s中提供的一种通用网络标准规范,因为k8s本身不提供网络解决方案
- 以插件方式使用, 为用户在pod创建或者销毁时动态配置网络
2.CNI插件的三种使用模式
- overlay 通过隧道通信
- 路由 通过路由通信
- underlay 直接使用底层网络的IP,与宿主机在同一个网络里进行通讯
Overlay | L3 Routing | Underlay | |
---|---|---|---|
描述 | 把二层报文封装在IP报文之上进行传输 | 通过三层路由的方式向IP报文传输到目的宿主机 | 直接使用底层网络的IP,与宿主机在同一个网络里进行通讯 |
网络要求 | 低:IP可达 | 二层可达或BGP可达 | 二层可达/交换机支持 |
性能 | 中:封包、拆包 | 高:路由转发 | 高:几乎没有损耗 |
IP类型 | 虚拟IP | 物理IP | |
集群外访问 | Ingress/NodePort | ||
访问控制 | Network Policy | Iptables/外部网络 | |
静态IP | 不支持 | 支持 | |
场景 | 对性要求不高的;网络环境不灵活的 | 大多数场景 | 对性能要求高的,需要和现有业务直接通信,需要静态IP |
开源产品 | flannel-vxlan,openshift-sdn | calico,flannel-hostgw | Macvlan/IPvlan |
3.选择网络方案需要考虑的维度
- 网络配置:给Pod、Service提供IP地址
- 网络策略:通过提供网络策略添加网络规则,实现网络隔离(多租户场景下非常有必要)
4.常见网络方案对比
- flannel 仅支持网络配置
- calico 支持网络配置及网络策略,三层BGP网络
- canel 使用calico提供网络策略,使用flannel提供网络配置
ls /etc/cni/net.d/
which calico
四、calico网络
官方网站: https://www.projectcalico.org/
calico设计优势
1, 更优的资源利用
- 二层网络通讯需要依赖广播消息机制,广播消息的开销与 host 的数量呈指数级增长,Calico 使用的三层路由方法,则完全抑制了二层广播,减少了资源开销。
- 二层网络使用 VLAN 隔离技术,有 4096 个规格限制,即便可以使用 vxlan 解决,但 vxlan 又带来了隧道开销的新问题。而 Calico 不使用 vlan 或 vxlan 技术,使资源利用率更高。
2, 可扩展性
- Calico 使用与 Internet 类似的方案,Internet 的网络比任何数据中心都大,Calico 同样天然具有可扩展性。
3, 简单而更容易 debug
- 因为没有隧道,意味着 workloads 之间路径更短更简单,配置更少,在 host 上更容易进行 debug 调试。
4. 更少的依赖
- Calico 仅依赖三层路由可达。
5, 可适配性
- Calico 较少的依赖性使它能适配所有 VM、Container、白盒或者混合环境场景。
优势总结:
- 由于Calico是一种纯三层的实现,因此可以避免与二层方案相关的数据包封装的操作,中间没有任何的NAT,没有任何的overlay,所以它的转发效率非常高.
- 因为它的包直接走原生TCP/IP的协议栈,它的隔离也因为这个栈而变得好做。因为TCP/IP的协议栈提供了一整套的防火墙的规则,所以它可以通过IPTABLES的规则达到比较复杂的隔离逻辑。
Calico架构图
Calico主要工作组件
1,Felix:
运行在每一台Host的agent进程,主要负责网络接口管理和监听、路由、ARP 管理、ACL 管理和同步、状态上报等。
[root@master1 ~]# ps -ef |grep felix |grep -v grep
其它节点上都可查到相关进程
2,etcd:
分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性,可以与kubernetes共用;
3,BGP Client(BIRD):
Calico 为每一台 Host 部署一个BGP Client,使用BIRD实现,BIRD 是一个单独的持续发展的项目,实现了众多动态路由协议比如 BGP、OSPF、RIP 等。在 Calico 的角色是监听 Host 上由 Felix 注入的路由信息,然后通过 BGP 协议广播告诉剩余 Host 节点,从而实现网络互通。
4,BGP Route Reflector:
在大型网络规模中,如果仅仅使用 BGP client 形成 mesh 全网互联的方案就会导致规模限制,因为所有节点之间俩俩互联,需要 N^2 个连接,为了解决这个规模问题,可以采用 BGP 的 Router Reflector 的方法,使所有 BGP Client 仅与特定 RR 节点互联并做路由同步,从而大大减少连接数。
calico通迅协议
calico主要通过两种协议来实现通信
- ipip协议 通过ipip隧道作为通信基础
- bgp协议 纯三层的路由交换
bgp协议主要由两种方式:
- BGP Speaker 全互联模式(node-to-node mesh)
- 网络成网状,两两互联,适合小规模集群
- BGP Speaker 路由反射模式(Route Reflector)
- BGP Speaker连接到一个或多个中心BGP Speaker,网络成星状。适合大规模节点集群
五、calicoctl工具
安装calicoctl工具
wget https://github.com/projectcalico/calicoctl/releases/download/v3.16.5/calicoctl-linux-amd64
mv calicoctl-linux-amd64 /bin/calicoctl
chmod a+x /bin/calicoctl
如果是kubeasz安装时选择了calico方案,默认就已经有calicoctl命令了
which calicoctl
查看calico节点信息
calicoctl get node
查看calico节点详细信息
calicoctl get node -o yaml
apiVersion: projectcalico.org/v3
items:
- apiVersion: projectcalico.org/v3
kind: Node
metadata:
creationTimestamp: 2021-05-28T15:19:50Z
name: master1
resourceVersion: "106311"
uid: 25042b99-bfc8-11eb-9498-000c29b92f96
spec:
bgp:
ipv4Address: 192.168.154.135/24
ipv4IPIPTunnelAddr: 172.20.137.64
orchRefs:
- nodeName: 192.168.154.135
orchestrator: k8s
- apiVersion: projectcalico.org/v3
kind: Node
metadata:
creationTimestamp: 2021-05-28T15:19:43Z
name: master2
resourceVersion: "106450"
uid: 21630471-bfc8-11eb-aeae-000c2921cc17
spec:
bgp:
ipv4Address: 192.168.154.138/24
ipv4IPIPTunnelAddr: 172.20.180.0
orchRefs:
- nodeName: 192.168.154.138
orchestrator: k8s
- apiVersion: projectcalico.org/v3
kind: Node
metadata:
creationTimestamp: 2021-05-28T15:19:44Z
name: node1
resourceVersion: "106867"
uid: 21968962-bfc8-11eb-a040-000c29885ff6
spec:
bgp:
ipv4Address: 192.168.154.139/24
ipv4IPIPTunnelAddr: 172.20.166.128
orchRefs:
- nodeName: 192.168.154.139
orchestrator: k8s
- apiVersion: projectcalico.org/v3
kind: Node
metadata:
creationTimestamp: 2021-05-28T15:19:45Z
name: node2
resourceVersion: "106590"
uid: 2214fb47-bfc8-11eb-96e2-000c299f1360
spec:
bgp:
ipv4Address: 192.168.154.140/24
ipv4IPIPTunnelAddr: 172.20.104.0
orchRefs:
- nodeName: 192.168.154.140
orchestrator: k8s
kind: NodeList
metadata:
resourceVersion: "127400"
查看当前ip池
calicoctl get ippool
查看当前网络模式
calicoctl node status
可以看到使用的是BGP的node-to-node mesh模式
netstat -anp | grep ESTABLISH | grep bird
我的k8s集群是4个节点,所以每个节点上能看到3个连接。(证明了节点之间是两两连接)
六、calico网络策略
网络策略介绍
参考: https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/
网络策略就是对网络进行隔离和限制。
CNI插件可以实现不同Node节点的Pod互通问题,这整个就是一个扁平化的网络。但是如果遇到以下场景呢?
- 多租户网络环境隔离
- 不同环境之间的隔离,如: 开发环境namespace与测试环境namespace之间的隔离
- 应用程序间的访问控制。例如微服务A允许访问微服务B,却不能访问微服务C等
当Pod暴露到外部时,需要做Pod黑白名单
这时候网络策略就派上用场了。
网络策略就相当于把iptables防火墙规则做成了YAML资源,只要iptables会玩,网络策略就不难。
官方YAML例子做讲解:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default # 代表此策略对default这个namespace生效(还要看下面的podSelector)
spec:
podSelector:
matchLabels:
role: db # 代表此策略对default这个namespace里的带有role=db标签的pod才生效
policyTypes:
- Ingress # 相当于iptables -P INPUT DROP,做成进入流量全拒绝
- Egress # 相当于iptables -P OUTPUT DROP,做成出去流量全拒绝
ingress: # 在双链接拒绝的情况下配置的进入白名单
- from:
- ipBlock: # 以ip段的方式配置的白名单
cidr: 172.17.0.0/16 # 允许的IP段
except:
- 172.17.1.0/24 # 允许的IP段基础上再拒绝的IP段或IP
- namespaceSelector: # 以namespace的标签配置的白名单
matchLabels:
project: myproject # 允许带有project=myproject标签的namespace里的所有pod
- podSelector:
matchLabels:
role: frontend # 允许相同namespace里的带有role=frontend标签的pod
ports:
- protocol: TCP
port: 6379 # 相当于一个端口的过滤条件。上面三种白名单都只能访问目标tcp:6379端口
egress: # 在双链接拒绝的情况下配置的出去白名单
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
namespace拒绝进入流量
1,创建两个命名空间
kubectl create ns dev
kubectl create ns test
2,分别运行一个pod
kubectl run nginx1 --image=nginx:1.15-alpine -n dev
kubectl run nginx2 --image=nginx:1.15-alpine -n test
3, 查看两个pod的IP
kubectl get pods -o wide -n dev
kubectl get pods -o wide -n test
4, 验证不同命名空间两个pod可以互通
kubectl exec -it nginx1 -n dev -- ping -c 2 172.20.104.25
kubectl exec -it nginx2 -n test -- ping -c 2 10.3.104.4
5,对dev命名空间创建网络策略
(拒绝所有进入流量,允许所有出去流量)
vim dev-netpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-netpolicy
spec:
podSelector: {} # 任意pod
policyTypes:
- Ingress # ingress代表进入
kubectl apply -f ingress-deny.yaml -n dev
kubectl get netpol -n dev
6, 验证dev的pod可以ping通test的pod
(对于dev来说,可以出去)
7, 验证test的pod不能ping通dev的pod
(对于dev来说,拒绝进入)
kubectl exec -it nginx2 -n test -- ping -c 2 172.20.104.24
namespace拒绝进出所有流量
1,修改yaml并应用
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-netpolicy
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress # 加1个Egress代表出, 其它不变
kubectl apply -f dev-netpolicy.yaml -n dev
2,验证
( 对于dev命名空间来说,进入与出去都拒绝)
出去ping不通
进入ping不通
kubectl exec -it nginx2 -n test -- ping -c 2 172.20.104.24
通过IP段与label定义策略
1, dev命名空间再创建一个名为nginx3的pod,test命名空间再创建一个名为nginx4的pod
kubectl run nginx3 --image=nginx:1.15-alpine -n dev
kubectl run nginx4 --image=nginx:1.15-alpine -n test
2, 为dev命名空间名为nginx1的pod打一个标记
kubectl label pod nginx1 app=nginx1 -n dev
3, 修改策略并应用
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-netpolicy
spec:
podSelector:
matchLabels:
app: nginx1 # 匹配pod的标签
policyTypes:
- Ingress
- Egress # 双链拒绝
ingress:
- from:
- ipBlock: # IP地址块
cidr: 172.20.0.0/16 # 允许的IP段
except:
- 172.20.104.24/32 # 拒绝的IP(必须加掩码32位),这个IP是test命名空间的nginx2的pod-ip
说明: 总的意思就是dev命名空间中带有app=nginx1标签的pod,允许172.20.0.0/16网段进来,但拒绝172.20.104.24进来
4,确认nginx1与nginx3的pod-IP
5, 验证
test命名空间的nginx4可以ping通dev命名空间的nginx1
[root@master1 ~]# kubectl exec -it nginx4 -n test -- ping -c 2 172.20.104.24
但test命名空间的nginx1可以ping不通dev命名空间的nginx1
(因为策略拒绝了)
[root@master2 ~]# kubectl exec -it nginx1 -n dev -- ping -c 2 172.20.104.24
但test命名空间的nginx2又可以ping通dev命名空间的nginx3
(因为只有nginx1才带有app=nginx1标签, nginx3不带此标签)
[root@master2 ~]# kubectl exec -it nginx2 -n test -- ping -c 2 172.20.104.26
同namespace通过label访问
1,修改策略并应用
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-netpolicy
spec:
podSelector:
matchLabels:
app: nginx1 # 只能访问app=nginx1标签的pod
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
access_nginx1: "true" # 表示只有带有access_nginx1=true标签的pod才能访问(同namespace下)
2, 验证: 同namespace下的nginx3 ping不通nginx1
kubectl exec -it nginx3 -n dev -- ping -c 2 172.20.104.31
3, 验证:
为nginx3打上 access_nginx1=true 标签就可以访问nginx1了
[root@master2 ~]# kubectl label pod nginx3 access_nginx1=true -n dev
[root@master2 ~]# kubectl exec -it nginx3 -n dev -- ping -c 172.20.104.31
写在最后: 以上几个小例子只是抛砖引玉,更多复杂策略请按照业务需求对照语法来实现。
七、修改为Route Reflector模式
关闭node-to-node模式
vim bgpconfig.yml
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
logSeverityScreen: Info
nodeToNodeMeshEnabled: false # 关闭node-to-node模式
asNumber: 61234 # 自定义AS号
calicoctl apply -f bgpconfig.yml
验证
calicoctl get bgpconfig
kubectl get pods -o wide
ping 172.20.166.161
ping不通k8s集群中的pod了
calicoctl get nodes --output=wide
创建bgppeer
vim bgppeer.yml
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: bgppeer-global
spec:
peerIP: 192.168.154.135 # 指定路由反射器节点(BGPPeer)
asNumber: 61234
calicoctl apply -f bgppeer.yml
calicoctl get bgppeer
现在为node specific
ping -c 2 172.20.166.161
k8s集群上的pod也可以ping得通了
验证集群各节点的连接
除了bgppeer节点有N-1个连接外,其它节点只有1个连接
[root@master1 ~]# netstat -anp | grep ESTABLISH | grep bird
[root@master2 ~]# netstat -anp | grep ESTABLISH | grep bird
[root@node1 ~]# netstat -anp | grep ESTABLISH | grep bird
[root@node2 ~]# netstat -anp | grep ESTABLISH | grep bird
改回mesh模式的方法
calicoctl delete -f bgppeer.yml
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
logSeverityScreen: Info
nodeToNodeMeshEnabled: true # 再改为True
asNumber: 61234