给 K8s 换心脏:把 CNI 从 Calico/Flannel 迁到 Cilium 的完整流程
给 K8s 换心脏把 CNI 从 Calico/Flannel 迁到 Cilium 的完整流程【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/ciliumKubernetes 集群的网络插件CNI决定了 Pod 之间、Pod 与外部世界之间所有流量的走向某种意义上它就是集群的心脏。很多团队最初选择 Calico 或 Flannel 作为起步方案但随着集群规模扩大、微服务数量增长kube-proxy iptables 的性能瓶颈、网络策略的粗粒度、排障时两眼一抹黑的可观测性缺失会逐渐变成刺痛运维的顽疾。Cilium 正是冲着这些问题来的——它用 eBPF 把数据面下沉到内核绕过整条 iptables 链还能在数据面上直接实现网络策略、负载均衡与全栈可观测性。但换心脏最让人恐惧的从来不是新技术本身而是迁移过程中的停机与未知风险。好消息是Cilium 官方在仓库中维护了一份完整的迁移方案见 迁移文档支持在不重启整个集群、不中断存量流量的前提下按节点逐个滚动替换 CNI。本文以这份官方流程为主线拆解从现状盘点、滚动替换到策略与可观测性对齐的完整路径并给出可直接落地的 Helm 配置与命令行步骤。一、迁移前的现状盘点与风险识别1.1 先想清楚为什么值得迁移在动手之前先确认迁移的收益是真实的。Cilium 的核心差异在于数据面传统 CNI 的转发路径通常要穿过 iptables/Netfilter 层而 Cilium 将 datapath 逻辑编译为 eBPF 程序直接挂载到内核网络栈绕过了 iptables。仓库的基准测试文档对此有直接的量化证据benchmark.rst在单流 TCP 吞吐测试中eBPF 方案甚至可以超过裸机节点间直连的基线——因为 eBPF 旁路了节点上仍然要穿越 iptables 的默认路径尽管它额外做了容器命名空间转发和策略执行。除此之外迁移还带来三笔隐性收益替代 kube-proxyCilium 的 eBPF 负载均衡可以在数据面完成 Service 转发集群可以逐步卸载 kube-proxy减少一层 iptables 维护成本策略内置NetworkPolicy 与 CiliumNetworkPolicy 直接在数据面执行无需依赖独立的策略代理可观测性Hubble 在内核数据面采集流量元数据服务依赖图、TCP/DNS/HTTP 指标开箱即用。1.2 盘点现状四件事必须提前确认迁移文档k8s-install-migration.rst明确指出热迁移live migration有几个硬性前提动手前必须逐项核对检查项要求说明Pod CIDR为 Cilium 分配一套全新的、未被占用的 Cluster CIDR新旧 CNI 必须使用不同 IP 段Linux 路由表才能自动隔离两套网络的流量封装协议/端口与现有 CNI 使用不同的封装协议或端口若旧网络用 VXLANCilium 要么改用 GENEVE要么用不同的 VXLAN 端口IPAM 模式使用 cluster-pool 模式这是官方强烈推荐的分配器能最大程度保证无 IP 冲突旧 CNI 类型必须基于 Linux 路由栈Flannel、Calico、AWS-CNI 均可BGP 路由方案暂不支持迁移1.3 风险清单哪些场景此路不通官方文档明确列出了当前未测试/不支持的迁移场景提前知晓可以避免踩坑BGP 路由方案基于 BGP 的 CNI 尚未纳入迁移测试IP 协议族切换例如从 IPv4 迁移到 IPv6 双栈从 chained 模式链式 CNI迁移已有 NetworkPolicy 提供方迁移期间 Cilium 的策略执行会被禁用否则非 Cilium Pod 的流量可能被错误丢弃。若集群已有策略提供方官方建议先临时删除所有 NetworkPolicy。另外需要特别强调迁移过程高度依赖现有集群的具体配置文档给出的警示是——强烈建议先在测试/实验集群上完整演练一遍可用 Kind 复现配置见 kind-config.yaml再在生产环境执行。这也符合任何心脏手术都先做体外循环演练的原则。二、滚动替换 CNI数据面切换的分步实操2.1 整体策略双 Overlay 共存逐节点接管热迁移的核心思想是双 Overlay 共存Cilium 先以secondary mode安装——只建立自己的 Overlay 网络、暂不接管任何 Pod 的 CNI 配置随后通过CiliumNodeConfig按节点逐个接管CNI 配置。同一节点上的 Pod 只能附着于一个网络但在迁移期间Cilium 管理的 Pod 与旧 CNI 管理的 Pod可以互相访问——前提就是前面说的独立 IP 段 独立封装端口由 Linux 路由表自动完成流量分离。整个流程官方总结为四步k8s-install-migration.rst以secondary模式安装 Cilium建立 Overlay但不管理任何 Pod逐节点执行 cordon → drain → 迁移 → reboot全部节点迁移完成后移除旧网络插件可选再次滚动重启节点清理 iptables 规则与残留网络接口等遗留资源。2.2 第一阶段以影子模式安装 Cilium先准备一套values-migration.yaml这份配置是迁移的关键每一行都有明确目的operator: unmanagedPodWatcher: restart: false # 迁移期间不要自动重启未被 Cilium 管理的 Pod routingMode: tunnel tunnelProtocol: vxlan tunnelPort: 8473 # 与旧 CNI 使用不同的 VXLAN 端口 cni: customConf: true # 不安装 CNI 配置文件暂不接管 uninstall: false # 关闭时不要删除 CNI 配置 ipam: mode: cluster-pool operator: clusterPoolIPv4PodCIDRList: [10.245.0.0/16] # 全新且未被占用的网段 policyEnforcementMode: never # 迁移期间禁用策略执行 bpf: hostLegacyRouting: true # 允许 Cilium 与现有 Overlay 之间的路由互通接下来生成初始配置并安装。先用 cilium-cli 自动探测集群的典型 Helm 值只生成清单、不直接应用再通过 Helm 安装$ cilium install --version 版本号 --values values-migration.yaml --dry-run-helm-values values-initial.yaml $ helm repo add cilium https://helm.cilium.io/ $ helm install cilium cilium/cilium --namespace kube-system --values values-initial.yaml安装完成后验证 Cilium 已建立 Overlay 但尚未接管任何 Pod$ cilium status --wait ... Cluster Pods: 0/3 managed by Cilium0/3 managed by Cilium正是影子模式已就绪的信号。2.3 第二阶段创建 per-node 接管配置Cilium 支持按节点粒度覆盖配置机制是CiliumNodeConfig对象详见 per-node-config.rst它由一组defaults配置项和一个nodeSelector标签选择器组成只有命中标签的节点才会套用这些配置。这个特性最初就是为了渐进式灰度而设计的。创建如下配置——初始不匹配任何节点通过后续打标签来逐步放量cat EOF | kubectl apply --server-side -f - apiVersion: cilium.io/v2 kind: CiliumNodeConfig metadata: namespace: kube-system name: cilium-default spec: nodeSelector: matchLabels: io.cilium.migration/cilium-default: true defaults: write-cni-conf-when-ready: /host/etc/cni/net.d/05-cilium.conflist custom-cni-conf: false cni-chaining-mode: none cni-exclusive: true EOF注意cni-exclusive: true——一旦接管Cilium 将成为该节点的唯一CNI这与cni-chaining-mode: none共同保证不会与旧 CNI 配置产生竞争。2.4 第三阶段单节点迁移的七个动作迁移以单个节点为单位进行官方明确建议不要先从控制面节点开始。对一个工作节点$NODE完整流程如下① 封锁并建议排空节点$ kubectl cordon $NODE $ kubectl drain --ignore-daemonsets $NODE排空不是强制的但不排空的话节点重启期间 Pod 会经历短暂的连接中断。② 打标签触发 CiliumNodeConfig 生效$ kubectl label node $NODE --overwrite io.cilium.migration/cilium-defaulttrue③ 重启该节点上的 Cilium Agent让其写入 CNI 配置文件$ kubectl -n kube-system delete pod --field-selector spec.nodeName$NODE -l k8s-appcilium $ kubectl -n kube-system rollout status ds/cilium -w④ 重启节点让 PodSandbox 全部重建、由新 CNI 接管# Kind 环境示例 $ docker restart $NODE⑤ 验证迁移成功——检查 Pod IP 是否落在 Cilium 的 CIDR 内、API Server 是否可达$ cilium status --wait $ kubectl get -o wide node $NODE $ kubectl -n kube-system run --attach --rm --restartNever verify-network \ --overrides{spec: {nodeName: $NODE, tolerations: [{operator: Exists}]}} \ --image ghcr.io/nicolaka/netshoot:v0.8 -- /bin/bash -c ip -br addr curl -s -k https://$KUBERNETES_SERVICE_HOST/healthz echo⑥ 解除封锁$ kubectl uncordon $NODE⑦ 重复以上步骤直到所有节点迁移完毕。每个节点完成后建议用cilium status确认Cluster Pods的受管数量在持续增长。2.5 迁移期间为什么不敢重启未管理 Pod这里有一个容易被忽略的细节影子模式下operator.unmanagedPodWatcher.restart被显式设为false。这是因为 Cilium Operator 里有一个专门的控制器源码见 operator/unmanagedpods/controller.go它会周期性地扫描那些已存在但未被 Cilium 管理的 Pod 并自动重启它们——平时这是新装 Cilium 时让存量 Pod 快速入网的贴心功能cilium-cli 安装时输出的♻️ Restarted unmanaged pod ...就来自它。但在迁移中途如果让它放开手脚就会把尚未迁移节点上的 Pod 提前重启而这些节点还没有写入 Cilium CNI 配置重启反而会造成无谓的流量中断。迁移期先按住它、迁移完成后重新放开正是这份配置的用心之处。三、迁移后的网络策略与可观测性对齐3.1 配置转正从影子模式切换为主 CNI当cilium status显示集群内所有 Pod 均已被 Cilium 管理后进入收尾阶段。需要把当初为迁移准备的临时配置全部翻转回来$ cilium install --version 版本号 --values values-initial.yaml --dry-run-helm-values \ --set operator.unmanagedPodWatcher.restarttrue --set cni.customConffalse \ --set policyEnforcementModedefault \ --set bpf.hostLegacyRoutingfalse values-final.yaml $ diff values-initial.yaml values-final.yaml确认差异无误后应用并滚动重启$ helm upgrade --namespace kube-system cilium cilium/cilium --values values-final.yaml $ kubectl -n kube-system rollout restart daemonset cilium $ cilium status --wait然后删除 per-node 配置让所有节点回归统一默认值$ kubectl delete -n kube-system ciliumnodeconfig cilium-defaultbpf.hostLegacyRouting从true翻转为false值得单独说明迁移期间它负责打通 Cilium 与旧 Overlay 之间的路由是新旧两套网络并存的粘合剂迁移完成后关闭它可以让 Cilium 走更高效的 eBPF host routing 路径——官方文档提示这一步会带来每次 Agent 重启时的短暂连接中断但对网络性能有明显改善相当于拆掉临时心脏支架。3.2 移除旧 CNI删插件容易清理残留难确认所有 Pod 都在 Cilium 网络中后才允许删除旧网络插件。但官方文档特别提醒大多数网络插件会在节点上留下残留资源——iptables 规则、网络接口等。这些残留会在节点下次重启时由系统自动清理。如果希望集群立刻干净可以对全集群再做一次滚动重启。若你的集群还运行着 kube-proxy并且准备用 Cilium 的 eBPF 负载均衡彻底替代它可以参考 per-node 文档per-node-config.rst中给出的渐进式方案先用CiliumNodeConfig让 kube-proxy-replacement 只在打了io.cilium.migration/kube-proxy-replacement: true标签的节点上生效并给 kube-proxy DaemonSet 打上反亲和、将其调度限制在未迁移节点逐节点验证cilium config get kube-proxy-replacement输出true后再删掉 kube-proxy DaemonSet。注意前提必须已配置k8sServiceHost与k8sServicePort否则 kube-proxy 卸载后 Cilium 将无法访问 API Server。3.3 策略对齐从禁用到精细化执行迁移期间policyEnforcementMode: never是为了避免误伤旧 CNI Pod 的流量但迁移完成后策略能力正是这次换芯的重要回报之一。policyEnforcementMode翻转为default后Cilium 将按需执行策略标准 KubernetesNetworkPolicyL3/L4与其他 CNI 生态兼容更细粒度的CiliumNetworkPolicy支持 L7 层HTTP 方法、路径、头字段策略例如限制某个 Service 只允许GET /api被特定身份访问。策略对齐的另一个关键动作是验证迁移完成后应重新应用此前临时删除的 NetworkPolicy并用cilium status观察策略执行状态确认没有因 CNI 切换而产生丢包或拒绝。3.4 可观测性对齐让 Hubble 接管监控心电迁移前集群的流量观测往往依赖抓包或外部工具被动且侵入。迁移到 Cilium 后可观测性可以直接从数据面获得——Hubble 在内核 eBPF 层面采集流量无需侵入应用。启用方式$ cilium hubble enable --ui这条命令背后源码见 cilium-cli/hubble/hubble.go会拉起 hubble-relay 与 hubble-ui 组件UI 则通过端口转发暴露cilium-cli/hubble/ui.go$ cilium hubble uiHubble 提供的可观测性维度恰好能补齐迁移前最缺的三块拼图服务依赖图自动绘制 Pod/Service 之间的实时流量拓扑回答谁在访问谁协议级指标TCP、DNS、HTTP 的分类流量与错误率直接定位慢接口与 DNS 故障网络策略视角Hubble 能展示被策略允许/拒绝的流量分布迁移后可以直观验证策略是否按预期生效。对齐完成后建议用cilium connectivity test做一轮全链路连通性测试覆盖 Pod-to-Pod、Pod-to-Service、DNS 解析等场景作为换心手术的最终验收。结语换心不是冒险而是可控的手术从 Calico/Flannel 迁移到 Cilium本质是一次可编排、可回滚、可验证的滚动手术而不是推倒重来的大冒险。官方迁移文档提供的关键支撑有三点双 Overlay 共存让新旧网络在迁移期内和平共处CiliumNodeConfig的 per-node 机制让接管粒度精确到单个节点、随时可以暂停迁移前后的 Helm 值翻转让影子模式与主 CNI 模式之间的切换有明确的完成信号。真正值得投入精力的永远是迁移前的盘点IP 段隔离、封装端口、策略禁用、实验集群演练和迁移后的对齐策略恢复、Hubble 可观测性、kube-proxy 替代、残留清理。把这两头做扎实中间按节点的滚动替换不过是按图索骥的机械动作。而当你看到cilium status显示全部 Pod 受管、Hubble 里流量拓扑清晰呈现的那一刻会明白这次换心值。【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考