Envoy Runtime 分层运行时配置:虚拟文件系统、动态更新与废弃特性治理
Envoy Runtime 分层运行时配置虚拟文件系统、动态更新与废弃特性治理【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoyEnvoy 的 Runtime运行时配置又称 feature flag是一套可在不重启进程的情况下修改服务器行为的机制其核心是一个由静态层、磁盘层、RTDS 层和管理层叠加而成的虚拟文件系统。本文基于 Runtime 配置文档 并结合 运行时实现源码、Admin 端处理器 与 Bootstrap Proto 定义 展开帮助读者掌握 Runtime 分层模型的配置方式、快照原子性机制、废弃特性deprecated feature的运行时治理手段以及可用于生产排障的runtime.*统计指标体系。Runtime 虚拟文件系统总览Envoy 的 Runtime 配置指定了一个虚拟文件系统树其中存放着可重载re-loadable的配置元素。这个虚拟文件系统可以看作由多个 overlay 层次叠加而成来源包括本地磁盘文件系统、静态 Bootstrap 配置、RTDSRuntime Discovery Service以及 Admin 控制台。从架构视角看Runtime 配置既可以修改各种服务器设置也是 Envoy 控制新行为/高风险变更回滚开关的主要手段——详见 Runtime 架构文档。对应 v3 API 中分层结构由envoy.config.bootstrap.v3.LayeredRuntime消息描述该消息定义于 bootstrap.proto// Runtime :ref:layers of the runtime. This is ordered // such that later layers in the list overlay earlier entries. repeated RuntimeLayer layers 1;每个RuntimeLayer通过 oneoflayer_specifier指定四种层类型之一proto 定义static_layergoogle.protobuf.Struct类型的静态键值对disk_layer从符号链接根目录下的本地磁盘目录加载admin_layer由 Admin 接口动态写入的覆盖层rtds_layer通过标准 xDS 端点订阅下发的层。在源码中这些层分别由StaticLayer→ProtoLayer、DiskLayer、AdminLayer、RtdsLayer→RtdsSubscription等类实现统一收敛到 LoaderImpl并通过 ThreadLocal 槽位把最新快照分发到所有工作线程。分层模型LayeringRuntime 可视为一个由多层组成的虚拟文件系统具体层叠顺序由 Bootstrap 中的layered_runtime配置指定。靠后的层会覆盖靠前的层中的同名键。文档给出的典型配置如下layers: - name: static_layer_0 static_layer: health_check: min_interval: 5 - name: disk_layer_0 disk_layer: { symlink_root: /srv/runtime/current, subdirectory: envoy } - name: disk_layer_1 disk_layer: { symlink_root: /srv/runtime/current, subdirectory: envoy_override, append_service_cluster: true } - name: admin_layer_0 admin_layer: {}这段配置体现了四层结构静态基础层 → 全局磁盘层 → 带 service cluster 特化的磁盘覆盖层 → Admin 覆盖层。需要说明的是在已弃用的Runtime旧版单层Bootstrap 配置中分层是隐式且固定的顺序为静态 Bootstrap 配置本地磁盘文件系统本地磁盘override_subdirectory覆盖子目录Admin 控制台覆盖。高层值同样覆盖低层值。分层合并的源码实现从源码结构看快照创建时各层的合并逻辑非常直观。SnapshotImpl 构造函数 按顺序遍历layers_将每一层的所有键值对写入统一的EntryMap后写入者覆盖先写入者SnapshotImpl::SnapshotImpl(Random::RandomGenerator generator, RuntimeStats stats, std::vectorOverrideLayerConstPtr layers) : layers_{std::move(layers)}, generator_{generator}, stats_{stats} { for (const auto layer : layers_) { for (const auto kv : layer-values()) { values_.erase(kv.first); values_.emplace(kv.first, kv.second); } } stats.num_keys_.set(values_.size()); }层名的唯一性在初始化阶段校验LoaderImpl::initLayers 中若出现重名层会直接返回Duplicate layer name错误同时最多只允许一个 Admin 层Too many admin layers specified in LayeredRuntime, at most one may be specified。文件系统布局规则配置指南的各章节描述了可用的 runtime 键例如上游集群的 runtime 设置。虚拟文件系统的组织遵循以下规则键中每个.表示层级中的新目录路径的末段即文件名文件内容就是该 runtime 值从文件读取数值时空格和换行会被忽略numerator和denominator是保留关键字不能出现在任何目录名中它们用于表示 FractionalPercent 的规范 JSON 编码。在磁盘层实现中DiskLayer::walkDirectory 负责递归遍历目录并生成键以prefix . entry.name_的方式拼接目录前缀最终得到如health_check.min_interval这样的键。这里还有两个值得注意的实现细节递归深度上限为16MaxWalkDepth 16超出会返回Walk recursion depth exceeded 16错误文件读取时会剔除以#开头的注释行见下文Comments一节空值文件仅含注释可作为占位符存在。/srv/runtime/current/envoy/health_check/min_interval ← 磁盘文件 └────────────┬────────────┘ └──┬──┘ 目录路径点分键前缀 末段为文件名静态 Bootstrap 层静态基础 Runtime 可以通过 Bootstrap 的Runtime.base字段指定其内容是一个protobuf JSON 表示google.protobuf.Struct。在分层模型中对应static_layer它在快照构建时被包装为 ProtoLayerwalkProtoValue会递归展开 Struct 字段把点分隔符映射为树边若字段本身包含numerator/denominator则按 FractionalPercent 处理而不是继续展开。本地磁盘层当虚拟文件系统落地到本地磁盘时其根为symlink_root subdirectory。例如键health_check.min_interval对应的完整文件路径为经由符号链接/srv/runtime/current/envoy/health_check/min_intervalDiskLayer的三个参数在 bootstrap.proto 中的语义字段说明symlink_root指向运行时树符号链接的路径实现假设运行时树通过符号链接访问靠原子换链完成切换Envoy 会监视该位置的变化并在变化时重新加载subdirectory根目录下实际加载的子目录便于多系统共用同一交付机制append_service_cluster是否在路径中追加 service cluster见下覆盖目录Overrides在layered_runtime中可以叠加任意数量的磁盘层。而在已弃用的旧版Runtime配置中存在一个专门的覆盖目录假设/srv/runtime/v1存放全局 runtime 配置的目录则典型配置为symlink_root:/srv/runtime/current符号链接指向/srv/runtime/v1subdirectory:envoyoverride_subdirectory:envoy_override按集群特化的子目录Cluster-specific subdirectories已弃用旧配置中override_subdirectory配合--service-clusterCLI 参数使用。假设--service-cluster设置为my-clusterEnvoy 会先在以下路径查找health_check.min_interval/srv/runtime/current/envoy_override/my-cluster/health_check/min_interval若找到其值将覆盖主查找路径中的值。这使得用户可以在全局默认值之上为单个集群定制 runtime 值。在分层模型中这一能力被推广为任意磁盘层上的append_service_cluster选项。源码中对应 createNewSnapshot 的路径拼接std::string path layer.disk_layer().symlink_root() / layer.disk_layer().subdirectory(); if (layer.disk_layer().append_service_cluster()) { absl::StrAppend(path, /, service_cluster_); }注意service_cluster_取自本地信息local_info.clusterName()即--service-cluster参数的值。通过符号链接交换更新运行时值更新任意 runtime 值分两步创建整个 runtime 树的硬拷贝并更新目标值用等效于以下命令的方式将符号链接根原子地从旧树换到新树/srv/runtime:~$ ln -s /srv/runtime/v2 new mv -Tf new current至于文件系统数据如何部署、回收等超出了本文档的范围。在监听机制上initLayers 会为每个磁盘层的symlink_root注册文件系统监视器Filesystem::Watcher事件类型为MovedTo一旦检测到文件移动或换链即触发loadNewSnapshot()重新构建快照。RTDS 层Runtime Discovery Service通过指定rtds_layer一个或多个 runtime 层可以指向常规 xDS 端点进行订阅与下发每个层订阅单个xDS 资源资源类型为envoy.service.runtime.v3.Runtime消息。RtdsLayer消息包含两个字段proto 定义name要订阅的资源名与rtds_config配置来源。源码侧的 RtdsSubscription 展示了其严格性订阅建立后要求每次更新恰好包含 1 个资源validateUpdateSize中added_resources_num removed_resources_num ! 1即报错下发资源的name必须与订阅时声明的层名一致否则返回Unexpected RTDS runtime (expecting ...)更新成功时把资源中的layerStruct拷贝进proto_随后调用loadNewSnapshot()重建快照资源被移除时则清空该层并重新加载配置更新失败时允许服务器启动继续init_target_.ready()但会记录日志。RTDS 层在快照中同样以ProtoLayer形式挂载createNewSnapshot并参与 InitManager 的RTDS初始化流程全部订阅就绪前不会标记 RTDS 初始化完成。Admin 控制台层运行时值可以通过 Admin 接口查看与修改查看/runtime端点修改/新增/runtime_modify端点用法为/runtime_modify?key1value1key2value2keyNvalueN或发送表单值空值表示删除先前添加的覆盖。如果根本没有配置 runtime则使用空的 provider效果是全部采用代码内建默认值仅/runtime_modify添加的值除外。注意谨慎使用/runtime_modify端点。修改几乎即时生效因此必须确保 Admin 接口得到妥善保护。关于 Admin 层还有一条硬性约束最多只能指定一个 Admin 层。如果指定了非空的layered_runtime但缺少 Admin 层任何对 Admin 控制台的修改操作都会得到503 响应。这一约束在源码中有完整链条Admin 层处理器 解析查询参数为键值对后调用server_.runtime().mergeValues(overrides)失败时以 503 返回异常信息LoaderImpl::mergeValues 在admin_layer_ nullptr时直接返回No admin layer specified错误AdminLayer::mergeValues 执行实际合并先erase再插入空字符串即删除键随后设置admin_overrides_active统计量有覆盖值为 1否则 0。该功能要求构建时启用 YAML 支持ENVOY_ENABLE_YAML否则返回Runtime admin reload requires YAML support。/runtime端点的输出结构handlerRuntime为 JSON包含layers各层名称列表与entries每个键在各层的值及最终生效值final_value。这为排查某个 runtime 键到底被哪一层覆盖提供了直接依据。原子性Atomicity与快照机制快照会在以下情形重建检测到 symlink 根目录下的文件移动操作或 symlink 根本身发生变化Admin 控制台覆盖被添加或修改。快照构建时所有 runtime 层都会被评估出错层被忽略并从生效层中剔除参见num_layers统计。由于遍历 symlink 根目录需要非零时间若需要严格的原子性应当让 runtime 目录保持不可变并通过符号链接变化来编排更新。此外symlink 根完全相同的磁盘层在检测到文件移动时只触发一次刷新而 symlink 根路径重叠但不相同的磁盘层可能触发多次重载。从源码看出错层被忽略体现在 createNewSnapshot磁盘层创建失败时仅累加error_layers计数并输出 debug 日志error loading runtime values for layer ... from disk该层不进入生效层向量。相应地load_success/load_error分别在所有层均无错误/存在错误时递增num_layers被设置为实际生效的层数。快照的线程安全模型同样值得注意LoaderImpl 的注释说明单个快照以shared_ptr形式通过 ThreadLocal 槽位在所有线程间共享主线程可以在工作线程仍在使用旧版本时切换进新的 runtime 快照从而保证读取方看到的一致性视图。Protobuf 与 JSON 表示运行时文件系统可以表示在 proto3 消息中的google.protobuf.Struct模拟 JSON 对象规则如下点分隔符映射为树边标量叶子整数、字符串、布尔、浮点用各自的 JSON 类型表示FractionalPercent通过其规范 JSON 编码表示。例如health_check.min_interval键的 YAML 表示health_check: min_interval: 5说明从浮点数解析的整数值会向下取整到最近的整数。源码中这一行为由 setNumberValue 实现同一数值会同时写入double_value_与uint_value_对3.1这类值转 uint 时即被截断为 3且当数值为整值时还会派生出bool_value_非零为true。注释Comments以#作为行首字符的行被视为注释。注释可用于为已有值提供上下文也常用于在空文件中作为占位符保留以便需要时随时部署。源码 DiskLayer::walkDirectory 中可见实现读取文件后逐行过滤#开头的行其余行含末尾换行去除处理拼接为原始值再通过ValueUtil::loadFromYaml解析——这也解释了为什么 runtime 文件内容可以写 JSON/YAML 标量。用 Runtime 覆盖治理废弃特性Envoy runtime 同时也是Envoy 特性弃用feature deprecation流程的一部分。按照 CONTRIBUTING.md 的破坏性变更策略特性弃用分三个阶段warn-by-default默认告警、fail-by-default默认失败、code removal代码移除。第一阶段Envoy 在 warning 日志中记录该特性已弃用并递增deprecated_feature_useruntime 统计量。建议用户查阅 DEPRECATED.md 了解如何迁移到新代码路径。第二阶段字段被标记为disallowed_by_default默认拒绝使用该字段的配置。可以通过运行时配置覆盖这一模式将envoy.deprecated_features:full_fieldname或envoy.deprecated_features:full_enum_value设置为true。例如对废弃字段Foo.Bar.Eep设置envoy.deprecated_features:Foo.bar.Eep为true。仓库中就有一个使用静态 runtime 允许 fail-by-default 字段的真实示例configs/using_deprecated_config.yaml其末尾为layered_runtime: layers: - name: static_layer static_layer: envoy.deprecated_features:envoy.config.trace.v2.ZipkinConfig.HTTP_JSON_V1: true envoy.deprecated_features:envoy.api.v2.route.CorsPolicy.allow_origin: true此外也可以将envoy.features.enable_all_deprecated_features设为true允许使用所有废弃字段。强烈不鼓励使用这些覆盖项请谨慎使用并尽快切换到新字段——fail-by-default 意味着旧代码路径的移除已迫在眉睫。对新代码路径的 bug 或功能缺漏尽早暴露远好于代码移除之后再发现。还有一个实用的前置自检手段启用运行时键envoy.features.fail_on_any_deprecated_feature用户可以在通常的 warn-by-default 阶段就触发配置加载失败从而在 Envoy 弃用时间表到来之前验证自己正在使用哪些字段。这三个键的判定逻辑可以在 SnapshotImpl::deprecatedFeatureEnabled 中找到当且仅当满足以下之一时特性被视为允许——(1) 不存在键key且默认值为true(2) 键key存在且值为true(3) 存在值为true的envoy.features.enable_all_deprecated_features且键key不存在false值。允许后每次使用都会递增deprecated_feature_use与deprecated_feature_seen_since_process_start两个统计量。重要注意1.14.1 之前的 Envoy 版本不能把整数解析为 runtime 布尔值必须显式写true或false。误将0当作false会导致回落到代码内默认值。这一点在利用 runtime 覆盖废弃特性时尤其危险可能引发意外行为。统计指标文件系统 runtime provider 在runtime.*命名空间下输出以下统计源码定义见 ALL_RUNTIME_STATS前缀在 generateStats 中拼接为runtime.名称类型说明admin_overrides_activeGauge若有任何 Admin 覆盖处于激活状态则为 1否则为 0deprecated_feature_useCounter使用废弃特性的总次数。详细使用信息以 Using deprecated option X from file Y 形式写入 warning 日志deprecated_feature_seen_since_process_startGauge使用废弃特性的次数。该值在 hot restart 期间不会结转load_errorCounter任一层加载出错的尝试总次数load_successCounter所有层加载均成功的尝试总次数num_keysGauge当前已加载的键数量num_layersGauge当前处于激活状态无加载错误的层数override_dir_existsCounter使用了覆盖目录的加载总次数override_dir_not_existsCounter未使用覆盖目录的加载总次数生产排障时可以这样利用这些指标load_error 0说明某层加载失败且被静默剔除应结合 debug 日志error loading runtime values for layer ...与num_layers确认实际生效层数admin_overrides_active 1提示存在 Admin 手工覆盖重启或重新部署后这些覆盖会丢失deprecated_feature_use持续增长则意味着配置仍在依赖即将移除的旧字段。小结Envoy Runtime 以虚拟文件系统 多层覆盖的模型把静态配置、磁盘交付、xDS 动态下发与 Admin 应急覆盖统一在同一套键语义下靠后的层覆盖靠前的层快照原子切换保证读取一致性符号链接换链提供可靠的更新编排。理解了 LayeredRuntime proto、分层加载与快照实现 以及 runtime.* 指标 之后无论是为灰度回滚配置动态开关、为不同 service cluster 定制参数还是治理即将移除的废弃字段都可以在这套机制上以可观测、可验证的方式落地。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →