TT Lab
开始
学习 学习路径 课程

CCA — Cilium 认证助理

逐个停掉组件:哪些停了,哪些撑住了

在 TT Lab 中继续学习

目标

在真正的 Cilium 1.20.1 中,逐个停掉组件,确认 cilium agent、operator、cilium-envoy、hubble-relay 各自负责什么。 先读出 cluster-pool IPAM 和 CiliumIdentity 记录在哪里,再记录组件缺席期间新 Pod、L7 请求、flow 查询和现有流量会怎样,然后恢复。

为什么重要

“Cilium 挂了”这类报告,取决于是哪个组件停了,会是完全不同的事故。Operator 负责整个集群只需做一次的工作,暂时缺席也不会介入转发和策略决策。没有 envoy,只有带 L7 规则的路径会停。没有 relay,只会断掉在一处查询多个节点的途径。没有 agent,已加载的数据路径会继续运转,但新 Pod 无法获得网络,调度会被阻止。

停掉组件的步骤,在记录完观测结果之后必须恢复(只有 agent 在第 6 步停掉,在第 7 步恢复)。恢复之后的整体评分,会把当时的记录与 Pod uid、身份、flow 时间点、恢复后 Pod 的创建时间进行核对,所以记录必须在组件停止期间生成。

环境准备约需 5 分钟。第 6 步和第 7 步之间没有 agent,所以 agent 命令无法使用。会话结束后,/root/cca-parts 中的文件会消失。

