查看原文
其他

教程|Istio1.1.0下的TCP流量控制

Venil Noronha 几米宋 2022-09-07

作者:Venil Noronha(VMWare开源技术中心工程师,关注Istio、Envoy项目)

译者:马若飞 原文地址:https://venilnoronha.io/raw-tcp-traffic-shaping-with-istio-1.1.0

Istio通过虚拟服务, 目标规则, Gateway等概念提供了复杂的路由机制。Istio 1.0通过加权路由定义启用了HTTP流量转移。我提交的Envoy 和Istio的pull request为TCP/TLS服务提供了类似的特性。这一特性已经在Envoy 1.8.0中发布了。Istio中的这一特性也会在即将发布的1.1.0版本中提供使用。

在本文中,我们将用Go编写的一个简单的TCP Echo服务,用Docker将其容器化并部署到Kubernetes上,并通过练习Istio的加权TCP路由特性来理解其在生产服务中的行为。

TCP Echo服务

在本文中,我们将创建一个简单的监听连接的TCP服务,并在客户端的请求数据加上一个简单的前缀,将其作为响应返回。图示如下:

让我们看一下TCP Echo服务端的Go代码:

  1. package main

  2. import (

  3.    "bufio"

  4.    "fmt"

  5.    "io"

  6.    "net"

  7.    "os"

  8. )

  9. // main作为程序入口点

  10. func main() {

  11.    // 通过程序入参获得端口和前缀

  12.    port := fmt.Sprintf(":%s", os.Args[1])

  13.    prefix := os.Args[2]

  14.    // 在给定端口上创建tcp监听

  15.    listener, err := net.Listen("tcp", port)

  16.    if err != nil {

  17.        fmt.Println("failed to create listener, err:", err)

  18.        os.Exit(1)

  19.    }

  20.    fmt.Printf("listening on %s, prefix: %s\n", listener.Addr(), prefix)

  21.    // 监听新的连接

  22.    for {

  23.        conn, err := listener.Accept()

  24.        if err != nil {

  25.            fmt.Println("failed to accept connection, err:", err)

  26.            continue

  27.        }

  28.        // 启用goroutine处理连接

  29.        go handleConnection(conn, prefix)

  30.    }

  31. }

  32. // handleConnection 处理连接的生命周期

  33. func handleConnection(conn net.Conn, prefix string) {

  34.    defer conn.Close()

  35.    reader := bufio.NewReader(conn)

  36.    for {

  37.        // 读取客户端请求数据

  38.        bytes, err := reader.ReadBytes(byte('\n'))

  39.        if err != nil {

  40.            if err != io.EOF {

  41.                fmt.Println("failed to read data, err:", err)

  42.            }

  43.            return

  44.        }

  45.        fmt.Printf("request: %s", bytes)

  46.        // 添加前缀作为response返回

  47.        line := fmt.Sprintf("%s %s", prefix, bytes)

  48.        fmt.Printf("response: %s", line)

  49.        conn.Write([]byte(line))

  50.    }

  51. }

要测试这个程序,复制上面代码并命名为 main.go,执行命令如下:

  1. $ go run -v main.go 9000 hello

  2. listening on [::]:9000, prefix: hello

我们可以通过 nc (Netcat)在TCP层面上和这段程序交互。要发送请求,可以使用BusyBox容器,如下所示:

  1. $ docker run -it --rm busybox sh -c 'echo world | nc docker.for.mac.localhost 9000'

  2. hello world

就像你看到的,在请求“world”前面加上了“hello”,“hello world”作为响应。注意,正在执行的BusyBox容器基于 Docker for Mac,这就是为什么我访问Echo服务端时用 docker.for.mac.localhost代替了 localhost

容器化TCP Echo服务

因为我们最终想要在Kubernetes集群上运行TCP Echo服务,现在让我们将它容器化并发布镜像到 Docker Hub。

首先,用下面的内容创建 Dockerfile

  1. # 使用golang容器构建可执行文件

  2. FROM golang:1.11 as builder

  3. WORKDIR /go/src/github.com/venilnoronha/tcp-echo-server/

  4. COPY main.go .

  5. RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main main.go

  6. # 负责bin文件到基于alpine的分离容器

  7. FROM alpine:3.8

  8. RUN apk --no-cache add ca-certificates

  9. WORKDIR /bin/

  10. COPY --from=builder /go/src/github.com/venilnoronha/tcp-echo-server/main .

  11. ENTRYPOINT [ "/bin/main" ]

  12. CMD [ "9000", "hello" ]

  13. EXPOSE 9000

