基于 Istio 实现服务的泳道发布

基于 Istio 的泳道发布,让多条需求在同一套共享测试环境里并行测试互不干扰,既省资源又提效。

1. 背景

随着公司业务规模迅速扩张,同一微服务往往需要并行承载多个功能需求的同时开发与测试。当前一套测试环境出现了分支抢占、配置串扰、缓存冲突等问题,导致整体交付效率下滑。

2. 什么是泳道

泳道可以理解为多个并行隔离的调用链,调用互不干扰,类似泳池中的泳道。其中一条基线泳道作为主干道常备所有服务的默认实例,其他泳道的缺失服务会自动回退到这条基线泳道。

使用流量泳道实现应用版本隔离,将链路透传请求头指定为引流请求头,使用链路透传请求头的内容向不同泳道引流。泳道中服务相互调用时,若目标服务不存在当前泳道则转发至基线泳道,保障链路完整性,简化流量管理。如下图:
istio_lane

图中基于 svcA、svcB、svcC 构建 v1、v2、v3 三条泳道,分别对应服务调用链的三个版本。其中 v1 为基线泳道,包含完整的三个服务,v2 仅包含 svcA、svcC 两个服务,v3 仅包含 svcB 一个服务。同时,链路透传请求头与引流请求头都指定为 X-Lane。

3. 方案探索

以下资源的 YAML 按流量进入集群→路由→后端实例的顺序编排,依次对应泳道发布的 5 个关键环节。

3.1 Gateway

作用:定义统一入口,声明域名、端口及 TLS 终止方式,让外部流量能够打到集群内部。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: common-inbound-gateway
namespace: devops
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
- hosts:
- '*'
port:
name: https
number: 443
protocol: HTTPS
tls:
credentialName: blazehu.com
mode: SIMPLE

3.2 VirtualService

作用:定义基于 HTTP 头部信息的路由规则,包括基线泳道。按 HTTP 头 X-Lane 的值把请求精准地导到 v1/v2/v3 三个泳道;若无该头,则默认落到基线泳道 v1。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: argocd-demo-vs
namespace: devops
spec:
gateways:
- common-inbound-gateway
hosts:
- argocd-demo.blazehu.com
http:
- match: # 定义基于 header 的路由规则
- headers:
X-Lane:
exact: v2
name: v2
route:
- destination:
host: argocd-demo-lanes
port:
number: 7777
subset: v2
- match:
- headers:
X-Lane:
exact: v3
name: v3
route:
- destination:
host: argocd-demo-lanes
port:
number: 7777
subset: v3
- name: v1 # 基线泳道 v1,没有 match 条件,作为默认路由
route:
- destination:
host: argocd-demo-lanes
port:
number: 7777
subset: v1

3.3 DestinationRule

作用:定义服务的子集和选择器,即给每个泳道打“版本标签”,让 VirtualService 在路由时能准确找到后端实例;同时定义负载均衡策略。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: argocd-demo-dr
namespace: devops
spec:
host: argocd-demo-lanes
subsets:
- labels:
app: argocd-demo
version: v1
name: v1
- labels:
app: argocd-demo
version: v2
name: v2
- labels:
app: argocd-demo
version: v3
name: v3

3.4 Service

作用:把标有 app: argocd-demo 的 Pod 暴露为 argocd-demo-lanes:7777,供 VS/DR 路由。

apiVersion: v1
kind: Service
metadata:
name: argocd-demo-lanes
namespace: devops
spec:
ports:
- name: http
port: 7777
protocol: TCP
targetPort: http
selector:
app: argocd-demo

3.5. Deployment

作用:定义工作负载,每个工作负载具有不同的标签。通过 version 标签把镜像 v1/v2/v3 部署成三套独立实例,对应三条泳道;缺失组件会自动回退到基线 v1,实现“按需部署”。

# v1(基线)
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-demo-v1
namespace: devops
spec:
replicas: 1
selector:
matchLabels:
app: argocd-demo
version: v1
template:
metadata:
labels:
app: argocd-demo
sidecar.istio.io/inject: 'true'
version: v1
spec:
containers:
- image: 'xxx/blazehu-demo:v1'
imagePullPolicy: Always
name: argocd-demo
ports:
- containerPort: 8080
name: http
protocol: TCP
---
# v2(仅改 version 与镜像)
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-demo-v2
namespace: devops
spec:
replicas: 1
selector:
matchLabels:
app: argocd-demo
version: v2
template:
metadata:
labels:
app: argocd-demo
sidecar.istio.io/inject: 'true'
version: v2
spec:
containers:
- image: 'xxx/blazehu-demo:v2'
imagePullPolicy: Always
name: argocd-demo
ports:
- containerPort: 8080
name: http
protocol: TCP
---
# v3(仅改 version 与镜像)
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-demo-v3
namespace: devops
spec:
replicas: 1
selector:
matchLabels:
app: argocd-demo
version: v3
template:
metadata:
labels:
app: argocd-demo
sidecar.istio.io/inject: 'true'
version: v3
spec:
containers:
- image: 'xxx/blazehu-demo:v3'
imagePullPolicy: Always
name: argocd-demo
ports:
- containerPort: 8080
name: http
protocol: TCP

通过 Gateway 入口、VirtualService 路由规则、DestinationRule 后端分组的组合,再辅以按版本标签区分的 Deployment。

3.6. 测试验证

istio_lane_argocd
本地配置 hosts即可访问测试(修改 etc/hosts):

blazehu@MACBOOK ~ % curl http://argocd-demo.blazehu.com/api/check
{"message":"v1"}

blazehu@MACBOOK ~ % curl http://argocd-demo.blazehu.com/api/check -H "X-Lane: v1"
{"message":"v1"}

blazehu@MACBOOK ~ % curl http://argocd-demo.blazehu.com/api/check -H "X-Lane: v2"
{"message":"v2"}

blazehu@MACBOOK ~ % curl http://argocd-demo.blazehu.com/api/check -H "X-Lane: v3"
{"message":"v3"}

3.7 响应头添加泳道信息

为了确保前后端泳道信息的一致性和可追踪性,并使前端能够识别请求所属的泳道,我们可以在响应中添加泳道的header头。仅当泳道有效时,才将其添加至响应头,以避免向前端传递无效或错误的泳道信息。这样,前端可以通过浏览器插件自动设置请求头,实现泳道信息的准确传递和验证。
方案一:VirtualService

http:
- match:
- headers:
X-Lane:
exact: 'v2'
name: v2
route:
- destination:
host: argocd-demo-lanes
subset: v2
port:
number: 7777
headers:
response:
add:
X-Lane: "v2" # 将请求头 x-lane 传递到响应头

方案二:EnvoyFilter

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: lane-response-header
namespace: devops
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.lua
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inlineCode: |
function envoy_on_request(request_handle)
local headers = request_handle:headers()
local lane = headers:get("x-lane") or "nil"
request_handle:logWarn("== envoy_on_request X-Lane: " .. lane)
request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua", "lane", lane)
end

function envoy_on_response(response_handle)
local headers = response_handle:headers()
response_handle:logWarn("start add headers")
local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.filters.http.lua")
local lane = metadata and metadata["lane"] or "nil"
if lane == "nil" then
response_handle:logWarn("X-Lane header not found in request")
else
headers:add("x-lane", lane)
response_handle:logWarn("set x-lane, lane:".. lane)
end
end
workloadSelector:
labels:
app: argocd-demo

两种方案均可,方案一通过 VirtualService 直接在路由层面处理请求和响应头,简单易用;方案二利用 EnvoyFilter 的 Lua 脚本在代理层面灵活操作请求和响应头,提供更高的灵活性和控制能力。

4. 落地实践

4.1. 模板渲染

  • 基础资源:在 base/ 目录下管理,包括 Gateway 和 Service 等基础资源。
  • 差异补丁:在 patch/ 目录下管理,包括 Deployment 和 VirtualService 的差异补丁。
  • 渲染管线:前端表单 → 后端生成全局唯一泳道名 → 模版渲染 → 推送到 Git → Argo CD 同步

4.2. 元数据原理

使用一个泳道 Lane CR(Custom Resource)或数据库作为唯一的数据来源。

补充说明:我们可以通过 CR + Controller 的方式实现上述的模版渲染以及元数据管理。即将泳道定义为 XLane 类型的 CRD,并实现对应的 Controller 来管理要实现泳道功能的 Istio VirtualService 和 DestinationRule (实现路由功能) 和 K8S deployment (实现部署功能)。

4.3. 功能清单

  • 创建:选应用 → 选分支(版本) → 填写 TTL → 一键生成
  • 查看:列表展示泳道、剩余 TTL、Pod 数等
  • 更新:仅允许改镜像或副本数,提交后滚动升级
  • 删除:二次确认后软删除并级联回收资源
  • 清理:TTL 到期自动删除;可一键续期,最多续 3 次

5. 参考