二、Envoy静态配置

因语雀和CSDN MarkDown格式有区别,导入到CSDN时可能会有格式显示问题,请查看原文链接:
https://www.yuque.com/dycloud/pss8ys/og9o5a4y3o26xzu6

一、静态配置详解

1.1 静态配置详解

纯静态配置资源配置方式主要是直接在配置文件中通过 <font style="color:#DF2A3F;">static_resources</font> 配置参数明确定义 <font style="color:#DF2A3F;">listeners</font><font style="color:#DF2A3F;">clusters</font><font style="color:#DF2A3F;">secrets</font> 的配置方式,各配置参数的数据类型如下面的配置所示:

json 复制代码
{
  "listeners": [],
  "clusters": [],
  "secrets": []
}

其中 <font style="color:#DF2A3F;">listeners</font> 用于配置纯静态类型的侦听器列表<font style="color:#DF2A3F;">clusters</font> 用于定义可用的集群列表及每个集群的端点 ,而可选的 <font style="color:#DF2A3F;">secrets</font> 用于定义 TLS 通信中用到数字证书等配置信息

具体使用时,admin 和 <font style="color:#DF2A3F;">static_resources</font> 两参数就可以提供一个最小化的资源配置,admin 也可以省略

1.2 Envoy 的简易静态配置

监听器主要用于定义 Envoy 监听的用于接受 Downstreams 请求的套接字、用于处理请求时调用的过滤器链及相关的其他配置属性。

下面我们将使用配置原型的 YAML 表示形式和代理 HTTP 从 <font style="color:rgb(64, 64, 64);background-color:rgb(252, 252, 252);">127.0.0.1:10000</font><font style="color:rgb(64, 64, 64);background-color:rgb(252, 252, 252);"> 127.0.0.1:1234 </font>的服务的运行示例。

yaml 复制代码
# Envoy自带的管理端口
admin:
  address:
    socket_address: { address: 127.0.0.1, port_value: 9901 }
# 静态配置区域
static_resources:
  # 定义了来自127.0.0.1:10000 端口的监听器,也就是Envoy对外的入口点
  # 类似与Envoy当作反向代理挂载本地的10000端口上
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 127.0.0.1, port_value: 10000 }
    # 定义过滤器链
    filter_chains:
    - filters:
      # 使用Envoy官方自带的http_connection_manager,这个是http代理核心过滤器,负责处理Http协议相关的所有内容
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          # Envoy 指标采集前缀,用于统计(如prometheus监控里会看到 ingress_http 下的各种流量指标)。
          stat_prefix: ingress_http
          # 自动侦测上游流量是 HTTP/1 还是 HTTP/2,并自动解包。建议通常用 AUTO
          codec_type: AUTO
          # 路由配置对象
          route_config:
            # 路由配置名称
            name: local_route
            # 域名规则(必有),这里 domains: ["*"],表示匹配所有请求域名
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              # 所有URL都匹配(prefix: "/"),转发到 downstream 名为 some_service 的集群,即你的后端服务池
              routes:
              - match: { prefix: "/" }
                route: { cluster: some_service }
          # http_filters链中加了router,这一步才能真正让请求匹配路由规则、进入上游集群。一般 http_connection_manager 的最后一个过滤器都是router
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  # 定义一个上游集群,名字叫 some_service 连接超时时长0.25s

  clusters:
  - name: some_service
    connect_timeout: 0.25s
    # 用于解析集群(生成集群端点)时使用的服务发现类型,可用值有STATIC、STRICT_DNS、LOGICAL_DNS、ORIGINAL_DST和EDS等;
    type: STATIC
    # ROUND_ROBIN 表示后端负载均衡策略按轮询
    lb_policy: ROUND_ROBIN
    # 为STATIC、STRICT_DNS或LOGICAL_DNS类型的集群指定成员获取方式;EDS类型的集成要使用eds_cluster_config字段配置;
    load_assignment:
      cluster_name: some_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
              # 这里就是本机127.0.0.1:1234,如果你有多个后端,可写多个endpoint
                address: 127.0.0.1
                port_value: 1234

1.3 过滤器链

官方自带的过滤器链可以到这里查看:

https://www.envoyproxy.io/docs/envoy/v1.35.0/api-v3/config/listener/v3/listener_components.proto#config-listener-v3-filter

1.4 Cluster 相关配置

  • 通常,集群代表了一组提供相同服务的上游服务器(端点)的组合 ,它可由用户静态配置 ,也能够通 过CDS动态获取
  • 集群需要在"预热"环节完成之后方能转为可用状态,这意味着集群管理器通过DNS解析或EDS服务完成端点初始化,以及健康状态检测成功之后才可用;
yaml 复制代码
  clusters:
  - name: some_service
    connect_timeout: 0.25s
    # 用于解析集群(生成集群端点)时使用的服务发现类型,可用值有STATIC、STRICT_DNS、LOGICAL_DNS、ORIGINAL_DST和EDS等;
    type: STATIC
    # ROUND_ROBIN 表示后端负载均衡策略按轮询
    lb_policy: ROUND_ROBIN
    # 为STATIC、STRICT_DNS或LOGICAL_DNS类型的集群指定成员获取方式;EDS类型的集成要使用eds_cluster_config字段配置;
    load_assignment:
      cluster_name: some_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
              # 这里就是本机127.0.0.1:1234,如果你有多个后端,可写多个endpoint
                address: 127.0.0.1
                port_value: 1234

1.4.1 type 服务发现类型

1.4.1.1 STATIC

静态集群,所有后端节点(endpoints)都直接在配置文件里写死。启动时加载一次(reload 配置会更新,不会通过 API 或服务发现机制动态变化)。

适用于后端服务地址变化极少,节点数量固定的场景或开发测试环境等。

1.4.1.2 STRICT_DNS

通过定时解析域名(如 service1.example.com)获取 endpoint 地址,每隔一段时间做一次 DNS 查询,节点自动增删必须所有 endpoint 都能查询成功(否则会全部失效)

适用于后端服务地址会变、更常用 DNS 记录做服务发现(如基于 K8S headless service、外部 DNS 自动注册等)。

yaml 复制代码
type: STRICT_DNS
load_assignment:
  cluster_name: backend
  endpoints:
  - lb_endpoints:
    - endpoint:
        address:
          socket_address:
            address: my-backend-service.default.svc.cluster.local
            port_value: 8080
1.4.1.3 LOGICAL_DNS

启动时先解析域名,获取首个返回的地址,之后会不断用同样方式刷新,但只用返回结果的第一个地址,DNS解析出多个 A 记录,只用第一个。

适用于后端只有一个实例(如离线存储、单点服务等),不需要对一个域名返回的多个节点进行负载均衡的场景。

1.4.1.4 EDS(Endpoint Discovery Service)

endpoint 动态发现由 xDS 控制平面下发endpoint 的变更通过 xDS 协议动态推送(如 Istio、Consul Connect、AWS App Mesh 等服务网格环境下)

适用于一整套 Envoy xDS 管理后台的分布式服务发现场景,集群规模大、节点频繁动态变化,使用 Isito,Envoy 会自动配置好 EDS,无需手动写 endpoint。

yaml 复制代码
type: EDS
eds_cluster_config:
  service_name: my-backend
1.4.1.5 ORIGINAL_DST
  • 原始目标转发。通常和iptables透明代理搭配使用。
  • Envoy自动识别网络包的目标IP/端口,直接转发而非自己指定endpoint。
  • Endpoint名义上多为 <font style="color:rgb(251, 71, 135);">0.0.0.0:0</font>,实际用流量包目的地址。

1.4.2 负载均衡算法

1.4.2.1 ROUND_ROBIN(轮询)
  • 每次请求按顺序分发给集群节点,平均分配流量。
  • 没有权重概念,所有端点收到的请求数理论上相等。
1.4.2.2 LEAST_REQUEST(最少请求)
  • 每次选择"当前连接请求数最少"的后端节点。
  • 如果节点数多于2,会随机选2个节点,再把请求分配给这两个中负载最低的节点(Power of Two Choices)。

场景:

  • 后端节点处理请求耗时不等,或某些请求后端停留时间较长,易"堆积"。
  • 适合高并发、请求处理时间差异较大场景。
1.4.2.3 RANDOM(随机)
  • 所有可用节点中随机选一个进行转发。

场景:

  • 小规模、不关心排队顺序,主要防"雪崩效应"的场景。
  • 对测试和仿真流量分布很有参考价值。
1.4.2.4 RING_HASH(一致性哈希)
  • 请求按照 key(如 <font style="color:rgb(51, 54, 57);">header</font><font style="color:rgb(51, 54, 57);">cookie</font><font style="color:rgb(51, 54, 57);">源IP</font>)的一致性哈希落到某一个或少数几个 endpoints 上。
  • 适合Session粘性(如用户会话保持)、缓存层(如 HTTP 缓存、KV 存储)等场景。

配置方式:

需要在 <font style="color:rgb(51, 54, 57);">route/cluster filter</font> 里配置<font style="color:rgb(251, 71, 135);">hash_policy</font>,或者在 cluster 配置中指明 key。

场景:

  • 粘性会话、缓存一致性、需无状态重定向的场合。
1.4.2.5 <font style="color:#DF2A3F;">MAGLEV</font>(Maglev 哈希)
  • Google Maglev 算法的负载均衡(种一致性哈希),比 <font style="color:rgb(51, 54, 57);">ring_hash</font> 分配更加均匀,节点变更时流量扰动更小。
  • 常用于大规模、高可用要求极高的生产环境。

场景:

  • 数千级/backend 时快速分配且担心节点增删容易扰乱粘性分配的流量分布场景。
1.4.2.6 <font style="color:#DF2A3F;">CLUSTER_PROVIDED</font>(集群提供)
  • 某些特殊场景下,由外部实体为集群提供自定义的负载均衡策略。
  • 例如 EDS 可动态带 weight,并由控制面决定如何分流。

场景:

  • 多活、分区、shard 按需策略的高阶自定义场景。
  • 一般用不到,依赖服务网格/xDS 控制面特殊功能。

二、静态配置案例学习

2.1 TCP 代理配置

2.1.1 配置介绍

  • TCP代理过滤器在下游客户端及上游集群之间执行1:1网络连接代理
    • 它可以单独用作隧道替换,也可以同其他过滤器(如MongoDB过滤器或速率限制过滤器)结合使用;
    • TCP代理过滤器严格执行由全局资源管理于为每个上游集群的全局资源管理器设定的连接限制
    • TCP代理过滤器检查上游集群的资源管理器是否可以在不超过该集群的最大连接数的情况下创建连接;
  • TCP代理过滤器可直接将请求路由至指定的集群,也能够在多个目标集群间基于权重进行调度转发;

 配置语法:

yaml 复制代码
{
  "stat_prefix": "...", # 用于统计数据中输出时使用的前缀字符;
  "cluster": "...", # 路由到的目标集群标识;
  "weighted_clusters": "{...}",
  "metadata_match": "{...}",
  "idle_timeout": "{...}", # 上下游连接间的超时时长,即没有发送和接收报文的超时时长;
  "access_log": [], # 访问日志;
  "max_connect_attempts": "{...}" # 最大连接尝试次数;
}

2.1.2 tcp 配置案例

https://www.envoyproxy.io/docs/envoy/v1.35.0/api-v3/extensions/filters/network/tcp_proxy/v3/tcp_proxy.proto#extension-envoy-filters-network-tcp-proxy

下面的示例基于TCP代理将下游用户(本机)请求代理至后端的两个web服务器

yaml 复制代码
static_resources:
  listeners:
    name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.tcp_proxy
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
          stat_prefix: tcp
          cluster: local_cluster

  clusters:
  - name: local_cluster
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: local_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: 172.31.1.11, port_value: 8080 }
        - endpoint:
            address:
              socket_address: { address: 172.31.1.12, port_value: 8080 }

docker-compose:

这个 docker-compose 中启动了 envoy 容器和两个 web 容器作为后端 endpoint

yaml 复制代码
version: '3.3'

services:
  envoy:
    image: envoyproxy/envoy:v1.35.0
    volumes:
    - ./envoy.yaml:/etc/envoy/envoy.yaml
    privileged: true # 云主机需要添加这个,自建无所谓
    networks:
      envoymesh:
        ipv4_address: 172.31.1.2
        aliases:
        - front-proxy
    depends_on:
    - webserver01
    - webserver02

  webserver01:
    image: harbor.test.com/nginx-app:v1.0
    environment:
      - PORT=8080
    hostname: webserver01
    networks:
      envoymesh:
        ipv4_address: 172.31.1.11
        aliases:
        - webserver01

  webserver02:
    image: harbor.test.com/nginx-app:v1.0
    environment:
      - PORT=8080
    hostname: webserver02
    networks:
      envoymesh:
        ipv4_address: 172.31.1.12
        aliases:
        - webserver02

networks:
  envoymesh:
    driver: bridge
    ipam:
      config:
        - subnet: 172.31.1.0/24
bash 复制代码
 [root@k8s-master01 /usr]# docker-compose  up -d 
 [root@VM_24_47_tlinux ~]# docker ps 
CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS         PORTS       NAMES
6c8a768a15ab   envoyproxy/envoy:v1.29.2   "/docker-entrypoint...."   4 minutes ago   Up 3 minutes   10000/tcp   tcp-front-proxy-envoy-1
427d3fe444b8   harbor.test.com/nginx-app:v1.0  "/bin/sh -c 'python3..."   4 minutes ago   Up 3 minutes               tcp-front-proxy-webserver02-1
264c4e103072   harbor.test.com/nginx-app:v1.0   "/bin/sh -c 'python3..."   4 minutes ago   Up 3 minutes               tcp-front-proxy-webserver01-1

启动后测试,可以发现已经可以正常的轮询了

