上一节我们学习了使用Istio Gateway将集群内的http服务暴露到集群外部。 Istio Gateway在接入集群外部流量时与K8S的Ingress类似istio-ingressgateway组件相当于k8s里的ingress-controller。 Istio Gateway对于K8S的不同之处在于在Istio Gateway资源中只定义对外暴露的端口、协议、域名,对于路由信息需要使用Istio的虚拟服务VirtualService的路由规则来配置,这样就可以将Istio提供的各种各样的功能应用到接入集群的流量上。

本节将首先学习如何使用Istio Gateway将集群内的tcp服务暴露到集群外部,同时体验Istio流量管理的TCP流量转移功能,体验如何使用Istio将TCP流量从微服务的一个版本逐步迁移到另一个版本。

准备测试环境,部署两个版本的tcp-echo服务

创建名称为istio-io-tcp-traffic-shifting的命名空间,并将其标记为自动注入Istio Sidecar。

1kubectl create namespace istio-io-tcp-traffic-shifting
2kubectl label namespace istio-io-tcp-traffic-shifting istio-injection=enabled

部署v1和v2两个版本的tcp-echo微服务:

1kubectl apply -f samples/tcp-echo/tcp-echo-services.yaml -n istio-io-tcp-traffic-shifting

上面命令向k8s部署了以下内容:

 1kubectl get svc,deploy,pod -n istio-io-tcp-traffic-shifting
 2NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
 3service/tcp-echo   ClusterIP   10.109.148.168   <none>        9000/TCP,9001/TCP   7m40s
 4
 5NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
 6deployment.apps/tcp-echo-v1   1/1     1            1           7m40s
 7deployment.apps/tcp-echo-v2   1/1     1            1           7m40s
 8
 9NAME                               READY   STATUS    RESTARTS   AGE
10pod/tcp-echo-v1-7dd5c5dcfb-l886c   2/2     Running   0          7m40s
11pod/tcp-echo-v2-56cd9b5c4f-slsb7   2/2     Running   0          7m40s

tcp-echo-services.yaml的内容如下:

 1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: tcp-echo
 5  labels:
 6    app: tcp-echo
 7    service: tcp-echo
 8spec:
 9  ports:
10  - name: tcp
11    port: 9000
12  - name: tcp-other
13    port: 9001
14  # Port 9002 is omitted intentionally for testing the pass through filter chain.
15  selector:
16    app: tcp-echo
17---
18apiVersion: apps/v1
19kind: Deployment
20metadata:
21  name: tcp-echo-v1
22  labels:
23    app: tcp-echo
24    version: v1
25spec:
26  replicas: 1
27  selector:
28    matchLabels:
29      app: tcp-echo
30      version: v1
31  template:
32    metadata:
33      labels:
34        app: tcp-echo
35        version: v1
36    spec:
37      containers:
38      - name: tcp-echo
39        image: docker.io/istio/tcp-echo-server:1.2
40        imagePullPolicy: IfNotPresent
41        args: [ "9000,9001,9002", "one" ]
42        ports:
43        - containerPort: 9000
44        - containerPort: 9001
45---
46apiVersion: apps/v1
47kind: Deployment
48metadata:
49  name: tcp-echo-v2
50  labels:
51    app: tcp-echo
52    version: v2
53spec:
54  replicas: 1
55  selector:
56    matchLabels:
57      app: tcp-echo
58      version: v2
59  template:
60    metadata:
61      labels:
62        app: tcp-echo
63        version: v2
64    spec:
65      containers:
66      - name: tcp-echo
67        image: docker.io/istio/tcp-echo-server:1.2
68        imagePullPolicy: IfNotPresent
69        args: [ "9000,9001,9002", "two" ]
70        ports:
71        - containerPort: 9000
72        - containerPort: 9001

使用Istio Gateway将tcp服务tcp-echo暴露到集群外部

查看一下istio-ingressgateway组件的service:

1kubectl get svc istio-ingressgateway -n istio-system -o yaml
 1apiVersion: v1
 2kind: Service
 3metadata:
 4  labels:
 5    app: istio-ingressgateway
 6  name: istio-ingressgateway
 7  namespace: istio-system
 8spec:
 9  clusterIP: 10.106.130.216
10  clusterIPs:
11  - 10.106.130.216
12  externalIPs:
13  - 192.168.96.50
14  externalTrafficPolicy: Cluster
15  ipFamilies:
16  - IPv4
17  ipFamilyPolicy: SingleStack
18  ports:
19  - name: status-port
20    nodePort: 31723
21    port: 15021
22    protocol: TCP
23    targetPort: 15021
24  - name: http2
25    nodePort: 30709
26    port: 80
27    protocol: TCP
28    targetPort: 8080
29  - name: https
30    nodePort: 31482
31    port: 443
32    protocol: TCP
33    targetPort: 8443
34  - name: tcp
35    nodePort: 32432
36    port: 31400
37    protocol: TCP
38    targetPort: 31400
39  - name: tls
40    nodePort: 31499
41    port: 15443
42    protocol: TCP
43    targetPort: 15443
44  selector:
45    app: istio-ingressgateway
46    istio: ingressgateway
47  sessionAffinity: None
48  type: LoadBalancer
49status:
50  loadBalancer: {}

