Envoy静态配置实战:从零搭建HTTP代理实现服务间通信

📅 2026/8/4 11:11:02
Envoy静态配置实战:从零搭建HTTP代理实现服务间通信
在分布式系统、微服务架构和云原生环境中服务之间的通信与协作是核心。当我们需要一个服务去调用另一个服务时通常会想到使用 HTTP 或 RPC 客户端。然而随着服务数量激增直接调用带来的耦合、服务发现、负载均衡、熔断降级等问题日益凸显。这时一个强大的服务网格Service Mesh就显得尤为重要而 Envoy 作为其中的数据平面代理扮演着“流量警察”的角色它本身并不直接发起业务请求但却能智能地控制所有进出服务的流量。本文将以一个具体的场景切入假设我们有一个名为nobody的服务这个名字源于项目标题中的趣味表述代表一个普通的后端服务它需要安全、可靠地调用另一个服务。我们将使用 Envoy 作为nobody服务的边车代理来演示如何配置一个从服务到外部 API 的 HTTP 调用并在此过程中理解 Envoy 的核心概念、配置模型以及排错方法。通过本篇教程你将能独立完成一个基于 Envoy 的简单服务间代理配置并掌握其关键配置项的含义和调试技巧。1. 理解 Envoy 的核心角色与配置模型在开始动手之前必须厘清 Envoy 在架构中的位置和工作原理。很多人初次接触时容易将其与 Nginx、HAProxy 等传统代理混淆或者误以为它需要复杂的编码。1.1 Envoy 是什么为什么是“数据平面”Envoy 是一个由 Lyft 开源的高性能 C 分布式代理专为云原生应用设计。在服务网格架构中Istio、Linkerd 等控制平面负责下发策略和配置而 Envoy 则作为数据平面以边车模式与应用容器部署在一起透明地拦截和处理所有进出该容器的网络流量。它的核心价值在于透明性业务代码无需感知 Envoy 的存在仍使用标准库进行网络调用。可观测性内置丰富的指标、日志和分布式追踪提供强大的可观测性。动态配置支持通过 xDS API 动态更新路由、集群、端点等配置实现无缝的服务发现和负载均衡。丰富的过滤器通过 HTTP、TCP 等过滤器链可以实现路由、限流、熔断、认证、请求/响应转换等复杂功能。对于我们的nobody服务而言Envoy 就像一个贴身保镖和导航员。nobody服务想“电”调用另一个服务时请求并不直接发出而是先交给身边的 Envoy 边车由它来决定请求发往何处、如何负载均衡、是否需要重试等。1.2 静态配置 vs. 动态配置Envoy 的配置是其灵魂主要分为两种方式静态配置将所有配置监听器、路由、集群等写入一个 YAML 或 JSON 文件在启动时加载。这种方式简单直观适合学习、测试和简单的生产场景。本文将采用静态配置。动态配置通过 xDS 协议从控制平面动态获取配置。这是服务网格的典型用法配置可以实时更新而无需重启 Envoy。这更复杂需要额外的控制平面组件。我们的教程将从静态配置开始这是理解所有概念的基础。1.3 关键配置组件解析一个最简单的 Envoy 配置通常包含以下几个核心部分它们构成了请求处理的流水线组件作用类比Listener定义 Envoy 监听哪个网络地址和端口等待连接进入。公司的总机号码和接线员。Filter处理连接或请求/响应的逻辑单元。多个过滤器组成过滤器链。接线员内部的处理流程如转接、记录、翻译。Route Configuration定义 HTTP 请求的路由规则根据请求头、路径等信息将请求匹配到不同的虚拟主机和路由。公司的内部电话簿和转接规则。Cluster定义上游服务的逻辑集合包括服务发现方式、负载均衡策略、连接池设置等。你要联系的外部部门或合作伙伴公司。Endpoint集群中具体的服务实例地址和端口。合作伙伴公司里具体的联系人电话。一个请求的简化流程是Listener接收请求 -HTTP Connection Manager过滤器处理 - 匹配Route- 找到目标Cluster- 从Cluster的Endpoint列表中选择一个发送请求。2. 环境准备与最小化 Envoy 部署我们将在一个干净的 Linux 环境中演示。你可以使用虚拟机、云服务器或 Docker 容器。2.1 系统环境与依赖操作系统Ubuntu 20.04 LTS 或 CentOS 7。本文以 Ubuntu 为例。Envoy 版本我们使用官方发布的稳定版本1.28.0。版本一致性很重要不同版本配置可能有细微差别。工具curl用于测试、jq可选用于格式化 JSON 输出。首先更新系统并安装必要的工具sudo apt-get update sudo apt-get install -y curl2.2 获取并运行 Envoy有多种方式运行 Envoy这里使用 Docker因为它能提供一致的环境且镜像包含了所有依赖。拉取官方 Envoy 镜像docker pull envoyproxy/envoy:v1.28-latest准备配置文件目录 在宿主机上创建一个工作目录用于存放我们的配置文件和测试。mkdir ~/envoy-tutorial cd ~/envoy-tutorial创建一个最简单的配置文件 我们先创建一个能启动但几乎不做任何事情的配置验证 Envoy 能运行。vim envoy-bootstrap.yamladmin: access_log_path: /tmp/admin_access.log address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 9901 static_resources: listeners: [] clusters: []这个配置只开启了 Admin 管理接口端口 9901没有定义任何监听器和集群。它就像一个待命的空壳。运行 Envoy 容器docker run -d --name envoy-tutorial \ -p 9901:9901 \ -p 10000:10000 \ -v $(pwd)/envoy-bootstrap.yaml:/etc/envoy/envoy.yaml \ envoyproxy/envoy:v1.28-latest-p 9901:9901将容器的 Admin 端口映射到宿主机。-p 10000:10000预留给后续我们业务监听器的端口。-v ...将宿主的配置文件挂载到容器内 Envoy 的默认配置路径。验证 Envoy 运行 访问 Admin 接口的/stats端点如果返回大量统计信息说明 Envoy 运行成功。curl -s http://localhost:9901/stats | head -20你应该能看到类似cluster_manager.active_clusters: 0这样的输出。3. 配置一个完整的 HTTP 代理让nobody服务“电”出去现在我们来配置一个实际场景nobody服务运行在本地希望通过 Envoy 代理访问一个外部的公共 API 服务。我们以httpbin.org这个用于 HTTP 测试的网站作为上游服务。3.1 配置设计目标在宿主机上通过curl http://localhost:10000/get发起请求该请求被 Envoy 监听器接收然后转发到httpbin.org的/get接口并将响应返回。我们需要在配置中定义一个 Listener监听0.0.0.0:10000处理 HTTP 流量。一个 HTTP Connection Manager 过滤器用于处理 HTTP 协议。一个 Route Configuration将所有流量路由到同一个集群。一个 Cluster定义上游为httpbin.org的端口 80。3.2 编写完整配置文件创建新的配置文件envoy-httpbin.yamladmin: access_log_path: /tmp/admin_access.log address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 9901 static_resources: listeners: - name: listener_http address: socket_address: protocol: TCP address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http http_filters: - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router route_config: name: local_route virtual_hosts: - name: httpbin_service domains: [*] # 匹配所有域名 routes: - match: prefix: / route: cluster: httpbin_cluster clusters: - name: httpbin_cluster type: LOGICAL_DNS # 对于生产环境通常使用 STRICT_DNS 或 EDSLOGICAL_DNS 适用于简单的静态主机名 dns_lookup_family: V4_ONLY load_assignment: cluster_name: httpbin_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: httpbin.org port_value: 803.3 关键配置详解Listener (listener_http):address: 定义了监听在0.0.0.0:10000即接受所有网络接口的请求。filter_chains: 过滤器链。这里只有一个过滤器。HTTP Connection Manager 过滤器:stat_prefix: 用于生成统计指标的前缀。http_filters: HTTP 级别的过滤器链。envoy.filters.http.router是终端过滤器负责将请求路由到上游集群是必须的。route_config: 核心路由配置。virtual_hosts.domains: [*]: 匹配所有 HTTPHost头。在代理外部服务时常用。routes.match.prefix: /: 匹配所有路径前缀。routes.route.cluster: httpbin_cluster: 将匹配的请求路由到名为httpbin_cluster的集群。Cluster (httpbin_cluster):type: LOGICAL_DNS: 使用 DNS 进行服务发现。Envoy 会异步解析httpbin.org并缓存其 IP 地址。对于 IP 可能变化的上游这不是最佳选择但对于演示和静态服务足够。load_assignment.endpoints: 定义了集群的端点。这里只有一个端点httpbin.org:80。3.4 更新并重启 Envoy停止旧容器docker stop envoy-tutorial docker rm envoy-tutorial使用新配置启动容器docker run -d --name envoy-tutorial \ -p 9901:9901 \ -p 10000:10000 \ -v $(pwd)/envoy-httpbin.yaml:/etc/envoy/envoy.yaml \ envoyproxy/envoy:v1.28-latest4. 运行验证与结果分析现在我们可以测试代理是否工作。4.1 基础功能测试通过宿主机直接向 Envoy 的监听端口10000发送请求curl -v http://localhost:10000/get预期结果你应该能收到来自httpbin.org/get的标准 JSON 响应其中包含你的请求头等信息。在curl -v的输出中你会看到Via: envoy的响应头这证实了请求经过了 Envoy 代理。4.2 深入验证查看 Envoy 内部状态Envoy 的 Admin 接口是强大的排错工具。检查集群状态curl -s http://localhost:9901/clusters | grep httpbin_cluster输出应显示httpbin_cluster::default_priority::healthy等状态信息表明集群健康且已发现端点。检查监听器curl -s http://localhost:9901/listeners | jq . # 使用 jq 美化输出可以看到我们定义的listener_http及其绑定地址。查看统计信息curl -s http://localhost:9901/stats | grep -E (cluster.httpbin_cluster|listener.10000)这里能看到该集群和监听器相关的各种计数器如请求数、成功数、失败数、延迟等。多发起几次curl请求观察cluster.httpbin_cluster.upstream_rq_total等指标的变化。4.3 测试路径匹配我们的配置匹配所有路径 (prefix: /)。可以测试不同的路径curl http://localhost:10000/status/200 curl http://localhost:10000/delay/2 curl http://localhost:10000/headers这些请求都会被正确代理到httpbin.org的相应路径。5. 常见问题排查与调试技巧配置 Envoy 时可能会遇到各种问题。以下是一些典型场景和排查路径。5.1 问题一Envoy 启动失败现象docker run命令执行后容器立刻退出。使用docker logs envoy-tutorial查看日志。常见原因与解决YAML 格式错误Envoy 对 YAML 格式非常严格。最常见的错误是缩进不对或冒号后缺少空格。使用在线 YAML 校验器或yamllint工具检查配置文件。配置语法错误例如typed_config下的type写错。仔细核对 Envoy 官方文档对应版本的配置字段。错误日志通常会明确指出哪一行有问题。端口冲突宿主机上的9901或10000端口已被占用。使用netstat -tulnp | grep :10000检查并更改配置中的端口号。5.2 问题二请求返回 503 或 404现象curl请求返回503 Service Unavailable或404 Not Found。排查步骤检查集群健康状态curl -s http://localhost:9901/clusters找到httpbin_cluster查看其健康状态。如果是healthy进入下一步。如果是unhealthy或timeout可能是网络问题或上游服务不可达。尝试在容器内ping httpbin.org或curl httpbin.org。检查路由匹配确认请求的Host头和路径能被虚拟主机和路由规则匹配。我们的配置使用domains: [*]和prefix: /基本能匹配所有请求。如果配置了特定域名或路径需要确保请求与之匹配。查看 Envoy 访问日志默认配置下访问日志可能未开启。为了调试可以在http_connection_manager配置中添加http_filters: ... access_log: - name: envoy.access_loggers.stdout typed_config: type: type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog重启 Envoy 后使用docker logs --tail 10 -f envoy-tutorial查看实时日志里面会记录每个请求的详细信息包括响应码。5.3 问题三请求超时现象请求长时间无响应最终超时。排查步骤检查端点解析对于LOGICAL_DNS类型Envoy 可能无法解析域名。在 Admin 接口查看/clusters端点信息确认解析出的 IP 是否正确。调整集群超时设置在cluster配置中可以添加超时配置。clusters: - name: httpbin_cluster connect_timeout: 5s # 连接超时 type: LOGICAL_DNS ...检查网络连通性进入 Envoy 容器内部尝试curl -v http://httpbin.org/delay/5看是否能在 5 秒内收到响应以排除容器网络问题。5.4 调试清单当 Envoy 代理不按预期工作时可以按以下清单逐步排查步骤检查项命令/方法1Envoy 容器是否正在运行docker ps2配置文件语法是否正确docker logs envoy-tutorial看启动日志3Admin 接口是否可访问curl http://localhost:9901/server_info4监听器是否已激活curl http://localhost:9901/listeners5目标集群状态是否健康curl http://localhost:9901/clusters6路由配置是否正确检查route_config用简单路径测试7上游服务是否可达在容器内curl上游地址8是否有访问日志开启stdout访问日志并查看容器日志6. 生产环境最佳实践与扩展方向将 Envoy 用于学习和小型项目上述配置足够。但对于生产环境需要考虑更多。6.1 配置优化建议使用更可靠的服务发现LOGICAL_DNS适用于 IP 稳定的服务。对于动态环境如 Kubernetes应使用STRICT_DNS定期解析或更好的EDS端点发现服务通过 xDS API 动态获取端点列表。配置健康检查clusters: - name: httpbin_cluster ... health_checks: # 添加健康检查 - timeout: 5s interval: 10s unhealthy_threshold: 3 healthy_threshold: 2 http_health_check: path: /status/200 ...这能确保流量只被发送到健康的实例。配置熔断和异常点检测clusters: - name: httpbin_cluster ... circuit_breakers: thresholds: - priority: DEFAULT max_connections: 1000 max_pending_requests: 1000 max_requests: 1000 max_retries: 3 outlier_detection: consecutive_5xx: 5 interval: 10s base_ejection_time: 30s max_ejection_percent: 50 ...这些设置可以防止故障扩散提升系统韧性。分离配置与证书将 TLS 证书、密钥等敏感信息通过卷挂载或密钥管理服务注入而不是写在配置文件中。6.2 安全考虑启用 TLS在生产中Listener 应配置transport_socket使用 TLS 终止与上游集群的连接也应使用 TLS。限制 Admin 接口Admin 接口包含大量信息应只绑定在127.0.0.1或通过防火墙严格限制访问源 IP。请求身份验证在http_filters链中插入envoy.filters.http.jwt_authn等过滤器来实现 API 级别的认证。6.3 从静态配置迈向动态配置静态配置管理复杂且无法实现动态更新。下一步的学习方向是集成控制平面学习使用 Istio 或单独部署 Envoy 的 xDS 管理服务器。理解 xDS 协议了解LDS、RDS、CDS、EDS等不同发现服务如何协同工作。使用 Envoy Go Control Plane这是一个用 Go 实现的 xDS 服务器库可以用来构建自己的简单控制平面动态管理 Envoy 配置。通过本篇教程你不仅完成了让nobody服务通过 Envoy 安全“电”出去的任务更重要的是理解了 Envoy 作为现代代理的核心配置模型和排错思路。记住Envoy 的强大在于其可观测性和动态性在后续实践中多利用 Admin 接口和日志来洞察流量行为并逐步将静态配置演进为更灵活的动态管理模式。