bash 复制代码
[root@VM_24_47_tlinux ~]# curl 172.31.1.2:8080
test demoapp v1.0 !! ClientIP: 172.31.1.1, ServerName: webserver02, ServerIP: 172.31.1.12!
[root@VM_24_47_tlinux ~]# curl 172.31.1.2:8080
test demoapp v1.0 !! ClientIP: 172.31.1.1, ServerName: webserver01, ServerIP: 172.31.1.11!
[root@VM_24_47_tlinux ~]# curl 172.31.1.2:8080
test demoapp v1.0 !! ClientIP: 172.31.1.1, ServerName: webserver02, ServerIP: 172.31.1.12!
[root@VM_24_47_tlinux ~]# curl 172.31.1.2:8080
test demoapp v1.0 !! ClientIP: 172.31.1.1, ServerName: webserver01, ServerIP: 172.31.1.11!
[root@VM_24_47_tlinux ~]# curl 172.31.1.2:8080
test demoapp v1.0 !! ClientIP: 172.31.1.1, ServerName: webserver01, ServerIP: 172.31.1.11!
[root@VM_24_47_tlinux ~]# curl 172.31.1.2:8080
test demoapp v1.0 !! ClientIP: 172.31.1.1, ServerName: webserver02, ServerIP: 172.31.1.12!

2.2 L4 过滤器 http_connection_manager

2.2.1 配置介绍

<font style="color:#DF2A3F;">http_connection_manager</font>通过引入L7过滤器链实现了对http协议的操纵,其中<font style="color:#DF2A3F;">router</font>过滤器用于配置路由转发;

yaml 复制代码
listeners:
  - name: # 监听器名称
    address:
      socket_address:
        address: ...        # 监听地址
        port_value: ...     # 监听端口
        protocol: ...       # 协议
    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: ... # 统计信息中使用的易读性的信息前缀
              route_config:    # 静态路由配置;动态配置应该使用rds字段进行指定
                name: ...      # 路由配置的名称
                virtual_hosts: # 虚拟主机列表,用于构成路由表
                  - name: ...                # 虚拟主机的逻辑名称,用于统计信息,与路由无关
                    domains: []              # 当前虚拟主机匹配的域名列表,支持用"*"通配符
                    # 匹配搜索次序:精确匹配、前缀通配、后缀通配及完全通配
                    routes: []               # 指定的域名下的路由列表,执行时按顺序搜索,第一个匹配到路由信息即为使用的路由机制
              http_filters:                  # 定义http过滤器链
                - name: envoy.filters.http.router # 调用7层的路由过滤器
  • 处理请求时,Envoy首先根据下游客户端请求的"host"来搜索虚拟主机列表中各<font style="color:#DF2A3F;">virtual_host</font>中的domains列表中的定义,第一个匹配到的Domain的定义所属的<font style="color:#DF2A3F;">virtual_host</font>即可处理请求的虚拟主机;
  • 而后搜索当前虚拟主机中的<font style="color:#DF2A3F;">routes</font>列表中的路由列表中各路由条目的match的定义,第一个匹配到的match后的 路由机制(route、redirect或direct_response)即生效;

2.2.2 路由配置详解

<font style="color:#DF2A3F;">route_config.virtual_hosts.routes</font>配置的路由信息用于将下游的客户端请求路由至合适的上游集群中某 Server上;

  • 其路由方式是将url匹配match字段的定义
    • <font style="color:#DF2A3F;">match</font>字段可通过<font style="color:#DF2A3F;">prefix</font>(前缀)、<font style="color:#DF2A3F;">path</font>(路径)或<font style="color:#DF2A3F;">safe_regex</font>(正则表达式)三者之一来表示匹配模式;
  • <font style="color:#DF2A3F;">match</font>相关的请求将由<font style="color:#DF2A3F;">route</font>(路由规则)、<font style="color:#DF2A3F;">redirect</font>(重定向规则)或<font style="color:#DF2A3F;">direct_response</font>(直接响应)三个字段其中之一完成路由;
  • <font style="color:#DF2A3F;">route</font>定义的路由目标必须是<font style="color:#DF2A3F;">cluster</font>(上游集群名称)、<font style="color:#DF2A3F;">cluster_header</font>(根据请求标头中的<font style="color:#DF2A3F;">cluster_header</font>的值确定目标集群)或<font style="color:#DF2A3F;">weighted_clusters</font>(路由目标有多个集群,每个集群拥有一定的权重)其中之一;

如下:

yaml 复制代码
routes: 
  - name: user_service_router    # 此路由条目的名称;
    match: 
      prefix: /user/            # 请求的URL的前缀;
    route:                      # 路由条目;
      cluster: user_service     # 目标下游集群;
  • **<font style="color:#262626;">match</font>**
    • 基于<font style="color:#DF2A3F;">prefix</font><font style="color:#DF2A3F;">path</font><font style="color:#DF2A3F;">regex</font>三者其中任何一个进行URL匹配

