# istio 0.5.0 新特性：流量镜像

在类似 Dark launch 的测试、发布过程中，流量复制是个非常有用的功能，istio 0.5.0 的更新，带来了一个新的路由相关特性：流量镜像。

这一场景中，我们会将正常的流量进行复制，将复制出来的流量分发给待上线的应用(V2)，使用实际流量对新版本应用进行测试；而现有客户端则仅会感知到单一版本(V1)的存在。

下面做个小实验来进行验证。

## 条件

基于 Kubernetes 运行的 Istio 0.5.0 版本部署。

## 部署

源码见后。

用`nginx:stabel-alpine`镜像，生成`v1`和`v2`两个版本的 Deployment，以及一个`target`服务：

~~~bash
istioctl kube-inject -f v1.deploy.yaml| kubectl apply -f -
istioctl kube-inject -f v2.deploy.yaml| kubectl apply -f -
kubectl apply -f service.yaml
~~~

用`dustise/sleep`镜像作为客户端服务，来制造请求访问目标服务。

~~~bash
istioctl kube-inject -f sleep.yaml | kubectl apply -f -
~~~

使用`kubectl get po`验证 Pod 生成情况，就绪之后开始进行验证。

~~~bash
NAME                         READY     STATUS    RESTARTS   AGE
sleep-6f569f4c9-hs6wx        2/2       Running   0          10m
target-v1-7f78d974c-mfhzz    2/2       Running   0          1h
target-v2-5886cd585d-tr22d   2/2       Running   0          1h
~~~

## 验证

### 初始化

首先我们生成一个缺省路由，将全部流量引入 v1：

~~~yaml
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: rule-all-http
spec:
  destination:
    name: target
  precedence: 1
  route:
  - labels:
      app: target
      version: v1
    weight: 100
~~~

使用`kubectl apply -f rules.yaml`应用这一规则。

接下来监控 nginx 的访问日志：

    kubectl logs -f [v1-pod] -c nginx
    kubectl logs -f [v2-pod] -c nginx

> 或者可以使用 `kubetail` 工具：`kubetail target -c nginx`，对部署内容的日志进行监控。

`kubectl exec -it [sleep-pod] -c sleep bash` 进入客户端服务 Pod，执行`curl http://target`，会看到在 `v1` Pod 中出现了访问迹象：

    [target-v1-7f78d974c-mfhzz] 127.0.0.1 - - [12/Feb/2018:15:28:30 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "-"
    [target-v1-7f78d974c-mfhzz] 127.0.0.1 - - [12/Feb/2018:15:28:31 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "-"
    [target-v1-7f78d974c-mfhzz] 127.0.0.1 - - [12/Feb/2018:15:28:32 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "-"

### 开始镜像

修改路由规则：

~~~yaml
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: rule-all-http
spec:
  destination:
    name: target
  precedence: 1
  route:
  - labels:
      app: target
      version: v1
    weight: 100
  - labels:
      app: target
      version: v2
    weight: 0
  mirror:
    name: target
    labels:
      app: target
      version: v2
~~~

**注意**这里为 `v2` 建立了一个权重为 `0` 的路由项，这是一个硬性规定，启用 `mirror` 的路由必须要在上面的 `route` 中具备一致的入口，以此通知 Envoy 进行相应的数据面操作。

使用 `kubectl apply -f rules.yaml` 来更新路由规则。

再次在客户端 Pod 中访问 `target` 服务，会发现两个版本的 Pod 中都出现了访问日志，并且数量上是一一对应的：

    [target-v2-5886cd585d-tr22d] 127.0.0.1 - - [12/Feb/2018:15:20:16 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "10.240
    .0.29"
    [target-v1-7f78d974c-mfhzz] 127.0.0.1 - - [12/Feb/2018:15:20:16 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "-"
    [target-v2-5886cd585d-tr22d] 127.0.0.1 - - [12/Feb/2018:15:20:17 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "10.240
    .0.29"
    [target-v1-7f78d974c-mfhzz] 127.0.0.1 - - [12/Feb/2018:15:20:17 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.38.0" "-"

> 从语法来看，仅支持一个复制

## 源码

### v1.deploy.yaml

将 `v1` 替换成 `v2` 生成另外一个部署。

~~~yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: target-v1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: target
        version: v1
    spec:
      containers:
      - image: nginx:stable-alpine
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:
        - containerPort: 80
~~~

### sleep.yaml

~~~yaml
apiVersion: v1
kind: Service
metadata:
  name: target
  labels:
    app: target
spec:
  ports:
  - name: http
    port: 80
  selector:
    app: target
~~~

### sleep.yaml

~~~yaml
apiVersion: v1
kind: Service
metadata:
  name: sleep
  labels:
    app: sleep
    version: v1
spec:
  selector:
    app: sleep
    version: v1
  ports:
    - name: ssh
      port: 80
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: sleep
        version: v1
    spec:
      containers:
      - name: sleep
        image: dustise/sleep
        imagePullPolicy: IfNotPresent
---
~~~
