Calico 用于 Kubernetes 中的网络:介绍和一点经验

Calico 用于 Kubernetes 中的网络:介绍和一点经验

本文的目的是向读者介绍 Kubernetes 中的网络和管理网络策略的基础知识,以及扩展标准功能的第三方 Calico 插件。 在此过程中,我们将使用我们操作经验中的真实示例来演示配置的简便性和一些功能。

Kubernetes 网络设备快速介绍

无法想象没有网络的 Kubernetes 集群。 我们已经发布了有关其基础知识的材料:“Kubernetes 网络图解指南“和”面向安全专业人员的 Kubernetes 网络策略简介“。

在本文中,需要注意的是,K8s 本身不负责容器和节点之间的网络连接:为此,各种 CNI 插件 (容器网络接口)。 关于这个概念的更多信息,我们 他们还告诉我.

例如,这些插件中最常见的是 绒布 — 通过在每个节点上架设网桥并为其分配子网,在所有集群节点之间提供完整的网络连接。 然而,完全且不受监管的可访问性并不总是有益的。 为了在集群中提供某种最小程度的隔离,有必要干预防火墙的配置。 在一般情况下,它被置于同一个 CNI 的控制之下,这就是为什么 iptables 中的任何第三方干预都可能被错误解释或完全忽略的原因。

并提供“开箱即用”的方式在 Kubernetes 集群中组织网络策略管理 网络策略API。 这一资源分布在选定的名称空间上,可以包含区分从一个应用程序到另一应用程序的访问的规则。 它还允许您配置特定 Pod、环境(命名空间)或 IP 地址块之间的可访问性:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

这不是最原始的例子 官方文档 可能会一劳永逸地阻碍人们了解网络政策如何运作的逻辑的愿望。 然而,我们仍然会尝试了解使用网络策略处理流量的基本原理和方法......

从逻辑上讲,有两种类型的流量:进入 Pod 的流量(Ingress)和从 Pod 流出的流量(Egress)。

Calico 用于 Kubernetes 中的网络:介绍和一点经验

实际上,政治根据运动方向分为这两类。

下一个必需的属性是选择器; 规则适用的人。 这可以是一个 Pod(或一组 Pod)或一个环境(即命名空间)。 一个重要的细节:这两种类型的对象都必须包含一个标签(标签 在 Kubernetes 术语中)——这些是政客们所使用的。

除了由某种标签联合起来的有限数量的选择器之外,还可以以不同的变体编写“允许/拒绝一切/所有人”之类的规则。 为此,使用以下形式的结构:

  podSelector: {}
  ingress: []
  policyTypes:
  - Ingress

— 在此示例中,环境中的所有 Pod 都被阻止接收传入流量。 通过以下结构可以实现相反的行为:

  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

同样对于传出:

  podSelector: {}
  policyTypes:
  - Egress

- 将其关闭。 以下是要包括的内容:

  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

回到集群的 CNI 插件的选择,值得注意的是 并非每个网络插件都支持 NetworkPolicy。 例如已经提到的Flannel不知道如何配置网络策略,这 是直接说的 在官方存储库中。 那里还提到了另一种选择——开源项目 印花布,它在网络策略方面显着扩展了 Kubernetes API 标准集。

Calico 用于 Kubernetes 中的网络:介绍和一点经验

了解 Calico:理论

Calico 插件可以与 Flannel 集成使用(子项目 渠道)或独立,涵盖网络连接和可用性管理功能。

使用 K8s“盒装”解决方案和 Calico 的 API 集提供了哪些机会?

以下是 NetworkPolicy 的内置内容:

  • 政治家受到环境的限制;
  • 策略应用于标有标签的 Pod;
  • 规则可以应用于 Pod、环境或子网;
  • 规则可以包含协议、命名或符号端口规范。

以下是 Calico 如何扩展这些功能:

  • 策略可以应用于任何对象:pod、容器、虚拟机或接口;
  • 规则可以包含特定的操作(禁止、许可、记录);
  • 规则的目标或源可以是端口、一系列端口、协议、HTTP 或 ICMP 属性、IP 或子网(第四代或第六代)、任何选择器(节点、主机、环境);
  • 此外,您可以使用 DNAT 设置和流量转发策略来调节流量的通过。

Calico 存储库中 GitHub 上的首次提交可以追溯到 2016 年 XNUMX 月,一年后该项目在组织 Kubernetes 网络连接方面占据了领先地位 - 例如,调查结果就证明了这一点: 由 New Stack 进行:

