在基于istio的多环境治理场景, 需要用户访问服务时带上环境标识来进行流量染色, 如
curl -H 'x-demo-env: feature-1' http://demo.com/x/y/z
上述的feature-1即为环境标识(可能是环境uuid或者可读的id等).
那么在istio vs中就可以通过识别http header来路由到特定环境, 这样就能做到最基本的路由策略:
同环境内优先路由, 否则回落到基线环境.
istio相应的VS大致如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
gateways:
- demo-internal/demo-default-gw
hosts:
- app1-service
http:
- match:
- headers:
x-demo-env:
exact: feature-1
route:
- destination:
host: app1
port:
number: 8000
subset: feature-1
- match: [] # fallback to base env
route:
- destination:
host: app1
port:
number: 8000
subset: base
istio相应的DR如下:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
spec:
host: app1-service
subsets:
- name: feature-1
labels:
x-demo-env: feature-1
- name: base
labels:
x-demo-env: base
上述实现中规中矩, 但是在web场景下有个体验问题:
用户需要通过浏览器插件或者其他手段来注入header: x-demo-env: feature-1.
如果用户用的是ie或者其他不支持特定插件的浏览器的话, 通过浏览器就没法用了.
由于是在内网环境, 我们可以通过DNS平台提供的接口创建子域名:
feature-1.demo.com, feature-2.demo.com, …
即将环境标识直接作为域名的一部分, 然后在istio gateway处通过EnvoyFilter将环境标识提取出来, 自动注入header, 这样体验会更好一些.
此时用户访问流程如下:

相应的EnvoyFilter代码简化如下:
apiVersion: networking.istio.io/v2alpha3
kind: EnvoyFilter
metadata:
name: demo-inject-header
namespace: istio-system
labels:
project: envoy-filter
app: demo-inject-header
spec:
workloadSelector:
# select by label in the same namespace
labels:
istio: ingressgateway
app: istio-ingressgateway-demo-mesh
configPatches:
# The Envoy config you want to modify
- applyTo: HTTP_FILTER
match:
context: GATEWAY
proxy:
proxyVersion: '^1\.1\d.*' # 这里根据实际情况可以写死
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value: # lua filter specification
name: demo-mesh.tof.checklogin.envoy.lua
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
inlineCode: |-
function envoy_on_request(request_handle)
local host = request_handle:headers():get(":authority")
-- host 应该匹配 [^.]+.demo.com
local domainRe = prefix.."([^.]+)%.demo%.com"
local dyeingKey = host:match(domainRe) or ""
if (not dyeingKey) or (string.len(dyeingKey) <= 0) then
request_handle:logWarn("not demo mesh host:"..host))
return
end
-- 将子域名转为染色key header
request_handle:logInfo("host:"..host.."|dyeingKey:"..tostring(dyeingKey))
-- 注意这里要用replace()而不是add(), 否则用户如果手动传了x-demo-env会有问题.
request_handle:headers():replace('x-demo-env', dyeingKey)
end
上述的inlineCode部分是lua代码, 相关文档参考:
https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter#configuration
(完)