
Istio CNI Node Agent 深度解析Sidecar 流量劫持、Ambient 模式与 CNI 插件实现原理【免费下载链接】istioConnect, secure, control, and observe services.项目地址: https://gitcode.com/GitHub_Trending/is/istio本篇基于 Istio 仓库的 cni/README.md 展开系统讲解 Istio CNI Node Agent 的三大职责CNI 插件安装、Sidecar 模式流量劫持、Ambient 模式事件上报、Sidecar 模式下的 Selection/Redirect API、CmdAdd完整工作流以及 Ambient 模式的环境变量与权限模型并结合cni/pkg下的插件实现、节点代理选项与 Helm chart 模板帮助你在生产集群中排查 CNI 相关问题并深入理解 Istio 无侵入流量拦截的底层机制。一、Istio CNI Node Agent 是什么Istio CNI Node Agent 以 DaemonSet 形式运行在集群每个节点上承担以下三类核心职责安装并守护 CNI 插件在每个节点的本地文件系统中安装istio-cni插件二进制更新该节点的 CNI 配置如/etc/cni/net.d并持续 watch 配置与二进制路径一旦被修改即重新安装。Sidecar 模式替代 istio-initPod 被容器运行时调度时由 CNI 插件使用 iptables 配置 sidecar 网络。这替代了 Istio 过去在用户应用 Pod 中注入带NET_ADMIN权限的特权initContainersistio-init的做法——彻底移除了用户应用 Pod 中对特权容器的需求。Ambient 模式事件上报CNI 插件本身不配置任何网络只负责把新 Pod 事件同步推送回运行在 Node Agent 内部的 ambient watch server。该 server 负责定位 Pod 的 netns 并通过 iptables 在其内部配置网络它还会 watch 启用 ambient 的 namespace以同样方式“补纳”已启动但新近被启用 ambient 的 Pod。Ambient 模式下的 Pod 注册全生命周期详见架构文档 ztunnel-cni-lifecycle.md。二、部署架构Helm Chart、install-cni 容器与插件二进制从 istio-cni Helm chart 的结构看部署由三部分构成install-cniDaemonSet主功能是安装并辅助节点 CNI但它同时是一个完整的 server会与 K8S 交互并 watch Pod 用于恢复repair。istio-cni-configConfigMap存放要合并进节点 CNI 链式配置的 CNI 插件配置。创建带ClusterRoleBinding的 service-accountistio-cni允许其获取 Pod 信息并在恢复场景下对 Pod 执行删除/修改。install-cni 容器的具体行为结合 pkg/install 源码install-cni容器在installAll流程中依次完成cni/pkg/install/install.go拷贝二进制将istio-cni和istio-iptables拷贝到/opt/cni/bin若目标目录只读会提示设置global.platform源码中对 read-only file system 错误有专门提示分支写 kubeconfig把 Pod 所在 service account 的 kubeconfig 写到 agent run 目录文件名固定为istio-cni-kubeconfig见 constants.go并周期性刷新宿主机上的 K8S JWT token供插件连接 K8S注入 CNI 插件配置将CNI_NETWORK_CONFIG插入/etc/cni/net.d/${CNI_CONF_NAME}的plugins列表。安装器会按文件扩展名.conf、.conflist在挂载的 CNI net 目录下查找配置文件文件名可用CNI_CONF_NAME环境变量显式指定Istio 自有配置默认为02-istio-cni.conflistconstants.go就绪探针与监控提供/readyz、/healthz端点并暴露 Prometheus 指标监控端口默认 8000见 constants.goUDS 日志转发建立 Unix Domain Socketlog.sock让istio-cni插件把日志发到本容器最终可通过普通kubectl logs查看CNI 插件进程不能写 stdoutCNI 规范用 stdin/stdout 传数据详见下文可选控制器按配置运行repair 控制器检测 istio 设置失败或处于特殊角落场景的 Pod 并重启之源码见 repaircontroller.go若启用 ambient则同时运行 watch Pod/Namespace 的ambient 控制器。安装后进程会进入 watch 循环持续比对配置与期望状态发生漂移时触发重装Installer.Run。istio-cni 插件CNI 插件可执行文件被拷贝到/opt/cni/bin当前仅实现 Kubernetes 场景Pod 创建时ADD 事件判断该 Pod 是否需要配置指向 Istio proxy 的 netns 重定向逻辑见下文 CmdAdd 工作流插件使用 install-cni 拷贝过来的 kubeconfig 和 JWT token 连接 K8S 获取 Pod 与 Namespace 信息。由于是短生命周期命令每次调用都新建连接判定需要重定向后调用istio-iptables并在 Pod netns 中执行nsenter --netk8s pod netns /opt/cni/bin/istio-iptables ...完成端口列表设置ambient 场景则走 ambient 事件逻辑。istio-iptables建立 iptables 规则把一组端口重定向到 Envoy 监听端口与istio-init容器共享代码根据 annotation/label 及其他设置生成iptables-save配置并应用。三、Sidecar 模式Selection API 与 Redirect APIIstio CNI 注入当前沿用与 init-container/inject 模式相同的 Pod annotation 体系。Selection API判定是否由 CNI 接管判定顺序为插件配置中的exclude namespaces最先生效ambient 判定满足以下条件时为真namespace labelistio.io/dataplane-modeambient和/或 pod labelistio.io/dataplane-modeambientPod 上不存在sidecar.istio.io/statusannotation该 annotation 由 sidecar 注入产生pod labelistio.io/dataplane-mode不等于nonesidecar 拦截启用需同时满足Pod 中不存在istio-init容器存在 istio-proxy 容器且未设置DISABLE_ENVOY环境变量该变量会触发 proxyless 模式istio-proxy 容器前两个参数为proxy与sidecar——或参数不足两个、或第一个参数不是proxysidecar.istio.io/inject不为falsesidecar.istio.io/status存在。这套规则在源码 cni/pkg/plugin/plugin.go 中逐条落地先检查istio-init容器排除、再检查DISABLE_ENVOY、再要求存在istio-proxy容器、检查 proxy 类型、解析sidecar.istio.io/inject新版 label API 优先于 annotation见 plugin.go最后要求sidecar.istio.io/statusannotation 存在。ambient 判定则由 isAmbientPod 通过编译后的EnablementSelector对 Pod 与 Namespace label 匹配完成。Redirect API重定向参数基于 annotation 的细粒度控制目前仅在sidecar模式支持实现细节见 cni/pkg/plugin/sidecar_redirect.go。可配置的参数包括参数说明redirectMode可设为 TPROXY要求 envoy 具备额外权限默认redirectREDIRECT 模式includeIPCidr/excludeIPCidr需要拦截 / 排除拦截的 IP 段includeInboundPorts/excludeInboundPorts入站端口白/黑名单includeOutboundPorts/excludeOutboundPorts出站端口白/黑名单excludeInterfaces排除的网卡接口reroute-virtual-interfaces重路由虚拟网卡旧名kubevirtInterfaces已废弃ISTIO_META_DNS_CAPTUREproxy 上的环境变量开启后启用 DNS 重定向INVALID_DROPproxy 上的环境变量改变 iptables 中对非法流量的行为reset → drop两个自动行为自动排除入站端口 15020、15021、15090这三个代理端口health、status、prometheus 指标等无需再绕回 Envoy源码中在构造重定向配置时直接追加redir.excludeInboundPorts 15020,15021,15090sidecar_redirect.go并有 plugin_test.go 的测试断言兜底自动识别 proxyUID/proxyGID代码从 istio-proxy 容器的RunAsUser/RunAsGroup自动提取 UID/GID 并排除其流量默认 1337见 kubernetes.go避免 proxy 自身流量被二次劫持。四、CmdAdd Sidecar 工作流源码级解读CmdAdd在新 Pod 创建时触发运行在节点上、位于 CNI 插件链中——Istio 在主 CNI 完成 Pod IP 与网络配置之后才被执行。完整工作流对照插件配置的排除列表检查 Pod namespace。配置必须排除 Istio 控制平面所在 namespace文档中留有 TODOPod 级别排除已足够未来可能让 Istiod 等组件也使用 ambient。若被排除则忽略该 Pod 并返回 prevResult为 Pod 设置重定向规则从 Pod 定义与 annotation 中取得端口列表以nsenter --netk8s pod netns /opt/cni/bin/istio-iptables ...方式在 Pod netns 中设置 iptables。以下情况会阻止重定向规则的设置Pod annotationsidecar.istio.io/inject为false或不存在sidecar.istio.io/statusannotationPod 含istio-initinitContainer——表示该 Pod 自行做注入设置返回 prevResult。源码层面CmdAdd还有几个关键实现细节panic 兜底CmdAdd用defer/recover捕获 panic确保任何异常都能以规范的 CNI 错误返回给容器运行时而不是让插件崩溃自我保护避免死锁插件先检查本次 ADD 是否属于自身的istio-cni-node-*PodisCNIPod。因为 kubeconfig 可能尚未由 install-cni 写入例如节点重启后直接创建 K8S client 会阻塞 CNI Pod 自身启动降级放行若 K8S API 不可达如凭证过期插件会重试最多 30 次间隔 1 秒见 plugin.go期间若发现该 Pod“看起来是”替换中的 istio-cni agent则基于 CNI 层传入的K8S_POD_NAME/K8S_NAMESPACE直接放行防止升级期间新 agent Pod 被旧插件卡死ambient 分支提前返回若插件配置启用 ambient 且该 Pod 判定为 ambient Pod插件仅通过 UDSpluginevent.sock的/cmdadd路径把事件推送给节点 agent 后即返回不做任何本地网络配置。插件的日志策略也很特别GetLoggingOptions日志写到 stderr、tee 到 UDS由 DaemonSet 读走并暴露为kubectl logs同时再 tee 一份滚动日志istio-cni.log上限 10MB见 constants.go以防 UDS server 宕机日志级别由插件配置的plugin_log_level控制。五、Ambient 模式设计细节“in-pod sidecar” 原理从文档设计说明看istio-cni实现 ambient 流量重定向的方式是指导 ztunnel 在应用 Pod 的网络命名空间内建立 socket其一端位于应用 Pod 内、另一端位于 ztunnel 的 Pod 内再配合 iptables 规则把流量通过这根 socket“管道”送入 ztunnel 并折返。这样带来的效果是行为上等效于 ztunnel 是 Pod 内 sidecar但无需把 ztunnel 注入 Pod manifest也不以任何方式变更应用 Pod不需要在 host 网络命名空间中配置任何网络规则/路由极大提升了对第三方 CNI 的兼容性——绝大多数情况下这种 in-pod ambient CNI 与第三方 CNI 的兼容性不逊于传统 sidecar 模式。关键环境变量以下环境变量在 cni/pkg/nodeagent/options.go 中注册默认值定义于 options.go环境变量默认值用途HOST_PROBE_SNAT_IP169.254.7.127应用于 SNAT 后的主机探测host probe报文便于 Pod 侧识别并跳过。覆盖默认 SNAT IP 时应使用 169.254.0.0/16 网段内任意地址HOST_PROBE_SNAT_IPV6fd16:9254:7127:1337:ffff:ffff:ffff:ffffIPv6 链路本地地址在默认设计上就抗冲突因此几乎永远不需要覆盖源码注释解释了其目的为了在 Pod 内可靠地识别 kubelet 健康探测流量与 kube-proxy 流量区分二者源 IP 通常相同ambient server 会在 host netns 中把已识别的 host probe 报文 SNAT 到固定的 APIPA/链路本地 IP。该 IP 具体取值无关紧要只要不可路由、不与任何现有地址冲突即可。六、权限要求与最小化能力集无论运行在何种模式Istio CNI Node Agent 都需要节点级特权权限在默认禁止特权工作负载的受限环境中需要将其加入白名单。若启用了 sidecar repair 模式或 ambient 模式node agent 还需要进入 Pod 网络命名空间并在其中执行网络配置的权限。当 sidecar repair 或 ambient 模式任一启用时容器启动时通过drop: ALL丢弃全部 Linux capabilities再显式加回实际需要的能力。从 istio-cni DaemonSet 模板 可以核对实际配置privileged: falseCapability用途NET_ADMIN允许访问 ipset 与路由表NET_RAW允许变更 iptables 的nat表SYS_PTRACErepair 与 ambient 模式描述 Pod 网络命名空间所需SYS_ADMINambient 与 repair 模式打开/proc下网络命名空间、进入 Pod netns 所需没有更细粒度的替代能力DAC_OVERRIDE丢弃全部能力后失去对他人拥有目录的读写权而 hostPath 挂载需要写权限此能力用于绕过该限制README 中列出的三项为CAP_SYS_ADMIN、CAP_NET_ADMIN、CAP_NET_RAW从 chart 模板看实际部署还额外加入了SYS_PTRACE与DAC_OVERRIDE以满足 netns 描述与 hostPath 写入需求。七、排障收集 CNI 日志CNI 插件由kubelet进程内的线程执行插件日志最终进入 syslog 并归属kubelet进程。三种收集方式1. 通过 istioctl / helm 调整日志级别# values 配置 values.global.logging.levelcni:debug,ambient:debug然后查看特定节点上istio-cniDaemonSet Pod 的日志。由于插件日志已通过 UDS tee 到 DaemonSetplugin.gokubectl logs中即可看到插件输出。2. 从特定节点的 syslog 收集在有journalctl的系统上查看最近 1000 条 kubelet 日志并支持vi式检索$ journalctl -t kubelet -n 1000 | less3. GKE 通过 Stackdriver 日志查看器GKE 集群的日志会被 Stackdriver 收集可通过项目日志查看器或gcloud logging read查询。例如抓取包含 cmdAdd 的最近 10 条 kubelet 日志$ gcloud logging read resource.typek8s_node AND jsonPayload.SYSLOG_IDENTIFIERkubelet AND jsonPayload.MESSAGE:cmdAdd --limit 10 --format json八、开发说明硬性依赖 Linuxistio-cni插件强依赖 Linux。虽为非 Linux 系统做了部分不可用构建的兼容但并不普遍实际上只支持在 Linux 上构建。非 Linux 开发环境请使用make shell架构支持Go 支持的大多数 Linux 架构均可工作Istio 仅在AMD64 与 ARM64上做过测试。开发相关入口可参考插件主体cni/pkg/plugin/plugin.go、重定向参数解析 cni/pkg/plugin/sidecar_redirect.go节点代理ambient server、Pod 缓存、netns 操作cni/pkg/nodeagent/repair 控制器cni/pkg/repair/repaircontroller.go安装器二进制拷贝、CNI 配置注入、watch 重装cni/pkg/install/install.go集成测试cni/test/install_cni.go、cni/test/install_k8s_test.go九、设计渊源该 CNI 插件的实现框架基于 containernetworking 官方 sample plugin链式插件、多 CNI 版本 prevResult 解析部署与安装细节则主要借鉴 Calico CNI 插件的做法CNI 安装脚本容器化并以 DaemonSet 部署其 DaemonSet ConfigMapinstall-cni容器与 RBAC 结构均参照 Calico 的对应清单设计这种“install-cni DaemonSet 负责安装 节点侧短生命周期插件负责 ADD 事件”的双组件模式正是本文 部署架构 一节描述的结构。关键路径速查内容路径本文源文档cni/README.mdCmdAdd / 排除判定cni/pkg/plugin/plugin.goRedirect 参数与自动排除端口cni/pkg/plugin/sidecar_redirect.goPod 信息提取proxyUID 等cni/pkg/plugin/kubernetes.goHOST_PROBE_SNAT_IP 注册cni/pkg/nodeagent/options.go常量UDS 名、kubeconfig、CNIBinDircni/pkg/constants/constants.go安装器cni/pkg/install/install.goDaemonSetcapabilities 配置manifests/charts/istio-cni/templates/daemonset.yamlAmbient 生命周期架构文档architecture/ambient/ztunnel-cni-lifecycle.md【免费下载链接】istioConnect, secure, control, and observe services.项目地址: https://gitcode.com/GitHub_Trending/is/istio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考