使用Envoy實現Service Mesh
Service Mesh是微服務設定中的通訊層。來自每個服務的所有請求都將通過服務網格。每個服務都有自己的代理服務,所有這些代理服務一起形成“服務網格”。因此,如果服務想要呼叫另一個服務,它不會直接呼叫目標服務,它會先將請求路由到本地代理,然後代理將其路由到目標服務。從本質上講,您的服務例項對外部世界一無所知,只瞭解本地代理。
當您談到“Service Mesh”時,您肯定會聽到“Sidecar”邊車這個詞,“Sidecar”是一個代理,可用於您的每個服務例項,每個“Sidecar”負責一個服務的一個例項。
服務網格提供什麼?
- 服務發現
- 可觀察性(指標)
- 限速
- 斷路器
- 交通流量
- 負載均衡
- 身份驗證和授權
- 分散式跟蹤
Envoy是一個用C ++編寫的高效能代理。使用Envoy構建您的“Service Mesh”並不是強制性的,您可以使用其他代理,如Nginx,Traefik等......但是對於這篇文章,我們將繼續使用Envoy。
好的,讓我們用3個服務構建一個“Service Mesh”設定。
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配置主要包括
- Listeners監聽器
- Route路由
- 叢集
- 端點
Listeners監聽器:
可以在一個Envoy例項中執行一個或多個偵聽器。第9-36行listeners是配置當前監聽器的地址和埠,每個監聽器也可以有一個或多個網路過濾器。使用這些過濾器可以實現路由,終止,流量轉移等大部分內容......“envoy.http_connection_manager”是我們在這裡使用的內建過濾器之一,除了這個特使還有其他幾個 ofollow,noindex" target="_blank">過濾器 。
Route路由:
第22-34行filter_chains開始配置我們的過濾器的路由規範,您應該接受請求的域和與每個請求匹配的路由匹配器,並將請求傳送到適當的叢集。
叢集:
叢集是Envoy將流量路由到的上游服務的規範。
第41-50行clusters定義了“service_a”,這是“Front Envoy”將與之交談的唯一上游。“connect_timeout”是在返回503之前獲得與上游服務的連線的時間限制。通常會有多個“service_a”例項,而envoy支援 多種負載均衡演算法 來路由流量。這裡我們使用簡單的迴圈法。
端點:
“hosts”指定我們要將流量路由到的service_a的例項,在我們的例子中,我們只有一個。
第48行,port_value: 8786,正如我們所討論的那樣,我們不直接與“service_a”對話,我們會與服務A的Envoy代理例項進行通訊,然後將其路由到本地服務A例項。
我們在這裡做客戶端負載平衡。Envoy快取“service_a”的所有主機,並且每5秒鐘它將繼續重新整理主機列表。Envoy支援主動和被動健康檢查。如果要使其處於活動狀態,請在群集配置中配置執行狀況檢查。
其他:
第2-7行,配置管理伺服器,可用於檢視配置,更改日誌級別,檢視統計資訊等...
第8行,“static_resources”,意味著我們手動載入所有配置,我們也可以動態地完成它,我們將在後面的文章中看看它是如何做到的。
配置比我們看到的要多得多,我們的目標是不經歷所有可能的配置,而是要有最小的配置才能開始。
service_a
這是“服務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例項的相應群集定義。
“服務A”需要與“服務B”和“服務C”對話,因此我們分別有兩個監聽器和叢集。在這裡,我們為每個上游(localhost,Service B和Service C)分別設定了監聽器,另一種方法是根據url或header向任何上游提供單個監聽器和路由。
服務B和服務C.
服務B和服務C處於葉級別,除了本地主機服務例項之外,不與任何其他上游通訊。所以配置會很簡單:
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
這裡沒什麼特別的,只有一個監聽器和一個叢集。
我們完成了所有配置,我們可以將此設定部署到kubernetes或使用docker-compose進行測試。執行docker-compose build and docker-compose up並點選localhost:8080 ,您應該看到請求成功通過所有服務和Envoy代理。您可以使用日誌進行驗證。
Envoy xDS
我們通過為每輛邊車提供配置來實現所有這些,並且根據服務配置在服務之間變化。最初使用2或3個服務手動管理這些邊車配置似乎沒有問題,但是當服務數量增加時,變得困難。此外,當邊車配置更改時,您必須重新啟動Envoy例項才能使更改生效。
如前所述,我們可以完全避免手動配置並使用api伺服器載入所有元件,叢集(CDS),端點(EDS),監聽器(LDS)和路由(RDS)。因此,每個邊車將與api伺服器通訊以獲取配置,並且當在api伺服器中更新新配置時,它會自動反映在Envoy例項中,從而避免重啟。
有關動態配置, 在這裡 ,並 在這裡 是你可以用一個例子XDS伺服器
如果我們要在Kubernetes中實現這個設定,它會是什麼樣子?
需要改變:
- Pod
- Service
Pod:
通常Pod規範只在其中定義了一個容器。但是根據定義,Pod可以容納一個或多個容器。由於我們想要為每個服務例項執行一個邊車代理,我們將Envoy容器新增到每個pod。因此,為了與外界通訊,服務容器將通過localhost與Envoy容器通訊。這就是部署檔案的樣子
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
我們在那裡添加了我們的Envoy側車。我們將在第33-39行的configmap中安裝我們的Envoy配置檔案。
服務Service
Kubernetes服務負責維護可以路由流量的Pod端點列表。通常kube-proxy在這些pod端點之間進行負載平衡。但在我們的情況下,如果你還記得,我們自己進行的客戶端負載平衡,所以我們不希望使用kube-proxy負載均衡,我們希望得到Pod端點列表並自己實現負載均衡。為此,我們可以使用“無頭服務”,它將返回端點列表。這是它的樣子:
kind: Service apiVersion: v1 metadata: name: servicea spec: clusterIP: None ports: - name: envoy-web port: 8786 targetPort: 8786 selector: app: servicea
第6行使服務無頭。此外,您應該注意我們沒有將kubernetes服務埠對映到應用程式的服務埠,但我們正在將它對映到Envoy偵聽器埠。流量首先流向Envoy。
在此處 找到所有配置和程式碼