服務網格代理Envoy入門


目錄

目錄 1

1. 前言 2

1.1. Envoy是什么 2

1.2. 如何入門Envoy 2

1.3. Envoy的源碼在哪 2

2. 縮略語 3

3. Envoy架構 3

3.1. 外部架構 3

3.2. 內部架構 4

4. Envoy配置文件 5

4.1. admin 5

4.2. static_resources 6

4.2.1. listeners 6

4.2.2. clusters 6

4.3. dynamic_resources 8

4.3.1. 怎么理解 8

4.3.2. 全動態配置 8

4.3.3. gRPC接口 9

4.3.4. REST接口 10

5. 試跑體驗 10

5.1. 體驗目標 10

5.2. 下載Envoy鏡像 10

5.3. 准備Envoy配置文件 10

5.4. 啟動Envoy容器 12

5.5. 啟動Envoy進程 12

5.6. 體驗效果 13

6. 動態配置 13

6.1. 動態配置EDS 13

6.1.1. Envoy配置文件 13

6.1.2. EDS服務源碼 15

6.1.3. 啟動EDS進程 17

6.1.4. 啟動Envoy進程 18

 

1. 前言

Envoy入門並不簡單,可以說有些陡峭,本文盡可能幫助降低入門門檻。本文內容主要基於Envoy-1.12.2版本,官方鏈接:

https://www.envoyproxy.io/docs/envoy/v1.12.2/configuration/overview/v2_overview

1.1. Envoy是什么

Envoy是Lyft開源的一個C++實現的代理(Proxy),和Nginx及HAProxy類似,可代理L3/L4層和L7層。代理是它最核心和基礎的功能,它也是服務網格框架Istio的Sidecar。

1.2. 如何入門Envoy

從研究Envoy的配置文件開始,Envoy支持多種格式的配置文件:YAML、JSON和PB等,其中YAML使用最多,官方示例基本都是YAML格式的。

配置文件中涉及多個概念,所以最好先將概念了解清楚,然后使用最簡單的配置走一遍流程,如果會用Docker則這一步會比較簡單。在了解概念之前,最好先了解Envoy的基本架構,以弄明白各概念間的協作關系。

1.3. Envoy的源碼在哪

Envoy的源碼托管在Github上:https://github.com/envoyproxy/envoy

2. 縮略語

縮寫

全寫

說明

lb

load balance

負載均衡

lb_policy

load balance policy

負載均衡策略

SNI

Server Name Indication

TLS的擴展,用來解決一個服務器擁有多個域名或證書。

工作原理:在連接到服務器建立SSL鏈接前,先發送要訪問的域名,服務器根據這個域名返回一個合適的證書。

TLS

Transport Layer Security

傳輸層安全性協議

L3

Layer 3

網絡層(IP)

L4

Layer 4

傳輸層(PORT)

L7

Layer 7

應用層(HTTP)

L2

Layer 2

數據鏈路層(MAC)

YAML

YAML Ain't a Markup Language

以數據做為中心的標記語(Yet Another Markup Language)

JSON

JavaScript Object Notation

JS 對象簡譜,一種輕量級的數據交換格式

REST

Representational State Transfer

表述性狀態傳遞,一種軟件架構風格

gRPC

Google RPC

谷歌開源的RPC框架

pb

Protocol buffers

谷歌開發的一種數據描述語言,常被簡稱為protobuf

3. Envoy架構

3.1. 外部架構

下圖展示了Envoy的外部架構,從圖很容易看到服務間、應用和服務間都是通過Envoy串聯起來的,Envoy是它們間的高速公路,Envoy的作用就是在各部分間轉發讀寫請求(也可叫讀寫操作),所以Envoy是名副其實的代理(Proxy)。

3.2. 內部架構

外部架構展示了Envoy的作用,但無法窺見它是如何實現的,Envoy的內部結構展示出了它的實現原理。

 

其中過濾器(Filter)是Envoy的核心中的核心,多Filter形成了過濾器鏈(Chain),和iptables的Chain類似,請求經過過濾器鏈后到達目的服務(Service)。

 

如果將Envoy看成黑盒,則它所處位置可定義成如下圖所示:

4. Envoy配置文件

對Envoy架構有初步了解后,再通過對Enovy配置文件的了解,將對掌握Enovy十分有幫助。Envoy的配置文件定義了代理轉發規則,規則也可通過gRPC或REST動態拉取。

