使用Envoy代替Nginx作为反向代理服务

1. 介绍

Envoy 是一个由 Lyft 开发并开源的高性能、可扩展的服务代理,属于 “Service Mesh” 领域。它主要用作反向代理、负载均衡器和 API 网关,但其灵活的架构使其也可以用于其他用途。Envoy 设计之初的目标就是为云原生应用提供更加智能的网络通信能力。Envoy 相比 Nginx 的优势如下:

云原生设计

  • Envoy:专为微服务架构设计,具有丰富的动态配置能力,适用于云原生环境和 Kubernetes 中的服务网格。
  • Nginx:虽然可以在微服务环境中使用,但最初并不是为云原生设计的,动态配置和服务发现能力相对较弱。

动态配置

  • Envoy:支持动态配置更新,无需重启即可应用新配置,减少了服务中断的风险。
  • Nginx:虽然支持某些程度的热加载,但大部分配置更新依然需要重启服务,导致短暂的服务中断。

Service Mesh 支持

  • Envoy:是许多 Service Mesh 解决方案(如 Istio)的核心数据平面代理,提供先进的流量管理功能,如熔断、限流、负载均衡和故障注入。
  • Nginx:虽然可以通过 Lua 脚本等方式实现某些功能,但不如 Envoy 集成得那么深入和简便。

高级协议支持

  • Envoy:支持 HTTP/1.1、HTTP/2 和 gRPC 的高级负载均衡和代理能力,还提供了全面的流量管理功能。
  • Nginx:也支持这些协议,但在 gRPC 和 HTTP/2 处理方面,Envoy 更加灵活和高效。

可观测性

  • Envoy:提供了全面的可观测性,包括分布式追踪、详细的统计数据、日志和指标,这些对于微服务架构中的故障排查和性能优化非常有帮助。
  • Nginx:虽然也提供日志和基本的指标收集,但在分布式追踪和深入的指标收集上,Envoy 更加强大。

扩展性

  • Envoy:设计为高度可扩展和可编程,支持通过插件扩展其功能,特别适合复杂的微服务架构。
  • Nginx:虽然也有丰富的模块化支持,但其扩展能力和动态配置的灵活性不如 Envoy。

多协议支持

  • Envoy:不仅支持 HTTP,还支持 TCP、UDP、gRPC、Redis 等多种协议的代理,适应性更强。
  • Nginx:主要针对 HTTP,但也支持 TCP 和 UDP,不过 Envoy 的多协议支持更加全面和灵活。

2. 安装Envoy

笔者的项目最早使用Nginx作为反向代理服务,为了在反向代理连接中利用multiplexing,决定将应用服务器的协议升级到HTTP2,但是Nginx的反向代理只支持HTTP1.1,在经过一系列调研后,决定使用Envoy替换掉Nginx作为新的反向代理服务。
笔者的服务器系统为ubuntu 22.04 LTS,可以直接在Envoy官网下载二进制包,下载链接在此,目前最新版本为1.31.0,下载后得到名为envoy-1.31.0-linux-x86_64的文件,将该文件放到/usr/local/bin下并改名为envoy

$ sudo cp envoy-1.31.0-linux-x86_64 /usr/local/bin/envoy
$ sudo chmod 777 /usr/local/bin/envoy

可以查看一下envoy的版本:

$ sudo envoy --version
envoy  version: 7b8baff1758f0a584dcc3cb657b5032000bcb3d7/1.31.0/Clean/RELEASE/BoringSSL

3. 配置Envoy

为便于管理,我们可以将envoy配置为systemd服务,创建文件/etc/systemd/system/envoy.service,编辑内容如下:

[Unit]
Description=Envoy Proxy
After=network.target

[Service]
ExecStart=/usr/local/bin/envoy -l debug -c /etc/envoy/envoy.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
LimitNOFILE=1048576
LimitNPROC=1048576
TimeoutStartSec=0
StartLimitBurst=3
StartLimitInterval=60s
TasksMax=infinity
User=root
Group=root

[Install]
WantedBy=multi-user.target

从服务配置中可以看到envoy自身的配置文件应位于/etc/envoy/envoy.yaml,我们稍后进行配置,首先了解下服务的整体架构:

  • envoy安装在192.168.3.31,监听80和443端口
  • 应用服务器1位于192.168.3.32,监听8443端口
  • 应用服务器2位于192.168.3.33,监听8443端口

证书的创建以及应用服务器的HTTP2协议配置过程不在此赘述,假设envoy和应用服务的证书已经配置好,编辑配置文件/etc/envoy/envoy.yaml如下:

# 管理员控制台的相关配置
admin:
  access_log_path: "/var/log/envoy/admin_access.log"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  listeners:	
  # 为80端口创建一个监听器
  - name: main_listener_http
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    per_connection_buffer_limit_bytes: 32768  # 每个连接的缓存大小限制在32 KiB
    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
          codec_type: AUTO
          stat_prefix: ingress_http
          use_remote_address: true
          normalize_path: true
          merge_slashes: true
          path_with_escaped_slashes_action: UNESCAPE_AND_REDIRECT
          common_http_protocol_options:
            idle_timeout: 3600s
            headers_with_underscores_action: REJECT_REQUEST
          stream_idle_timeout: 300s
          request_timeout: 300s
          generate_request_id: false
          access_log:
          - name: envoy.access_loggers.file
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
              path: "/var/log/envoy/access.log"
              log_format: # 访问日志格式与nginx相同,便于迁移
                text_format: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% - - [%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %BYTES_SENT% \"%REQ(REFERER)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-FORWARDED-FOR)%\"\n"
          route_config:
            name: local_route
            request_headers_to_add:
            - header:
                key: X-Client-IP
                value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
            - header:
                key: X-Proxy
                value: "envoy"
            - header:
                key: X-Request-Start
                value: "%START_TIME(%s.%3f)%"
            virtual_hosts:
            - name: backend
              domains:
              - "192.168.3.31:80" # 该域名下的请求匹配当前路由规则
              routes:
              - match:
                  prefix: "/"
                redirect: # 所有http请求重定向为https
                  scheme_redirect: "https"
                  port_redirect: 443
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  # 为443端口创建一个监听器
  - name: main_listener_https 
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 443
    listener_filters:
    - name: "envoy.filters.listener.tls_inspector"
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
    per_connection_buffer_limit_bytes: 32768  # 每个连接的缓存大小限制在32 KiB
    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
          codec_type: AUTO
          stat_prefix: ingress_http
          use_remote_address: true
          normalize_path: true
          merge_slashes: true
          path_with_escaped_slashes_action: UNESCAPE_AND_REDIRECT
          common_http_protocol_options:
            idle_timeout: 3600s
            headers_with_underscores_action: REJECT_REQUEST
          http2_protocol_options:
            max_concurrent_streams: 100
            initial_stream_window_size: 65536
            initial_connection_window_size: 1048576
          stream_idle_timeout: 300s
          request_timeout: 300s
          generate_request_id: false
          access_log:
          - name: envoy.access_loggers.file
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
              path: "/var/log/envoy/access.log"
              log_format: # 访问日志格式与nginx相同,便于迁移
                text_format: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT% - - [%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %BYTES_SENT% \"%REQ(REFERER)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-FORWARDED-FOR)%\"\n"
          route_config:
            name: local_route
            request_headers_to_add:
            - header:
                key: X-Client-IP
                value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
            - header:
                key: X-Proxy
                value: "envoy"
            - header:
                key: X-Request-Start
                value: "%START_TIME(%s.%3f)%"
            virtual_hosts:
            - name: backend
              domains:
              - "192.168.3.31:443"
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: service # 将请求代理到service集群
                  max_stream_duration:
                    max_stream_duration: 0s
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
      # envoy证书配置
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            alpn_protocols: ["h2", "http/1.1"]
            tls_certificates:
            - certificate_chain:
                filename: "/etc/envoy/cert/server.pem"
              private_key:
                filename: "/etc/envoy/cert/server.key"
  clusters:
  # 后端集群配置
  - name: service # 名为service的代理集群,与route_config中配置的一致
    per_connection_buffer_limit_bytes: 32768  # 每个连接的缓存大小限制在32 KiB
    type: STRICT_DNS
    load_assignment:
      cluster_name: service
      endpoints: # 两个后端服务器配置
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 192.168.3.32
                port_value: 8443
        - endpoint:
            address:
              socket_address:
                address: 192.168.3.33
                port_value: 8443
    transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
          common_tls_context:
            alpn_protocols: ["h2", "http/1.1"]
    # http2相关配置
    typed_extension_protocol_options:
      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
        "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
        explicit_http_config:
          http2_protocol_options:
            initial_stream_window_size: 65536  # 64 KiB
            initial_connection_window_size: 1048576  # 1 MiB
overload_manager:
  resource_monitors:
  - name: envoy.resource_monitors.global_downstream_max_connections
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig
      max_active_downstream_connections: 50000

编辑完毕后,启动envoy服务:

$ sudo systemctl start envoy

现在可以通过浏览器在443端口访问服务了,并且可以通过Chrome中的开发人员工具看到请求均使用http2协议,除此之外可以在后端应用服务处理请求时打印协议版本号,也可以发现从envoy向应用服务器发送的请求也使用http2协议。

4. 其他

由于Envoy官方并未提供日志切分压缩功能,因此需要使用logrotate工具处理,先确认下logrotate是否在cron任务中:

sudo vi /etc/cron.daily/logrotate

然后创建文件/etc/logrotate.d/envoy,内容如下:

/var/log/envoy/access.log {
    daily
    rotate 7
    missingok
    compress
    notifempty
    nocreate
    sharedscripts
    copytruncate
}

该配置对envoy的访问日志/var/log/envoy/access.log进行切分并压缩,且只保留7天内的日志

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值