构建容器并发布镜像到Docker Hub:

  1. $ docker build -t vnoronha/tcp-echo-server:latest .

  2. Sending build context to Docker daemon  60.93kB

  3. ...

  4. Successfully built d172af115e18

  5. Successfully tagged vnoronha/tcp-echo-server:latest

  6. $ docker push vnoronha/tcp-echo-server:latest

  7. The push refers to repository [docker.io/vnoronha/tcp-echo-server]

  8. b4cc76510de6: Pushed

  9. ...

  10. latest: digest: sha256:0a45b5a0d362db6aa9154717ee3f2b... size: 949

部署TCP Echo服务到Kubernetes

服务配置

我们需要部署2个版本的TCP ECHO服务,用不同的前缀展示路由行为。创建 service.yaml,用Kubernetes Service 和2个 Deployments 构建2个版本的TCP ECHO服务。

  1. apiVersion: v1

  2. kind: Service

  3. metadata:

  4.  name: tcp-echo-server

  5.  labels:

  6.    app: tcp-echo-server

  7.    istio: ingressgateway # use istio default controller

  8. spec:

  9.  selector:

  10.    app: tcp-echo-server

  11.  ports:

  12.  - port: 9000

  13.    name: tcp

  14. ---

  15. apiVersion: extensions/v1beta1

  16. kind: Deployment

  17. metadata:

  18.  name: tcp-echo-server-v1

  19. spec:

  20.  replicas: 1

  21.  template:

  22.    metadata:

  23.      labels:

  24.        app: tcp-echo-server

  25.        version: v1

  26.    spec:

  27.      containers:

  28.      - name: tcp-echo-server

  29.        image: vnoronha/tcp-echo-server:latest

  30.        args: [ "9000", "one" ] # prefix: one

  31.        imagePullPolicy: IfNotPresent

  32.        ports:

  33.        - containerPort: 9000

  34. ---

  35. apiVersion: extensions/v1beta1

  36. kind: Deployment

  37. metadata:

  38.  name: tcp-echo-server-v2

  39. spec:

  40.  replicas: 1

  41.  template:

  42.    metadata:

  43.      labels:

  44.        app: tcp-echo-server

  45.        version: v2

  46.    spec:

  47.      containers:

  48.      - name: tcp-echo-server

  49.        image: vnoronha/tcp-echo-server:latest

  50.        args: [ "9000", "two" ] # prefix: two

  51.        imagePullPolicy: IfNotPresent

  52.        ports:

  53.        - containerPort: 9000

部署Minikube

Minikube是Kubernetes本地开发的最佳工具。用下面的命令启动Minikube实例:

  1. $ minikube start --bootstrapper kubeadm       \

  2.                 --memory=8192                \

  3.                 --cpus=4                     \

  4.                 --kubernetes-version=v1.10.0 \

  5.                 --vm-driver=virtualbox

  6. Starting local Kubernetes v1.10.0 cluster...

  7. ...

  8. Kubectl is now configured to use the cluster.

  9. Loading cached images from config file.

安装 Istio

在撰写本文时,Istio 1.1.0还没有发布。因此我使用了Istio的 Daily Pre-Release来演示这个新特性。请参考Istio文档学习下载和配置Istio。

一旦配置完成,这里有一个完全部署Istio组件的简单方法:

  1. $ kubectl apply -f install/kubernetes/helm/istio/templates/crds.yaml

  2. customresourcedefinition.apiextensions.k8s.io/virtualservices.networking.istio.io created

  3. ...

  4. customresourcedefinition.apiextensions.k8s.io/templates.config.istio.io created

  5. customresourcedefinition.apiextensions.k8s.io/handlers.config.istio.io created

  6. $ kubectl apply -f install/kubernetes/istio-demo.yaml

  7. namespace/istio-system created

  8. ...

  9. destinationrule.networking.istio.io/istio-policy created

  10. destinationrule.networking.istio.io/istio-telemetry created

使用Istio代理部署TCP Echo服务

为了演示Istio的路由机制,我们以sidecar模式部署 tcp-echo-server

  1. $ kubectl apply -f <(istioctl kube-inject -f service.yaml)

  2. service/tcp-echo-server created

  3. deployment.extensions/tcp-echo-server-v1 created

  4. deployment.extensions/tcp-echo-server-v2 created

通过下面的命令来验证服务运行:

  1. $ kubectl get pods

  2. NAME                                  READY     STATUS    RESTARTS   AGE

  3. tcp-echo-server-v1-78684f5697-sv5r5   2/2       Running   0          56s

  4. tcp-echo-server-v2-74bf9999c8-hhhf9   2/2       Running   0          56s

  5. $ kubectl logs tcp-echo-server-v1-78684f5697-sv5r5 tcp-echo-server

  6. listening on [::]:9000, prefix: one

  7. $ kubectl logs tcp-echo-server-v2-74bf9999c8-hhhf9 tcp-echo-server

  8. listening on [::]:9000, prefix: two

Istio加权TCP路由

这是本练习的最后一部分,定义 VirtualServiceDestinationRule和带有权重路由的 Gateway,并验证系统行为。

路由配置

创建带有两个 subsetDestinationRule来代表两个版本的 TCP Echo服务。 Gateway 容许流量通过端口 31400访问服务。最后, VirtualService限定了80%的流量必须被路由到TCP Echo服务的v1版本,20%被路由到v2版本。

  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4.  name: destination

  5. spec:

  6.  host: tcp-echo-server

  7.  subsets:

  8.  - name: v1

  9.    labels:

  10.      version: v1

  11.  - name: v2

  12.    labels:

  13.      version: v2

  14. ---

  15. apiVersion: networking.istio.io/v1alpha3

  16. kind: Gateway

  17. metadata:

  18.  name: gateway

  19. spec:

  20.  selector:

  21.    istio: ingressgateway

  22.  servers:

  23.  - port:

  24.      number: 31400

  25.      name: tcp

  26.      protocol: TCP

  27.    hosts:

  28.    - "*"

  29. ---

  30. apiVersion: networking.istio.io/v1alpha3

  31. kind: VirtualService

  32. metadata:

  33.  name: route

  34. spec:

  35.  hosts:

  36.  - "*"

  37.  gateways:

  38.  - gateway

  39.  tcp:

  40.  - match:

  41.    - port: 31400

  42.    route:

  43.    - destination:

  44.        host: tcp-echo-server

  45.        port:

  46.          number: 9000

  47.        subset: v1

  48.      weight: 80

  49.    - destination:

  50.        host: tcp-echo-server

  51.        port:

  52.          number: 9000

  53.        subset: v2

  54.      weight: 20

部署路由配置

为了让配置生效,复制上面的配置内容并创建文件 route-config.yaml,用下面的命令进行安装:

  1. kubectl apply -f route-config.yaml

  2. destinationrule.networking.istio.io/destination created

  3. gateway.networking.istio.io/gateway created

  4. virtualservice.networking.istio.io/route created

验证Istio的TCP路由行为

先来确定一下Ingress的IP:

  1. $ minikube ip

  2. 192.168.99.100

现在可以通过Ingress发送一些请求到加权负载均衡的TCP Echo服务:

  1. $ for i in {1..10}; do

  2. for> docker run -it --rm busybox sh -c '(date; sleep 1) | nc 192.168.99.100 31400'

  3. for> done

  4. one Sat Oct 20 04:38:05 UTC 2018

  5. two Sat Oct 20 04:38:07 UTC 2018

  6. two Sat Oct 20 04:38:09 UTC 2018

  7. one Sat Oct 20 04:38:12 UTC 2018

  8. one Sat Oct 20 04:38:14 UTC 2018

  9. one Sat Oct 20 04:38:17 UTC 2018

  10. one Sat Oct 20 04:38:19 UTC 2018

  11. one Sat Oct 20 04:38:22 UTC 2018

  12. one Sat Oct 20 04:38:24 UTC 2018

  13. two Sat Oct 20 04:38:27 UTC 2018

如你所见,大约80%的请求带有“one”前缀,剩下20%带有“two”前缀。这证明了加权TCP路由器确实生效了。

下图能让你很好地了解这个示范的情景:

清理

只需要像下面一样删除Minikube的部署:

  1. $ minikube stop && minikube delete

  2. Stopping local Kubernetes cluster...

  3. Machine stopped.

  4. Deleting local Kubernetes cluster...

  5. Machine deleted.

总结

如本文所示,即将发布的Istio 1.1.0版本配置加权TCP路由非常容易。本文提供了构建一个加权TCP路由的思路,让你学会如何从头开始控制TCP流量。

点击【阅读原文】跳转到ServiceMesher网站上浏览可以查看文中的链接。

相关阅读

  • SOFAMesh(https://github.com/alipay/sofa-mesh)基于Istio的大规模服务网格解决方案

  • SOFAMosn(https://github.com/alipay/sofa-mosn)使用Go语言开发的高性能Sidecar代理

合作社区

参与社区

以下是参与ServiceMesher社区的方式,最简单的方式是联系我!

  • 加入微信交流群:关注本微信公众号后访问主页右下角有获取联系方式按钮,添加好友时请注明姓名-公司

  • 社区网址:http://www.servicemesher.com

  • Slack:https://servicemesher.slack.com (需要邀请才能加入)

  • GitHub:https://github.com/servicemesher

  • Istio中文文档进度追踪:https://github.com/servicemesher/istio-official-translation

  • Twitter: https://twitter.com/servicemesher

  • 提供文章线索与投稿:https://github.com/servicemesher/trans



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存