Calico 用于 Kubernetes 中的网络:介绍和一点经验

许多使用 K8s 的大型托管解决方案,例如 亚马逊EKS, Azure AKS, 谷歌GKE 其他人开始推荐使用它。

至于性能,这里一切都很棒。 在测试他们的产品时,Calico 开发团队展示了天文数字般的性能,在 50000 个物理节点上运行了 500 多个容器,每秒创建 20 个容器。 没有发现缩放问题。 这样的结果 被宣布 已经在第一个版本的公告中。 针对吞吐量和资源消耗的独立研究也证实 Calico 的性能几乎与 Flannel 一样好。 例如:

Calico 用于 Kubernetes 中的网络:介绍和一点经验

该项目发展非常迅速,它支持在流行的解决方案管理的K8s、OpenShift、OpenStack中工作,在部署集群时可以使用Calico 考普斯,有参考Service Mesh网络的建设(这是一个例子 与 Istio 结合使用)。

与 Calico 一起练习

在使用普通 Kubernetes 的一般情况下,安装 CNI 归结为使用该文件 calico.yaml, 从官方网站下载, 通过使用 kubectl apply -f.

通常,当前版本的插件与 Kubernetes 的最新 2-3 个版本兼容:旧版本中的操作未经测试且无法保证。 据开发人员称,Calico 运行在 3.10 以上的 Linux 内核上,运行 CentOS 7、Ubuntu 16 或 Debian 8,基于 iptables 或 IPVS。

环境内隔离

为了进行一般性理解,让我们看一个简单的案例,以了解 Calico 表示法中的网络策略与标准策略有何不同,以及创建规则的方法如何简化其可读性和配置灵活性:

Calico 用于 Kubernetes 中的网络:介绍和一点经验

集群中部署了 2 个 Web 应用程序:Node.js 和 PHP,其中之一使用 Redis。 要阻止从 PHP 访问 Redis,同时保持与 Node.js 的连接,只需应用以下策略:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-redis-nodejs
spec:
  podSelector:
    matchLabels:
      service: redis
  ingress:
  - from:
    - podSelector:
        matchLabels:
          service: nodejs
    ports:
    - protocol: TCP
      port: 6379

本质上,我们允许从 Node.js 传入 Redis 端口的流量。 他们显然没有禁止任何其他事情。 NetworkPolicy一出现,其中提到的所有选择器就开始被隔离,除非另有说明。 但是,隔离规则不适用于选择器未涵盖的其他对象。

该示例使用 apiVersion Kubernetes 开箱即用,但没有什么可以阻止您使用它 来自 Calico 交付的同名资源。 那里的语法更详细,因此您需要按以下形式重写上述情况的规则:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-redis-nodejs
spec:
  selector: service == 'redis'
  ingress:
  - action: Allow
    protocol: TCP
    source:
      selector: service == 'nodejs'
    destination:
      ports:
      - 6379

上述用于允许或拒绝通过常规 NetworkPolicy API 的所有流量的结构包含带有括号的结构,这些结构难以理解和记忆。 对于 Calico,要将防火墙规则的逻辑更改为相反的逻辑,只需更改 action: Allowaction: Deny.

环境隔离

现在想象这样一种情况:应用程序生成业务指标,以便在 Prometheus 中收集并使用 Grafana 进行进一步分析。 上传的内容可能包含敏感数据,默认情况下这些数据也是公开可见的。 让我们隐藏这些数据以免被窥探:

Calico 用于 Kubernetes 中的网络:介绍和一点经验

通常,Prometheus 放置在单独的服务环境中 - 在示例中它将是这样的命名空间:

apiVersion: v1
kind: Namespace
metadata:
  labels:
    module: prometheus
  name: kube-prometheus

领域 metadata.labels 事实证明这并非偶然。 正如刚才提到的, namespaceSelector (也 podSelector)与标签一起操作。 因此,要允许从特定端口上的所有 Pod 获取指标,您必须添加某种标签(或从现有标签中获取),然后应用如下配置:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          module: prometheus
    ports:
    - protocol: TCP
      port: 9100

如果您使用 Calico 策略,语法将如下所示:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  ingress:
  - action: Allow
    protocol: TCP
    source:
      namespaceSelector: module == 'prometheus'
    destination:
      ports:
      - 9100

一般来说,通过针对特定需求添加此类策略,可以防止集群中应用程序的运行受到恶意或意外干扰。

