kubernetes 集群中 cilium 的实践及其网络通信解析
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kubernetes 集群中 cilium 的实践及其网络通信解析相关的知识,希望对你有一定的参考价值。
参考技术ACilium 是一个基于 eBPF 和 XDP 的高性能容器网络方案的开源项目,目标是为微服务环境提供网络、负载均衡、安全功能,主要定位是容器平台。
Why Cilium ?
现在应用程序服务的发展已从单体结构转变为微服务架构,微服务间的的通信通常使用轻量级的 http 协议。微服务应用往往是经常性更新变化的,在持续交付体系中为了应对负载的变化通常会横向扩缩容,应用容器实例也会随着应用的更新而被创建或销毁。
这种高频率的更新对微服务间的网络可靠连接带来了挑战:
Cilium 通过利用 BPF 具有能够透明的注入网络安全策略并实施的功能,区别于传统的 IP 地址标识的方式,Cilium 是基于 service / pod /container 标识来实现的,并且可以在应用层实现 L7 Policy 网络过滤。总之,Cilium 通过解藕 IP 地址,不仅可以在高频变化的微服务环境中应用简单的网络安全策略,还能在支持 L3/L4 基础上通过对 http 层进行操作来提供更强大的网络安全隔离。BPF 的使用使 Cilium 甚至可以在大规模环境中以高度可扩展的方式解决这些挑战问题。
Cilium 的主要功能特性 :
此处使用外部的 etcd 的部署方式,外部 etcd 安装 cilium 在较大的运行环境中能够提供更好的性能。
Requirements
安装 helm 3
挂载 BPF 文件系统
在 kubernetes 集群所有 node 上挂载 bpf 文件系统
kubernetes 配置
cilium 安装
当使用外部 etcd 作为 cilium 的 k-v 存储,etcd 的 IP 地址需要在 cilium 的 configmap 中配置。
使用 helm 安装 cilium
安装 cilium 连接测试用例
此用例将会部署一系列的 deployment,它们会使用多种路径来相互访问,连接路径包括带或者不带服务负载均衡和各种网络策略的组合。
部署的 podName 表示连接方式,readiness/liveness 探针则可指示连接是否成功。
安装 hubble https://github.com/cilium/hubble
hubble 是一个用于云原生工作负载的完全分布式网络和安全可视化平台。它建立在 Cilium 和 eBPF 的基础上,以完全透明的方式深入了解服务以及网络基础结构的通信和行为。
hubble 对前面安装的测试用例监控信息
cilium 在 kubernetes 集群中安装好后,此处我们来探究一下在不同 node 上 pod 间的 vxlan 通信方式。
cilium 安装完后,cilium agent 会在 node 上创建 cilium_net 与 cilium_host 一对 veth pair 及用于跨宿主机通信的 cilium_vxlan ,然后在 cilium_host 上配置其管理的 CIDR IP 作为网关。
如上图示中,通过抓包分析 Container A 与 Container B 之前的通信路径。
Container A ping Container B (以下称 Container A -> CA , Container B -> CB)
进入 Node01 上 CA 内 (10.244.1.154),ping CB ip 地址 10.224.6.11
进入 Node01 上 cilium agent 容器内,查看 CA 容器作为 cilium endpoint 的信息
CA 容器 ping 出的 icmp 包将会经过网卡 lxc8b528e748ff4 (lxcxxA) 路由到 cilium_host 网关,路由方式与传统的通过 Linux bridge 这样的二层设备转发不一样,cilium 在每个容器相关联的虚拟网卡上都附加了 bpf 程序,通过连接到 TC ( traffic control ) 入口钩子的 bpf 程序将所有网络流量路由到主机端虚拟设备上,由此 cilium 便可以监视和执行有关进出节点的所有流量的策略,例如 pod 内的 networkPolicy 、L7 policy、加密等规则。
CA 容器内的流量要跨宿主机节点路由到 CB 容器,则需要 cilium_vxlan VTEP 设备对流量包进行封装转发到 Node02 上。bpf 程序会查询 tunnel 规则并将流量发送给 cilium_vxlan ,在 cilium agent 容器内可查看到 bpf 的 tunnel 规则
在 Node01 上对 cilium_vxlan 抓包,可看到 CA 容器对 icmp 包经过了 cilium_vxlan
再对 Node01 上的 eth0 抓包,可看到 cilium_vxlan 已将 CA 的流量进行 vxlan 封包,src ip 改为本机 node ip 192.168.66.226, dst ip 改为 192.168.66.221
到 Node02 上对 eth0 抓包,可看到 CA 容器的流量包已到 Node02 上
对 cilium_vxlan 抓包即可看到 CA 容器过来对流量包已被解封
至此,CA 容器对流量已到达 cilium 创建的虚拟网卡。
我们知道 Linux 内核本质上是事件驱动的(the Linux Kernel is fundamentally event-driven), cilium 创建的虚拟网卡接收到流量包将会触发连接到 TC ( traffic control ) ingress 钩子的 bpf 程序,对流量包进行相关策略对处理。
查看 cilium 官方给出的 ingress/egress datapath,可大致验证上述 cilium 的网络通信路径。
首先是 egress datapath ,图中橙黄色标签为 cilium component,有 cilium 在宿主机上创建的 bpf 程序(对应着红色标签的 kernel bpf 钩子),若使用 L7 Policy 则还有 cilium 创建的 iptables 规则。流量从某个容器 endpoint 通过容器上的 veth pair 网卡 lxcxxx 出发,即会触发 bpf_sockops.c / bpf_redir.c bpf 程序,若使用了 L7 Policy 则进入用户空间进行 L7 层的数据处理,若没有使能 L7 Policy 则将触发 TC egress 钩子,bpf_lxc 对数据进行处理(若使能 L3 加密,则触发其他 bpf 钩子),数据最终被路由到 cilium_hos t 网关处,再根据 overlay 模式(vxlan 等)将数据发送出去。
在 cilium ingress datapath 中,数据流量进入主机网络设备上,cilium 可根据相关配置,对数据流量进行预处理(prefilter/L3 加解密/ 负载均衡/ L7 Policy 处理)或直接路由到 cilium_host 触发相应到 bpf 程序,再到最终的 endpoint 处。
使用 Cilium 增强 Kubernetes 网络安全
在本篇,我们分别使用了 Kubernetes 原生的网络策略和 Cilium 的网络策略实现了 Pod 网络层面的隔离。不同的是,前者只提供了基于 L3/4 的网络策略;后者支持 L3/4、L7 的网络策略。
通过网络策略来提升网络安全,可以极大降低了实现和维护的成本,同时对系统几乎没有影响。
尤其是基于 eBPF 技术的 Cilium,解决了内核扩展性不足的问题,从内核层面为工作负载提供安全可靠、可观测的网络连接。
背景为什么说 Kubernetes 网络存在安全隐患?集群中的 Pod 默认是未隔离的,也就是 Pod 之间的网络是互通的,可以互相通信的。
这里就会有问题,比如由于数据敏感服务 B 只允许特定的服务 A 才能访问,而服务 C 无法访问 B。要禁止服务 C 对服务 B 的访问,可以有几种方案:
以上两种方案各有利弊:
继续向基础设施下层找方案,从网络层入手。Kubernetes 提供了的网络策略 *NetworkPolicy*[1],则可以实现“网络层面的隔离”。
示例应用在进一步演示 NetworkPolicy 的方案之前,先介绍用于演示的示例应用。我们使用 Cilium 在互动教程 Cilium getting started[2] 中使用的“星球大战”场景。
这里有三个应用,星战迷估计不会陌生:
80
端口提供 web 服务,有 2 个 副本,通过 Kubernetes Service 的负载均衡为帝国战机对外提供”登陆“服务。如图所示,我们使用了 Label 对三个应用进行了标识:
org
和class
。在执行网络策略时,我们会使用这两个标签识别负载。
# app.yaml
---
apiVersion: v1
kind: Service
metadata:
name: deathstar
labels:
app.kubernetes.io/name: deathstar
spec:
type: ClusterIP
ports:
- port: 80
selector:
org: empire
class: deathstar
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deathstar
labels:
app.kubernetes.io/name: deathstar
spec:
replicas: 2
selector:
matchLabels:
org: empire
class: deathstar
template:
metadata:
labels:
org: empire
class: deathstar
app.kubernetes.io/name: deathstar
spec:
containers:
- name: deathstar
image: docker.io/cilium/starwars
---
apiVersion: v1
kind: Pod
metadata:
name: tiefighter
labels:
org: empire
class: tiefighter
app.kubernetes.io/name: tiefighter
spec:
containers:
- name: spaceship
image: docker.io/tgraf/netperf
---
apiVersion: v1
kind: Pod
metadata:
name: xwing
labels:
app.kubernetes.io/name: xwing
org: alliance
class: xwing
spec:
containers:
- name: spaceship
image: docker.io/tgraf/netperf
Kubernetes 网络策略可以通过官方文档[3]获取更多详细信息,这里我们直接放出配置:
# native/networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy
namespace: default
spec:
podSelector:
matchLabels:
org: empire
class: deathstar
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
org: empire
ports:
- protocol: TCP
port: 80
podSelector
:表示要应用网络策略的工作负载均衡,通过 label 选择到了 deathstar 的 2 个 Pod。policyTypes
:表示流量的类型,可以是 Ingress
或 Egress
或两者兼具。这里使用 Ingress
,表示对选择的 deathstar Pod 的入站流量执行规则。ingress.from
:表示流量的来源工作负载,也是使用 podSelector
和 Label 进行选择,这里选中了 org=empire
也就是所有“帝国的战机”。ingress.ports
:表示流量的进入端口,这里列出了 deathstar 的服务端口。接下来,我们测试下。
测试先准备环境,我们使用 K3s[4] 作为 Kubernetes 环境。但由于 K3s 默认的 CNI 插件 Flannel 不支持网络策略,我们需要换个插件,这里选择 Calico[5],即 K3s + Calico 的方案。
先创建一个单节点的集群:
curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE="644" INSTALL_K3S_EXEC="--flannel-backend=none --cluster-cidr=10.42.0.0/16 --disable-network-policy --disable=traefik" sh -
此时,所有的 Pod 都处于 Pending
状态,因为还需要安装 Calico:
kubectl apply -f https://projectcalico.docs.tigera.io/manifests/calico.yaml
待 Calico 成功运行后,所有的 Pod 也会成功运行。
接下来就是部署应用:
kubectl apply -f app.yaml
执行策略前,执行下面的命令看看“战机能否登陆死星”:
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
从结果来看,两种 ”战机“(Pod 负载)都可以访问 deathstar 服务。
此时执行网络策略:
kubectl apply -f native/networkpolicy.yaml
再次尝试”登陆“,xwing 的登陆请求会停在那(需要使用 ctrl+c 退出,或者请求时加上 --connect-timeout 2
)。
使用 Kubernetes 网络策略实现了我们想要的,从网络层面为服务增加了白名单的功能,这种方案没有改造成本,对系统也几乎无影响。
Cilium 还没出场就结束了?我们继续看:
有时我们的服务会对外暴露一些管理端点,由系统调用执行一些管理上的操作,比如热更新、重启等。这些端点是不允许普通服务来调用,否则会造成严重的后果。
比如示例中,tiefighter 访问了 deathstar 的管理端点 /exhaust-port
:
kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Panic: deathstar exploded
goroutine 1 [running]:
main.HandleGarbage(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
/code/src/github.com/empire/deathstar/
temp/main.go:9 +0x64
main.main()
/code/src/github.com/empire/deathstar/
temp/main.go:5 +0x85
出现了 Panic 错误,检查 Pod 你会发现 dealthstar 挂了。
Kubernetes 的网络策略仅能工作在 L3/4 层,对 L7 层就无能为力了。
还是要请出 Cilium。
Cilium 网络策略由于 Cilium 涉及了 Linux 内核、网络等众多知识点,要讲清实现原理篇幅极大。故这里仅摘取了官网的介绍,后期希望有时间再写一篇关于实现的。
Cilium 简介Cilium[6] 是一个开源软件,用于提供、保护和观察容器工作负载(云原生)之间的网络连接,由革命性的内核技术 eBPF[7] 推动。
eBPF 是什么?
Linux 内核一直是实现监控/可观测性、网络和安全功能的理想地方。不过很多情况下这并非易事,因为这些工作需要修改内核源码或加载内核模块, 最终实现形式是在已有的层层抽象之上叠加新的抽象。eBPF 是一项革命性技术,它能在内核中运行沙箱程序(sandbox programs), 而无需修改内核源码或者加载内核模块。
将 Linux 内核变成可编程之后,就能基于现有的(而非增加新的)抽象层来打造更加智能、 功能更加丰富的基础设施软件,而不会增加系统的复杂度,也不会牺牲执行效率和安全性。
我们来看下 Cilium 的网络策略:
# cilium/networkpolicy-L4.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L7 policy to restrict access to specific HTTP call"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
与 Kubernetes 的原生网络策略差异不大,参考前面的介绍也都看懂,我们直接进入测试。
测试由于 Cilium 本身就实现了 CNI,所以之前的集群就不能用了,先卸载集群:
k3s-uninstall.sh
# !!!切记要清理之前的 cni 插件
sudo rm -rf /etc/cni/net.d
还是使用同样的命令创建单节点的集群:
curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE="644" INSTALL_K3S_EXEC="--flannel-backend=none --cluster-cidr=10.42.0.0/16 --disable-network-policy --disable=traefik" sh -
# cilium 会使用该变量
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
接下来安装 Cilium CLI:
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz,.sha256sum
sha256sum --check cilium-linux-amd64.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz,.sha256sum
cilium version
cilium-cli: v0.10.2 compiled with go1.17.6 on linux/amd64
cilium image (default): v1.11.1
cilium image (stable): v1.11.1
cilium image (running): unknown. Unable to obtain cilium version, no cilium pods found in namespace "kube-system"
安装 Cilium 到集群:
cilium install
待 Cilium 成功运行:
cilium status
/¯¯\\
/¯¯\\__/¯¯\\ Cilium: OK
\\__/¯¯\\__/ Operator: OK
/¯¯\\__/¯¯\\ Hubble: disabled
\\__/¯¯\\__/ ClusterMesh: disabled
\\__/
Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1
DaemonSet cilium Desired: 1, Ready: 1/1, Available: 1/1
Containers: cilium Running: 1
cilium-operator Running: 1
Cluster Pods: 3/3 managed by Cilium
Image versions cilium-operator quay.io/cilium/operator-generic:v1.11.1@sha256:977240a4783c7be821e215ead515da3093a10f4a7baea9f803511a2c2b44a235: 1
cilium quay.io/cilium/cilium:v1.11.1@sha256:251ff274acf22fd2067b29a31e9fda94253d2961c061577203621583d7e85bd2: 1
部署应用:
kubectl apply -f app.yaml
待应用启动后测试服务调用:
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
执行 L4 网络策略:
kubectl apply -f cilium/networkpolicy-L4.yaml
再次尝试“登陆”死星,xwing 战机同样无法登陆,说明 L4 层的规则生效。
我们再尝试 L7 层的规则:
# cilium/networkpolicy-L7.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L7 policy to restrict access to specific HTTP call"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "POST"
path: "/v1/request-landing"
执行规则:
kubectl apply -f cilium/networkpolicy-L7.yaml
这回,使用 tiefighter 调用死星的管理接口:
kubectl exec tiefighter -- curl -s -XPUT deathstar.default.svc.cluster.local/v1/exhaust-port
Access denied
# 登陆接口工作正常
kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
这回返回了 Access denied,说明 L7 层的规则生效了。
参考资料网络策略 NetworkPolicy: https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/
[2]Cilium getting started: https://play.instruqt.com/isovalent/tracks/cilium-getting-started
[3]官方文档: https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/
[4]K3s: https://k3s.io
[5]Calico: https://www.tigera.io/project-calico/
[6]Cilium: https://cilium.io
[7]eBPF: https://ebpf.io
文章转载自云原生指北。点击这里阅读原文了解更多。
CNCF概况(幻灯片)
扫描二维码联系我们!
CNCF (Cloud Native Computing Foundation)成立于2015年12月,隶属于Linux Foundation,是非营利性组织。
CNCF(云原生计算基金会)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。请长按以下二维码进行关注。
以上是关于kubernetes 集群中 cilium 的实践及其网络通信解析的主要内容,如果未能解决你的问题,请参考以下文章
关于centos8+kubeadm1.20.5+cilium+hubble的安装过程中cilium的配置问题--特别强调
使用CalicoFlannelWeave和Cilium的终极指南