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

Envoy 内部结构

不重启也能换掉配置

在 TT Lab 中继续学习

目标

确认静态配置的局限之后,把同一个代理改为文件订阅方式,在不重启的情况下替换集群和监听器,并观察错误的更新被拒绝的情形。

为什么重要

服务网格和 Ingress 控制器所做工作的核心就是这个。但只读文档的话,“控制平面挂了,网格就挂了”这样的错误说法会留在脑子里。亲手制造一次拒绝,就会同时记住两件事:被拒绝之后流量依然流动,所以可能没有人知道就过去了好几周。只有了解这两点的人,才会给 update_rejected 设置告警。

步骤

  1. 以 ok 启动两个上游(8096、8097)。在 /root/envd-xds/static.yaml 中放置 / 返回 static=v1 的静态配置并启动(管理端口 9977,监听器 127.0.0.1:10077),然后保持运行,把该文件中的 v1 改为 v2。分别在修改前和修改后发起请求,在 /root/envd-xds/01-static.txt 中写入 before=、after_edit= 两行(不要重新启动)。
  2. 创建 /root/envd-xds/dyn.yaml——不设 static_resources,只放置 node(id 为 envd-xds-1,cluster 为 envd-xds)和 dynamic_resources,lds_config 订阅 /root/envd-xds/xds/lds.yaml,cds_config 订阅 /root/envd-xds/xds/cds.yaml,两者都通过 path_config_source 订阅。同时创建这两个资源文件——监听器在 /marker 上返回 lds=v1,其余请求发往集群 pool,集群 pool 的端点为 8096、8097 两个。启动后请求 /marker 和 /,在 /root/envd-xds/02-subscribe.txt 中写入 marker=、upstream_ok=(/ 请求返回 200 则为 yes)两行。
  3. 在 /root/envd-xds/xds/cds.yaml 中去掉端点 8097(只保留一个端点)。保持代理不动,等到生效后请求八次,在 /root/envd-xds/03-reload.txt 中写入 p8096=、p8097=、total= 三行。
  4. 从 /config_dump 中只提取集群一节,保存到 /root/envd-xds/04-dump.json,并在 /root/envd-xds/04-dump.txt 中写入 static_clusters=(静态集群数)、dynamic_clusters=(动态集群数)、dynamic_name=(动态集群的名称)三行。
  5. 把 /root/envd-xds/xds/lds.yaml 中 /marker 的响应从 lds=v1 改为 lds=v2。保持代理不动,等到生效后,在 /root/envd-xds/05-lds.txt 中写入 marker_after=(/marker 的响应)和 upstream_still_ok=(/ 请求仍然返回 200 则为 yes)两行。
  6. 把 /root/envd-xds/xds/cds.yaml 中集群的 name 行删掉,故意发送一次错误的更新(schema 要求名称为必填)。稍等片刻后,在 /root/envd-xds/06-rejected.txt 中写入 still_serving=(/ 请求仍然返回 200 则为 yes)、update_rejected=(cluster_manager.cds.update_rejected 统计的值)、endpoints=(/clusters 中剩余的 pool 端点数)三行。
  7. 在 /root/envd-xds/07-stats.txt 中写入 cds_success=、cds_rejected=、lds_success=、cds_reload= 四行——分别是 cluster_manager.cds.update_success、cluster_manager.cds.update_rejected、listener_manager.lds.update_success、cluster_manager.cds.config_reload 统计的值。
  8. 在 /root/envd-xds/08-report.md 中写入 static_needs_restart=(第 1 步的结果是“没有变化”则为 yes)、reload_count=(第 7 步的 cds_reload)、rejected_kept_old=(第 6 步中被拒绝之后流量仍然流动则为 yes)、endpoints_after=(第 3 步之后剩余的端点数)四行,并在下面至少写四行学到的内容。

参考

静态配置即使修改文件也不会改变

以 ok 启动两个上游(8096、8097)。在 /root/envd-xds/static.yaml 中放置 / 返回 static=v1 的静态配置并启动(管理端口 9977,监听器 127.0.0.1:10077),然后保持运行,把该文件中的 v1 改为 v2。分别在修改前和修改后发起请求,在 /root/envd-xds/01-static.txt 中写入 before=、after_edit= 两行(不要重新启动)。

static_resources 在进程启动时只会被读取一次。所以即使修改了文件,在重新启动之前也什么都不会发生。这就是“要修改代理配置就必须重启”这句话的真相,也是服务网格想要解决的问题。修改时用 sed -i 比较方便。注意不要重新启动——这一步的要点是展示“没有变化”。

改为从外部获取配置

创建 /root/envd-xds/dyn.yaml——不设 static_resources,只放置 node(id 为 envd-xds-1,cluster 为 envd-xds)和 dynamic_resources,lds_config 订阅 /root/envd-xds/xds/lds.yaml,cds_config 订阅 /root/envd-xds/xds/cds.yaml,两者都通过 path_config_source 订阅。同时创建这两个资源文件——监听器在 /marker 上返回 lds=v1,其余请求发往集群 pool,集群 pool 的端点为 8096、8097 两个。启动后请求 /marker 和 /,在 /root/envd-xds/02-subscribe.txt 中写入 marker=、upstream_ok=(/ 请求返回 200 则为 yes)两行。