根据 Calico 的创建者的说法,最佳实践是“阻止所有内容并显式打开您需要的内容”方法,记录在 官方文档 (其他人也遵循类似的方法 - 特别是在 已经提到过的文章).

使用额外的 Calico 对象

让我提醒您,通过 Calico API 的扩展集,您可以调节节点的可用性,而不仅限于 pod。 在下面的示例中使用 GlobalNetworkPolicy 在集群中传递 ICMP 请求的功能已关闭(例如,从 pod 到节点、pod 之间或从节点到 IP pod 的 ping):

apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: block-icmp
spec:
  order: 200
  selector: all()
  types:
  - Ingress
  - Egress
  ingress:
  - action: Deny
    protocol: ICMP
  egress:
  - action: Deny
    protocol: ICMP

在上述情况下,集群节点仍然可以通过 ICMP 相互“联系”。 这个问题是通过以下方式解决的 GlobalNetworkPolicy,应用于实体 HostEndpoint:

apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: deny-icmp-kube-02
spec:
  selector: "role == 'k8s-node'"
  order: 0
  ingress:
  - action: Allow
    protocol: ICMP
  egress:
  - action: Allow
    protocol: ICMP
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
  name: kube-02-eth0
  labels:
    role: k8s-node
spec:
  interfaceName: eth0
  node: kube-02
  expectedIPs: ["192.168.2.2"]

VPN 案例

最后,我将给出一个非常真实的示例,当一组标准策略还不够时,使用 Calico 函数来处理近集群交互的情况。 要访问 Web 应用程序,客户端使用 VPN 隧道,并且此访问受到严格控制并仅限于允许使用的特定服务列表:

Calico 用于 Kubernetes 中的网络:介绍和一点经验

客户端通过标准 UDP 端口 1194 连接到 VPN,并在连接后接收到 Pod 和服务的集群子网的路由。 推送整个子网,以免在重新启动和地址更改期间丢失服务。

配置中的端口是标准的,这对配置应用程序并将其传输到 Kubernetes 集群的过程带来了一些细微差别。 例如,去年年底,AWS LoadBalancer for UDP 确实出现在有限的区域列表中,而 NodePort 由于在所有集群节点上转发而无法使用,并且无法扩展服务器实例的数量容错的目的。 另外,您必须更改端口的默认范围......

经过搜索可能的解决方案,选择以下方案:

  1. 具有 VPN 的 Pod 按节点调度 hostNetwork,即为实际IP。
  2. 该服务通过外部发布 ClusterIP。 端口物理安装在节点上,可以从外部访问,但需要进行少量保留(有条件地存在真实 IP 地址)。
  3. 确定豆荚升起的节点超出了我们故事的范围。 我只想说,你可以将服务紧紧地“钉”在一个节点上,或者编写一个小型 sidecar 服务来监视 VPN 服务的当前 IP 地址并编辑向客户端注册的 DNS 记录 - 无论谁有足够的想象力。

从路由的角度来看,我们可以通过VPN服务器发布的IP地址来唯一标识VPN客户端。 下面是限制此类客户端访问服务的原始示例,在上述 Redis 上进行了说明:

apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
  name: vpnclient-eth0
  labels:
    role: vpnclient
    environment: production
spec:
  interfaceName: "*"
  node: kube-02
  expectedIPs: ["172.176.176.2"]
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: vpn-rules
spec:
  selector: "role == 'vpnclient'"
  order: 0
  applyOnForward: true
  preDNAT: true
  ingress:
  - action: Deny
    protocol: TCP
    destination:
      ports: [6379]
  - action: Allow
    protocol: UDP
    destination:
      ports: [53, 67]

在这里,严格禁止连接到端口 6379,但同时保留 DNS 服务的操作,在制定规则时,该服务的功能经常受到影响。 因为,如前所述,当选择器出现时,除非另有指定,否则默认拒绝策略将应用于它。

结果

因此,使用 Calico 的高级 API,您可以灵活配置和动态更改集群内部和周围的路由。 一般来说,它的使用看起来就像用大炮射麻雀,并且在平坦网络上的简单 Kubernetes 安装中实现具有 BGP 和 IP-IP 隧道的 L3 网络看起来很可怕......但是,除此之外,该工具看起来非常可行和有用。

隔离集群来满足安全要求可能并不总是可行,这就是 Calico(或类似的解决方案)可以解决的问题。 本文中给出的示例(稍作修改)已用于我们在 AWS 中的多个客户端安装。

PS

另请阅读我们的博客:

来源: habr.com

添加评论