【注意】最后更新于 July 27, 2021,文中内容可能已过时,请谨慎使用。
流量镜像,也叫称为影子流量,是指将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。
本节将测试使用istio流量管理中的流量镜像功能。会在k8s的default命名空间内部署httpbin
服务的v1和v2两个版本,首先把流量全部路由到httpbin:v1
,然后执行规则将一部分流量镜像到v2版本。
部署服务并配置默认路由到v1版本
因为default命名空间在前面部署bookinfo应用时已经开启了istio sidecar的自动注入功能,所以这里直接部署httpbin
应用v1和v2的两个版本。
httpbin v1的deployment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v1
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF
|
httpbin v2的deployment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v2
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v2
template:
metadata:
labels:
app: httpbin
version: v2
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF
|
httpbin的k8s service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
kubectl create -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
EOF
|
创建httpbin的目标规则,并配置好v1和v2两个服务子集:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF
|
为了能从集群外部访问httpbin,给它创建一个Ingress Gateway:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- httpbin.example.com
tls:
httpsRedirect: true
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: bookinfo-credential # must be the same as secret
hosts:
- httpbin.example.com
EOF
|
这里将使用二级域名httpbin.example.com
作为入口,因为前面为bookinfo应用创建Gateway时创建好的证书bookinfo-credential里是*.example.com
的通配证书,这里直接使用这个证书bookinfo-credential。
接下来为httpbin服务创建虚拟服务,并与上面创建的gateway关联,创建好入口路由规则:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
gateways:
- httpbin-gateway
hosts:
- httpbin.example.com
http:
- route:
- destination:
host: httpbin
port:
number: 8000
subset: v1
weight: 100
EOF
|
此时从入口域名httpbin.example.com
访问httpbin服务时,所有的流量都将被路由到httpbin:v1
。
在集群外部访问https://httpbin.example.com/headers
,正常返回信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
curl https://httpbin.example.com/headers
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.example.com",
"User-Agent": "curl/7.64.1",
"X-B3-Parentspanid": "f11310681322428c",
"X-B3-Sampled": "1",
"X-B3-Spanid": "d7d9533be9694d50",
"X-B3-Traceid": "7fad3179541ed266f11310681322428c",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Internal": "true",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=6bbc6b64389016a1ed1dcf2219cfb8e626021e6e1d54205e586441ac15b2b7f5;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"
}
}
|
此时在k8s中分别查看httbin服务v1和v2两个版本pod的日志,发现v1有访问日志,v2没有访问日志,说明上面配置生效,目前是所有的流量都被配置路由到了httpbin v1上。
1
2
3
4
5
6
7
8
9
|
export V1_POD=$(kubectl get pod -l app=httpbin,version=v1 -o jsonpath={.items..metadata.name})
kubectl logs "$V1_POD" -c httpbin
127.0.0.6 - - [27/Jul/2021:14:05:19 +0000] "GET /headers HTTP/1.1" 200 599 "-" "curl/7.64.1"
export V2_POD=$(kubectl get pod -l app=httpbin,version=v2 -o jsonpath={.items..metadata.name})
kubectl logs "$V2_POD" -c httpbin
无访问日志打印
|
镜像流量到v2版本
接下来改变httpbin虚拟服务中的路由规则,配置镜像100%的流量到v2版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
gateways:
- httpbin-gateway
hosts:
- httpbin.example.com
http:
- route:
- destination:
host: httpbin
port:
number: 8000
subset: v1
weight: 100
mirror:
host: httpbin
subset: v2
mirrorPercentage:
value: 100.0
EOF
|
现在这个是虚拟服务的路由规则是将100%的流量路由到httpbin:v1
,同时将100%的相同流量镜像到httpbin:v2
。
需要注意,当流量被镜像时,请求将发送到镜像服务,并在headers中的Host/Authority
属性值上追加-shadow
。例如cluster-1
变为cluster-1-shadow
,
另外注意镜像的流量是即发即弃的,就是说镜像请求的响应会被丢弃。
此时在集群外部访问https://httpbin.example.com/headers
,在k8s中分别查看httbin服务v1和v2两个版本pod的日志,发现两个版本的pod都有访问日志。
总结
流量镜像(Traffic Mirroring),也称为影子流量(Traffic Shadowing),
是一种强大的、无风险的测试应用版本的方法,它将实时流量的副本发送给被镜像的服务。
使用流量镜像,可以搭建一个与原环境类似的环境以进行验收测试,从而提前发现问题。
由于镜像流量存在于主服务关键请求路径带外,终端用户在测试全过程不会受到影响。另外在使用流量镜像之前,必须确认镜像服务连接的数据状态存储(例如数据库等)是独立的,避免污染关键请求路径上的状态存储。
参考