Day 12 / 共 20 天 · 第 3 周 sidecar 与安全
pilot-agent
sidecar 容器里的 1 号进程不是 Envoy,而是 pilot-agent。它是 Istio 控制面(istiod)和你学过的 Envoy 之间的胶水层:拉起 Envoy、本地发证书、代理 xDS。昨天(Day 11)Envoy 被注入进了 Pod,今天看这个 Pod 里"谁在管 Envoy"——正是 pilot-agent。
📍 你在整门课的位置 · 第 3 周「sidecar 与安全」(W1 控制面全景 ✓ · W2 xDS 生成 ✓)
D11 sidecar 注入→
D12 pilot-agent→
D13 bootstrap→
D14 mTLS·CA→
D15 SDS·授权
L01
三大职责
🤔 痛点:Envoy 直接连 istiod 不行吗?
如果 Envoy 自己连控制面,它就得自己去申请证书、自己管私钥、自己维护跟 istiod 的连接、自己处理 Wasm 插件下载……把这些"杂活"塞进数据面代理,既臃肿又不安全(私钥落在 Envoy 手里)。
💡 本质:给 Envoy 配个"贴身管家",杂活外包
续用 Day 11 的类比:Envoy 是住户,pilot-agent 就是那个贴身管家。住户只管收发快递(转发流量),办证件(证书)、跟物业总部对接(连 istiod 拿配置)、代收特殊包裹(Wasm 插件)这些杂事全交给管家。管家把脏活累活挡在外面,住户轻装上阵——这就是为什么 sidecar 容器的 1 号进程是 agent 而不是 Envoy。
⚠️ 常见误解:很多人以为
istio-proxy 容器里跑的就是 Envoy。其实 1 号进程是 pilot-agent,Envoy 是它 fork 出来的子进程。agent 挂了整个 sidecar 就没了——它才是"户主"。pilot-agent = "Envoy 的贴身管家"
🧠 记忆口诀:管家三件事 = 「拉起、发证、传话」(拉起 Envoy · 发证书 SDS · 传话 xDS 代理)。
istio-proxy 容器里,pilot-agent 是主进程,Envoy 是它拉起的子进程。管家干三件事:①拉起并托管 Envoy——生成启动配置、启动 envoy 进程、优雅关闭;②本地 SDS 服务器——给 Envoy 发 TLS 证书(Envoy 从不直接碰私钥文件);③xDS 代理——坐在 istiod 和 Envoy 中间转发配置,顺便做本地增强(DNS、Wasm 缓存)。为什么 Envoy 不自己连 istiod?因为有个管家统一处理证书、连接、增强,更安全也更灵活。🧠 记忆口诀:管家三件事 = 「拉起、发证、传话」(拉起 Envoy · 发证书 SDS · 传话 xDS 代理)。
L02
入口 proxy 命令
// pilot/cmd/pilot-agent/main.go:32 main() → NewRootCommand(把 sds.NewServer 作为工厂注入)
// pilot/cmd/pilot-agent/app/cmd.go newProxyCommand RunE(:101)启动主线:
// :109 ConstructProxyConfig
// :137 istioagent.NewAgent(proxyConfig, agentOptions, secOpts, envoyOptions)
// :144 按 StatusPort 启动 status server
// :155 agent.Run(ctx) 启动 SDS、DNS、xDS proxy 和 Envoy,返回 wait() 阻塞
读法:容器启动跑的是
pilot-agent proxy sidecar ...。NewAgent 构造 Agent,agent.Run 是启动总线。SDS 用工厂注入(便于替换实现,又是工厂模式)。L03
Agent.Run 启动
// pkg/istio-agent/agent.go:354 func (a *Agent) Run(ctx) (func(), error)
// :356 initLocalDNSServer() // 可选本地 DNS 捕获
// :399 initSdsServer() // 本地 SDS(L05)
// :403 initXdsProxy(a) // xDS 代理(L06)
// :418 startFileWatcher // 监控根证书变化,变了重建连接
// :426 initializeEnvoyAgent + go a.envoyAgent.Run(ctx) // 拉起 Envoy(L04)
读法:启动顺序:DNS → SDS → xDS proxy → 文件监控 → Envoy。先把"给 Envoy 供证书、供配置"的基础设施起好,最后才拉 Envoy。
Agent struct(:128)持有所有子系统。管家 agent 一手托三件事(SDS/xDS 代理/status),左手喂 Envoy,右手连总部 istiod。启动顺序:先起这些基础设施,最后才拉 Envoy 子进程。
L04
拉起 Envoy
// agent.go:296 initializeEnvoyAgent
// :312 bootstrap.New(...).CreateFile() // 生成 Envoy bootstrap JSON(Day 13)
// :337 envoy.NewProxy(a.envoyOpts)
// :344 envoy.NewAgent(...) // 带优雅下线(drain)能力的 Envoy 托管器
pilot-agent 怎么"拉起" Envoy?
很直接:①先给 Envoy 写一个启动配置文件(bootstrap,Day 13 讲怎么生成);②然后以子进程方式启动真正的
envoy 二进制,把配置文件路径传给它。之后 agent 一直盯着这个子进程:证书要轮转就通过 SDS 推给它、配置更新通过 xDS 代理转给它、要关闭时先让它 drain(优雅排空连接)再退出。这就是"托管"——agent 是 Envoy 的父进程和生命周期管理者。回想 Envoy 课 Day 02 的启动,那个 bootstrap 就是 agent 在这里生成的。L05
本地 SDS
// agent.go:448 initSdsServer
// :461 newSecretManager(createCaClient) // 创建证书管理器
// :484 sdsServer = SDSFactory(secOpts, secretCache, ...)
// :485 secretCache.RegisterSecretHandler(sdsServer.OnSecretUpdate) // 证书轮转回调推送
// SDS 通过 UDS(Unix socket)把证书喂给本地 Envoy,证书以 InlineBytes 内联,不落盘
为什么 Envoy 不自己管证书?
安全。私钥是最敏感的东西——如果落盘或让 Envoy 直接管,泄露风险大。Istio 的设计:pilot-agent 跑一个本地 SDS(Secret Discovery Service)服务器,通过 Unix socket(不走网络)把证书内联推给 Envoy——私钥全程在内存、不落盘。证书快过期了,agent 自动重新申请、通过
OnSecretUpdate 推给 Envoy 热更新。Envoy 只管用证书,申请/轮转/存储全由 agent 代劳。Day 14-15 详讲证书怎么申请。L06
xDS 代理
// pkg/istio-agent/xds_proxy.go:79 type XdsProxy
// 把 Envoy↔istiod 的所有 xDS 连接合并成单条 TCP + 多路 gRPC 流
// :125 initXdsProxy:注册 NameTableType(DNS)、ProxyConfigType(PCDS)handler
// :300 handleStream / :360 handleUpstream:Envoy 请求转发给 istiod、响应回传
// 对 ECDS(Wasm)可 rewriteAndForward(:524)把远程加载改成本地文件
xDS 代理:中间人的价值
Envoy 不直接连 istiod,而是连本地的 agent xDS 代理,代理再连 istiod。为什么多这一跳?①合并连接——一个节点上多个 xDS 类型复用一条到 istiod 的连接;②mTLS——代理用工作负载证书对 istiod 做双向认证;③本地增强——代理能拦截并改写 xDS,比如把"从远程 URL 加载 Wasm 插件"改写成"从本地缓存文件加载"(更快更可靠)、注入本地 DNS 表、注入健康信息。Higress 还在这里加了 ECDS 版本单调性校验(
:722),解决高频热更新 Wasm 时的版本乱序竞态。📝 举个例子:合并连接省了多少
一个节点上 Envoy 要 5 类 xDS(LDS/RDS/CDS/EDS/SDS)+ DNS 表 + Wasm 加载。没有代理:Envoy 可能开多条连接、每条都要单独跟 istiod 做 mTLS 握手 →
有 agent 代理:Envoy 只连本地 UDS(0 网络开销),agent 用一条到 istiod 的 mTLS 长连接承载全部 →
本地那句"从 https://.../plugin.wasm 加载"还被代理悄悄改写成"从本地缓存文件
/var/lib/istio/.../xxx.wasm 加载",更快也不怕网络抖动。L07
status server
agent 还跑一个 status server(pilot/cmd/pilot-agent/status/server.go,端口 15020):/healthz/ready(:66)健康检查、/app-health/(:94)应用探针改写。
为什么要改写探针?
开了 mTLS STRICT 模式后,所有到 Pod 的流量都要求 mTLS。但 kubelet 的健康探针不会带 Istio 证书——它会被 mTLS 拒绝,导致 Pod 一直"不健康"被重启。解决:注入时把业务容器的探针改写成打到 agent 的 15020,agent 再转发到业务真实探针。这样 kubelet 探针经 agent 中转绕过了 mTLS 要求。这是 sidecar 模式一个必须处理的细节——否则健康检查全挂。
L08
今日小结 + 动手
🧠 今天你应该能回答
- pilot-agent 的三大职责?为什么它是 1 号进程?
- Agent.Run 的启动顺序?
- agent 怎么拉起 Envoy?
- 为什么证书由 agent 的 SDS 管而非 Envoy 自己?
- xDS 代理多这一跳的价值?为什么要改写健康探针?
✋ 动手
cd /Users/bitmart/work/codes/github/higress-group/istio
grep -n 'func (a \*Agent) Run\|initSdsServer\|initXdsProxy\|initializeEnvoyAgent' pkg/istio-agent/agent.go
grep -n 'type XdsProxy\|func initXdsProxy' pkg/istio-agent/xds_proxy.go
明天预告 · Day 13:Envoy bootstrap 生成——agent 怎么生成 Envoy 的启动配置?text/template 渲染、node metadata 注入、必备 stats matcher,以及 Higress 定制的 bootstrap 模板。直接连回 Envoy 课。