zl程序教程

您现在的位置是:首页 >  其他

当前栏目

老骥伏枥-Network Policy之iptables实现

2023-04-18 12:53:29 时间

今天这篇是Network Policy系列第二篇,前一篇链接在这里“镜子-或许我们也和Pod一样生活在虚拟世界”。嗯,二哥是一个贴心的人。

上一篇结尾处我用一个外包公司将研发和测试按项目组打散混坐的例子来比喻K8s将各个Pod分散到不同的Node上这样一个编排策略。如果你是这家公司的老板,该如何解决不同team之间信息乱窜的问题呢?我们先来看看K8s是如何解决这个问题的,看看能不能给从中找到一些思路。

1. Network policy

Network policy初探

K8s Network Policy一种资源,资源名称是NetworkPolicy。你或许要问什么是资源,什么是自定义资源(CRD),它和自定义api server(custome api server)又有什么关系?这些不急,以后再细聊。

Network Policy顾名思义,首先它是一种Policy, 其次它与Network相关。Network Policy主要的功能有:

  • Policy以namespace为范围边界(比如你在namespace default的上下文创建policy)
  • Policy通过label selectors来应用到具体的Pods
  • Policy里面可以指定哪些特定的流量可以进/出其它的pods, namespaces, or CIDRs
  • Policy里面可以指定哪些特定的协议(TCP, UDP, SCTP),named ports 或者port numbers

下面是一个Network Policy的简单示例:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: netpol-demo
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: falco

这个示例中,在名为default的namespace里面,所有带有label app=falco的Pod都会被完全隔离,变成一个个信息孤岛。

为什么Network policy非常重要

我们之前在聊K8s网络模型以及K8s网络与宿主机网络对比的时候,有一个词反复提到过:扁平。

扁平的目的是为了让容器之间可以非常简单、直接、高效地通信。但现实是残酷的,简单固然非常的重要,但隐私也同样需要重视。

一个典型的对外提供服务的SaaS在K8s内部是通过Ingress+service+Pods组合搭建的。其中Ingress和Client之间是HTTPS,但Ingress和内部的Pod之间很多情况下是直接通过HTTP,而非走加密通道。Ingress通过服务发现的方式感知service的存在,service通过endpoint列表得知可以为之提供服务的Pod有哪些。

图1:K8s方式,一个典型的通过Ingress+service+Pods组合搭建的SaaS服务

设想一下,假如完全不管隐私的话,万一在生产环境混进了一个有问题的Pod,它里面跑着一个恶意的容器,这个容器就可以用各种网络嗅探工具记录这个K8s集群所有的流量。

Network Policy就是为了解决这个问题而出生的。但我们不禁要问题一个问题:以前的直接控制firewall, iptables的方式不香吗?为什么非要用Network Policy不可呢?

K8s方式与传统的微服务部署方式安全控制机制对比

在云原生这个概念落地之前,微服务之间的安全控制机制是依赖iptables创建若干条规则来控制流量的过滤、转发等操作。比如像下面这样的一条rule,将所有的访问者都拒之门外。在云平台上,这些规则的集合常称为安全组。

iptables -t filter -A INPUT -s 0.0.0.0/0.0.0.0 -d X.X.X.X -j DROP

实际上上面这条简单的规则是设置在Firewall上面的。当然Operator不可能一条一条手工去这样编写rule,他们是在云平台安全组控制平面去做配置,但是最终这些配置变成了iptables的表+链+规则。如下图所示:

图2:传统的架构,Firewall+Load Balance+VM集群

这样做当然没有问题,但它基于一个前提:如无特殊情况,Load Balance的IP地址是固定不变的,即使躲在LB后面的VM会被不断地创建和销毁。

我们来对比图1和图2,会发现以下几个有趣的现象:

  1. 传统的架构,在一个VM上跑一个(或几个)微服务,微服务的IP地址为该VM的地址。而对于K8s的方式,每个Pod都会有自己的IP,且和运行它的VM IP地址不同,正常情况下它们俩不在一个网段。
  2. 传统架构的调度是以VM为粒度,也即以VM为单位来scale out和scale in。而K8s却是以Pod为单位调度,以Pod为最小粒度来scale out和scale in。
  3. 传统的架构是以Firewall为安全边界,安全组设置作用于Firewall。而K8s方式里,Firewall消失了,看起来service起到了负载均衡的作用,但它也只是一个普通的K8s resource,无法承担安全的重任,且它的地址是不固定的,生命周期更不固定。
  4. 传统的架构,VM的创建和销毁的速度、频率、效率虽然相比Bare Metal有巨大的改进,但与K8s方式所用的Pod相比,还是显得笨重了许多。而在K8s方式里,Pod会出现剧烈的、快速的、高频次的大规模扩大和收缩的情况。
  5. 传统的架构是在云平台控制平面通过配置安全组并下发到Firewall来实现安全控制,而K8s却选择通过Network Policy这种声明式API。

对比之后,可以看见与传统方式相比,K8s对安全控制的理念、方式都发生了比较大的改变。这种改变既是源于基础架构的变化,编排方式的改变,也是微服务技术演变过程中,业界(学术界和工程领域)对服务治理新认识、新理解的体现。

2. iptables and eBPF

又一次需要提到CNI了。我们来从10000米高空来俯视一下K8s和CNI的关系。CNI, CRI, CSI组成了K8s规范的三大金刚,其中CNI完成与容器网络相关的大部分工作。只要满足CNI规定的最低要求,即可成为一个CNI插件。

图3:CNI在K8s规范中的位置

Network Policy这活也是网络插件在干,在这方面K8s就是个甩手掌柜,把所有的事都外包出去了。K8s只需要关注Network Policy请求这件事,至于如何实现则由具体的插件来完成。这里(强行)插播一条广告:CNI插件是如何知道它要处理Network Policy请求的呢?这个地方涉及到informer和controller机制,二哥后面安排上。

Network Policy有两种典型的实施方案:iptables和eBPF。前者是老骥伏枥,K8s网络插件对 Pod 进行隔离,靠在宿主机上生成 NetworkPolicy对应的 iptable 规则来实现的。而后者为最近几年大火的新秀,它以可编程、稳定、高效、安全出名,被作为下一代网络、安全和监控的首选技术。Cilium即以eBPF为核心玩出了各种各样令人眼花缭乱的,小鹿乱撞的功能。

今天这篇我们先聊下iptables这种实现方式,eBPF因为涉及到更多的背景知识,我把它放到下一篇再细聊。

3. Network Policy实现之iptables

上文提到的使用iptables来实现Network Policy,其实是通过在Pod所属的Node上生成相应的iptables规则来实现的。拿文章开头提到的Network Policy的简单示例来说,在对应的Node上会看到如下所示的iptables rule。

-N cali-pro-ksa.default.falco
-A cali-fw-cali2ca4b6a803a -m comment --comment "cali:aCru1WtJ8ymbN17_" -j cali-pro-ksa.default.falco

这个示例所涉及到的pod为 falco ,它的部署方式是daemontset。这就意味每个Node上都会运行这样一个pod。所以自然地每个Node上面的iptables里面都会添加这样的chain和rule。

你肯定会由此想到一个问题:那岂不是每个Node上面的iptables都会变得很庞大?是的,你的顾虑正是事实。不只是变大,过于庞大的iptables还会导致package经过协议栈的时候速度变慢。这是由于iptables的工作模式决定的。老骥伏枥,虽志在千里,可无耐已烈士暮年,而我们下一篇将要聊到的eBPF则是后起之秀代代强。

图4展示了使用Cilium这个CNI生成的庞大的iptables规则图。规则分为两大部分:K8s自己设置的规则以及Cilium添加的规则。

虽然前文提到Cilium更钟情于eBPF,但在不得已的情况下(例如Linux kernel版本太低)它也可以基于iptables工作。但无论图4有多复杂,总逃不脱iptables的表+链+规则的基本工作框架。理解了iptables,相信你可以非常快速地读懂这些规则。

图4:Cilium实现的 iptables规则图(图片选自https://docs.cilium.io/en/v1.10/concepts/ebpf/iptables/)

当我们尝试去解构各种云服务网络组成时,会发现无论是简单的Docker,还是Kubernetes,或者SDN(Software-defined networking ),都大量地用到了虚拟交换机也即bridge,又称网桥。它的出现和大量使用在客观上要求iptables能够过滤bridge中的数据包,比如二层的网桥在转发包时也需要被iptables的FORWARD规则所过滤。本文把这部分略过,感兴趣的你可以搜索bridge-netfilterebtables找到更多参考资料。

4. Network Policy实现之eBPF

欲知此事,且听下回分解。