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

Envoy 内部结构

部署前先筛查,再启动第二个代理

在 TT Lab 中继续学习

目标

亲手编写 bootstrap,在部署前只检查配置,并在一台机器上启动第二个 Envoy。

为什么重要

配置有误的代理,会在重启的那一刻暴露问题。那时旧进程已经被停掉,没有时间回退。--mode validate 不占用端口,只读取配置,把这类事故提前到部署之前。如果能指出 bootstrap 的层次结构,别人写的配置也能在几秒内读懂;而 --base-id 和管理端口的风险,如果不亲身经历一次,就一定会在生产环境中第一次遇到。

步骤

  1. 在 /root/envd-boot/boot.yaml 中编写 bootstrap——管理端口 9911,监听器名称 edge 监听 127.0.0.1:10011,stat_prefix 为 edge,集群名称 origin 指向 127.0.0.1:8081。运行 envoy --mode validate -c /root/envd-boot/boot.yaml,把输出和退出码保存到 /root/envd-boot/01-validate.txt。最后一行必须是 rc=0。
  2. 把 /root/envd-boot/boot.yaml 复制为 /root/envd-boot/boot-typo.yaml,然后把 HttpConnectionManager 删掉一个字母,改成 HttpConnectionManger。用 envoy --mode validate 检查,并把输出和退出码保存到 /root/envd-boot/02-typo.txt(最后一行为 rc=1)。
  3. 在 /root/envd-boot/boot.yaml 中添加 node——id 为 edge-1,cluster 为 envd-edge。在 8081 上启动上游并启动 Envoy 后,从管理端口的 /config_dump 中只提取 bootstrap 一节里的 node,保存到 /root/envd-boot/03-node.json。
  4. 用 yq 读取 /root/envd-boot/boot.yaml,把五个值以 열쇠=값(占位符依次为键与值)格式写成五行,保存到 /root/envd-boot/04-layers.txt——listener=(监听器名称)、filter=(网络过滤器名称)、stat_prefix=、route_cluster=(路由指向的集群)、cluster_endpoint=(该集群的端点,格式为 주소:포트(占位符依次为地址与端口))。
  5. 创建只修改了端口的 /root/envd-boot/boot.yaml 副本 /root/envd-boot/boot-second.yaml(管理端口 9912,监听器 10012)。先不带选项启动并确认失败,然后加上 --base-id 7 使其成功。在 /root/envd-boot/05-baseid.txt 中写入 without_base_id_rc=(非 0 的值)、包含失败原因的一行,以及 with_base_id_ready=(第二个 Envoy 的 /ready 响应)。
  6. 用 --concurrency 1 --log-level info 重新启动 Envoy,从管理端口的 /server_info 中只提取 command_line_options,保存到 /root/envd-boot/06-cli.json。concurrency 必须为 1,log_level 必须为 info。
  7. 向管理端口 POST /quitquitquit 来结束 Envoy,并在 /root/envd-boot/07-quit.txt 中写入 after_quit=(此后 /ready 的 HTTP 状态码)。然后重新启动,追加 after_restart=(/ready 的响应字符串)。最后 Envoy 必须在 9911 上保持运行。
  8. 在 /root/envd-boot/08-report.md 中写入 validate_rc=、typo_rc=、second_envoy=、admin_bind= 四行(依次为第 1 步和第 2 步的退出码、第二个 Envoy 所需的选项名称、管理端口应绑定的地址),并在下面用至少四行写下学到的内容。

参考

部署之前先只检查配置

在 /root/envd-boot/boot.yaml 中编写 bootstrap——管理端口 9911,监听器名称 edge 监听 127.0.0.1:10011,stat_prefix 为 edge,集群名称 origin 指向 127.0.0.1:8081。运行 envoy --mode validate -c /root/envd-boot/boot.yaml,把输出和退出码保存到 /root/envd-boot/01-validate.txt。最后一行必须是 rc=0。

--mode validate 会读取配置并检查到 schema,并且不占用任何端口就结束。因此在已经运行着 Envoy 的机器上、在 CI 中都可以运行。之所以要单独记录退出码,是因为只看输出,人会产生误判——经过管道后,$? 会变成最后一个命令的退出码,所以请不要使用管道,直接接收并记录。

写错一个字母,进程就根本启动不了

把 /root/envd-boot/boot.yaml 复制为 /root/envd-boot/boot-typo.yaml,然后把 HttpConnectionManager 删掉一个字母,改成 HttpConnectionManger。用 envoy --mode validate 检查,并把输出和退出码保存到 /root/envd-boot/02-typo.txt(最后一行为 rc=1)。

@type 不是注释,而是用来选择按哪个 Protocol Buffers 消息来解析的键。名称不在列表中时,Envoy 没有办法解析该过滤器,会把整份配置一并拒绝。在生产环境中,这种拼写错误会在重启的那一刻暴露,而那时旧进程已经被停掉了。

在配置中写明这个代理是谁

在 /root/envd-boot/boot.yaml 中添加 node——id 为 edge-1,cluster 为 envd-edge。在 8081 上启动上游并启动 Envoy 后,从管理端口的 /config_dump 中只提取 bootstrap 一节里的 node,保存到 /root/envd-boot/03-node.json。

node 是这个代理向控制平面介绍自己时使用的标识。只使用静态配置时,没有它也能启动;但一旦接入 xDS,服务器就会根据这个值来决定把什么配置发给谁。/config_dump 的第一节是 BootstrapConfigDump,其中有 bootstrap.node。请用 jq 只提取这一处。

按路径逐一指出五个层次

用 yq 读取 /root/envd-boot/boot.yaml,把五个值以 열쇠=값(占位符依次为键与值)格式写成五行,保存到 /root/envd-boot/04-layers.txt——listener=(监听器名称)、filter=(网络过滤器名称)、stat_prefix=、route_cluster=(路由指向的集群)、cluster_endpoint=(该集群的端点,格式为 주소:포트(占位符依次为地址与端口))。

阅读 Envoy 配置的顺序始终相同——listener → filter_chain → http_connection_manager → route_config → cluster。如果能逐一指出这五个位置,即使第一次见到的配置也不会迷路。请像 yq '.static_resources.listeners[0].name' 这样一层一层往下取。不要用眼睛抄值,用工具提取才不会出错。

第二个 Envoy 启动不了

创建只修改了端口的 /root/envd-boot/boot.yaml 副本 /root/envd-boot/boot-second.yaml(管理端口 9912,监听器 10012)。先不带选项启动并确认失败,然后加上 --base-id 7 使其成功。在 /root/envd-boot/05-baseid.txt 中写入 without_base_id_rc=(非 0 的值)、包含失败原因的一行,以及 with_base_id_ready=(第二个 Envoy 的 /ready 响应)。

Envoy 启动时会申请一块共享内存区域(热重启时用来传递统计数据的位置)。该区域的名称由 base id 决定,默认值为 0,所以同一台机器上的第二个进程,即使端口都已经错开,也会在这个位置发生冲突。失败日志的第一行就直接说明了这一点。失败的一方会立刻退出,因此不需要 setsid,直接运行并接收退出码即可。

确认命令行选项保存在哪里

用 --concurrency 1 --log-level info 重新启动 Envoy,从管理端口的 /server_info 中只提取 command_line_options,保存到 /root/envd-boot/06-cli.json。concurrency 必须为 1,log_level 必须为 info。

配置文件中没有、却会改变行为的地方,就是命令行。--concurrency 是工作线程数,默认值是 CPU 核心数,所以每个工作线程的负载均衡状态各自独立——这是统计分配情况的实验结果不稳定的最常见原因。实际应用了什么,不要靠猜测,要从 /server_info 读取。用 jq '.command_line_options' 只提取这一处。

管理端口连让进程停止的按钮都有

向管理端口 POST /quitquitquit 来结束 Envoy,并在 /root/envd-boot/07-quit.txt 中写入 after_quit=(此后 /ready 的 HTTP 状态码)。然后重新启动,追加 after_restart=(/ready 的响应字符串)。最后 Envoy 必须在 9911 上保持运行。

管理端口里不只有统计数据——/quitquitquit 会结束进程,/drain_listeners 会切断流量。由于没有认证,任何能访问到这个端口的人都可以让代理下线。所以不能把 admin.address 设为 0.0.0.0。对已经停止的服务器执行 curl,连接本身就无法建立,所以 HTTP 状态码会显示为 000。

整理成部署前检查清单

在 /root/envd-boot/08-report.md 中写入 validate_rc=、typo_rc=、second_envoy=、admin_bind= 四行(依次为第 1 步和第 2 步的退出码、第二个 Envoy 所需的选项名称、管理端口应绑定的地址),并在下面用至少四行写下学到的内容。

检查清单是给别人读的文字。写值的时候,要让人能看出这个值来自哪里;说明行中不要写“做了什么”,而要写“所以今后要做什么”。second_envoy= 中直接写选项名称。