Envoy是一種高性能C++分布式代理,專為單個服務和應用程式設計,作為Service Mesh中的重要組件,充分理解其配置就顯得尤為重要,本文列出了使用Envoy而不用其他代理的原因,并給出了Envoy及其服務的配置,然后對其進行詳細解讀,幫助讀者理解其配置,從而掌握Envoy,

服務網格是微服務設定中的通信層,也就是說往返于每個服務的所有請求都通過網格,服務網格在微服務設定中也成為基礎架構層,它能夠讓服務之間的通信變得安全可靠,關于Service Mesh的基礎內容,我們已經在這篇文章中詳細介紹過,
每一個服務都有自己的代理服務(sidecars),然后所有代理服務一起形成服務網格,Sidecars處理服務之間的通信,也就是說所有的流量都會通過網格并且該透明層可以控制服務之間如何互動,
服務網格通過由API控制的組件提供可觀察性、服務發現以及負載均衡等,
實際上,如果一個服務要呼叫另一個服務,它不會直接呼叫目標服務,而是先將請求路由到本地代理,然后代理再將該請求路由到目標服務,這一程序意味著服務實體不會和其他服務直接接觸,僅與本地代理進行通信,

根據ThoughtWorks Technology Radar(這是一份半年度的檔案,用于評估現有技術和新生技術的風險和收益)指出,“服務網格提供一致的發現、安全性、跟蹤(tracing)、監控以及故障處理,而無需共享資源(如API網關或ESB),一個十分典型的用例是輕量的反向代理行程會與每個服務行程或單獨的容器一起部署,”
當談到服務網格時,不可避免談到的是“sidecar”——可用于每個服務實體的代理,每個sidecar負責管理一個服務的一個實體,我們將在本文中進一步詳細討論sidecar,
服務網格可以交付什么?

當前,越來越多的企業和組織開始轉向微服務架構,這樣的企業需要服務網格所提供的上述功能,解耦庫的使用或自定義代碼的方法無疑是贏家,
為什么使用Envoy?
Envoy不是構建一個服務網格的唯一選擇,市面上還有其他的代理如Nginx、Traefik等,我之所以選擇Envoy,這個用C++撰寫的高性能代理,是因為我更喜歡Envoy的輕量、強大的路由,及其提供的可觀察性和可擴展性,
讓我們首先構建1個包含3個服務的服務網格設定,這是我們正在嘗試構建的架構:

Front Envoy
在我們的設定中Front Envoy是一個邊緣代理,我們通常在其中執行TLS終止、身份驗證、生成請求頭等操作,
我們來看看Front Envoy配置:
---
admin:
access_log_path: "/tmp/admin_access.log"
address:
socket_address:
address: "127.0.0.1"
port_value: 9901
static_resources:
listeners:
-
name: "http_listener"
address:
socket_address:
address: "0.0.0.0"
port_value: 80
filter_chains:
filters:
-
name: "envoy.http_connection_manager"
config:
stat_prefix: "ingress"
route_config:
name: "local_route"
virtual_hosts:
-
name: "http-route"
domains:
- "*"
routes:
-
match:
prefix: "/"
route:
cluster: "service_a"
http_filters:
-
name: "envoy.router"
clusters:
-
name: "service_a"
connect_timeout: "0.25s"
type: "strict_dns"
lb_policy: "ROUND_ROBIN"
hosts:
-
socket_address:
address: "service_a_envoy"
port_value: 8786
Envoy配置主要由以下部分組成:
1、 監聽器(Listener)
2、 路由
3、 集群
4、 端點
監聽器
一個或多個監聽器可以在單個Envoy實體中運行,在以上9到36行的代碼提到了當前監聽器的地址和埠,每個監聽器也可以有一個或多個網路過濾器,這些過濾器可以啟用路由、tls終止、流量轉移等活動,除了envoy.http_connection_manager使用的是內置過濾器之外,Envoy還有其他幾個過濾器,
路 由
22行到34行代碼為過濾器配置了路由規范,同時它也指定了我們所接受請求的域以及路由匹配器,路由匹配器可以根據配置的規則匹配每個請求,并將請求轉發到適當的集群,
集 群
集群是Envoy將流量路由到的上游服務規范,41行到48行代碼定義了“Service A”,這是Front Envoy要通信的唯一上游,“connect_timeout”是在回傳503之前建立與上游服務的連接的時間限制,
通常情況下,有多個“Serivce A”實體,并且Envoy支持多種負載均衡演算法來路由流量,在本例中,我們使用了一個簡單的回圈演算法,
端 點
“host”指定我們要將流量路由到的Service A的實體,在本例中,我們只有1個實體,
第47行代碼沒有直接與Service A進行通信,而是與Service A的Envoy代理實體進行通信,該代理將路由到本地Service A實體,
我們還可以利用回傳Service A的所有實體的服務名稱(如Kubernetes中的headless服務),來執行客戶端負載均衡,Envoy快取Service A的所有host,并每5秒重繪一次host串列,
此外,Envoy還支持主動和被動的健康檢查,因此,如果我們要進行主動健康檢查,我們需要在集群配置部分對其進行配置,
其 他
第2行到第7行配置了管理服務器,它能夠幫助查看配置、更改日志級別、查看統計資訊等,
第8行的“static_resources”可以手動加載所有配置,我們將在下文討論如何動態地執行此操作,
雖然這里描述了許多其他配置,但是我們的目標不是全面介紹所有配置,而是以最少的配置開始,
Service A
這是Service A的Envoy配置:
admin:
access_log_path: "/tmp/admin_access.log"
address:
socket_address:
address: "127.0.0.1"
port_value: 9901
static_resources:
listeners:
-
name: "service-a-svc-http-listener"
address:
socket_address:
address: "0.0.0.0"
port_value: 8786
filter_chains:
-
filters:
-
name: "envoy.http_connection_manager"
config:
stat_prefix: "ingress"
codec_type: "AUTO"
route_config:
name: "service-a-svc-http-route"
virtual_hosts:
-
name: "service-a-svc-http-route"
domains:
- "*"
routes:
-
match:
prefix: "/"
route:
cluster: "service_a"
http_filters:
-
name: "envoy.router"
-
name: "service-b-svc-http-listener"
address:
socket_address:
address: "0.0.0.0"
port_value: 8788
filter_chains:
-
filters:
-
name: "envoy.http_connection_manager"
config:
stat_prefix: "egress"
codec_type: "AUTO"
route_config:
name: "service-b-svc-http-route"
virtual_hosts:
-
name: "service-b-svc-http-route"
domains:
- "*"
routes:
-
match:
prefix: "/"
route:
cluster: "service_b"
http_filters:
-
name: "envoy.router"
-
name: "service-c-svc-http-listener"
address:
socket_address:
address: "0.0.0.0"
port_value: 8791
filter_chains:
-
filters:
-
name: "envoy.http_connection_manager"
config:
stat_prefix: "egress"
codec_type: "AUTO"
route_config:
name: "service-b-svc-http-route"
virtual_hosts:
-
name: "service-b-svc-http-route"
domains:
- "*"
routes:
-
match:
prefix: "/"
route:
cluster: "service_c"
http_filters:
-
name: "envoy.router"
clusters:
-
name: "service_a"
connect_timeout: "0.25s"
type: "strict_dns"
lb_policy: "ROUND_ROBIN"
hosts:
-
socket_address:
address: "service_a"
port_value: 8081
-
name: "service_b"
connect_timeout: "0.25s"
type: "strict_dns"
lb_policy: "ROUND_ROBIN"
hosts:
-
socket_address:
address: "service_b_envoy"
port_value: 8789
-
name: "service_c"
connect_timeout: "0.25s"
type: "strict_dns"
lb_policy: "ROUND_ROBIN"
hosts:
-
socket_address:
address: "service_c_envoy"
port_value: 8790
11行到39行定義了一個監聽器來路由流量到實際的Service A實體,在103行到111行中找到service_a實體的相應集群定義,
Service A與Service B和Service C進行通信,它指向了兩個以上的監聽器以及集群,在本例中,我們為每個上游(localhost、Service B和Service C)分離了監聽器,另一種方法是使用單個監聽器,并根據URL或請求頭路由到任意上游,
Service B 和 Service C
Service B和Service C處于葉級,除了本地主機服務實體外,不與任何其他上游通信,因此其配置十分簡單,
而讓事情變得復雜的是上述配置中的單個監聽器和單個集群,
配置完成后,我們將此設定部署到Kubernetes或使用docker-compose對其進行測驗,你可以運行docker-compose build和docker-compose up并點擊localhost:8080,以查看請求是否成功通過所有服務和Envoy代理,我們可以使用日志對其進行驗證,
Envoy xDS
我們為每個sidecar提供了配置,并且根據不同的服務,服務之間的配置也有所不同,雖然我們可以手動制作和管理sidecar配置,但最初至少要提供2或3個服務,并且隨著服務數量的增加,配置會變得十分復雜,此外,每次sidecar配置更改時,你都必須重新啟動Envoy實體,以使更改生效,
正如上文所討論的,我們可以通過使用API server來避免手動配置并加載所有組件:集群(CDS)、端點(EDS)、監聽器(LDS)以及路由(RDS),所以每個sidecar將與API server通信并接收配置,當新配置更新到API server時,它將自動反映在Envoy實體中,從而避免了重新啟動,
你可以在以下鏈接中了解關于動態配置的資訊:
https://www.envoyproxy.io/docs/envoy/latest/configuration/overview/v2_overview#dynamic
這有一個簡單的xDS server:
https://github.com/tak2siva/Envoy-Pilot
如何在Kubernetes中實作
本部分將討論如果我們要在Kubernetes中實作所討論的設定該怎么辦,以下是架構圖:
因此,將需要更改:
-
Pod
-
服務
部署Pod
盡管Pod規范中僅定義了一個容器——按照定義,一個Pod可以容納一個或多個容器,為了對每個服務實體運行sidecar代理,我們將Envoy容器添加到每個Pod中,為了與外界通信,服務容器將通過localhost與Envoy容器進行對話,
部署檔案如下所示:
admin:
access_log_path: "/tmp/admin_access.log"
address:
socket_address:
address: "127.0.0.1"
port_value: 9901
static_resources:
listeners:
-
name: "service-b-svc-http-listener"
address:
socket_address:
address: "0.0.0.0"
port_value: 8789
filter_chains:
-
filters:
-
name: "envoy.http_connection_manager"
config:
stat_prefix: "ingress"
codec_type: "AUTO"
route_config:
name: "service-b-svc-http-route"
virtual_hosts:
-
name: "service-b-svc-http-route"
domains:
- "*"
routes:
-
match:
prefix: "/"
route:
cluster: "service_b"
http_filters:
-
name: "envoy.router"
clusters:
-
name: "service_b"
connect_timeout: "0.25s"
type: "strict_dns"
lb_policy: "ROUND_ROBIN"
hosts:
-
socket_address:
address: "service_b"
port_value: 8082
在容器部分添加了Envoy sidecar,并且在33到39行的configmap中我們掛載了我們的Envoy組態檔,
更改服務
Kubernetes服務負責維護Pod端點串列,該串列可以路由流量,盡管kube-proxy通常處理Pod端點之間的負載均衡,但在本例中,我們將執行客戶端負載均衡,并且我們不希望kube-proxy進行負載均衡,此外,我們想要提取Pod端點串列并對其進行負載均衡,為此,我們需要使用“headless服務“來回傳端點串列,
如下所示:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: servicea
spec:
replicas: 2
template:
metadata:
labels:
app: servicea
spec:
containers:
- name: servicea
image: dnivra26/servicea:0.6
ports:
- containerPort: 8081
name: svc-port
protocol: TCP
- name: envoy
image: envoyproxy/envoy:latest
ports:
- containerPort: 9901
protocol: TCP
name: envoy-admin
- containerPort: 8786
protocol: TCP
name: envoy-web
volumeMounts:
- name: envoy-config-volume
mountPath: /etc/envoy-config/
command: ["/usr/local/bin/envoy"]
args: ["-c", "/etc/envoy-config/config.yaml", "--v2-config-only", "-l", "info","--service-cluster","servicea","--service-node","servicea", "--log-format", "[METADATA][%Y-%m-%d %T.%e][%t][%l][%n] %v"]
volumes:
- name: envoy-config-volume
configMap:
name: sidecar-config
items:
- key: envoy-config
path: config.yaml
有兩件事需要注意,一是第6行使服務變成headless,二是我們不是將Kubernetes服務埠映射到應用程式的服務埠,而是映射到Envoy監聽器埠,這意味著,流量首先通向Envoy,即便如此,Kubernetes也可以完美運行,
在本文中,我們看到了如何使用Envoy代理構建服務網格,其中,我們設定了所有通信都將通過網格,因此,現在網格不僅有大量有關流量的資料,而且還具有控制權,
以下鏈接中你可以獲取我們所討論的配置和代碼:
https://github.com/dnivra26/envoy_servicemesh
原文鏈接:
https://www.thoughtworks.com/insights/blog/building-service-mesh-envoy-0
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/47581.html
標籤:其他
上一篇:docker容器和虛擬機的比較
