一、Envoy介紹
官方文檔解釋:
Envoy是專為大型現SOA(面向服務架構)設置的L7代理和通信總線。該項目源於以下理念:網絡對應用程序來說應該是透明的。當網絡和應用程序出現問題時,應該很容易確定問題的根源。
為了做到上述目標,Envoy提供了以下高級功能:
- 進程外架構:Envoy是一個獨立進程,伴隨每個應用程序服務運行。所有的Envoy形成一個透明的通信網格,每個應用程發送消息到本地主機或從本地主機接受消息,但不知道網絡拓撲。Envoy可以使用任何應用程序語言,並且易升級部署。
- 現代C++11代碼庫:性能優異。
- L3/L4過濾器架構、HTTP L7過濾器架構、推薦使用HTTP/2。
- gRPC支持:方便支持gRPC,特別是在負載和代理上。
- 服務發現、健康檢查、高級的負載均衡方案、Tracing、統計與監控
- 動態配置:通過動態配置API實現配置的動態調整,而無需重啟Envoy服務。
Envoy的一些術語:
二、Envoy實踐
1. Service Mesh
Service Mesh用於處理服務間通信的基礎設施層,用於在雲原生應用復雜的服務拓撲中實現可靠的請求傳遞。
在實踐中,Service Mesh基本來說是一組輕量級的服務代理和應用邏輯的服務在一起,同生共死,並且對於應用服務是透明的。通過sidecar,Service Mesh會抽離為服務中的通用功能,比如服務注冊發現,負載均衡,熔斷降級,限流擴容,監控等功能,把這些功能放到sidecar中,通過代理proxy的方式於業務服務進行通信。
目前Service Mesh已經進入了以Istio為代表的第二代,由Data Panel(Proxy)、Control Panel兩部分組成。Istio是對Service Mesh的產品化實踐,幫助微服務實現了分層解耦,架構圖如下:
邏輯上Istio分為數據平面(data panel)和控制平面(control panel),數據平面由一些智能代理組成,微服務之間的網絡通信,控制平面負責對智能代理進行管理和配置。Istio自己沒有實現Data Panel,而是在現有的Data Panel實現上做了Control Panel來達成目標。而今天的主角Envoy作為一個主打的Service Mesh方案的proxy,其設計處處考慮Service Mesh。
2. 快速開始運行簡單示例
Envoy目前不提供單獨的預先編譯好的二進制文件,但提供了Docker鏡像。如果希望在Docker容器外使用Envoy,則需要構建它。Envoy提供了兩個版本的API,v1和v2,現階段通常使用v2。v2的API提供了兩種方式的訪問,一種是HTTP REST方式,一種是GRPC方式。Envoy的啟動配置文件分為兩種:靜態配置和動態配置。靜態配置是將所有信息都放在配置文件中,啟動的時候直接加載。動態配置需要提供一個Envoy的服務端,用於動態生成Envoy需要的服務發現接口(XDS),通過發現服務來動態的調整配置信息。
讓我們通過靜態配置方式運行一個簡單實例並進行分析。
我們拉取Envoy鏡像后,Envoy被安裝到/usr/local/bin目錄下。envoy進程啟動的時候需要指定一些參數,最重要的是--config-yaml參數,用於指定envoy進程啟動的時候需要讀取的配置文件地址。Docker配置文件默認放在/etc/envoy中,名字為envoy.yaml。所以啟動容器的時候需要將自定義的envoy.yaml配置文件掛載到指定目錄下替換掉默認的配置文件。
讓我們運行一個envoy實例,通過查看默認配置文件來看看默認功能是什么。
$ docker pull envoyproxy/envoy $ docker run --rm -d -p 10000:10000 --name envoy envoy proxy/envoy $ docker exec -it envoy /bin/bash #進入envoy容器 $ cat /etc/envoy/envoy.yaml #查看默認配置文件
默認配置文件內容如下:

admin: access_log_path: /tmp/admin_access.log address: socket_address: protocol: TCP address: 127.0.0.1 port_value: 9901 static_resources: listeners: - name: listener_0 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.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.google.com cluster: service_google http_filters: - name: envoy.filters.http.router clusters: - name: service_google connect_timeout: 0.25s type: LOGICAL_DNS # Comment out the following line to test on v6 networks dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN load_assignment: cluster_name: service_google endpoints: - lb_endpoints: - endpoint: address: socket_address: address: www.google.com port_value: 443 transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext sni: www.google.com
admin是administration服務必須的配置,address指定監聽地址。
static_resources包含了Envoy啟動時靜態配置的所有內容,而不是Envoy在運行時動態配置的資源。
這個配置表明,實例監聽127.0.0.1:10000,支持http協議訪問,http訪問域名為domains所描述內容時,將流量路由到cluster: service_google服務,即www.google.com:443。
了解配置文件后,我們自己重寫一下配置文件:
#為了避免出現Cannot find field錯誤,建議使用docker cp命令把默認配置文件拷貝到宿主機下,然后進行修改。 $ docker cp envoy:/etc/envoy/envoy.yaml .
$ vim envoy.yaml #按照下面去修改

admin: access_log_path: /tmp/admin_access.log address: socket_address: protocol: TCP address: 127.0.0.1 port_value: 9901 static_resources: listeners: - name: listener_0 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.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.google.com cluster: some_service http_filters: - name: envoy.filters.http.router clusters: - name: some_service connect_timeout: 0.25s type: LOGICAL_DNS # Comment out the following line to test on v6 networks dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN load_assignment: cluster_name: some_service endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 port_value: 80 transport_socket: name: envoy.transport_sockets.tls typed_config: "@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext sni: 127.0.0.1
$ docker run -d -p 10000:10000 -v `pwd`/envoy.yaml:/etc/envoy/envoy.yaml --name envoy envoyproxy/envoy #運行實例,把配置文件掛載到相應目錄 $ docker run -d --network=container:envoy --name nginx nginx #在運行一個nginx容器,共享envoy容器網絡,以此模擬sidecar
根據配置文件規則,envoy監聽10000端口,同時在宿主機端口10000暴漏出來。當有請求到達監聽后,envoy會對所有請求路由到some_service這個cluster上,而該cluster的upstream指向本地80端口,也就是nginx服務上。
中途一直報這個錯誤,然后我把端口換成10001,並注釋掉clusters后面的一些內容后,成功訪問到nginx服務。(至於為什么,我也不太清楚,還處於摸索狀態)。
3. 動態配置實踐
我們發現上面envoy.yaml文件中我們的endpoint里的adres是固定的,但實際應用中,比如在k8s集群中,pod的IP不是固定的,我們不可能每次都去修改yaml文件,這個時候就需要用到動態配置了。動態配置可以實現全動態,即實現LDS(Listener Discovery Service)、CDS(Cluster Discovery Service)、RDS(Route Discovery Service)、EDS(Endpoint Discovery Service),以及ADS(Aggregated Discovery Service)。
--service-cluster
和
--service-node
,也可以在envoy.yaml配置文件中指定
node.cluster
和
node.id。
參考連接:
https://blog.csdn.net/huwei0518/article/details/97626386
https://zhuanlan.zhihu.com/p/35830194
https://www.jianshu.com/p/90f9ee98ce70