部署前先筛查,再启动第二个代理
目标
亲手编写 bootstrap,在部署前只检查配置,并在一台机器上启动第二个 Envoy。
为什么重要
配置有误的代理,会在重启的那一刻暴露问题。那时旧进程已经被停掉,没有时间回退。--mode validate 不占用端口,只读取配置,把这类事故提前到部署之前。如果能指出 bootstrap 的层次结构,别人写的配置也能在几秒内读懂;而 --base-id 和管理端口的风险,如果不亲身经历一次,就一定会在生产环境中第一次遇到。
步骤
- 在
/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。 - 把
/root/envd-boot/boot.yaml复制为/root/envd-boot/boot-typo.yaml,然后把HttpConnectionManager删掉一个字母,改成HttpConnectionManger。用envoy --mode validate检查,并把输出和退出码保存到/root/envd-boot/02-typo.txt(最后一行为rc=1)。 - 在
/root/envd-boot/boot.yaml中添加node——id为edge-1,cluster为envd-edge。在8081上启动上游并启动 Envoy 后,从管理端口的/config_dump中只提取 bootstrap 一节里的node,保存到/root/envd-boot/03-node.json。 - 用
yq读取/root/envd-boot/boot.yaml,把五个值以열쇠=값(占位符依次为键与值)格式写成五行,保存到/root/envd-boot/04-layers.txt——listener=(监听器名称)、filter=(网络过滤器名称)、stat_prefix=、route_cluster=(路由指向的集群)、cluster_endpoint=(该集群的端点,格式为주소:포트(占位符依次为地址与端口))。 - 创建只修改了端口的
/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响应)。 - 用
--concurrency 1 --log-level info重新启动 Envoy,从管理端口的/server_info中只提取command_line_options,保存到/root/envd-boot/06-cli.json。concurrency必须为 1,log_level必须为info。 - 向管理端口 POST
/quitquitquit来结束 Envoy,并在/root/envd-boot/07-quit.txt中写入after_quit=(此后/ready的 HTTP 状态码)。然后重新启动,追加after_restart=(/ready的响应字符串)。最后 Envoy 必须在9911上保持运行。 - 在
/root/envd-boot/08-report.md中写入validate_rc=、typo_rc=、second_envoy=、admin_bind=四行(依次为第 1 步和第 2 步的退出码、第二个 Envoy 所需的选项名称、管理端口应绑定的地址),并在下面用至少四行写下学到的内容。
参考
- 启动 Envoy 时,请用
setsid --fork nohup envoy -c <파일> --log-level warn > <로그> 2>&1 </dev/null(占位符依次为配置文件与日志文件)使它与 shell 完全脱离。如果只用&启动,到下一步时它已经退出了。 - 重新启动前,请用
pkill -x envoy清理。pkill -f 'envoy -c'会连包含该字符串的 shell 自身一起杀掉。 - 等待启动时,不要使用固定的
sleep,而要使用循环,直到/ready返回 LIVE 为止。 - 镜像中已经有用于模拟上游的服务器:
python3 /opt/lab/envoy/upstream.py <포트> ok(占位符为端口)。最小配置示例位于/opt/lab/envoy/minimal.yaml。 - 常见错误——像
envoy --mode validate ... | tee这样接上管道,$?就不再是 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= 中直接写选项名称。