Envoy配置文件支持四種書寫格式:json、yaml、pb和pb_text,官方文檔和示例基本使用yaml格式。可將Envoy配置文件分解成三大部分:

admin

定義Envoy進程的管理端口

static_resources

靜態配置,定義靜態資源

dynamic_resources

態配置,定義動態資源,static_resources中一些配置可通過服務調用(接口調用)動態拉取。

4.1. admin

管理配置,比較簡單,內容一般如下:

admin:

  access_log_path: /tmp/admin_access.log

  address:

    socket_address: { address: 192.168.0.1, port_value: 9901 }

 

通過admin可以查詢到大量的配置、監控和運維等數據:

4.2. static_resources

static_resources又可分解為兩大部分:

listeners

定義監聽器,服務下游(downstream)

clusters

定義上游(upstream)的微服務集群

4.2.1. listeners

在listeners中,可以定義多個listener,每個listener由三部分組成:

name

定義listener名稱

address

定義listener的監聽地址和端口

filter_chains

定義過濾器(Filter)鏈,這是最核心和最復雜的部分

4.2.2. clusters

定義上游集群,Envoy最基礎的功能即是將來自下游的請求轉發給上游。clusters的內容包括五大部分,其中load_assignment部分是核心:

name

下游集群名,可定義一或多個

connect_timeout

連接上游的超時時長,可帶單位,如“0.25s”表示250毫秒

type

集群類型,如STATIC、STRICT_DNS、LOGICAL_DNS和EDS等

lb_policy

負載均衡策略,如ROUND_ROBIN表示輪詢

load_assignment

type為STATIC、STRICT_DNS和LOGICAL_DNS時,如果type為EDS則使用eds_cluster_config

 

lb_policy可取值:

ROUND_ROBIN

輪詢

LEAST_REQUEST

請求最少

RING_HASH

環形哈希

RANDOM

隨機

MAGLEV

一致性哈希算法

CLUSTER_PROVIDED

定制

 

load_assignment示例:

load_assignment:

  cluster_name: some_service

  endpoints:

  - lb_endpoints:

    - endpoint:

      address:

        socket_address:

          address: 127.0.0.1

          port_value: 1234

 

clusters示例:

clusters:

- name: some_service

  connect_timeout: 0.25s

  type: STATIC

  lb_policy: ROUND_ROBIN

  load_assignment:

    cluster_name: some_service

    endpoints:

    - lb_endpoints:

      - endpoint:

          address:

            socket_address:

              address: 127.0.0.1

              port_value: 1234

4.3. dynamic_resources

static_resources中的四種動態資源可通過動態服務發現(xDS)來動態配置,共有六種動態服務發現:

縮寫

全寫

說明

LDS

Listener Discovery Service

監聽器(資源)發現服務,解決有哪些監聽器問題

CDS

Cluster Discovery Service

集群(資源)發現服務,解決有哪些集群問題

RDS

Route Discovery Service

路由(資源)發現服務,解決有哪些路由規則問題

EDS

Endpoint Discovery Service

端點(資源)發現服務,解決集群內有哪些端點問題

ADS

Aggregated Discovery Service

這並不是一個獨立的發現服務,而是對其它發現服務的聚合

 

動態配置在啟動Envoy進程時,需要指定id和cluster,否則報錯“node 'id' and 'cluster' are required.”。

4.3.1. 怎么理解

怎么理解dynamic_resources?在static_resouces基礎上,動態拉取動態資源,即有動態資源配置不是直接寫在配置中,而是需要通過服務調用動態取得,Envoy支持gRPC/HTTP2和REST兩種方式動態拉取。

4.3.2. 全動態配置

可以部分動態配置,也可全動態配置。下列為一個官方的全動態配置示例:

admin:

  access_log_path: /tmp/admin_access.log

  address:

    socket_address: { address: 127.0.0.1, port_value: 9901 }

 

dynamic_resources:

  lds_config:

    api_config_source:

      api_type: GRPC

      grpc_services:

        envoy_grpc:

          cluster_name: xds_cluster

  cds_config:

    api_config_source:

      api_type: GRPC

      grpc_services:

        envoy_grpc:

          cluster_name: xds_cluster

 

static_resources:

  clusters:

  - name: xds_cluster

    connect_timeout: 0.25s

    type: STATIC

    lb_policy: ROUND_ROBIN

    http2_protocol_options: {}

    upstream_connection_options:

      # configure a TCP keep-alive to detect and reconnect to the admin

      # server in the event of a TCP socket half open connection

      tcp_keepalive: {}

    load_assignment:

      cluster_name: xds_cluster

      endpoints:

      - lb_endpoints:

        - endpoint:

            address:

              socket_address:

                address: 127.0.0.1

                port_value: 5678

 