提示regex将会被safe_regex取代

复制代码
- <font style="color:rgb(0,0,0);">可额外根据</font>`<font style="color:#DF2A3F;">headers</font>`<font style="color:rgb(0,0,0);">和</font>`<font style="color:#DF2A3F;">query_parameters</font>`<font style="color:rgb(0,0,0);">完成报文匹配 </font>
- <font style="color:rgb(0,0,0);">匹配的到报文可有三种路由机制 </font>
    * <font style="color:rgb(243,193,9);"> </font>`<font style="color:#DF2A3F;">redirect</font>`<font style="color:rgb(0,0,0);"> (重定向规则)</font>
    * `<font style="color:#DF2A3F;">direct_response</font>`<font style="color:rgb(0,0,0);"> (直接响应)</font>
    * `<font style="color:#DF2A3F;">route</font>`(路由规则)
  • **route**
    • 支持<font style="color:#DF2A3F;">cluster</font><font style="color:#DF2A3F;">weighted_clusters</font><font style="color:#DF2A3F;">cluster_header</font>三者之一 定义目标路由
    • 转发期间可根据<font style="color:#DF2A3F;">prefix_rewrite</font><font style="color:#DF2A3F;">host_rewrite</font>完成URL重写;
    • 可额外配置流量管理机制,例如<font style="color:#DF2A3F;">timeout</font><font style="color:#DF2A3F;">retry_policy</font><font style="color:#DF2A3F;">cors</font><font style="color:#DF2A3F;">request_mirror_policy</font><font style="color:#DF2A3F;">rate_limits</font>等;

2.2.2 L7 Front Proxy 代理配置案例

下面是一个<font style="color:#DF2A3F;">egress</font>类型的Envoy配置示例,它定义了两个<font style="color:#DF2A3F;">virtual_host</font>,不过,发往第二个<font style="color:#DF2A3F;">virtual_host</font>的请求将被重定向至第一个;

yaml 复制代码
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    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
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: web_service_1
              domains: ["*.nice.io", "nice.io"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: local_cluster }
            - name: web_service_2
              domains: ["*.dujie.com","dujie.com"]
              routes:
              - match: { prefix: "/" }
                redirect:
                  host_redirect: "www.nice.io"
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: local_cluster
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: local_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: 172.31.2.11, port_value: 8080 }
        - endpoint:
            address:
              socket_address: { address: 172.31.2.12, port_value: 8080 }

docker-compose:

yaml 复制代码
version: '3.3'

services:
  envoy:
    image: envoyproxy/envoy:v1.35.0
    volumes:
    - ./envoy.yaml:/etc/envoy/envoy.yaml
    privileged: true
    networks:
      envoymesh:
        ipv4_address: 172.31.2.2
        aliases:
        - front-proxy
    depends_on:
    - webserver01
    - webserver02

  webserver01:
    image: harbor.test.com/nginx-app:v1.0
    environment:
      - PORT=8080
    hostname: webserver01
    networks:
      envoymesh:
        ipv4_address: 172.31.2.11
        aliases:
        - webserver01

  webserver02:
    image: harbor.test.com/nginx-app:v1.0
    environment:
      - PORT=8080
    hostname: webserver02
    networks:
      envoymesh:
        ipv4_address: 172.31.2.12
        aliases:
        - webserver02

networks:
  envoymesh:
    driver: bridge
    ipam:
      config:
        - subnet: 172.31.2.0/24

启动后测试

由下面的命令及结果可知,发往第一个"domain"的请求被调度至后端主机的各端点之上

发往第二个"domain"的请求被重定向至"<font style="color:#DF2A3F;">http://www.nice.io</font>",而该Location又对应于上面的第一

个domain中的主机

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src]# curl -H "host: www.nice.io" 172.31.2.2:8080
test demoapp v1.0 !! ClientIP: 172.31.2.1, ServerName: webserver01, ServerIP: 172.31.2.11!

[root@VM_24_47_tlinux /usr/local/src]# curl -H "host: www.nice.io" 172.31.2.2:8080
test demoapp v1.0 !! ClientIP: 172.31.2.1, ServerName: webserver02, ServerIP: 172.31.2.12!

[root@VM_24_47_tlinux /usr/local/src]# curl -i -H "host: www.dujie.com" 172.31.2.2:8080
HTTP/1.1 301 Moved Permanently
location: http://www.nice.io/
date: Tue, 29 Jul 2025 07:22:09 GMT
server: envoy
content-length: 0

2.3 L7 Ingress Listener 代理配置

将运行的envoy和demoapp两个应用的容器设定为共享同一个Network Namespace来模拟Ingress代理的场景

  • envoy容器(Sidecar):监听于 0.0.0.0:80 端口,接收客户端请求,并代理至demoapp容器
  • demoapp容器:监听于127.0.0.1:8080 端口,接收来自于同一网络名称 空间中的envoy代理的请求
yaml 复制代码
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 80 }
    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
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: web_service_1
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: local_cluster }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: local_cluster
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: local_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: 127.0.0.1, port_value: 8080 }

docker-compose

yaml 复制代码
version: '3'

services:
  envoy:
    image: envoyproxy/envoy:v1.35.0
    volumes:
    - ./envoy.yaml:/etc/envoy/envoy.yaml
    privileged: true
    environment:
      - ENVOY_UID=0
      - ENVOY_GID=0
    networks:
      envoymesh:
        ipv4_address: 172.31.3.2
        aliases:
        - ingress

  webserver01:
    image: harbor.test.com/nginx-app:v1.0
    environment:
      - PORT=8080
      - HOST=127.0.0.1
    network_mode: "service:envoy"
    depends_on:
    - envoy

networks:
  envoymesh:
    driver: bridge
    ipam:
      config:
        - subnet: 172.31.3.0/24

运行后测试

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src]# curl 172.31.3.2
test demoapp v1.0 !! ClientIP: 127.0.0.1, ServerName: e3247efc115b, ServerIP: 172.31.3.2!
[root@VM_24_47_tlinux /usr/local]# curl -i 172.31.3.2
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 97
server: envoy
date: Tue, 29 Jul 2025 07:45:57 GMT
x-envoy-upstream-service-time: 1

test demoapp v1.0 !! ClientIP: 127.0.0.1, ServerName: e3247efc115b, ServerIP: 172.31.3.2!

2.4 L7 Egress Listener 代理配置

将运行的envoy和client两个应用的容器设定为共享同一个Network Namespace来模拟Egress代理的场景

  • envoy容器(Sidecar):监听于127.0.0.1:8080 套接字,接收客户端请求,并代理至demoapp容器
  • client容器:直接向同一网络名称空间中的envoy代理的127.0.0.1:80套接字发请求
yaml 复制代码
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address: { address: 127.0.0.1, port_value: 8080 }
    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
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: web_service_1
              domains: ["*"]
              routes:
              - match: { prefix: "/" }
                route: { cluster: web_cluster }
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: web_cluster
    connect_timeout: 0.25s
    type: STATIC
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: web_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: 172.31.4.11, port_value: 80 }
        - endpoint:
            address:
              socket_address: { address: 172.31.4.12, port_value: 80 }

docker-compose

yaml 复制代码
version: '3.3'

services:
  envoy:
    image: envoyproxy/envoy:v1.35.0
    volumes:
    - ./envoy.yaml:/etc/envoy/envoy.yaml
    privileged: true
    networks:
      envoymesh:
        ipv4_address: 172.31.4.2
        aliases:
        - front-proxy
    depends_on:
    - webserver01
    - webserver02

  client:
    image: test/admin-box:v1.2
    network_mode: "service:envoy"
    entrypoint: ["tail", "-f", "/dev/null"]
    depends_on:
    - envoy

  webserver01:
    image: harbor.test.com/nginx-app:v1.0
    hostname: webserver01
    networks:
      envoymesh:
        ipv4_address: 172.31.4.11
        aliases:
        - webserver01

  webserver02:
    image: harbor.test.com/nginx-app:v1.0
    hostname: webserver02
    networks:
      envoymesh:
        ipv4_address: 172.31.4.12
        aliases:
        - webserver02

networks:
  envoymesh:
    driver: bridge
    ipam:
      config:
        - subnet: 172.31.4.0/24

测试,进入 admin-box 容器

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src]# docker exec -it ef40cac7aa22 /bin/bash 
root@6fc7eba941d3 /# 
root@6fc7eba941d3 /# curl 127.0.0.1:8080/hostname
ServerName: webserver02
root@6fc7eba941d3 /# curl 127.0.0.1:8080/hostname
ServerName: webserver01
root@6fc7eba941d3 /# curl 127.0.0.1:8080/hostname
ServerName: webserver01
root@6fc7eba941d3 /# curl 127.0.0.1:8080/hostname
ServerName: webserver02

2.5 使用 SSL/TLS 保护流量

确保 HTTP 流量安全对于保护用户隐私和数据是至关重要的。下面我们来学习下如何在 Envoy 中配置 SSL 证书。

2.5.1 SSL 证书

这里我们将为 example.com 域名生成一个自签名的证书,当然如果在生产环境时候,需要使用正规 CA 机构购买的证书,或者使用 Let's Encrypt 的免费证书服务。下面的命令会在目录 certs/ 中创建一个新的证书和密钥:

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src]# mkdir certs; cd certs;
[root@VM_24_47_tlinux /usr/local/src/certs]# openssl req -nodes -new -x509 -keyout example-com.key -out example-com.crt -days 365 -subj '/CN=example.com/O=dujie.com/C=CN';
Generating a 2048 bit RSA private key
................+++
...............................................+++
writing new private key to 'example-com.key'
-----

2.5.2 流量保护

在 Envoy 中保护 HTTP 流量,需要通过添加 <font style="color:#DF2A3F;">transport_socket</font> 过滤器,该过滤器提供了为 Envoy 代理中配置的域名指定证书的功能,请求 HTTPS 请求时候,就使用匹配的证书。我们这里直接使用上一步中生成的自签名证书即可。

我们这里的 Envoy 配置文件中包含了所需的 HTTPS 支持的配置,我们添加了两个监听器,一个监听器在 8080 端口上用于 HTTP 通信,另外一个监听器在 8443 端口上用于 HTTPS 通信

在 HTTPS 监听器中定义了 HTTP 连接管理器,该代理将代理 <font style="color:#DF2A3F;">/service/1</font><font style="color:#DF2A3F;">/service/2</font> 这两个端点的传入请求,这里我们需要通过 <font style="color:#DF2A3F;">envoy.transport_sockets.tls</font> 配置相关证书,如下所示:

yaml 复制代码
transport_socket:
  name: envoy.transport_sockets.tls
  typed_config:
    # 一个监听传输套接字,用于使用 TLS 接受下游连接(客户端)。
    "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
    common_tls_context:
      tls_certificates:
        - certificate_chain:
            filename: "/usr/local/src/certs/example-com.crt"
          private_key:
            filename: "/usr/local/src/certs/example-com.key"

在 TLS 上下文中定义了生成的证书和密钥,如果我们有多个域名,每个域名都有自己的证书,则需要通过 <font style="color:#DF2A3F;">tls_certificates</font> 定义多个证书链。

2.5.3 自动跳转

定义了 TLS 上下文后,该站点将能够通过 HTTPS 提供流量了,但是如果用户是通过 HTTP 来访问的服务,为了确保安全,我们可以将其重定向到 HTTPS 版本服务上去。

在 HTTP 配置中,我们将 <font style="color:#DF2A3F;">https_redirect: true</font> 的标志添加到过滤器的配置中即可实现跳转功能。

yaml 复制代码
route_config:
  virtual_hosts:
    - name: backend
      domains:
        - "example.com"
      routes:
        - match:
            prefix: "/"
          redirect:
            path_redirect: "/"
            https_redirect: true

当用户访问网站的 HTTP 版本时,Envoy 代理将根据过滤器配置来匹配域名和路径,匹配到过后将请求重定向到站点的 HTTPS 版本去。完整的 Envoy 配置如下所示:

yaml 复制代码
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001

static_resources:
  listeners:
    - name: listener_http # 监听器的名称
      address:
        socket_address:
          address: 0.0.0.0 # 监听器的地址
          port_value: 8080 # 监听器的端口

      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
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定义http过滤器链
                  - name: envoy.filters.http.router # 调用7层的路由过滤器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "example.com"
                      routes:
                        - match:
                            prefix: "/"
                          redirect:
                            path_redirect: "/"
                            https_redirect: true

    - name: listener_https # 监听器的名称
      address:
        socket_address:
          address: 0.0.0.0 # 监听器的地址
          port_value: 8443 # 监听器的端口

      filter_chains:
        - transport_socket:
            name: envoy.transport_sockets.tls
            typed_config:
              # 一个监听传输套接字,用于使用 TLS 接受下游连接(客户端)。
              "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
              common_tls_context:
                tls_certificates:
                  - certificate_chain:
                      filename: "/usr/local/src/certs/example-com.crt"
                    private_key:
                      filename: "/usr/local/src/certs/example-com.key"
          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
                access_log:
                  - name: envoy.access_loggers.stdout
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters: # 定义http过滤器链
                  - name: envoy.filters.http.router # 调用7层的路由过滤器
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "example.com"
                      routes:
                        - match:
                            prefix: "/service/1"
                          route:
                            cluster: service1
                            prefix_rewrite: "/"
                        - match:
                            prefix: "/service/2"
                          route:
                            cluster: service2
                            prefix_rewrite: "/"

  clusters:
    - name: service1
      connect_timeout: 0.25s
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: service1
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 172.31.5.11
                      port_value: 80
    - name: service2
      connect_timeout: 0.25s
      type: STRICT_DNS
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: service2
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 172.31.5.12
                      port_value: 80

docker-compose

yaml 复制代码
version: '3.3'

services:
  envoy:
    image: envoyproxy/envoy:v1.35.0
    container_name: envoy
    volumes:
      # envoy.yaml 挂载(请确保路径对应你的配置)
      - ./envoy.yaml:/etc/envoy/envoy.yaml
      # 证书目录挂载(按你的 envoy.yaml 配置来,放 crt/key 文件)
      - ./certs:/usr/local/src/certs
    environment:
      - ENVOY_UID=0
      - ENVOY_GID=0
    privileged: true
    networks:
      envoymesh:
        ipv4_address: 172.31.5.2
        aliases:
          - front-proxy
    depends_on:
      - webserver01
      - webserver02
    # 暴露端口给主机
    ports:
      - "80:8080"    # HTTP
      - "443:8443"   # HTTPS
      - "8001:8001"  # Envoy Admin

  webserver01:
    image: test/demoapp:v1.0
    environment:
      - PORT=80  
    hostname: webserver01
    networks:
      envoymesh:
        ipv4_address: 172.31.5.11
        aliases:
          - webserver01

  webserver02:
    image: test/demoapp:v1.0
    environment:
      - PORT=80 
    hostname: webserver02
    networks:
      envoymesh:
        ipv4_address: 172.31.5.12
        aliases:
          - webserver02

networks:
  envoymesh:
    driver: bridge
    ipam:
      config:
        - subnet: 172.31.5.0/24

启动后测试,首先请求 HTTP 服务,由于配置了自动跳转,所以应该会被重定向到 HTTPS 的版本上

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src/certs]# curl -H "Host: example.com" http://localhost -i
HTTP/1.1 301 Moved Permanently
location: https://example.com/
date: Tue, 29 Jul 2025 08:46:54 GMT
server: envoy
content-length: 0

我们可以看到上面有 <font style="color:#DF2A3F;">HTTP/1.1 301 Moved Permanently</font> 这样的重定向响应信息。然后我们尝试直接请求 HTTPS 的服务:

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src/certs]# curl -k -H "Host: example.com" https://localhost/service/1 -i
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 98
server: envoy
date: Tue, 29 Jul 2025 08:52:20 GMT
x-envoy-upstream-service-time: 1

test demoapp v1.0 !! ClientIP: 172.31.5.1, ServerName: webserver01, ServerIP: 172.31.5.11!
[root@VM_24_47_tlinux /usr/local/src/certs]# curl -k -H "Host: example.com" https://localhost/service/2 -i
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 98
server: envoy
date: Tue, 29 Jul 2025 08:52:24 GMT
x-envoy-upstream-service-time: 1

test demoapp v1.0 !! ClientIP: 172.31.5.1, ServerName: webserver02, ServerIP: 172.31.5.12!

我们可以看到通过 HTTPS 进行访问可以正常得到对应的响应,需要注意的是由于我们这里使用的是自签名的证书,所以需要加上 -k 参数来忽略证书校验,如果没有这个参数则在请求的时候会报错:

bash 复制代码
[root@VM_24_47_tlinux /usr/local/src/certs]# curl -H "Host: example.com" https://localhost/service/2 -i
curl: (60) Peer's certificate issuer has been marked as not trusted by the user.
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

我们也可以通过管理页面去查看证书相关的信息,上面我们启动容器的时候绑定了宿主机的 8001 端口,所以我们可以通过访问 <font style="color:#DF2A3F;">http://localhost:8001/certs</font> 来获取到证书相关的信息:

相关推荐
David爱编程3 小时前
理解Pod的容器共享资源模型
云原生·容器·kubernetes
Johny_Zhao4 小时前
SeaTunnel的搭建部署以及测试
linux·网络·网络安全·信息安全·kubernetes·云计算·containerd·devops·seatunnel·系统运维
云和数据.ChenGuang8 小时前
envFrom 是一个 列表类型字段bug
大数据·云原生·容器·kubernetes·云计算·bug
__Smile°11 小时前
Gitlab+Jenkins+K8S+Registry 建立 CI/CD 流水线
linux·ci/cd·docker·kubernetes·gitlab·jenkins
用户307500930379313 小时前
配置containerd镜像管理
kubernetes
�FENG15 小时前
Prometheus 监控平台部署 (云原生环境)
云原生·kubernetes·prometheus·监控告警
only_Klein21 小时前
K8S部署ELK(二):部署Kafka消息队列
elk·kafka·kubernetes
David爱编程1 天前
kube-proxy与CNI 的关系详解:职责划分与协同机制
云原生·容器·kubernetes
追风筝的小青年2 天前
ubuntu24中部署k8s 1.30.x-底层用docker
docker·容器·kubernetes