Bootstrap

使用Cilium/eBPF实现大规模云原生网络和安全

大家读完觉得有帮助记得关注和点赞!!!

目录

抽象

1 Trip.com 云基础设施

1.1 分层架构

1.2 更多细节

2 纤毛在 Trip.com

2.1 推出时间表

2.2 自定义

2.3 优化和调整

2.3.1 解耦安装

2.3.2 避免重试/重启风暴

2.3.3 稳定性优先

2.3.4 规划规模

2.3.5 性能调优

2.3.6 可观察性和警报

2.3.7 其他选项

2.4 多集群解决方案

2.4.1 集群网格

2.4.2 KVStoreMesh

3 高级故障排除技巧

3.1 使用delve/dlv

3.2 使用bpftrace

3.2.1 使用绝对路径

2.3.2 带 PID/proc/

3.3 操作 BPF 映射bpftool

3.4 使用 API 操作 kvstore 内容etcdctl

4 总结


抽象

Trip.com 年,我们将第一个 Cilium 节点(裸机)投入生产 在 2019 年。从那时起,我们几乎所有的 Kubernetes 集群 - 都是本地的 公共云中的裸机和自我管理的集群 - 已切换到 Cilium。

现在有 ~10K 个节点,~300K Pod 在 Kubernetes 上运行, Cilium 为我们的关键业务服务提供支持,例如酒店 搜索引擎、金融/支付服务、内存数据库、数据存储 服务,其中涵盖了延迟方面的广泛要求, 吞吐量等

根据我们 4 年的经验,观众将学习 Cilium 的云原生网络和安全性,包括:

  1. 如何使用 CiliumNetworkPolicy 进行 L3/L4 访问控制,包括将安全模型扩展到 BM/VM 实例;
  2. 我们名为 KVStoreMesh 的新多集群解决方案作为 ClusterMesh 的替代方案,以及我们如何使其与社区兼容以便轻松升级;
  3. 大规模构建稳定性,例如管理控制平面和多集群中断,以及我们因此对 Cilium 所做的改进。

1 Trip.com 云基础设施

1.1 分层架构

Trip.com 云团队负责公司在全球的基础设施,如下所示:

  • 在底部,我们有数据中心和几个公共云供应商;
  • 底部上方是我们用于 BM、VM 和容器的编排系统;
  • 再上一层是内部开发的持续交付平台 (CI/CD);
  • 顶部是我们的业务服务和相应的中间件;
  • 在垂直方向上,我们在不同级别都有安全和管理工具。

云范围是图中所示的框。

1.2 更多细节

有关我们基础设施的更多具体信息:

  • 我们的大部分工作负载现在都在 Kubernetes 上运行,我们有 3 个大型 集群和几个小型集群,具有总节点和 pod;~10k~300k
  • 大多数 Kubernetes 节点都是刀片服务器;哪
  • 使用内部维护的 4.19/5.10 内核运行;
  • 对于主机间网络,我们将 BGP 用于本地集群和 ENI 适用于云上的自建集群。

2 纤毛在 Trip.com

2.1 推出时间表

以下是我们推出流程的简单时间表:

  1. 我们从 2018 年开始研究云原生网络解决方案 [9],当然,Cilium 胜出;
  2. 我们的第一个纤毛节点于 2019 年投入生产 [10];
  3. 从那时起,我们的各种业务和基础设施服务开始透明地迁移到 Cilium。

2021 年,随着大多数在线业务已经在 Cilium 中, 我们启动了一个基于 Cilium 网络策略的安全项目 [8]。

我们使用的功能:

  • 服务负载均衡 (eBPF/XDP)
  • CiliumNetworkPolicy (CNP)
  • eBPF 主机路由
  • eBPF 带宽管理器
  • 哈勃(部分)
  • Rsyslog 驱动程序
  • 性能提升选项,如 sockops、eBPF 重定向

2.2 自定义

首先,此处显示了两个 Kubernetes 集群,

我们基于上述拓扑的一些自定义:

  1. 使用 docker-compose 部署 cilium 以去除 kubernetes 依赖 [1];
  2. 为每个代理分配一个用于 Kubernetes 身份验证的专用证书,而不是所有代理共享的 seviceaccount;
  3. 我们已经帮助完善了 Cilium 的 rsyslog 驱动程序,并已将所有代理日志发送到 ClickHouse 进行故障排除;
  4. 添加了一些补丁来促进业务迁移,但这并不是那么通用,所以我们没有将它们上游化;
  5. 使用 BIRD 作为 BGP 代理,而不是建议的 kube-router,我们已经为 Cilium 文档贡献了 BGP+Cilium 指南;
  6. 我们开发了一种新的多集群解决方案,称为 KVStoreMesh [4]。更多关于这个 lader 的信息。

2.3 优化和调整

现在是优化和调整部分。

2.3.1 解耦安装

刚才说的,我们做的第一件事就是解耦 Cilium 从 Kubernetes 部署/安装:没有 daemonset,没有 configmap。所有 代理所需的配置位于节点上。

这使得代理受 Kubernetes 中断的影响较小,但更重要的是,每个代理现在在配置和升级方面都是完全独立的

2.3.2 避免重试/重启风暴

第二个考虑因素是避免重试风暴和突发启动, 因为请求将激增两个数量级甚至更高 当发生中断时,这很容易崩溃或卡住中央组件 如 Kubernetes apiserver/etcd 和 kvstore 一样。

我们使用内部开发的重启回退 (jitter+backoff) 机制来避免此类情况。 抖动窗口是根据 Cluster Scale 计算的。 如

  • 对于具有 1000 个节点的集群,抖动窗口可能是 20 分钟,在此期间,每个代理只能启动一次,然后退出。
  • 对于具有 5000 个节点的集群,抖动窗口可能为 60 分钟。
  • 退避机制作为 bash 脚本实现(所有状态都保存在本地文件中),使用 在 docker-compose 中作为 “pre-start hook” 时,Cilium 代码没有变化

此外,我们还为每个代理分配了一个不同的证书(每个代理都有一个 专用用户名,但属于公共用户组),这将启用 Kubernetes 使用 APF(API 优先级和公平性)对 Cilium 代理执行速率限制。也没有更改 Cilium 代码来实现这一点。

如果您想了解有关 Kubernetes AuthN/AuthZ 模型的更多信息,请参阅我们之前的博客 [2,3]。

2.3.3 稳定性优先

Trip.com 7x24 小时在全球范围内提供在线预订服务,因此随时 在任何一天,业务服务中断都会导致 公司。因此,我们不能冒险让网络等基础服务通过简单的 “快速故障” 规则自行重启,而倾向于必要的人工干预和决策。

当失败出现时,我们希望像 Cilium 这样的服务能更有耐心,只是 在那里等待并保持当前业务不中断,让系统开发人员和 维护者决定做什么;快速失败和自动重试 在这种情况下,让事情变得更糟

一些特定选项(带有示例配置)来调整此行为:

  • --allocator-list-timeout=3h
  • --kvstore-lease-ttl=86400s
  • --k8s-syn-timeout=600s
  • --k8s-heartbeat-timeout=60s

请参阅 Cilium 文档或源代码以找出每个选项的内容 确切的含义,并根据您的需要自定义它们。

2.3.4 规划规模

根据您的集群规模,某些内容需要提前规划。 例如,身份相关标签 () 直接确定 集群中的最大 pod:一组标签映射到 在 Cilium 中只有一个身份,所以在它的设计中,所有具有相同标签的 Pod 都共享相同的身份。 但是,如果你的 Bean 太细粒度(不幸的是,这是默认的 case),这可能会导致每个 Pod 都有不同的身份,在最坏的情况下,你的集群规模为 上限为 64K pod,因为表示身份 替换为整数。有关更多信息,请参阅 [8]。--labels--labels=<labels>16bit

此外,还有一些参数需要根据 您的工作负载吞吐量,例如 Identity Allocation Mode、Connection Tracking Table。

选项:

  • --cluster-id/--cluster-name:避免多集群场景中的身份冲突;
  • --labels=<labellist>Identity Relevent 标签
  • --identity-allocation-mode和 kvstore 基准测试(如果使用 kvstore 模式)

    我们使用模式,并在大型集群的专用刀片服务器上运行 kvstore (cilium etcd)。kvstore

  • --bpf-ct-*
  • --api-rate-limit
  • 用于减少可观测性数据量的 Monitor 聚合选项

2.3.5 性能调优

Cilium 包含许多高性能选项,例如 sockops 和 BPF 主机 routing,当然,所有这些功能都需要特定的内核版本 支持。

  • --socops-enable
  • --bpf-lb-sock-hostns-only

此外,禁用一些调试级别选项也是必要的:

  • --disable-cnp-status-updates

2.3.6 可观察性和警报

我们要讨论的最后一个方面是可观测性。

  • 度量
  • 伐木
  • 描图

除了来自 Cilium agent/operator 的指标数据外,我们还 还收集了所有代理/操作员日志(日志记录数据)并发送 到 ClickHouse 进行分析,这样,我们就可以对异常指标进行告警,以及 错误/警告日志、

此外,跟踪也很有帮助,稍后会详细介绍。

2.3.7 其他选项

  • --enable-hubble
  • --keep-config
  • --log-drivers
  • --policy-audit-mode

2.4 多集群解决方案

现在我们来看一下多集群问题。

由于历史原因,我们的业务部署在不同的 数据中心和 Kubernetes 集群。 因此,存在没有 L4/L7 边界网关的集群间通信。 这对访问控制来说是一个问题,因为 Cilium 身份是一个集群范围的对象

2.4.1 集群网格

这个问题的社区解决方案是 ClusterMesh,如下所示,

ClusterMesh 要求每个 agent 连接到所有集群中的每个 kvstore, 有效地产生点对点网格。虽然这个解决方案是直接的 向前,它遇到了稳定性和可扩展性问题,尤其是对于大型 集群。

简而言之,当单个集群宕机时,故障很快就会传播到所有集群 网格中的其他集群, 最终,所有集群可能会同时崩溃,如下所示:

从本质上讲,这是因为 ClusterMesh 中的集群耦合得太紧密了。

2.4.2 KVStoreMesh

我们解决这个问题的概念非常简单: 从所有远程 KVSache 中提取元数据,并在筛选后推送到本地 KVSares。

三集群案例更清楚地显示了这个概念:只涉及 kvstores,

在 ClusterMesh 中,代理从远程 kvstores 获取远程元数据;在 KVStoreMesh 中,它们从本地获取。

感谢 cilium 的良好设计,这只需要对代理和操作符 [4] 进行一些改进和/或错误修复,我们已经 他们中的一些人被上游。kvstoremesh-operator 是新引入的,目前在内部维护; 我们也会在下一个版本投入更多精力将其上游化。

此外,我们还开发了一个简单的解决方案,让 Cilium 注意我们的遗留工作负载,例如 OpenStack 中的虚拟机, the-solultion 称为 CiliumExeternalResource。请看我们之前的 博客 [8] 如果你感兴趣的话。

3 高级故障排除技巧

现在让我们回到一些方便的东西。

第一个是调试。

3.1 使用delve/dlv

Delve 是一个好朋友,我们的 docker-compose 方式使调试更加容易。 由于每个代理程序都是独立部署的,因此命令可以 在节点上执行以启动/停止/重新配置代理。

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#999988"><em># Start cilium-agent agent container with entrypoint `sleep 10d`, then enter the container</em></span>
<span style="color:#000000"><strong>(</strong></span>node<span style="color:#000000"><strong>)</strong></span> <span style="color:#008080">$ </span>docker <span style="color:#0086b3">exec</span> <span style="color:#000080">-it</span> cilium-agent bash

<span style="color:#000000"><strong>(</strong></span>cilium-agent ctn<span style="color:#000000"><strong>)</strong></span> <span style="color:#008080">$ </span>dlv <span style="color:#0086b3">exec</span> /usr/bin/cilium-agent <span style="color:#000080">--</span> <span style="color:#000080">--config-dir</span><span style="color:#000000"><strong>=</strong></span>/tmp/cilium/config-map
Type <span style="color:#dd1144">'help'</span> <span style="color:#000000"><strong>for </strong></span>list of commands.
<span style="color:#000000"><strong>(</strong></span>dlv<span style="color:#000000"><strong>)</strong></span>

<span style="color:#000000"><strong>(</strong></span>dlv<span style="color:#000000"><strong>)</strong></span> <span style="color:#0086b3">break </span>github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Endpoint<span style="color:#000000"><strong>)</strong></span>.regenerateBPF
Breakpoint 3 <span style="color:#0086b3">set </span>at 0x1e84a3b <span style="color:#000000"><strong>for </strong></span>github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Endpoint<span style="color:#000000"><strong>)</strong></span>.regenerateBPF<span style="color:#000000"><strong>()</strong></span> /go/src/github.com/cilium/cilium/pkg/endpoint/bpf.go:591
<span style="color:#000000"><strong>(</strong></span>dlv<span style="color:#000000"><strong>)</strong></span> <span style="color:#0086b3">break </span>github.com/cilium/cilium/pkg/endpoint/bpf.go:1387
Breakpoint 4 <span style="color:#0086b3">set </span>at 0x1e8c27b <span style="color:#000000"><strong>for </strong></span>github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Endpoint<span style="color:#000000"><strong>)</strong></span>.syncPolicyMapWithDump<span style="color:#000000"><strong>()</strong></span> /go/src/github.com/cilium/cilium/pkg/endpoint/bpf.go:1387
<span style="color:#000000"><strong>(</strong></span>dlv<span style="color:#000000"><strong>)</strong></span> <span style="color:#000000"><strong>continue</strong></span>
...

<span style="color:#000000"><strong>(</strong></span>dlv<span style="color:#000000"><strong>)</strong></span> clear 1
Breakpoint 1 cleared at 0x1e84a3b <span style="color:#000000"><strong>for </strong></span>github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Endpoint<span style="color:#000000"><strong>)</strong></span>.regenerateBPF<span style="color:#000000"><strong>()</strong></span> /go/src/github.com/cilium/cilium/pkg/endpoint/bpf.go:591
<span style="color:#000000"><strong>(</strong></span>dlv<span style="color:#000000"><strong>)</strong></span> clear 2
Breakpoint 2 cleared at 0x1e8c27b <span style="color:#000000"><strong>for </strong></span>github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Endpoint<span style="color:#000000"><strong>)</strong></span>.syncPolicyMapWithDump<span style="color:#000000"><strong>()</strong></span> /go/src/github.com/cilium/cilium/pkg/endpoint/bpf.go:1387
</code></span></span></span>

我们以这种方式跟踪了几个 bug。

3.2 使用bpftrace

另一个有用的工具是用于实时跟踪的 bpftrace。

但请注意,跟踪容器进程存在一些差异。 您需要在节点上找到 cilium-agent 二进制文件的 PID 命名空间或绝对路径。

3.2.1 使用绝对路径

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#999988"><em># Check cilium-agent container</em></span>
<span style="color:#008080">$ </span>docker ps | <span style="color:#0086b3">grep </span>cilium-agent
0eb2e76384b3        cilium:20220516   <span style="color:#dd1144">"/usr/bin/cilium-agent ..."</span>   4 hours ago    Up 4 hours   cilium-agent

<span style="color:#999988"><em># Find the merged path for cilium-agent container</em></span>
<span style="color:#008080">$ </span>docker inspect <span style="color:#000080">--format</span> <span style="color:#dd1144">"{{.GraphDriver.Data.MergedDir}}"</span> 0eb2e76384b3
/var/lib/docker/overlay2/0a26c6/merged <span style="color:#999988"><em># 0a26c6.. is shortened for better viewing</em></span>
<span style="color:#999988"><em># The object file we are going to trace</em></span>
<span style="color:#008080">$ </span><span style="color:#0086b3">ls</span> <span style="color:#000080">-ahl</span> /var/lib/docker/overlay2/0a26c6/merged/usr/bin/cilium-agent
/var/lib/docker/overlay2/0a26c6/merged/usr/bin/cilium-agent <span style="color:#999988"><em># absolute path</em></span>

<span style="color:#999988"><em># Or you can find it bruteforcelly if there are no performance (e.g. IO spikes) concerns:</em></span>
<span style="color:#008080">$ </span>find /var/lib/docker/overlay2/ <span style="color:#000080">-name</span> cilium-agent
/var/lib/docker/overlay2/0a26c6/merged/usr/bin/cilium-agent <span style="color:#999988"><em># absolute path</em></span>
</code></span></span></span>

无论如何,在找到目标文件并检出其中的符号后,

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span>nm /var/lib/docker/overlay2/0a26c6/merged/usr/bin/cilium-agent
0000000001d3e940 T type..hash.github.com/cilium/cilium/pkg/k8s.ServiceID
0000000001f32300 T type..hash.github.com/cilium/cilium/pkg/node/types.Identity
0000000001d05620 T type..hash.github.com/cilium/cilium/pkg/policy/api.FQDNSelector
0000000001d05e80 T type..hash.github.com/cilium/cilium/pkg/policy.PortProto
...
</code></span></span></span>

