fhevm Listener Helm Chart 深度实战:多链区块链监听器与 eRPC 代理的 Kubernetes 部署指南
【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm
导读
本文围绕 fhevm 仓库中的 Listener Helm Chart(charts/listener/)展开,系统讲解如何在 Kubernetes 上为每条区块链部署一个独立的 fhevm 区块链监听器(listener)实例,并通过共享的 PostgreSQL、Redis/RabbitMQ 基础设施与可选的 eRPC 代理组成一套完整的多链索引服务。读完本文,你将掌握:Listener Chart 的"三层深合并"配置体系(symlink +mergeOverwrite)、新增链的完整操作步骤、eRPC 代理的三种配置方式(profile 基底、局部覆盖、整体替换),以及 Secrets 的 External Secrets Operator 与 fallback 双模式接入方法,能够直接在生产或测试环境复制落地。
说明:本文所述配置文件、模板与默认值均以当前仓库实际内容为准。文中所有仓库相对路径均可直接点击跳转到源码。
一、架构总览:一链一实例,共享存储与消息队列
Listener Chart 的核心设计是每条链一个独立的 listener 实例,同时共享同一套后端基础设施。其部署拓扑如下:
+------------------+ | eRPC proxy | | (optional) | +--------+---------+ | +-------------------+-------------------+ | | +--------v---------+ +----------------v--------+ | listener:ethereum | | listener:base-sepolia | +--------+---------+ +----------------+--------+ | | +-------------------+-------------------+ | +--------------+--------------+ | | +--------v--------+ +--------v----------+ | PostgreSQL | | Redis / RabbitMQ | +-----------------+ +-------------------+从 Helm 模板的实现看,这个架构图与代码完全对应:
values.yaml中的每个listeners[]条目会在 deployment.yaml 中各生成一个 Deployment(replicas: 1),并在 configmap.yaml 中生成对应的 ConfigMap;- 所有 listener 实例共享同一套 PostgreSQL 数据库与 Redis/RabbitMQ 消息代理(broker);
- eRPC 作为可选的 RPC 负载均衡/故障转移/缓存代理,位于所有 listener 之前,为其提供统一的上游 RPC 入口(如
http://listener-erpc:4000/listener-indexer/evm/1)。
该 Chart 的版本信息见 Chart.yaml:version: 0.2.0,appVersion: "0.2.0",type: application。
二、前置条件与快速开始
2.1 环境要求
- Helm 3.x
- Kubernetes 1.24+
2.2 三步部署
helm dependency update charts/listener helm install listener charts/listener -n listener --create-namespace第一步会拉取 Chart.yaml 中声明的子图表依赖(PostgreSQL、Redis、RabbitMQ 等 Bitnami 子图表);第二步在新建的listener命名空间中完成部署。由于values.yaml中listeners默认为空列表,直接安装只会拉起基础设施与可选组件,实际监听链必须通过--set或自定义 values 文件添加listeners[]条目(详见第四节)。
2.3 命名规则
每个 listener 实例的 Deployment 与 ConfigMap 命名遵循<release>-<chart>-<listener.name>的拼接规则(超长时截断到 63 字符以保证 DNS 兼容),该逻辑定义在 _helpers.tpl 的listener.instanceName模板中。这意味着name字段直接决定了资源名,同一 Chart 内不可重复。
三、核心机制:symlink + 深合并(Config merge strategy)
Listener 与 eRPC 的配置都采用symlink + deep-merge模式,这是理解整个 Chart 的钥匙。
3.1 规范配置文件与符号链接
Listener 的规范配置文件(canonical config)存放在仓库根目录的 listener/config/ 下(例如 listener-default.yaml、erpc-base.yaml、erpc-public.yaml),并被符号链接进charts/listener/configs/:
charts/listener/configs/ listener-default.yaml -> config/listener-default.yaml erpc-base.yaml -> config/erpc-base.yaml erpc-public.yaml -> config/erpc-public.yaml当前仓库的 charts/listener/configs/ 目录下确实存在这三个文件。这个设计的价值在于:只要应用配置发生变化,Helm Chart 一定感知得到(因为打包的是同一份文件内容),从而强制要求 Chart 版本随配置同步递增。
3.2 模板如何加载配置
在 configmap.yaml 中可以看到完整的加载逻辑:
{{- $defaultConfig := .Files.Get "configs/listener-default.yaml" | fromYaml }} {{- $common := .Values.commonConfig | default dict }} ... {{- $perListener := $listener.config | default dict }} {{- $nameOverride := dict "name" $listener.name }} {{- $merged := mergeOverwrite (deepCopy $defaultConfig) $common $perListener $nameOverride }} {{- toYaml $merged | nindent 4 }}核心是 Helm 内置的mergeOverwrite:依次将commonConfig、listeners[].config深合并到listener-default.yaml的默认值之上(后者覆盖前者,即 last wins),最后强制注入name字段,确保 listener 名称永远与实际条目一致、不会漂移。最终的 ConfigMap 数据挂载为/config/config.yaml,Deployment 通过args: ["--config", "/config/config.yaml"]传入 Rust 应用。
3.3 为什么要深合并而不是整体覆盖
从 config.rs 的Settings结构可以看出,Listener 的 Rust 配置是一个多层嵌套结构(database、broker、blockchain、telemetry、log),每个子结构又有大量字段。如果用简单的 YAML 替换,那么任何一层缺失都会导致整份配置失效;深合并允许只写"与默认值不同的键",其余自动继承——这正是 Helm 模板使用mergeOverwrite的根源。
补充:除了 YAML 文件,Rust 应用还支持环境变量覆盖,格式为
APP_SECTION__FIELD(双下划线分隔),例如APP_DATABASE__DB_URL、APP_BROKER__BROKER_URL。见 config.rs 中Settings::new的实现,以及第五节 Secrets 的应用方式。
四、Listener 配置详解:三层深合并
Listener 配置由3 层深合并组成(后层覆盖前层):
| 层级 | 来源 | 作用 |
|---|---|---|
| 1. 基础默认值 | configs/listener-default.yaml | 对应 RustSettings结构体所有字段的默认值 |
| 2. 公共覆盖 | values.yaml→commonConfig | 面向所有 listener 的运维级覆盖(如 broker 类型) |
| 3. 单链覆盖 | values.yaml→listeners[].config | 链级专属值(chain_id、rpc_url 等) |
其中第 1 层的 listener-default.yaml 内容非常完整,是配置的"单一事实来源",关键默认值如下:
name: listener http_port: 8080 database: db_url: "placeholder-overridden-by-env" migration_max_attempts: 5 iam_auth: # IAM 认证,enabled=false 时使用 db_url enabled: false ssl_ca_path: "placeholder-overridden-by-env" pool: max_connections: 12 min_connections: 2 acquire_timeout_secs: 30 idle_timeout_secs: 600 max_lifetime_secs: 1800 broker: broker_type: amqp # 默认 amqp,Chart 的 commonConfig 会覆盖为 redis broker_url: "placeholder-overridden-by-env" ensure_publish: false blockchain: type: evm chain_id: 1 rpc_url: "http://placeholder" network: placeholder finality_depth: 64 finality_tag: true # 使用节点 finalized 区块标签;false 时 final = head - finality_depth finality_active: true strategy: automatic_startup: true block_start_on_first_start: "current" range_size: 100 loop_delay_ms: 1000 max_parallel_requests: 50 block_fetcher: block_receipts batch_receipts_size_range: 10 compute_block: false compute_block_allow_skipping: true max_exponential_backoff_ms: 20000 catchup: prefetch: 5 claim_min_idle_secs: 3600 catchup_max_sub_range: 100 range_prefetch: 1 telemetry: enabled: true metrics_port: 9090 log: format: json show_file_line: false show_thread_ids: true show_timestamp: true show_target: true show_constants: true level: info值得注意的实现细节:block_start_on_first_start支持"current"字符串或数字两种形式,这在 Rust 侧由BlockStartConfig枚举反序列化处理,见 config.rs 中的单元测试test_block_start_config_from_u64与test_block_start_config_from_string_current;而StrategyConfig的默认值(range_size: 100、max_parallel_requests: 50、block_fetcher: BlockReceipts等)同样有test_strategy_config_defaults测试锚定,与 YAML 完全一致。
4.1 添加一条新链
在values.yaml的listeners[]中新增一个条目即可,只需声明与默认值不同的字段:
listeners: - name: polygon config: blockchain: chain_id: 137 rpc_url: http://listener-erpc:4000/listener-indexer/evm/137 network: polygon-mainnet strategy: block_start_on_first_start: 70000000 range_size: 50 env: []数据库、broker、连接池、策略等其余配置全部从基础文件 +commonConfig继承。
4.2 覆盖全局共享配置(commonConfig)
对所有listener 生效的配置放在commonConfig,只需写与config/listener-default.yaml不同的键:
commonConfig: broker: broker_type: redis # 将默认 amqp 覆盖为 redis database: pool: max_connections: 20 # 高吞吐集群下提升连接池values.yaml中默认的commonConfig正是{broker: {broker_type: redis}}——这也是生产环境最常用的"切换 broker 类型"场景。
4.3 单链局部覆盖(listeners[].config)
config块使用与 Rust 配置文件完全相同的嵌套结构,listener-default.yaml 中的任意字段都可按链覆盖。例如为 ethereum 主网开启消息持久化并调整最终性深度与拉取策略:
listeners: - name: ethereum config: broker: ensure_publish: true # 仅此链开启持久化投递 blockchain: chain_id: 1 rpc_url: http://listener-erpc:4000/listener-indexer/evm/1 network: ethereum-mainnet finality_depth: 128 strategy: block_start_on_first_start: 24572795 range_size: 10 max_parallel_requests: 104.4 单链资源与调度覆盖
每个 listener 可以独立覆盖资源配额、安全上下文与调度约束:
listeners: - name: ethereum config: { ... } resources: requests: cpu: "2" memory: 2Gi nodeSelector: dedicated: blockchain tolerations: - key: dedicated value: blockchain effect: NoSchedule模板层面,Deployment 通过default $.Values.xxx $listener.xxx实现"per-listener 优先、否则继承根级"的语义(见 deployment.yaml),支持覆盖的字段包括resources、podSecurityContext、securityContext、nodeSelector、tolerations、affinity以及env。其中环境变量的合并策略为:per-listener env 优先,同名字段覆盖根级 env,由 _helpers.tpl 中的listener.mergedEnv模板实现,且所有 value 均经过tpl渲染(因此可在 value 中引用如{{ .Values.secretName }})。
4.5 新增一个配置字段的标准流程
当 Rust 的Settings结构体新增字段时,Chart 侧需要同步的步骤为:
- 在
config/listener-default.yaml中带上默认值新增该字段(这是config.rs之外的第二份权威声明); - Helm Chart 通过 symlink 自动感知到该字段,无需修改模板;
- 由于结构相同,per-listener 覆盖立即生效;
- 递增
Chart.yaml的version(当前为0.2.0)。
这条流程之所以顺畅,正是因为模板中的 3 层合并对字段的增删是透明的——mergeOverwrite只关心键路径,不关心字段清单。
4.6 部署回滚保障:配置校验和
deployment.yaml 中为每个 listener Pod 注入了checksum/config注解:
checksum/config: {{ printf "%s%s%s" (default文件) (commonConfig) (listener.config) | sha256sum }}只要三层配置中的任何一层发生变化,Pod 模板的注解随之变化,Kubernetes 会自动滚动重建 listener Pod,确保配置变更不会"静默失效"。
五、eRPC 代理:可选的 RPC 网关
eRPC 是一个可选组件,为 listener 提供 RPC 负载均衡、故障转移与缓存能力。Chart 内联部署一个 eRPC Deployment(官方并无独立 Helm Chart),相关模板见 deployment.yaml 后半部分与 configmap.yaml。
5.1 配置 Profile
eRPC 通过erpc.baseConfig字段选择基础配置文件,当前 Chart 内置两个 profile:
| Profile | 文件 | 适用场景 |
|---|---|---|
erpc-base.yaml | configs/erpc-base.yaml | 最小化默认配置,面向通用应用的独立 eRPC(默认值) |
erpc-public.yaml | configs/erpc-public.yaml | 面向 listener 集群调优、使用公共 RPC 节点 |
erpc: enabled: true baseConfig: erpc-base.yaml # 或改用 erpc-public.yaml两个 profile 的定位差异在内容上体现得很明显:
- erpc-base.yaml 只包含 server、metrics、rate limiters 与一个通用
projects[0].id: main骨架,networks: []为空,由使用者自行补充网络与上游; - erpc-public.yaml 则是一份高度调优的"公共节点专供"配置:
logLevel: warn、server.maxTimeout: 20s(需覆盖 ethereum receipts 的 3 上游 × 5s 预算)、全部走 eRPC 公共端点仓库、按方法(eth_blockNumber、eth_getBlockByNumber、eth_getBlockReceipts等)细分 timeout/retry/hedge/circuitBreaker 策略,并为 avalanche/binance/ethereum/polygon/base/sepolia/fuji/bsc-testnet/amoy/base-sepolia 预置了网络定义、评分权重与 selectionPolicy 过滤函数(如 sepolia 与 base-sepolia 用 metrics 实时剔除 errorRate/ throttledRate/blockHeadLag 超限的上游)。
5.2 新增一个 eRPC Profile
# 1. 在仓库根目录 config/ 下创建配置文件 # config/erpc-<profile>.yaml # 2. 建立符号链接(在仓库根目录执行) ln -s ../../../config/erpc-<profile>.yaml charts/listener/configs/erpc-<profile>.yaml # 3. 部署时通过 --set 指定 helm install listener charts/listener --set erpc.baseConfig=erpc-<profile>.yaml5.3 局部覆盖(Partial overrides)
erpc.config会在基础 profile 之上做深合并,不会整体替换基础配置:
erpc: baseConfig: erpc-public.yaml config: logLevel: info server: maxTimeout: 60s对应模板逻辑为mergeOverwrite $base $overrides(见 configmap.yaml)。
5.4 整体替换(Full replacement)
需要完全绕过基础 profile 与erpc.config时,使用--set-file传入完整配置文件:
helm install listener charts/listener \ --set-file erpc.configFile=path/to/custom-erpc.yaml5.5 禁用 eRPC
erpc: enabled: false禁用后,listener 的rpc_url应直接指向你自己的 RPC 服务商端点,例如https://eth-mainnet.example.com,而不再走http://listener-erpc:4000/...。
5.6 eRPC 启动参数
一个易踩的坑:eRPC 镜像的入口是erpc,其配置文件路径以位置参数传入。Chart 默认传入args: ["/config/erpc.yaml"](指向 ConfigMap 挂载点),否则 eRPC 会去搜索内置的硬编码路径列表(如/home/nonroot/erpc.yaml、/erpc.yaml等),导致挂载的配置被静默忽略、代理以默认公共端点启动。相关注释与默认值均记录在 values.yaml 的erpc.args处。
六、Secrets:敏感信息注入的两种模式
数据库 URL、broker URL 等敏感值通过环境变量引用 Kubernetes Secret注入(对应 config.rs 的环境变量覆盖机制)。Chart 支持两种模式。
6.1 使用 External Secrets Operator(默认)
externalSecret: enabled: true # 假定名为 "listener-secrets" 的 Secret 已存在 secretName: listener-secrets此模式下 Chart不渲染任何 Secret 资源,Pod 直接引用外部已存在的 Secret(无论是 ESO 创建还是手工创建)。values.yaml默认通过env的valueFrom.secretKeyRef注入APP_DATABASE__DB_URL:
env: - name: APP_DATABASE__DB_URL valueFrom: secretKeyRef: name: "database-credentials" key: database-url6.2 不使用 External Secrets Operator(fallback)
externalSecret: enabled: false fallbackSecret: name: listener-secrets data: database-url: "postgres://postgres:postgres@listener-postgresql:5432/listener" broker-url: "redis://listener-redis-master:6379"此时 secret.yaml 模板会渲染一个Opaque类型的 Secret,将fallbackSecret.data作为stringData写入,供环境变量引用。两条判断逻辑可以在模板中验证:{{- if not .Values.externalSecret.enabled }}才渲染 fallback Secret。
七、子图表依赖(Sub-chart dependencies)
Chart 通过helm dependency管理三个可选基础设施子图表:
| 依赖 | 默认 | 关闭方式 |
|---|---|---|
| PostgreSQL | 启用 | postgresql.enabled: false |
| Redis | 启用 | redis.enabled: false |
| RabbitMQ | 禁用 | rabbitmq.enabled: true |
将对应子图表enabled: false后即可接入外部托管的同类型服务,此时只需保证fallbackSecret.data(或 ESO Secret)中的database-url/broker-url指向外部地址即可。子图表的具体配置项参考 Bitnami 官方 Chart 文档。
八、Values Reference 完整参考
下表完整继承自 README.md,并结合 values.yaml 的实际默认值做了校正与补充:
| Key | 默认值 | 说明 |
|---|---|---|
image.repository | hub.zama.org/ghcr/zama-ai/fhevm/listener/listener-core | Listener 容器镜像(README 中记载的ghcr.io/zama-ai/listener为历史值,以 values.yaml 实际值为准) |
image.tag | ""(回退到 appVersion) | 镜像 tag 覆盖 |
commonConfig | {broker: {broker_type: redis}} | 共享配置覆盖(在基础默认值之上合并) |
listeners | [](values.yaml 实际为空,README 示例含 ethereum、base-sepolia 两条) | 按链拆分的 listener 实例 |
listeners[].name | - | 链名(决定 Deployment/ConfigMap 命名) |
listeners[].config | {} | 链级配置覆盖(与 Rust 配置结构一致) |
listeners[].env | [] | 单链环境变量覆盖 |
listeners[].resources | 继承根级resources | 单链资源覆盖 |
secretName | listener-secrets | 存放敏感值的 K8s Secret 名称 |
env | APP_DATABASE__DB_URL引用database-credentials | 共享环境变量(合并进所有 listener Pod) |
podSecurityContext | runAsNonRoot: true+runAsUser: 10000+seccompProfile: RuntimeDefault | Pod 级安全上下文 |
securityContext | readOnlyRootFilesystem+capabilities.drop: [ALL]+seccompProfile: RuntimeDefault | 容器级安全上下文 |
resources | requests/limits 均为cpu: "1"、memory: 1Gi | 默认资源配额 |
metrics.enabled | true | 是否暴露 Prometheus 指标端口 |
metrics.path | /metrics | 指标抓取路径 |
metrics.serviceMonitor.enabled | false | 是否生成 ServiceMonitor(interval 30s、scrapeTimeout 10s) |
erpc.enabled | false(values.yaml 实际值;README 表格记为true) | 是否部署 eRPC 代理 |
erpc.baseConfig | erpc-base.yaml | eRPC 基础配置 profile |
erpc.config | {} | 在基础配置之上深合并的局部覆盖 |
erpc.configFile | "" | 整体替换配置(通过--set-file) |
erpc.replicas | 1 | eRPC 副本数 |
erpc.image.repository/tag | ghcr.io/erpc/erpc/0.0.63 | eRPC 镜像 |
erpc.args | ["/config/erpc.yaml"] | eRPC 启动参数(指向挂载的 ConfigMap) |
erpc.service.httpPort | 4000 | eRPC HTTP 端口 |
erpc.service.metricsPort | 4001 | eRPC Prometheus 指标端口 |
erpc.podSecurityContext | nonroot +seccompProfile: RuntimeDefault | eRPC Pod 级安全上下文 |
erpc.securityContext | readOnlyRoot + capDropAll +seccompProfile: RuntimeDefault | eRPC 容器级安全上下文 |
erpc.resources | requests 250m/256Mi,limits 1/512Mi | eRPC 资源配额 |
externalSecret.enabled | true | 使用已存在的 Secret(ESO 或手工) |
fallbackSecret.name | listener-secrets | ESO 禁用时渲染的 Secret 名称 |
fallbackSecret.data | {} | ESO 禁用时的 Secret 数据 |
postgresql.enabled | true | 是否部署 PostgreSQL 子图表 |
redis.enabled | true | 是否部署 Redis 子图表 |
rabbitmq.enabled | false | 是否部署 RabbitMQ 子图表 |
安全上下文默认值说明:
seccompProfile.type: RuntimeDefault是满足 Pod Security Standard "restricted (strict)" 级别以及 Kyvernorestrict-seccomp-strict策略的必要条件,相关设计注释详见 values.yaml。
九、与 fhevm 生态的衔接
Listener 是整个 fhevm 全栈框架(Fully Homomorphic Encryption + blockchain)中的数据入口组件之一。它与仓库中 listener/crates/ 下的 Rust 实现(listener_core、shared、consumer等 crate)一一对应:Chart 的每一层配置最终都会落到 RustSettings结构体(config.rs)的字段上,而strategy.block_fetcher: block_receipts、batch_receipts_size_range等参数则直接控制着 listener 核心的区块拉取与回执处理逻辑。若需要深入理解参数背后的行为,可以沿着 listener-default.yaml 的每个键在 listener_core 中溯源对应实现。
十、部署后的验证与排障建议
- 校验渲染结果:
helm template listener charts/listener -f my-values.yaml可以离线检查三层合并后的最终 ConfigMap 与 Secret 内容是否符合预期; - 确认配置生效:查看 listener Pod 挂载的
/config/config.yaml是否为预期合并结果(合并逻辑见 configmap.yaml); - 检查 Secret 引用:若使用 ESO 模式,务必先确认名为
listener-secrets的 Secret 已存在,否则 Pod 将因secretKeyRef解析失败而无法启动; - eRPC 配置是否被读取:确认 Deployment 的
args包含/config/erpc.yaml,避免 eRPC 以默认配置静默启动(见 5.6 节); - 观测指标:listener 的 metrics 端口来自配置合并链
telemetry.metrics_port(默认 9090),由 _helpers.tpl 中的listener.metricsPort模板动态推导;eRPC 指标端口为 4001,均可通过 ServiceMonitor 接入 Prometheus。
以上所有配置项、模板逻辑与默认值均可在本仓库对应路径中逐一核对,建议结合自身链的区块高度、RPC 限流与最终性要求,在 listener-default.yaml 的默认值基础上做针对性调优。
【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考