Day 13 / 共 20 天 · 第 3 周 sidecar 与安全

Envoy bootstrap 生成

还记得 Envoy 课 Day 02 那个"启动配置文件"吗?它就是 pilot-agent 在这里生成的。今天看 agent 怎么用 Go 模板渲染出 Envoy 的 bootstrap,直接连回你学过的 Envoy。昨天(Day 12)说管家会"拉起 Envoy",其中第一步就是给它写一份启动配置——那份配置就是今天的主角。

📍 你在整门课的位置 · 第 3 周「sidecar 与安全」(W1 控制面全景 ✓ · W2 xDS 生成 ✓)
D11 sidecar 注入 D12 pilot-agent D13 bootstrap D14 mTLS·CA D15 SDS·授权
L01

bootstrap 是什么

🤔 痛点:为什么不写死一份 bootstrap? 每个 Pod 的身份都不同——命名空间、服务账号、IP、代理类型(sidecar 还是 gateway)都不一样。如果所有 Envoy 用同一份写死的 bootstrap,它们就没法向控制面报上"我是谁",istiod 也没法给它下发专属配置(回想 Day 01 的"带 Proxy 翻译")。
💡 本质:bootstrap = 新员工的"入职登记表",现填现用 bootstrap 就像新员工报到时填的登记表:姓名工号(node id)、坐哪个工位(admin 端口)、汇报给哪个领导(xDS 的 istiod 地址)。这张表不能提前印好——得根据"这次入职的是谁"现场填。pilot-agent 就是那个帮新 Envoy 填表的人:拿当前 Pod 的信息,套进模板,生成一份专属登记表,Envoy 拿着它就能"上岗连总部"。
⚠️ 常见误解:别把 bootstrap 和 xDS 搞混。bootstrap 是"开机那一刻必须有"的静态配置(写在文件里,只读一次);xDS 是"运行中不断热更新"的动态配置(走 gRPC 流)。bootstrap 的唯一使命,就是把 Envoy 引导到能连上 xDS 的那一刻。
bootstrap = "Envoy 的出生证明" Envoy 启动时需要一份初始配置(bootstrap),告诉它:我是谁(node id)、admin 端口在哪、去哪连控制面拿动态配置(xDS 的 gRPC 地址)、暴露哪些统计端口。xDS 是"运行时热更新"的动态配置,但 bootstrap 是"启动那一刻必须有"的静态配置——它是引导 Envoy 连上 xDS 的第一步。在 Istio 里,这份 bootstrap 由 pilot-agent 根据当前 Pod 的信息动态生成,而不是写死。回想 Envoy 课:Envoy 从 bootstrap 里的 ADS 地址连上控制面,之后一切配置走 xDS——那个 ADS 地址就指向本地 agent 的 xDS 代理(Day 12)。
L02

生成流程

// pkg/bootstrap/instance.go
// :43 type Instance interface { WriteTo; CreateFile }
// :111 CreateFile():MkdirAll → GetEffectiveTemplatePath → os.Create → WriteTo
// :62 WriteTo:newTemplate(templateFile) 用 Go text/template + sprig 解析
//   → toTemplateParams() 算参数 → t.Execute 渲染
读法:生成 = "读模板 → 算参数 → 渲染 → 写文件"。用 Go 标准库 text/template(+ sprig 函数库)——模板里 {{ .nodeID }} 这样的占位符被参数填充。Day 12 的 initializeEnvoyAgent 就是调 bootstrap.New(...).CreateFile() 触发这个流程。
① 读模板*_bootstrap_tmpl.json ② 算参数toTemplateParams ③ 渲染text/template ④ 写文件envoy-rev0.json {{ .nodeID }} 这类占位符被参数填充
生成四步流水线:和"填登记表"一模一样——拿到空白表(模板)、查清信息(参数)、逐格填(渲染)、交上去(写文件)。
📝 简化版 → 真实版 如果让你写,大概会是:tmpl := readFile(path); params := {nodeID, xdsAddr}; render(tmpl, params) → writeFile()。真实版多出的每一处都为了解决一个问题:用 sprig 函数库(模板里能调 toJson 等函数)、路径优先级(L03,允许 Higress 换模板)、option.Instance 逐项算参数(L04,可测试)。骨架相同,"多出来的肌肉"都是工程化考量。
L03

模板路径优先级