bootstrap 中完全可以没有 static_resources。这时代理一启动就先去订阅,资源送达之后才真正开始监听。资源文件的形态是在 resources: 下列出声明了 "@type" 的列表——监听器是 Listener,集群是 Cluster 类型。真正的控制平面会用 gRPC 发送这些内容,而发送内容的形态无论是文件还是 gRPC 都是相同的。

修改文件后,不重新启动也能生效

在 /root/envd-xds/xds/cds.yaml 中去掉端点 8097(只保留一个端点)。保持代理不动,等到生效后请求八次,在 /root/envd-xds/03-reload.txt 中写入 p8096=、p8097=、total= 三行。

文件订阅方式会察觉文件发生了变化并重新读取。不是立刻生效,可能需要几秒钟,所以请使用循环一直等到出现想要的结果,而不是固定的 sleep。服务网格中每当 Pod 启动或消失,发生的就是这件事——不重启代理,只替换端点列表。是否生效也可以通过 /clusters 查看。注意:在原位覆盖文件不会产生更新。请写成新文件后用 mv 替换。

动态获取的配置位于转储的另一个位置

从 /config_dump 中只提取集群一节,保存到 /root/envd-xds/04-dump.json,并在 /root/envd-xds/04-dump.txt 中写入 static_clusters=(静态集群数)、dynamic_clusters=(动态集群数)、dynamic_name=(动态集群的名称)三行。

/config_dump 的集群一节中,static_clusters 和 dynamic_active_clusters 是分开的,因为需要区分 bootstrap 中写的内容和从外部获取的内容。在生产环境中确认“我发送的配置有没有送达”时,看的就是动态这一侧。静态一侧为空而动态一侧有内容,就说明订阅正常运行。也可以用 ?resource= 选择要获取的一节,但在这一步中,获取全部再用 jq 同时统计这两处会更容易。

监听器也无中断地替换

把 /root/envd-xds/xds/lds.yaml 中 /marker 的响应从 lds=v1 改为 lds=v2。保持代理不动,等到生效后,在 /root/envd-xds/05-lds.txt 中写入 marker_after=(/marker 的响应)和 upstream_still_ok=(/ 请求仍然返回 200 则为 yes)两行。

替换监听器比替换集群更重——因为这涉及重新打开套接字。Envoy 会先准备好新的监听器,再一边排空旧监听器一边替换,所以这期间请求也不会中断。这就是为什么不必为了修改一条路由规则而进行部署。集群一侧的配置没有动过,所以通往上游的路径应该保持不变——也请一并确认这一点。

错误的更新会被拒绝,旧配置保留

把 /root/envd-xds/xds/cds.yaml 中集群的 name 行删掉,故意发送一次错误的更新(schema 要求名称为必填)。稍等片刻后,在 /root/envd-xds/06-rejected.txt 中写入 still_serving=(/ 请求仍然返回 200 则为 yes)、update_rejected=(cluster_manager.cds.update_rejected 统计的值)、endpoints=(/clusters 中剩余的 pool 端点数)三行。

这是 xDS 中最重要的性质。更新一旦有误,代理就会拒绝它,并继续使用最后一次成功的配置。即使控制平面发来了损坏的配置,流量也会继续流动。所以“控制平面挂了,网格就挂了”这种说法并不准确——只是收不到新配置,而当前的配置仍然在正常运行。作为代价,没有人会告诉你,所以必须监控拒绝统计和版本。统计名称中包含 update_rejected。有一点需要注意——并不是所有错误都会被拒绝。像未知的枚举值这样,默认校验器只给出警告就放过的情况也存在。只有像缺少必填字段这样 schema 无法接受的情况,才会被拒绝。

用数字查看更新是否送达

在 /root/envd-xds/07-stats.txt 中写入 cds_success=、cds_rejected=、lds_success=、cds_reload= 四行——分别是 cluster_manager.cds.update_success、cluster_manager.cds.update_rejected、listener_manager.lds.update_success、cluster_manager.cds.config_reload 统计的值。

在有控制平面的环境中,最先看的数字就是这些。update_success 停滞,说明配置没有送达;update_rejected 上升,说明配置送是送来了,但代理无法接受。这是完全不同的两个问题,需要修复的地方也不同——前者是连接或订阅问题,后者是发送方所生成的配置有问题。本实验中,前面的步骤已经故意制造了一次拒绝,所以能看到那个数字。

留下动态配置运维备忘

在 /root/envd-xds/08-report.md 中写入 static_needs_restart=(第 1 步的结果是“没有变化”则为 yes)、reload_count=(第 7 步的 cds_reload)、rejected_kept_old=(第 6 步中被拒绝之后流量仍然流动则为 yes)、endpoints_after=(第 3 步之后剩余的端点数)四行,并在下面至少写四行学到的内容。

请在说明行中用自己的话写下“控制平面挂了,什么会停止,什么会继续”。在网格故障处理中,这一句话用得最多。值请取自前面步骤的文件。