上例為gRPC/HTTP2方式動態拉取配置,提供配置的服務名為xds_cluster,服務端口為“127.0.0.1:5678”。

4.3.3. gRPC接口

1) CDS

POST /envoy.api.v2.ClusterDiscoveryService/StreamClusters

 

gRPC服務定義在文件cds.proto,鏈接地址:

https://github.com/envoyproxy/envoy/blob/v1.12.2/api/envoy/api/v2/cds.proto

 

2) EDS

POST /envoy.api.v2.EndpointDiscoveryService/StreamEndpoints

 

gRPC服務定義在文件eds.proto,鏈接地址:

https://github.com/envoyproxy/envoy/blob/v1.12.2/api/envoy/api/v2/eds.proto

 

3) LDS

POST /envoy.api.v2.ListenerDiscoveryService/StreamListeners

 

gRPC服務定義在文件lds.proto,鏈接地址:

https://github.com/envoyproxy/envoy/blob/v1.12.2/api/envoy/api/v2/lds.proto

 

4) RDS

POST /envoy.api.v2.RouteDiscoveryService/StreamRoutes

 

gRPC服務定義在文件rds.proto,鏈接地址:

https://github.com/envoyproxy/envoy/blob/v1.12.2/api/envoy/api/v2/rds.proto

4.3.4. REST接口

1) CDS

POST /v2/discovery:clusters

 

 

2) EDS

POST /v2/discovery:endpoints

 

3) LDS

POST /v2/discovery:listeners

 

4) RDS

POST /v2/discovery:routes

5. 試跑體驗

試跑Enovy要求有Docker基礎(如無基礎可參考《Docker入門之安裝Docker》和《Docker入門之創建鏡像初步》),從源代碼構建會有些復雜,所以本文直接使用官方提供的Docker景象作為試跑對象。試跑前提要求Docker環境已准備好,並且試跑機要能訪問外網。

5.1. 體驗目標

代理https://www.baidu.com,當訪問本機的8080端口時,實際為訪問被代理的https://www.baidu.com。

5.2. 下載Envoy鏡像

執行下列命令拉取Envoy的Docker鏡像:

docker pull envoyproxy/envoy

5.3. 准備Envoy配置文件

在本地主備配置文件“/tmp/bd.yaml”,文件內容如下:

$ cat /tmp/bd.yaml

admin:

  access_log_path: /tmp/admin_access.log

  address:

    socket_address:

      protocol: TCP

      address: 0.0.0.0 # 管理地址

      port_value: 8081 # 管理端口

 

static_resources:

  listeners: # 監聽器數組

  - name: listener_0 # 監聽器

    address:

      socket_address:

        protocol: TCP

        address: 0.0.0.0 # 監聽地址

        port_value: 8080 # 監聽端口

    filter_chains: # 過濾器鏈

    - filters: # 過濾器數組

      - name: envoy.http_connection_manager # 過濾器名

        typed_config:

          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager

          stat_prefix: ingress_http

          route_config: # 路由配置

            name: local_route # 路由配置名

            virtual_hosts: # 虛擬主機數組

            - name: local_service

              domains: ["*"] # 需代理的域名數組

              routes: # 定義路由

              - match:

                  prefix: "/" # 匹配規則

                route:

                  host_rewrite: www.baidu.com # 將HOST重寫為

                  cluster: bd_service # 下游集群名,通過它找到下游集群的配置

          http_filters:

          - name: envoy.router

  clusters: # 下游集群數組

  - name: bd_service # 下游集群名

    connect_timeout: 0.25s # 連接下游的超時時長

    type: LOGICAL_DNS

    # Comment out the following line to test on v6 networks

    dns_lookup_family: V4_ONLY # 域名查找范圍,這里表示只查找IPV4地址

    lb_policy: ROUND_ROBIN # 負載均衡策略

    load_assignment:

      cluster_name: bd_service

      endpoints:

      - lb_endpoints:

        - endpoint:

            address:

              socket_address:

                address: www.baidu.com

                port_value: 443

    transport_socket:

      name: envoy.transport_sockets.tls

      typed_config:

        "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext

        sni: www.baidu.com

 

 