您可以像下面这样启动用户空间探测,打印您希望看到的内容:

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span>bpftrace <span style="color:#000080">-e</span> <span style="color:#dd1144">\</span>
  <span style="color:#dd1144">'uprobe:/var/lib/docker/overlay2/0a26c6/merged/usr/bin/cilium-agent:"github.com/cilium/cilium/pkg/endpoint.(*Endpoint).regenerateBPF" {printf("%s\n", ustack);}'</span>
Attaching 1 probe...

        github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Endpoint<span style="color:#000000"><strong>)</strong></span>.regenerateBPF+0
        github.com/cilium/cilium/pkg/endpoint.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>EndpointRegenerationEvent<span style="color:#000000"><strong>)</strong></span>.Handle+1180
        github.com/cilium/cilium/pkg/eventqueue.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>EventQueue<span style="color:#000000"><strong>)</strong></span>.run.func1+363
        sync.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>Once<span style="color:#000000"><strong>)</strong></span>.doSlow+236
        github.com/cilium/cilium/pkg/eventqueue.<span style="color:#000000"><strong>(</strong></span><span style="color:#000000"><strong>*</strong></span>EventQueue<span style="color:#000000"><strong>)</strong></span>.run+101
        runtime.goexit+1
</code></span></span></span>

2.3.2 带 PID/proc/<PID>

更方便、更简洁的方法是找到 PID 命名空间并将其传递给 ,这将使命令更短:bpftrace

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span><span style="color:#0086b3">sudo </span>docker inspect <span style="color:#000080">-f</span> <span style="color:#dd1144">'{{.State.Pid}}'</span> cilium-agent
109997
<span style="color:#008080">$ </span>bpftrace <span style="color:#000080">-e</span> <span style="color:#dd1144">'uprobe:/proc/109997/root/usr/bin/cilium-agent:"github.com/cilium/cilium/pkg/endpoint.(*Endpoint).regenerate" {printf("%s\n", ustack); }'</span>
</code></span></span></span>

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span>bpftrace <span style="color:#000080">-p</span> 109997 <span style="color:#000080">-e</span> <span style="color:#dd1144">'uprobe:/usr/bin/cilium-agent:"github.com/cilium/cilium/pkg/endpoint.(*Endpoint).regenerate" {printf("%s\n", ustack); }'</span>
</code></span></span></span>

3.3 操作 BPF 映射bpftool

现在考虑一个具体问题:如何确定 CNP 是否真的生效?有几种方法:

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span>kubectl get cnp <span style="color:#000080">-n</span> <ns> <cnp> <span style="color:#000080">-o</span> yaml       <span style="color:#999988"><em># spec & status in k8s</em></span>
<span style="color:#008080">$ </span>cilium endpoint get <ep <span style="color:#0086b3">id</span><span style="color:#000000"><strong>></strong></span>                 <span style="color:#999988"><em># spec & status in cilium userspace</em></span>
<span style="color:#008080">$ </span>cilium bpf policy get <ep <span style="color:#0086b3">id</span><span style="color:#000000"><strong>></strong></span>               <span style="color:#999988"><em># summary of kernel bpf policy status</em></span>
</code></span></span></span>
  • 查询 Kubernetes?不,这太高了;
  • 查看终端节点状态?不,这是一个用户空间状态,仍然太高级别了;
  • 使用 cilium 命令检查 bpf 策略?嗯,这确实是一个总结 的 BPF 策略,但摘要代码本身也可能有 bug;

最底层的策略状态是内核中的 BPF 策略映射,我们可以通过 bpftool 查看它:

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span>bpftool map dump pinned cilium_policy_00794 <span style="color:#999988"><em># REAL & ULTIMATE policies in the kernel!</em></span>
</code></span></span></span>

但是要使用此工具,您首先需要熟悉一些 Cilium 数据结构。 比如,IP 地址如何对应一个身份,以及如何组合 身份、端口、协议、流量方向在 BPF 策略映射中形成一个键。

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#999988"><em># Get the corresponding identity of an (client) IP address</em></span>
<span style="color:#008080">$ </span>cilium bpf ipcache get 10.2.6.113
10.2.6.113 maps to identity 298951 0 0.0.0.0

<span style="color:#999988"><em># Convert a numeric identity to its hex representation</em></span>
<span style="color:#008080">$ </span><span style="color:#0086b3">printf</span> <span style="color:#dd1144">'%08x'</span> 298951
00048fc7

<span style="color:#999988"><em># Search if there exists any policy related to this identity</em></span>
<span style="color:#999988"><em>#</em></span>
<span style="color:#999988"><em># Key format: identity(4B) + port(2B) + proto(1B) + direction(1B)</em></span>
<span style="color:#999988"><em># For endpoint 794's TCP/80 ingress, check if allow traffic from identity 298951</em></span>
<span style="color:#008080">$ </span>bpftool map dump pinned cilium_policy_00794 | <span style="color:#0086b3">grep</span> <span style="color:#dd1144">"c7 8f 04 00"</span> <span style="color:#000080">-B</span> 1 <span style="color:#000080">-A</span> 3
key:
c7 8f 04 00 00 50 06 00 <span style="color:#999988"><em># 4B identity + 2B port(80) + 1B L4Proto(TCP) + direction(ingress)</em></span>
value:
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
</code></span></span></span>

键和值数据结构:

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#999988"><em>// PolicyKey represents a key in the BPF policy map for an endpoint. It must</em></span>
<span style="color:#999988"><em>// match the layout of policy_key in bpf/lib/common.h.</em></span>
<span style="color:#999988"><em>// +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapKey</em></span>
<span style="color:#000000"><strong>type</strong></span> PolicyKey <span style="color:#000000"><strong>struct</strong></span> {
	Identity         <span style="color:#445588"><strong>uint32</strong></span> <span style="color:#dd1144">`align:"sec_label"`</span>
	DestPort         <span style="color:#445588"><strong>uint16</strong></span> <span style="color:#dd1144">`align:"dport"`</span> <span style="color:#999988"><em>// In network byte-order</em></span>
	Nexthdr          <span style="color:#445588"><strong>uint8</strong></span>  <span style="color:#dd1144">`align:"protocol"`</span>
	TrafficDirection <span style="color:#445588"><strong>uint8</strong></span>  <span style="color:#dd1144">`align:"egress"`</span>
}

<span style="color:#999988"><em>// PolicyEntry represents an entry in the BPF policy map for an endpoint. It must</em></span>
<span style="color:#999988"><em>// match the layout of policy_entry in bpf/lib/common.h.</em></span>
<span style="color:#999988"><em>// +k8s:deepcopy-gen:interfaces=github.com/cilium/cilium/pkg/bpf.MapValue</em></span>
<span style="color:#000000"><strong>type</strong></span> PolicyEntry <span style="color:#000000"><strong>struct</strong></span> {
	ProxyPort <span style="color:#445588"><strong>uint16</strong></span> <span style="color:#dd1144">`align:"proxy_port"`</span> <span style="color:#999988"><em>// In network byte-order</em></span>
	Flags     <span style="color:#445588"><strong>uint8</strong></span>  <span style="color:#dd1144">`align:"deny"`</span>
	Pad0      <span style="color:#445588"><strong>uint8</strong></span>  <span style="color:#dd1144">`align:"pad0"`</span>
	Pad1      <span style="color:#445588"><strong>uint16</strong></span> <span style="color:#dd1144">`align:"pad1"`</span>
	Pad2      <span style="color:#445588"><strong>uint16</strong></span> <span style="color:#dd1144">`align:"pad2"`</span>
	Packets   <span style="color:#445588"><strong>uint64</strong></span> <span style="color:#dd1144">`align:"packets"`</span>
	Bytes     <span style="color:#445588"><strong>uint64</strong></span> <span style="color:#dd1144">`align:"bytes"`</span>
}

<span style="color:#999988"><em>// pkg/maps/policymap/policymap.go</em></span>

<span style="color:#999988"><em>// Allow pushes an entry into the PolicyMap to allow traffic in the given</em></span>
<span style="color:#999988"><em>// `trafficDirection` for identity `id` with destination port `dport` over</em></span>
<span style="color:#999988"><em>// protocol `proto`. It is assumed that `dport` and `proxyPort` are in host byte-order.</em></span>
<span style="color:#000000"><strong>func</strong></span> (pm <span style="color:#000000"><strong>*</strong></span>PolicyMap) Allow(id <span style="color:#445588"><strong>uint32</strong></span>, dport <span style="color:#445588"><strong>uint16</strong></span>, proto u8proto<span style="color:#000000"><strong>.</strong></span>U8proto, trafficDirection trafficdirection<span style="color:#000000"><strong>.</strong></span>TrafficDirection, proxyPort <span style="color:#445588"><strong>uint16</strong></span>) <span style="color:#445588"><strong>error</strong></span> {
	key <span style="color:#000000"><strong>:=</strong></span> newKey(id, dport, proto, trafficDirection)
	pef <span style="color:#000000"><strong>:=</strong></span> NewPolicyEntryFlag(<span style="color:#000000"><strong>&</strong></span>PolicyEntryFlagParam{})
	entry <span style="color:#000000"><strong>:=</strong></span> newEntry(proxyPort, pef)
	<span style="color:#000000"><strong>return</strong></span> pm<span style="color:#000000"><strong>.</strong></span>Update(<span style="color:#000000"><strong>&</strong></span>key, <span style="color:#000000"><strong>&</strong></span>entry)
}
</code></span></span></span>

bpftool在紧急情况下也会来救援,例如当 流量被拒绝,但您的 Kubernetes 或 Cilium 代理无法准备就绪, 只需插入一个 allow-any 规则,如下所示:

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#999988"><em># Add an allow-any rule in emergency cases</em></span>
<span style="color:#008080">$ </span>bpftool map update pinned <map> <span style="color:#dd1144">\</span>
  key hex 00 00 00 00 00 00 00 00 <span style="color:#dd1144">\</span>
  value hex 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 noexist
</code></span></span></span>

3.4 使用 API 操作 kvstore 内容etcdctl

我们要分享的最后一个技能是操作 kvstore 内容。

同样,这需要对 Cilium 数据模型有深入的了解。 例如,将以下三个条目插入 kvstore 中,

<span style="color:#333333"><span style="background-color:#f8f8f8"><span style="background-color:#f6f8fa"><code><span style="color:#008080">$ </span>etcdctl put <span style="color:#dd1144">"cilium/state/identities/v1/id/15614229"</span> <span style="color:#dd1144">\</span>
  <span style="color:#dd1144">'k8s:app=app1;k8s:io.cilium.k8s.policy.cluster=cluster1;k8s:io.cilium.k8s.policy.serviceaccount=default;k8s:io.kubernetes.pod.namespace=ns1;'</span>

<span style="color:#008080">$ </span>etcdctl put <span style="color:#dd1144">'k8s:app=app1;k8s:io.cilium.k8s.policy.cluster=cluster1;k8s:io.cilium.k8s.policy.serviceaccount=default;k8s:io.kubernetes.pod.namespace=ns1;/10.3.9.10'</span> <span style="color:#dd1144">\</span>
  15614229

<span style="color:#008080">$ </span>etcdctl put <span style="color:#dd1144">"cilium/state/ip/v1/cluster1/10.3.192.65"</span> <span style="color:#dd1144">\</span>
  <span style="color:#dd1144">'{"IP":"10.3.192.65","Mask":null,"HostIP":"10.3.9.10","ID":15614299,"Key":0,"Metadata":"cilium-global:cluster1:node1:2404","K8sNamespace":"ns1","K8sPodName":"pod1"}'</span>
</code></span></span></span>

所有 cilium-agents 都会收到通知,在 Kubernetes 、 namespace 中创建了一个 pod,其中包含 PoIP、NodeIP、NodeName、pod 标签和 条目中的身份信息。cluster1default

从本质上讲,这就是我们在 CER 解决方案中将 VM、BM 和非纤毛 pod 注入 Cilium 世界的方式(有关详细信息,请参阅我们之前的帖子 [8]); 它也是 Cilium 网络策略的基础。

警告:操纵 kvstores 和 BPF 映射是危险的, 因此,我们不建议在生产环境中执行这些操作 环境中,除非您知道自己在做什么。

4 总结

我们从 1.4 开始一直在使用 Cilium,现在已经一路升级到 1.10(2022.11 更新)。 它支持我们的业务和基础设施关键服务。 凭借 4 年的经验,我们相信它不仅为大规模生产做好了准备, 而且在性能、功能、社区等方面也是最佳候选人之一。1.11

最后,我想特别感谢 Andre、Denial、Joe、Martynas、Paul、Quentin、Thomas 以及所有 Cilium 的家伙。社区非常好,过去帮了我们很多。

;