// instance.go:94 GetEffectiveTemplatePath 优先级:
//   CustomConfigFile > ProxyBootstrapTemplatePath > 默认 DefaultCfgDir
//   (./var/lib/istio/envoy/envoy_bootstrap_tmpl.json)
//   再被环境变量 ISTIO_BOOTSTRAP 覆盖
读法:模板可换——默认用内置模板,也可用户自定义。Higress 就是通过换模板来定制 Envoy 启动配置(L07)。又是"约定默认 + 可覆盖"的分层。
📝 举个例子:谁的模板最终生效 默认情况 → 用内置 envoy_bootstrap_tmpl.json; 运维设了 ProxyBootstrapTemplatePath → 用它; 又设了环境变量 ISTIO_BOOTSTRAP=/etc/my.json它最终胜出。 优先级:CustomConfigFile > ProxyBootstrapTemplatePath > 默认,再被 ISTIO_BOOTSTRAP 覆盖。Higress 正是靠"提供自己的模板路径"接管了这一步(L07)。
L04

模板参数

// pkg/bootstrap/config.go:89 toTemplateParams()
//   拆 DiscoveryAddress 得 discovery host
//   xdsType(:94):默认 GRPC;features.DeltaXds 时 DELTA_GRPC;waypoint 强制 DELTA_GRPC
//   用一串 option.Instance 逐项拼装参数(pkg/bootstrap/option/ 定义每个变量取值)
读法:toTemplateParams 把当前环境(控制面地址、node 信息、代理类型)算成模板变量。每个变量的取值逻辑封装成一个 option.Instance——模块化、可测试。算好的参数喂给模板渲染出最终 JSON。
L05

node metadata

agent.go:260 generateNodeMetadata()bootstrap.GetNodeMetaData:把 ServiceNode/Platform/InstanceIPs/ProxyConfig/EnvoyPrometheusPort/EnvoyStatusPort 打包成 Envoy 的 node.metadata

node metadata = "Envoy 的自我介绍" Envoy 连上控制面时,要报上自己的身份和属性——我在哪个命名空间、什么服务账号、什么标签、什么版本、代理类型(sidecar 还是 gateway)。这些信息打包进 node.metadata,随 xDS 请求发给 istiod。istiod 靠这些信息决定"该给这个 Envoy 下发什么配置"(回想 Envoy 课 xDS 的 DiscoveryRequest.node 字段)。所以 node metadata 是控制面"认识"每个数据面代理的名片——生成 bootstrap 时就把它填好。
L06

必备 stats

// config.go:57 requiredEnvoyStatsMatcherInclusionPrefixes =
//   "cluster_manager,listener_manager,server,cluster.xds-grpc,wasm"
// 还有 RBAC 相关 stats、vhost\..*\.route\..* 正则等
为什么 bootstrap 要指定"必备统计项"? Envoy 的 stats 极多,全开销大,所以可以用 matcher 筛选。但有些 stats 是"系统运行必需的"——比如 cluster.xds-grpc(xDS 连接状态)是 pilot-agent 判断"Envoy 是否连上控制面、是否 ready"的依据。bootstrap 里这个必备清单保证这些关键 stats 一定被采集,否则健康检查(Day 12 status server)就没数据判断。这把 Day 12 的 status server、今天的 bootstrap、以及后面遥测(Day 16)串起来了——它们共享同一套 stats 基础设施。
L07

Higress 模板

tools/packaging/common/ 里能直接对比上游 vs Higress 模板:

  • 上游:envoy_bootstrap.jsonenvoy_bootstrap_v3_gateway.json
  • Higress:higress_envoy_bootstrap.jsonhigress_envoy_bootstrap_lite.json

Higress 版含 layered_runtimedeferred_stat_options.enable_deferred_creation_stats=true、条件性 bootstrap_extensions(metadata_discovery)等定制。

读法:Higress 通过提供自己的 bootstrap 模板来定制 Envoy 启动行为(延迟创建 stats 省内存、metadata 发现等)——不改 Envoy 代码,只换配置模板。这呼应 Envoy 课 Day 19 讲的"Higress 靠配置/裁剪定制 Envoy 而非魔改源码"。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • bootstrap 和 xDS 的区别?bootstrap 里的 ADS 地址指向哪?
  • 生成流程四步?用什么模板引擎?
  • 模板路径优先级?Higress 怎么利用它定制?
  • node metadata 起什么作用?
  • 为什么 bootstrap 要指定必备 stats?

✋ 动手

cd /Users/bitmart/work/codes/github/higress-group/istio
grep -n 'func.*CreateFile\|GetEffectiveTemplatePath\|toTemplateParams' pkg/bootstrap/instance.go pkg/bootstrap/config.go
ls tools/packaging/common/*bootstrap*.json
head -30 tools/packaging/common/higress_envoy_bootstrap.json
明天预告 · Day 14mTLS 与 CA——服务间怎么自动双向 TLS 加密?istiod 内建的 CA 怎么签发证书、SPIFFE 身份怎么工作。安全是服务网格的核心价值之一。
← Day 12 pilot-agent Day 14 · mTLS 与 CA →