注意istio-ingressgateway的Service的ports中name为tcp的端口31400,这个端口就是用来接入tcp流量到集群内部的。 暴露tcp服务与http服务不同,多个http服务可以使用80和443结合具体hostname域名的路由统一搞定; 而暴露每个tcp服务都需要一个单独的端口, 默认部署的istio-ingressgateway Service中已经为我们设置好了一个31400,可以直接使用,但如果还想暴露另外一个tcp服务到集群外部的话,就需要修改istio-ingressgateway Service,添加新的tcp端口。

注意因为istio-ingressgateway Service具有externalIP, 这里是192.168.96.50,我们可以直接使用192.168.96.50:31400访问暴露的服务,如果没有externalIP的话,就要使用31400对应的NodePort 32343来问。

搞清楚istio-ingressgateway的作用后,再看一下如何创建istio Gateway,samples/tcp-echo/tcp-echo-all-v1.yaml的内容如下:

 1apiVersion: networking.istio.io/v1alpha3
 2kind: Gateway
 3metadata:
 4  name: tcp-echo-gateway
 5spec:
 6  selector:
 7    istio: ingressgateway
 8  servers:
 9  - port:
10      number: 31400
11      name: tcp
12      protocol: TCP
13    hosts:
14    - "*"
15---
16apiVersion: networking.istio.io/v1alpha3
17kind: DestinationRule
18metadata:
19  name: tcp-echo-destination
20spec:
21  host: tcp-echo
22  subsets:
23  - name: v1
24    labels:
25      version: v1
26  - name: v2
27    labels:
28      version: v2
29---
30apiVersion: networking.istio.io/v1alpha3
31kind: VirtualService
32metadata:
33  name: tcp-echo
34spec:
35  hosts:
36  - "*"
37  gateways:
38  - tcp-echo-gateway
39  tcp:
40  - match:
41    - port: 31400
42    route:
43    - destination:
44        host: tcp-echo
45        port:
46          number: 9000
47        subset: v1

应用上面的tcp-echo-all-v1.yaml到k8s中:

1kubectl apply -f samples/tcp-echo/tcp-echo-all-v1.yaml -n istio-io-tcp-traffic-shifting

上面的命令执行后,创建了目标规则tcp-echo-destination,此目标规则应用到了目标服务k8s serivcetcp-echo上,并配置了服务子集(subset),v1和v2两个服务子集分别对应labelversion=v1和labelversion=v2的服务实例。 上面的命令执行后还创建了Gateway tcp-echo-gateway,这个gateway匹配了任意主机名的31400端口,但光有Gateway不行,还需要使用虚拟服务配置路由规则,这里的路由规则是将tcp-echo-gateway gateway 31400端口的流量路由到目标k8s服务tcp-echo服务子集的9000端口上。

在集群外部使用nc命令测试:

 1for i in {1..10}; do \
 2sh -c "(date; sleep 1) | nc 192.168.96.50 31400"; \
 3done
 4one Sun Jul 25 15:00:41 CST 2021
 5one Sun Jul 25 15:00:42 CST 2021
 6one Sun Jul 25 15:00:43 CST 2021
 7one Sun Jul 25 15:00:44 CST 2021
 8one Sun Jul 25 15:00:45 CST 2021
 9one Sun Jul 25 15:00:46 CST 2021
10one Sun Jul 25 15:00:47 CST 2021
11one Sun Jul 25 15:00:48 CST 2021
12one Sun Jul 25 15:00:49 CST 2021
13one Sun Jul 25 15:00:50 CST 2021

10个请求中的输出结果都是one说明请求流量被100%的路由到了v1版本的服务。

接下来通过以下命令,将20%流量从tcp-echo:v1迁移到tcp-echo:v2:

1kubectl apply -f samples/tcp-echo/tcp-echo-20-v2.yaml -n istio-io-tcp-traffic-shifting

tcp-echo-20-v2.yaml文件的内容如下,

 1apiVersion: networking.istio.io/v1alpha3
 2kind: VirtualService
 3metadata:
 4  name: tcp-echo
 5spec:
 6  hosts:
 7  - "*"
 8  gateways:
 9  - tcp-echo-gateway
10  tcp:
11  - match:
12    - port: 31400
13    route:
14    - destination:
15        host: tcp-echo
16        port:
17          number: 9000
18        subset: v1
19      weight: 80
20    - destination:
21        host: tcp-echo
22        port:
23          number: 9000
24        subset: v2
25      weight: 20

通过虚拟服务tcp-echo路由规则中的权重weight分配,将80%流量留给tcp-echo:v1,将20%流量留给tcp-echo:v2。 此时在集群外部再次使用nc命令测试:

 1for i in {1..10}; do \
 2sh -c "(date; sleep 1) | nc 192.168.96.50 31400"; \
 3done
 4two Sun Jul 25 15:04:41 CST 2021
 5one Sun Jul 25 15:04:42 CST 2021
 6one Sun Jul 25 15:04:43 CST 2021
 7one Sun Jul 25 15:04:44 CST 2021
 8one Sun Jul 25 15:04:45 CST 2021
 9one Sun Jul 25 15:04:46 CST 2021
10two Sun Jul 25 15:04:47 CST 2021
11one Sun Jul 25 15:04:48 CST 2021
12one Sun Jul 25 15:04:49 CST 2021
13one Sun Jul 25 15:04:50 CST 2021

10个请求中差不多有20%的请求被路由到了版本v2的服务商。

以上利用了istio的路由权重特性完成了将tcp-echo服务的一部分TCP流量从旧版本迁移到了新版本。

参考