步骤

  1. 用 kubectl apply -f /opt/fixtures/cca-components/workload.yaml 在 cca-parts 中启动 app(副本 2 个)、api-l7、client。记录到 /root/cca-parts/ipam.json——ipam_mode、cluster_pool、mask_size(分别取自 cilium-config 的 ipam、cluster-pool-ipv4-cidr、cluster-pool-ipv4-mask-size,最后一个为数字),node_pod_cidrs(CiliumNode 的 spec.ipam.podCIDRs 列表),status_pool、capacity(agent 中 cilium-dbg status 的 IPAM 行里 allocated from 之后的网段,以及 / 之后的数字),router_ip(VM 上 cilium_host 设备的 IPv4),pods(cca-parts 中 4 个 Pod 的名称到 IP 的字典)。
  2. 比较 app 的两个副本 Pod 和 client 的 CiliumEndpoint 身份编号,并读取 app 的 CiliumIdentity 对象。在 /root/cca-parts/identity.json 中记录 app_identity、app_endpoints(app Pod 的 2 个名称)、client_identity、security_labels(把该 CiliumIdentity 的 security-labels 转成 키=값(占位符依次为键和值)形式的字符串并排序后的列表)、allocation_mode(cilium-config 的 identity-allocation-mode)。
  3. 把 cilium-operator 缩到 replicas 0,并等到所有 Operator Pod 都消失。在这种状态下,在 cca-parts 中创建 Pod born-no-operator(镜像与 client 相同的 curl,标签 born=no-operator,命令 sleep 86400),等它 Ready 后,在 /root/cca-parts/operator-down.json 中记录 operator_replicas(此时 Deployment 的 spec.replicas,数字)、pod_uid、pod_ip、identity(该 Pod 的 CiliumEndpoint 身份)。记录完之后,把 Operator 恢复为 1,并等到 available。
  4. 在 cca-parts 中创建 CiliumNetworkPolicy l7-get——endpointSelector 为 app=api-l7,ingress 一项:fromEndpoints 为 app=client,toPorts 中为 TCP “8080” 以及 rules.http [{method: GET, path: /allowed}]。确认从 client 访问 api-l7 的 /allowed 为 200、/secret 为 403 之后,向 kube-system 中的 cilium-envoy DaemonSet 添加不存在的 nodeSelector cca-lab/envoy: "off",把 envoy Pod 撤掉。在此期间,从 client 请求 api-l7 的 /allowed(限时 5 秒)和 app 服务(http://app:8080/),并在 agent 内用 hubble 读取 client→api-l7 的 flow,在 /root/cca-parts/envoy-down.json 中记录 l7_code、plain_code(HTTP 状态码字符串,无响应时为“000”)、flows(client→api-l7 flow 的 compact 行列表)。然后移除该 nodeSelector 键以恢复 envoy,并等到 /allowed 再次返回 200。
  5. 把 kube-system 中的 hubble-relay 缩到 replicas 0。把在 VM shell 中 hubble status --server <hubble-relay 서비스 ClusterIP>:80(占位符为 hubble-relay 服务的 ClusterIP)失败时的那一行错误,以及在 agent 内读取到的 hubble observe --last 5 -o compact 的 flow 行和 hubble status 的 Current/Max Flows 行,分别以 relay_error、local_flows(列表)、local_status 记录到 /root/cca-parts/relay-down.json 中。然后把 relay 恢复为 1,并等到经由 relay 的 status 成功。
  6. 向 kube-system 中的 cilium DaemonSet 添加 nodeSelector cca-lab/agent: "off",把 agent Pod 撤掉(这一步不恢复)。Pod 消失后,确认节点 taint,从 client 请求 app 服务,然后在 cca-parts 中创建 curl Pod orphan(命令 sleep 86400),等待 FailedScheduling 事件。在 /root/cca-parts/agent-down.json 中记录 existing_code(client→app 状态码字符串)、taint(节点上的 cilium taint 的 key:effect)、orphan_uid、orphan_phase、reason、message(orphan 的 FailedScheduling 事件)。
  7. 从 cilium DaemonSet 中移除 nodeSelector 键 cca-lab/agent,恢复 agent。轮询直到 agent 变为 Ready、节点上的 agent-not-ready taint 消失、orphan 被调度(PodScheduled=True)之后,在 /root/cca-parts/restore.json 中记录 taint_removed(true/false)、orphan_scheduled_at(orphan 的 PodScheduled 条件的 lastTransitionTime)、agent_pod(新 agent Pod 的名称)。
  8. 在 /root/cca-parts/report.txt 中写入八行 키=값(占位符依次为键和值)形式的内容——ipam_mode、node_pod_cidr、identity_allocation_mode、without_operator_new_pod(born-no-operator 当前的 phase)、without_envoy_l7、without_envoy_plain(envoy-down.json 中的两个状态码)、without_agent_existing、without_agent_new_pod(agent-down.json 中的 existing_code 和 orphan_phase)。值必须与记录文件和当前配置一致。

参考

Pod 地址是从哪里切分出来的

用 kubectl apply -f /opt/fixtures/cca-components/workload.yaml 在 cca-parts 中启动 app(副本 2 个)、api-l7、client。记录到 /root/cca-parts/ipam.json——ipam_mode、cluster_pool、mask_size(分别取自 cilium-config 的 ipam、cluster-pool-ipv4-cidr、cluster-pool-ipv4-mask-size,最后一个为数字),node_pod_cidrs(CiliumNode 的 spec.ipam.podCIDRs 列表),status_pool、capacity(agent 中 cilium-dbg status 的 IPAM 行里 allocated from 之后的网段,以及 / 之后的数字),router_ip(VM 上 cilium_host 设备的 IPv4),pods(cca-parts 中 4 个 Pod 的名称到 IP 的字典)。

在 cluster-pool 模式下,整个集群的地址池会按固定大小切分给每个节点,并写入 CiliumNode,各节点的 agent 在这一片之内分配 Pod 地址。请确认地址池、分片和实际地址之间是否存在包含关系。充当节点网关的 cilium_host 也从同一分片中获得地址。

两个副本共用同一个身份

比较 app 的两个副本 Pod 和 client 的 CiliumEndpoint 身份编号,并读取 app 的 CiliumIdentity 对象。在 /root/cca-parts/identity.json 中记录 app_identity、app_endpoints(app Pod 的 2 个名称)、client_identity、security_labels(把该 CiliumIdentity 的 security-labels 转成 키=값(占位符依次为键和值)形式的字符串并排序后的列表)、allocation_mode(cilium-config 的 identity-allocation-mode)。

身份不是每个 Pod 一个,而是每个与安全相关的标签集合一个。在 crd 分配方式下,该集合以名为 CiliumIdentity 的集群范围对象保存,名称就是编号。请确认附在 Pod 名称上的哈希之类的值不会进入安全标签。

Operator 缺席期间诞生的 Pod

把 cilium-operator 缩到 replicas 0,并等到所有 Operator Pod 都消失。在这种状态下,在 cca-parts 中创建 Pod born-no-operator(镜像与 client 相同的 curl,标签 born=no-operator,命令 sleep 86400),等它 Ready 后,在 /root/cca-parts/operator-down.json 中记录 operator_replicas(此时 Deployment 的 spec.replicas,数字)、pod_uid、pod_ip、identity(该 Pod 的 CiliumEndpoint 身份)。记录完之后,把 Operator 恢复为 1,并等到 available。

官方文档把 Operator 描述为负责“不是每个节点而是整个集群只需做一次的工作”的组件,并说明它不在转发或策略决策的路径上。请观察:节点上已经有 podCIDR 分片时,地址由谁分配;第一次见到的标签组合的身份对象由谁创建。请用新的身份编号检查 kubectl get ciliumidentity。

撤掉 envoy 之后只有 L7 路径停了

在 cca-parts 中创建 CiliumNetworkPolicy l7-get——endpointSelector 为 app=api-l7,ingress 一项:fromEndpoints 为 app=client,toPorts 中为 TCP “8080” 以及 rules.http [{method: GET, path: /allowed}]。确认从 client 访问 api-l7 的 /allowed 为 200、/secret 为 403 之后,向 kube-system 中的 cilium-envoy DaemonSet 添加不存在的 nodeSelector cca-lab/envoy: "off",把 envoy Pod 撤掉。在此期间,从 client 请求 api-l7 的 /allowed(限时 5 秒)和 app 服务(http://app:8080/),并在 agent 内用 hubble 读取 client→api-l7 的 flow,在 /root/cca-parts/envoy-down.json 中记录 l7_code、plain_code(HTTP 状态码字符串,无响应时为“000”)、flows(client→api-l7 flow 的 compact 行列表)。然后移除该 nodeSelector 键以恢复 envoy,并等到 /allowed 再次返回 200。

在没有 sidecar 的 Cilium 中,HTTP 规则由节点上的 envoy 判定。eBPF 在完成 L3/L4 判定后,会把该连接交给代理,如果没有 envoy 来接收,会怎样呢?没有 L7 规则的服务不经过代理。nodeSelector 的键中如果有 /,在 JSON patch 路径中要写成 ~1。请用 hubble observe --since <시각> --namespace cca-parts -o compact(占位符为时间点)读取 flow。

即使 relay 挂了,节点仍在收集 flow

把 kube-system 中的 hubble-relay 缩到 replicas 0。把在 VM shell 中 hubble status --server <hubble-relay 서비스 ClusterIP>:80(占位符为 hubble-relay 服务的 ClusterIP)失败时的那一行错误,以及在 agent 内读取到的 hubble observe --last 5 -o compact 的 flow 行和 hubble status 的 Current/Max Flows 行,分别以 relay_error、local_flows(列表)、local_status 记录到 /root/cca-parts/relay-down.json 中。然后把 relay 恢复为 1,并等到经由 relay 的 status 成功。

Hubble 在每个 agent 上把 flow 收集到环形缓冲区,relay 是可以在一处查询多个节点缓冲区的中继。请比较二者之一停止时各会失去什么。失败命令的错误是从标准错误输出的,所以用 2>&1 来接收。

撤掉 agent 之后,已有的请求仍在继续

向 kube-system 中的 cilium DaemonSet 添加 nodeSelector cca-lab/agent: "off",把 agent Pod 撤掉(这一步不恢复)。Pod 消失后,确认节点 taint,从 client 请求 app 服务,然后在 cca-parts 中创建 curl Pod orphan(命令 sleep 86400),等待 FailedScheduling 事件。在 /root/cca-parts/agent-down.json 中记录 existing_code(client→app 状态码字符串)、taint(节点上的 cilium taint 的 key:effect)、orphan_uid、orphan_phase、reason、message(orphan 的 FailedScheduling 事件)。

agent 是加载并更新 BPF 程序和 map 的一方,并不是亲自搬运数据包的进程。agent 缺席期间,已经加载的内容会怎样?新 Pod 因为 CNI 无法为其接上网络,Operator 会给节点打上 taint 以阻止调度。事件用 kubectl get events --field-selector involvedObject.name=orphan 查看。

agent 回来后 taint 被解除

从 cilium DaemonSet 中移除 nodeSelector 键 cca-lab/agent,恢复 agent。轮询直到 agent 变为 Ready、节点上的 agent-not-ready taint 消失、orphan 被调度(PodScheduled=True)之后,在 /root/cca-parts/restore.json 中记录 taint_removed(true/false)、orphan_scheduled_at(orphan 的 PodScheduled 条件的 lastTransitionTime)、agent_pod(新 agent Pod 的名称)。

加 taint 和解除 taint 都是 Operator 根据 agent Pod 的状态来做的。被调度之后,容器变为 Running 还需要更长时间(sandbox 重试),所以这一步只需等待到被调度。agent 刚换完时,hubble-relay 在重新连接 peer 期间也会短暂失去 Ready。请在最后的整体评分之前,再确认一次 orphan 是否为 Running。

谁停了,什么会停

在 /root/cca-parts/report.txt 中写入八行 키=값(占位符依次为键和值)形式的内容——ipam_mode、node_pod_cidr、identity_allocation_mode、without_operator_new_pod(born-no-operator 当前的 phase)、without_envoy_l7、without_envoy_plain(envoy-down.json 中的两个状态码)、without_agent_existing、without_agent_new_pod(agent-down.json 中的 existing_code 和 orphan_phase)。值必须与记录文件和当前配置一致。

报告是一张表,为每个组件各写一行对应关系:“缺席期间继续的”和“停止的”。记录文件用 jq 读取,配置从 cilium-config 和 CiliumNode 中读取。