基于客戶端IP進行路由是較為常見的需求,例如內網和公網的用戶需要訪問不同版本的應用、不同地區的用戶需要訪問不同類別的內容等。因此ASM提供了自定義插件能力,可以將請求的客戶端IP轉換為請求Header進行路由。本文將介紹如何在ASM中利用自定義插件實現基于客戶端IP的路由。
背景信息
ASM使用Envoy作為網格代理來進行實際的流量轉發,利用Envoy豐富的插件生態來對網格能力進行擴展。以HTTP協議為例,Envoy提供了高度靈活的擴展接口,您可以直接利用這些接口讀取到詳細的請求元數據,甚至可以修改請求Header、Body等屬性。Envoy的插件可以分為以下三類:
原生Envoy Filter:基于C++開發,難度大,但性能更好。
WASM插件:可以使用多種語言開發,更加安全、靈活,開發難度較低。
Lua插件:使用Lua進行開發,靈活度受限,但開發難度最低。
本文將使用一個簡單的Lua插件,將請求的Client IP轉換為請求的Header,通過虛擬服務匹配該Header,實現不同來源的請求返回不同的響應。
前提條件
已部署httpbin應用到集群中,并且可以通過ASM網關正常訪問。具體操作,請參見部署httpbin應用。
步驟一:確認客戶端源IP
首先,部署sleep應用到集群作為客戶端。后面我們將分別從sleep和本地客戶端來訪問httpbin服務,從而測試不同源IP的請求。
本地客戶端特指當前場景下執行文檔操作的用戶終端,包括個人電腦,ECS等。例如使用個人電腦登錄ECS,再通過kubectl訪問ACK集群時,ECS就是本地客戶端。
使用ACK集群kubecofig創建sleep.yaml。
apiVersion: v1 kind: ServiceAccount metadata: name: sleep --- apiVersion: v1 kind: Service metadata: name: sleep labels: app: sleep service: sleep spec: ports: - port: 80 name: http selector: app: sleep --- apiVersion: apps/v1 kind: Deployment metadata: name: sleep spec: replicas: 1 selector: matchLabels: app: sleep template: metadata: labels: app: sleep spec: terminationGracePeriodSeconds: 0 serviceAccountName: sleep containers: - name: sleep image: registry.cn-hangzhou.aliyuncs.com/acs/curl:8.1.2 command: ["/bin/sleep", "infinity"] imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /etc/sleep/tls name: secret-volume volumes: - name: secret-volume secret: secretName: sleep-secret optional: true ---
執行以下命令,部署sleep應用。
kubectl apply -f sleep.yaml
執行以下命令,配置入口網關IP地址的環境變量。關于網關IP的獲取方式,請參見獲取入口網關地址。
export ASM_GATEWAY_IP=112.35.xx.xx
執行以下命令,獲取本地客戶端IP。
curl ${ASM_GATEWAY_IP}/ip
執行以下命令,獲取sleep Pod的IP。
kubectl exec deploy/sleep -it -- curl ${ASM_GATEWAY_IP}/ip
說明訪問httpbin應用
/ip
路徑的請求將會返回客戶端IP。這種獲取客戶端IP的方式需要網關中Service的ExternalTrafficPolicy設為local
。
步驟二:創建Lua插件并應用到ASM網關上
Lua插件是通過Envoy的Lua Filter實現的。您可以自行編寫Lua代碼,并將Lua代碼直接作為Lua Filter配置的一部分發送給Envoy。Envoy會在請求處理時調用配置的Lua代碼來執行相應操作。
本文中需要的Lua代碼片段如下。
function envoy_on_request(request_handle)
-- 定義envoy_on_request函數,處理接收到的請求
-- 參數request_handle用來獲取并修改請求信息
local client_address = request_handle:streamInfo():downstreamRemoteAddress()
-- 獲取請求源IP
request_handle:headers():add("x-client-address", client_address)
-- 設置請求header,header的key為x-client-address
end
創建Envoy過濾器資源,具體操作,請參見使用Envoy過濾器模板創建Envoy過濾器。
創建Envoy過濾器模板過程中,將下方YAML粘貼至多版本適配Envoy過濾器模板的輸入框中。
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: add-header-by-ip namespace: istio-system spec: workloadSelector: labels: istio: ingressgateway configPatches: - applyTo: HTTP_FILTER match: context: GATEWAY listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" patch: operation: INSERT_BEFORE value: name: envoy.lua typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" inlineCode: | function envoy_on_request(request_handle) local client_address = request_handle:streamInfo():downstreamRemoteAddress() request_handle:headers():add("x-client-address", client_address) end
綁定至工作負載時,請選擇選定工作負載綁定,選擇istio-system命名空間,工作負載類型選擇Service,選中ASM網關對應的Service(名稱為
istio-${ASM網關名稱}
)。綁定完成之后,單擊Envoy過濾器。可以看到對應的Envoy過濾器已經創建成功。
步驟三:創建基于Client IP的路由規則
使用ASM的kubeconfig,修改httpbin應用中部署的虛擬服務,最終YAML如下所示:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: httpbin-vs
namespace: default
spec:
gateways:
- httpbin
hosts:
- '*'
http:
- match:
- headers:
x-client-address:
prefix: ${獲取到的Sleep客戶端IP}
directResponse:
status: 200
body:
string: "from sleep"
- match:
- headers:
x-client-address:
prefix: ${獲取到的本機IP}
directResponse:
status: 200
body:
string: "from my host"
- name: test
route:
- destination:
host: httpbin.default.svc.cluster.local
port:
number: 8000
上述配置的含義是:
所有來自sleep的請求,將直接返回一個
from sleep
字符串。所有來自本地客戶端的請求,將直接返回一個
from my host
字符串。
步驟四:測試
在本地客戶端執行以下命令。
curl ${ASM_GATEWAY_IP}/ip
預期輸出:
from my host
使用ACK集群的kubeconfig執行以下命令。
kubectl exec deploy/sleep -it -- curl ${ASM_GATEWAY_IP}/ip
預期輸出:
from sleep
相關文檔
在熟悉Envoy架構的前提下,Lua插件相較于其他Envoy插件開發難度最低。關于如何開發Envoy的Lua插件,請參見Lua script for HTTP filters。
WASM插件是基于WebAssembly For Proxies規范編寫的Envoy插件,支持多種語言。您可以嘗試使用較低入門難度的Golang來開發WASM插件。具體操作,請參見使用Go為網格代理編寫WASM插件。