基于客户端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。
执行以下命令,部署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插件。