由於代理的是域名www.baidu.com,所以clusterstype值需為LOGICAL_DNS或strict_dns,type還有如下幾個取值(不區分大小寫):

STATIC

缺省值,在集群中列出所有可代理的主機(Endpoints)

LOGICAL_DNS

Envoy使用DNS添加主機,但如果DNS不再返回時,也不會丟棄

STRICT_DNS

Envoy將監控DNS,而每個匹配的A記錄都將被認為是有效的

OriginalDst

 

EDS

Envoy調用一個外部的gRPC或REST服務查找被代理的主機(Endpoints)

自定義值

 

 

訪問https://www.baidu.com,一定要配置transport_socket,否則將報錯“upstream connect error or disconnect/reset before headers. reset reason: connection failure”。如果是訪問http://www.baidu.com,則不用配置transport_socket,sni不是必須的。

5.4. 啟動Envoy容器

在宿主機上,執行下列命令啟動Envoy容器:

docker run -it --rm -v=/tmp:/tmp -p 8080:8080 -p 8081:8081 envoyproxy/envoy bash

 

可在宿主機上執行命令“docker ps|grep envoy”,檢查Envoy容器是否起來了。

5.5. 啟動Envoy進程

在Envoy容器中,執行下列命令拉起Envoy進程:

envoy -c /tmp/bd.yaml

 

啟動成功可看到如下日志:

[info][config] [source/server/configuration_impl.cc:62] loading 0 static secret(s)

[info][config] [source/server/configuration_impl.cc:68] loading 1 cluster(s)

[info][config] [source/server/configuration_impl.cc:72] loading 1 listener(s)

[info][config] [source/server/configuration_impl.cc:97] loading tracing configuration

[info][config] [source/server/configuration_impl.cc:117] loading stats sink configuration

[info][main] [source/server/server.cc:549] starting main dispatch loop

[info][upstream] [source/common/upstream/cluster_manager_impl.cc:161] cm init: all clusters initialized

[info][main] [source/server/server.cc:528] all clusters initialized. initializing init manager

[info][config] [source/server/listener_manager_impl.cc:578] all dependencies initialized. starting workers

5.6. 體驗效果

假設Envoy容器所在機器IP為192.168.1.21,則訪問http://192.168.1.21:8080等同於訪問https://www.baidu.com。

6. 動態配置

6.1. 動態配置EDS

Envoy定時訪問EDS服務取EDS配置。

6.1.1. Envoy配置文件

$ cat /tmp/bd.yaml

admin:

  access_log_path: /tmp/admin_access.log

  address:

    socket_address:

      protocol: TCP

      address: 0.0.0.0 # 管理地址

      port_value: 8081 # 管理端口

 

static_resources:

  listeners: # 監聽器數組

  - name: listener_0 # 監聽器

    address:

      socket_address:

        protocol: TCP

        address: 0.0.0.0 # 監聽地址

        port_value: 8080 # 監聽端口

    filter_chains: # 過濾器鏈

    - filters: # 過濾器數組

      - name: envoy.http_connection_manager # 過濾器名

        typed_config:

          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager

          stat_prefix: ingress_http

          route_config: # 路由配置

            name: local_route # 路由配置名

            virtual_hosts: # 虛擬主機數組

            - name: local_service

              domains: ["*"] # 需代理的域名數組

              routes: # 定義路由

              - match:

                  prefix: "/" # 匹配規則

                route:

                  host_rewrite: www.baidu.com # 將HOST重寫為

                  cluster: bd_service # 下游集群名,通過它找到下游集群的配置

          http_filters:

          - name: envoy.router

  clusters: # 下游集群數組

  - name: bd_service # 下游集群名

    connect_timeout: 0.25s # 連接下游的超時時長

    type: eds

    lb_policy: ROUND_ROBIN # 負載均衡策略

    eds_cluster_config:

      eds_config:

        api_config_source:

          api_type: rest

          refresh_delay: "10s" # 動態一定要有這個配置

          cluster_names: [xds_cluster] # 這里並不提供靜態的endpoints,需訪問EDS服務得到

    transport_socket:

      name: envoy.transport_sockets.tls

      typed_config:

        "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext

        sni: www.baidu.com

  - name: xds_cluster

    connect_timeout: 0.25s

    type: static

    lb_policy: ROUND_ROBIN

    load_assignment:

      cluster_name: xds_cluster

      endpoints:

      - lb_endpoints:

        - endpoint:

            address:

              socket_address:

                address: 127.0.0.1 # EDS的服務地址

                port_value: 2020 # EDS的服務端口

6.1.2. EDS服務源碼

// 演示Envoy的動態EDS(Endpoint Discovery Service)

// 執行命令“go build eds.go ”,即生成可執行程序eds

package main

 

import (

  "encoding/json"

  "fmt"

  "net/http"

  "time"

)

 

// {

//   "version_info": "0",

//   "resources": [

//     {

//       "@type": "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment",

//       "cluster_name": "some_service",

//       "endpoints": [

//         {

//           "lb_endpoints": [

//             {

//               "endpoint": {

//                 "address": {

//                   "socket_address": {

//                     "address": "127.0.0.2",

//                     "port_value": 1234

//                   }

//                 }

//               }

//             }

//           ]

//         }

//       ]

//     }

//   ]

// }

 

type SocketAddress struct {

  Address string `json:"address"`

  PortValue int `json:"port_value"`

}

 

type Address struct {

  SocketAddress SocketAddress `json:"socket_address"`

}

 

type Endpoint struct {

  Address Address `json:"address"`

}

 

type LbEndpoint struct {

  Endpoint Endpoint `json:"endpoint"`

}

 

type LbEndpoints struct {

  LbEndpoints []LbEndpoint `json:"lb_endpoints"`

}

 

type Resource struct {

  Type string `json:"@type"`

  ClusterName string `json:"cluster_name"`

  Endpoints []LbEndpoints `json:"endpoints"`

}

 

type EDS struct {

  VersionInfo string `json:"version_info"`

  Resources []Resource `json:"resources"`

}

 

func DiscoveryEndpointsHandler(w http.ResponseWriter, r *http.Request) {

  // LbEndpoint

  var lb_endpoint1 LbEndpoint

  lb_endpoint1.Endpoint.Address.SocketAddress.Address = "180.101.49.12"

  lb_endpoint1.Endpoint.Address.SocketAddress.PortValue = 443

 

  // LbEndpoint

  var lb_endpoint2 LbEndpoint

  lb_endpoint2.Endpoint.Address.SocketAddress.Address = "180.101.49.11"

  lb_endpoint2.Endpoint.Address.SocketAddress.PortValue = 443

 

  // LbEndpoint

  var lb_endpoint3 LbEndpoint

  lb_endpoint3.Endpoint.Address.SocketAddress.Address = "14.215.177.38"

  lb_endpoint3.Endpoint.Address.SocketAddress.PortValue = 443

 

  // LbEndpoints

  var lb_endpoints LbEndpoints

  lb_endpoints.LbEndpoints = append(lb_endpoints.LbEndpoints, lb_endpoint1)

  lb_endpoints.LbEndpoints = append(lb_endpoints.LbEndpoints, lb_endpoint2)

  lb_endpoints.LbEndpoints = append(lb_endpoints.LbEndpoints, lb_endpoint3)

 

  // Resource

  var resource Resource

  resource.Type = "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment"

  resource.ClusterName = "bd_service"

  resource.Endpoints = append(resource.Endpoints, lb_endpoints)

 

  // EDS

  var eds EDS

  eds.VersionInfo = "0"

  eds.Resources = append(eds.Resources, resource)

 

  // struct to json

  jsonBytes, err := json.Marshal(eds)

  if err != nil {

    fmt.Println(err)

  }

 

  // json to string

  str := string(jsonBytes)

 

  // output json string

  now := time.Now()

  // 注意只能是“2006-01-02 15:04:05

  fmt.Printf("[%s] %s\n", now.Format("2006-01-02 15:04:05"), string(jsonBytes))

  fmt.Println(str)

  fmt.Fprintln(w, str)

}

 

func main() {

  http.HandleFunc("/v2/discovery:endpoints", DiscoveryEndpointsHandler)

  http.ListenAndServe("0.0.0.0:2020", nil)

}

6.1.3. 啟動EDS進程

先將程序xds復制到容器中(以容器ID為0779d56f4f65為例):

docker cp eds 0779d56f4f65:/tmp

 

進入容器:

docker container exec -it 0779d56f4f65 /bin/bash

 

然后,在Envoy容器中啟動EDS進程:

/tmp/eds

6.1.4. 啟動Envoy進程

在Envoy容器中啟動Envoy進程:

envoy -c /tmp/bd.yaml --service-cluster xds_cluster --service-node 1


  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM