本文通过一个示例来讲述如何通过一个简单且标准的Istio规则来控制TCP入口流量的路由,从而实现TCP入口流量路由的统一管理。

背景信息

使用Istio的流量管理模型,将流量与基础设施扩容进行解耦,让运维人员可以通过Pilot指定流量遵循的规则。通过将流量从基础设施扩容中解耦,就可以让Istio提供各种独立于应用程序代码之外的流量管理功能。 这些功能都是通过部署的Envoy sidecar代理来实现的。

在一个典型的网格中,通常有一个或多个用于终结外部TLS的链接,将流量引入网格的负载均衡器(我们称之为Gateway),然后流量通过sidecar Gateway流经内部服务。下图描绘了网关在网格中的使用情况。Gateway

Istio Gateway为HTTP和TCP流量配置了一个负载均衡,多数情况下在网格边缘进行操作,用于启用一个服务的入口(ingress)流量。网格中可以存在任意数量的Gateway,并且多个不同的Gateway可以实现共存。 对于入口流量管理,您可能会问为什么不直接使用Kubernetes Ingress API ? 原因是Ingress API无法表达Istio的路由需求。Ingress试图在不同的HTTP代理之间取一个公共的交集,因此只能支持最基本的HTTP路由,最终导致需要将代理的其他高级功能放入到注解(annotation)中,而注解的方式在多个代理之间是不兼容的,无法移植。

此外,Kubernetes Ingress本身不支持TCP协议。因此,即使TCP不是NGINX的限制,也无法通过Ingress创建来配置NGINX Ingress Controller以进行TCP负载均衡。当然,可以通过创建一个Kubernetes ConfigMap,来使用NGINX的TCP负载均衡功能,具体请参见Support for TCP/UDP Load Balancing 。因此,这种配置在多个代理中无法兼容与移植。

与Kubernetes Ingress不同,Istio Gateway通过将L4-L6配置与L7配置分离的方式克服了Ingress的上述缺点。 Gateway只用于配置L4-L6功能(例如,对外公开的端口、TLS配置),所有主流的代理均以统一的方式实现了这些功能。 然后,通过在Gateway上绑定VirtualService的方式,可以使用标准的Istio规则来控制进入Gateway的HTTP和TCP流量。

前提条件

  • 您已成功创建一个Kubernetes集群,且集群版本为1.11.2及以上。请参见创建Kubernetes托管版集群
  • 您已连接到Kubernetes集群的Master节点,请参见通过kubectl连接Kubernetes集群
  • 您已经安装nc。如果未安装nc,根据操作系统的不同, 使用yum或apt-get等工具进行安装。

部署Istio

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,选择服务网格 > Istio管理
  3. 根据以下信息部署Istio。
    配置项 说明
    集群 部署Istio的目标集群。
    命名空间 默认命名空间为istio-system。
    发布名称 默认发布的名称为Istio。
    最新版本 显示Istio的最新版本。
    Pilot设置

    跟踪采样百分比(0-100):默认取值为100。

    启用服务就近访问:服务网格ASM通过Envoy代理为应用服务提供了全局负载均衡能力,您可以在多个跨地域的ACK集群中部署运行应用服务的实例。ASM将这些应用服务的运行状况、路由和后端信息提供给Envoy代理,使其能够以最佳方式将流量路由至某个服务位于多个地域的应用实例。ASM会根据发送请求的Envoy代理位置,针对目标服务的工作负载实例,进行优先级排序。开启该项功能之后,当所有实例都正常时,请求将保留在同一位置,即保持服务就近访问。

    控制Egress流量
    • 拦截对外访问的地址范围:拦截直接对外访问的地址范围。默认情况下,已包含集群Pod CIDR与Service CIDR,使用英文半角逗号分隔。

      选中全部则会拦截所有对外访问的地址。

    • 放行对外访问的地址范围:Istio服务网格内的服务可以直接对外访问的地址范围。默认情况下为空,使用英文半角逗号分隔。
    说明 直接放行对外访问的地址范围的优先级高于拦截对外访问的地址范围。

    例如您将同一个IP地址同时配置在直接放行对外访问的地址与拦截对外访问的地址时,您仍可以直接访问此地址,即直接放行对外访问的地址范围生效。

    网关设置 设置是否创建默认入口网关。选中创建默认入口网关创建默认入口网关,配置如下。
    • 负载均衡类型:支持选择公网内网两种类型。
    • 使用已有负载均衡:选择一个负载均衡。如没有可选的负载均衡,在右侧点击创建负载均衡,具体操作参见创建负载均衡实例
      说明 使用已有的负载均衡实例会强制覆盖已有监听。
    • 端口映射:设置您需要添加的服务端口容器端口
    证书管理 设置是否启用 cert-manager 管理证书。cert-manager可用于使用存储在Kubernetes Secret资源中的任意签名密钥对来获取证书。
    可观测性设置 选中启用 Prometheus 度量日志收集
    • 选择新建Prometheus服务时:
      您可以选中持久化存储且需要设置TSDB地址。
      说明 使用持久化存储时,您需要登录时序时空数据库控制台开通阿里云时序时空数据库服务。
    • 选择使用已有Prometheus服务时:需设置服务地址。

      如果是在同一集群内,可以使用[服务名].[命名空间]:[端口]的方式,如:http://prometheus-default-server.mygw1:80。如果不是在同一集群内,则需要提供能访问的IP地址,如:http://1.2.3.4:9090。

    选中启用Grafana度量展示时,您需要设置用户名和密码。默认都是admin。
    选中启用 Kiali 可视化服务网格时,您需要设置用户名和密码。默认都是admin。
    链路追踪设置 启用链路追踪:启用该选项,则需要开通阿里云链路追踪服务。同时需要指定对应的接入点地址。例如,接入点地址http://tracing-analysis-dc-hz.aliyuncs.com/.../api/v1/spans,表示启用该选项后,zipkin客户端根据v1版本的 API的公网(或者内网)接入点地址把采集数据传输到链路跟踪。
    说明 如果使用内网接入点,请确保Kubernetes集群与链路追踪服务在相同区域,保证网络互通。
  4. 单击部署Istio,在提示对话框中单击知道了
    在部署页面下方,可实时查看部署进展及状态。部署Istio

TCP Server镜像

您可以直接使用已经构建好的镜像文件:registry.cn-hangzhou.aliyuncs.com/wangxining/tcptest:0.1或者安装如下操作构建TCP Server镜像。

  1. https://github.com/osswangxining/Istio-TCPRoute-Sample地址克隆代码库。
  2. 切换到代码目录可以看到一个Dockerfile。
    Dockerfile
  3. 运行以下命令构建镜像。
    docker build -t {镜像仓库地址} .

部署应用

  1. 执行如下命令,部署服务。
    cd k8s
    kubectl apply -f deployment.yml
    kubectl apply -f service.yml
    说明 上面的命令会创建1个Service(tcp-echo) 与2个Deployment(tcp-echo-v1tcp-echo-v2),其中这2个Deployment都包含了标签app: tcp-echo,并且Service(tcp-echo)对应到上述2个Deployment:
    selector:
        app: "tcp-echo"
  2. 执行kubectl get pods --selector=app=tcp-echo,确认TCP Server的Pod启动。
    NAME                           READY     STATUS    RESTARTS   AGE
    tcp-echo-v1-7c775f57c9-frprp   2/2       Running   0          1m
    tcp-echo-v2-6bcfd7dcf4-2sqhf   2/2       Running   0          1m
  3. 执行kubectl get service --selector=app=tcp-echo,确认TCP Server的服务。
    NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    tcp-echo   ClusterIP   172.19.46.255   <none>        3333/TCP   17h

定义Gateway

创建并拷贝内容到gateway.yaml文件中,并执行kubectl apply -f gateway.yaml,创建2个Gateway,其中一个Gateway监听31400端口,另一个Gateway监听31401端口。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: tcp-echo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 31400
      name: tcp
      protocol: TCP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: tcp-echo-gateway-v2
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 31401
      name: tcp
      protocol: TCP
    hosts:
    - "*"
这两个Gateway共用一个Ingress Gateway服务,该Ingress Gateway使用了Loadbalancer方式暴露,可提供对外使用的IP地址。ingressgateway

创建Istio规则

创建并拷贝如下内容到destination-rule-all.yamlvirtualservice.yaml文件中。并分别执行kubectl apply -f destination-rule-all.yamlkubectl apply -f virtualservice.yaml命令。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: tcp-echo
spec:
  hosts:
  - "*"
  gateways:
  - tcp-echo-gateway
  - tcp-echo-gateway-v2
  tcp:
  - match:
    - port: 31400
      gateways:
        - tcp-echo-gateway
    route:
    - destination:
        host: tcp-echo.default.svc.cluster.local
        subset: v1
        port:
          number: 3333
  - match:
    - port: 31401
      gateways:
        - tcp-echo-gateway-v2
    route:
    - destination:
        host: tcp-echo.default.svc.cluster.local
        subset: v2
        port:
          number: 3333

体验TCP路由功能

  1. 查看Ingress Gateway的地址。

    单击左侧导航栏中的服务,在右侧上方选择对应的集群和命名空间,在列表中找到istio-ingressgateway的外部端点地址。

  2. 打开终端,运行以下命令。
    nc INGRESSGATEWAY_IP 31400
  3. 输入文字进行如下交互,可以看到该端口的TCP流量转发到了版本v1对应的Pod。
    Welcome, you are connected to node cn-beijing.i-2zeij4aznsu1dvd4mj5c.
    Running on Pod tcp-echo-v1-7c775f57c9-frprp.
    In namespace default.
    With IP address 172.16.2.90.
    Service default.
    hello, app1
    hello, app1
    continue..
    continue..
  4. 查看版本v1的Pod的日志。
    kubectl logs -f tcp-echo-v1-7c775f57c9-frprp -c tcp-echo-container | grep Received
    2018/10/17 07:32:29 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Raw Data: [104 101 108 108 111 44 32 97 112 112 49 10]
    2018/10/17 07:32:29 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Data (converted to string): hello, app1
    2018/10/17 07:34:40 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Raw Data: [99 111 110 116 105 110 117 101 46 46 10]
    2018/10/17 07:34:40 6c7f4971-40f1-4f72-54c4-e1462a846189 - Received Data (converted to string): continue..
  5. 打开另外一个终端,运行以下命令。
    nc INGRESSGATEWAY_IP 31401
  6. 输入文字进行如下交互,可以看到该端口的TCP流量转发到了版本v2对应的Pod。
    Welcome, you are connected to node cn-beijing.i-2zeij4aznsu1dvd4mj5b.
    Running on Pod tcp-echo-v2-6bcfd7dcf4-2sqhf.
    In namespace default.
    With IP address 172.16.1.95.
    Service default.
    hello, app2
    hello, app2
    yes,this is app2
    yes,this is app2
  7. 查看版本v2的Pod的日志。
    kubectl logs -f tcp-echo-v2-6bcfd7dcf4-2sqhf -c tcp-echo-container | grep Received
    2018/10/17 07:36:29 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Raw Data: [104 101 108 108 111 44 32 97 112 112 50 10]
    2018/10/17 07:36:29 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Data (converted to string): hello, app2
    2018/10/17 07:36:37 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Raw Data: [121 101 115 44 116 104 105 115 32 105 115 32 97 112 112 50 10]
    2018/10/17 07:36:37 1a70b9d4-bbc7-471d-4686-89b9234c8f87 - Received Data (converted to string): yes,this is app2