前面介绍了Istio如何完成流量劫持,此篇博客将介绍Istio中如何配置VirtualService、Gateway、DestinationRule来完成流量管理。还是先从理论开始,先看看VirtualService、Gateway、DestinationRule的概念。
VirtualService:在Istio服务网格中定义路由规则
DestinationRule:在VirtualService路由生效后,配置应用与请求的策略集
ServiceEntry:通常用于在Istio服务网格之外启用对服务的请求
Gateway:为HTTP/TCP流量配置负载均衡器,最常见的是在网格的边缘的操作,以启用应用程序的入口流量。Istio中gateway分为IngressGateway和EgressGateway,分别管理进来的流量和出去的流量。下面是对上面3个对象配置中的各个字段含义解析。

VirtualService字段名称字段说明
spec.hosts定义路由规则关联一组的 hosts,可以是带有通配符的 DNS 名称或者 IP 地址(IP 地址仅能应用于来源流量为边缘代理网关)。该字段能应用于 HTTP 和 TCP 流量。在 Kubernetes 环境中,可以使用 service 的名称作为缩写,Istio 会按照 VirtualService所在 namespace 补齐缩写,例如在 default namespace 的 VirtualService 包含 host 缩写 reviews 会被补齐为 reviews.default.svc.cluster.local。为避免误配置,推荐填写 host 全称
spec.gateways定义应用路由规则的来源流量,可以是一个或多个网关,或网格内部的 sidecar,指定方式为 <gateway namespace>/<gateway name>,保留字段 mesh 表示网格内部所有的 sidecar,当该参数缺省时,会默认填写 mesh,即该路由规则的来源流量为网格内部所有 sidecar
spec.http定义一组有序的(优先匹配靠前的路由规则)应用于 HTTP 流量的路由规则,HTTP 路由规则会应用于网格内部的 service 端口命名为 http-, http2-, grpc- 开头的流量以及来自 gateway 的协议为 HTTP, HTTP2, GRPC, TLS-Terminated-HTTPS 的流量
spec.http.match定义路由的匹配规则列表,单个匹配规则项内所有条件是且关系,列表中多个匹配规则之间为或关系
spec.http.route定义路由转发目的地列表,一条 HTTP 路由可以是重定向或转发(默认),转发的目的地可以是一个或多个服务(服务版本)。同时也可以配置权重、header 操作等行为
spec.http.redirect定义路由重定向,一条 HTTP 路由可以是重定向或转发(默认),如规则中指定了 passthrough 选项,route、redirect 均会被忽略。可将 HTTP 301 重定向到另外的 URL 或 Authority
spec.http.rewrite定义重写 HTTP URL 或 Authority headers,不能与重定向同时配置,重写操作会在转发前执行
spec.http.timeout定义 HTTP 请求的超时时间
spec.http.retries定义 HTTP 请求的重试策略
spec.http.fault定义 HTTP 流量的故障注入策略,开启时超时和重试策略不会开启
spec.http.mirror定义将 HTTP 流量复制到另一个指定的目的端,被复制的流量按照“best effort”原则,sidecar/网关不会等待复制流量的响应结果就会从源目的端返回响应。镜像流量的目的服务端会产生监控指标。
spec.http.mirrorPercent定义流量镜像的复制百分比,缺省时复制100%的流量。最大值为100
spec.http.corsPolicy定义 CORS 策略(跨域资源共享,Cross-Origin Resource Sharing,CORS),更多关于 CORS 的介绍请参见 CORS,关于 Istio CORS 策略配置语法请参见 CorsPolicy
spec.http.headers定义 header 操作规则,包括 request 和 response header 的更新,增加,移除操作
spec.tcp定义一组有序的(优先匹配靠前的路由规则)应用于 TCP 流量的路由规则,该路由规则会应用于任何非 HTTP 和 TLS 的端口
spec.tcp.match定义 TCP 流量路由的匹配规则列表,单个匹配规则项内所有条件是且关系,列表中多个匹配规则之间为或关系
spec.tcp.route定义 TCP 连接转发的目的端
spec.tls定义一组有序的(优先匹配靠前的路由规则)应用于未终止的 TLS 或 HTTPS 流量的路由规则,该路由规则会应用于网格内部的 service 端口命名为 https-,tls- 开头的流量,来自 gateway 的端口协议为 HTTPS, TLS 的未终止加密流量,Service Entry 使用 HTTPS, TLS 协议的端口。当 https-, tls- 端口未关联 VirtualService 规则时将会被视为 TCP 流量
spec.tls.match定义 TLS 流量路由的匹配规则列表,单个匹配规则项内所有条件是且关系,列表中多个匹配规则之间为或关系
spec.tls.route定义连接转发的目的端

DestinationRule字段名称字段含义
spec.host关联 DestinationRule 配置的服务名称,可以是自动发现的服务(例如 Kubernetes service name),或通过 ServiceEntry 声明的 hosts。如填写的服务名无法在上述源中找到,则该 DestinationRule 中定义的规则无效
spec.subsets定义服务的版本(subsets),版本可通过标签键值对匹配匹配服务中的endpoints。可以在 subsets 级覆盖流量策略配置
spec.trafficPolicy定义流量策略,包括负载均衡、连接池、健康检查、TLS 策略
spec.trafficPolicy.loadBalancer配置负载均衡算法,可配置:简单负载均衡算法(round robin, least conn, random...),一致性哈希(会话保持,支持按 header name,cookie,IP,query parameter 哈希),地域感知负载均衡算法
spec.trafficPolicy.connectionPool配置与上游服务的连接量,可设置 TCP/HTTP 的连接池
spec.trafficPolicy.outlierDetection配置从负载均衡池中驱逐不健康的 hosts
spec.trafficPolicy.tls连接上游服务的 client 端 TLS 相关配置,与 PeerAuthentication 策略(server 端 TLS 模式配置)配合使用
spec.trafficPolicy.portLevelSettings配置端口级别的流量策略,端口级别的流量策略会覆盖服务 / subsets 级别的流量策略配置
GateWay字段名称字段含义
metadata.nameGateway 名称
metadata.namespaceGateway 命名空间
spec.selectorGateway 使用填写的标签键值对匹配配置下发的边缘代理网关实例
spec.servers.port.number端口
spec.servers.port.protocol通信协议,支持:HTTP, HTTPS, GRPC, HTTP2, MONGO, TCP, TLS,请注意同一网关同一端口的协议配置需要保持一致。
spec.servers.port.name端口名称
spec.severs.hosts域名,支持通配符 *
spec.servers.tls.httpsRedirect值为 true 时,边缘代理网关会对所有 http 请求返回 301 重定向,要求客户端发起 https 请求
spec.servers.tls.mode配置当前端口的 TLS 安全认证模式,如需要开启当前端口的安全认证则需要填写。支持:PASSTHROUGH, SIMPLE, MUTUAL, AUTO_PASSTHROUGH, ISTIO_MUTUAL
spec.servers.tls.credentialName配置发现 TLS 证书密钥的 secret 的名称
spec.servers.tls.serverCertificate设置端口的 TLS 证书密钥通过 file mount 形式(不推荐,推荐采用填写 credentialName 字段加载证书私钥)挂载时需要填写的证书路径字段,Istio 默认使用网关所在命名空间下 istio-ingressgateway-certs secret 加载证书至路径 /etc/istio/ingressgateway-certs
spec.servers.tls.privateKey设置端口的 TLS 证书密钥通过 file mount 形式(不推荐,推荐采用填写 credentialName 字段加载证书私钥)挂载时需要填写的私钥路径字段,Istio 默认使用网关所在命名空间下 istio-ingressgateway-certs secret 加载私钥至路径 /etc/istio/ingressgateway-certs
spec.servers.tls.caCertificates设置端口的 TLS 证书密钥通过 file mount 形式(不推荐,推荐采用填写 credentialName 字段加载证书私钥)挂载时需要填写的跟证书路径字段,Istio 默认使用网关所在命名空间下 istio-ingressgateway-ca-certs 加载根证书至路径 /etc/istio/ingressgateway-ca-certs,双向认证时需要配置根证书

接着通过实际例子演示如果通过配置VirtualService等完成流量管理。

案例一:部署后端服务simple,配置istio的VirtualService和Gateway,gateway网关添加在istio-ingress pod上,gateway网关expose出80端口。VirtualService将simple网关、80端口发送的,且请求的hosts是“simple.cncamp.io”的请求路由到后端务"simple.simple.svc.cluster.local"上。之前在CoreDNS章节介绍过,这个域名地址实际就是部署的后端simple服务的service地址。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: simple
spec:
  gateways:
    - simple
  hosts:
    - simple.cncamp.io
  http:
    - match:
        - port: 80
      route:
        - destination:
            host: simple.simple.svc.cluster.local
            port:
              number: 80
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: simple
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - simple.cncamp.io
      port:
        name: http-simple
        number: 80
        protocol: HTTP
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "80"
      labels:
        app: simple
    spec:
      containers:
        - name: simple
          imagePullPolicy: Always
          image: cncamp/httpserver:v1.0-metrics
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: simple
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: simple

上面的simple 服务的部署配置文件,通过配置文件创建相关对象。

kubectl create ns simple
kubectl create -f simple.yaml -n simple
kubectl create -f istio-specs.yaml -n simple

查看istio-ingress的service的IP地址,通过istio-ingress的IP地址发送请求。

 可以看到请求返回了响应,说明整个路由转发成功发送到了后端服务。

案例二:再部署一个服务nginx,配置VirtualService根据不同URL转发到不同的后端服务的路有规则,VirtulaService中还设置了rewrite字段。Gateway与上面相同,还是expose出80端口即可。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: simple
spec:
  gateways:
    - simple
  hosts:
    - simple.cncamp.io
  http:
  - match:
    - uri:
        exact: "/simple/hello"
    rewrite:
      uri: "/hello"
    route:
      - destination:
          host: simple.simple.svc.cluster.local
          port:
            number: 80
  - match:
    - uri:
        prefix: "/nginx"
    rewrite:
      uri: "/"
    route:
      - destination:
          host: nginx.simple.svc.cluster.local
          port:
            number: 80

 nginx应用部署的yaml文件内容如下所示

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: nginx

执行下面的命令启动pod以及访问应用。

kubectl apply -f nginx.yaml -n simple
kubectl apply -f istio-specs.yaml -n simple
curl -H "Host: simple.cncamp.io" $INGRESS_IP/simple/hello
curl -H "Host: simple.cncamp.io" $INGRESS_IP/nginx

可以看到nginx和simple服务都访问成功。

案例三:上面都是http方式访问,如果要通过https访问,配置tls即可,Gateway中expose 443端口,并配置了tls,tls是一个secret,后面会通过命令生成这个secret。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpsserver
spec:
  gateways:
    - httpsserver
  hosts:
    - httpsserver.cncamp.io
  http:
    - match:
        - port: 443
      route:
        - destination:
            host: httpserver.securesvc.svc.cluster.local
            port:
              number: 80
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: httpsserver
spec:
  selector:
    istio: ingressgateway
  servers:
    - hosts:
        - httpsserver.cncamp.io
      port:
        name: https-default
        number: 443
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: cncamp-credential

后端服务httpserver的部署yaml文件内容如下所示。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpserver
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpserver
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "80"
      labels:
        app: httpserver
    spec:
      containers:
        - name: httpserver
          imagePullPolicy: Always
          image: cncamp/httpserver:v1.0-metrics
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: httpserver
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: httpserver
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=cncamp Inc./CN=*.cncamp.io' -keyout cncamp.io.key -out cncamp.io.crt
kubectl create -n istio-system secret tls cncamp-credential --key=cncamp.io.key --cert=cncamp.io.crt
kubectl apply -f istio-specs.yaml -n securesvc

通过openssl命令生成key和cert,通过kubectl create secret tls命令生成secret对象,通过https访问访问服务。

curl --resolve httpsserver.cncamp.io:443:$INGRESS_IP https://httpsserver.cncamp.io/healthz -v -k

可以看到访问成功,因为是https方式访问,故可以看到有TLS握手的过程。

 案例四:模拟金丝雀部署,将应用的两个版本同时部署上,配置路由到V1和V2的规则。 如果请求的header中包含jesse,则路有到V2版本,否则路有到V1版本。Destination中定义V1和V2.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: canary
spec:
  hosts:
    - canary
  http:
    - match:
        - headers:
            user:
              exact: jesse
      route:
        - destination:
            host: canary
            subset: v2
    - route:
      - destination:
          host: canary
          subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: canary
spec:
  host: canary
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2
      trafficPolicy:
        loadBalancer:
          simple: ROUND_ROBIN

模拟的后端服务V1和V2版本,区分V1和V2应用主要是通过lables version进行区分的。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: canary
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "80"
      labels:
        app: canary
        version: v1
    spec:
      containers:
        - name: canary
          imagePullPolicy: Always
          image: cncamp/httpserver:v1.0-metrics
          ports:
            - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: canary
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "80"
      labels:
        app: canary
        version: v2
    spec:
      containers:
        - name: canary
          imagePullPolicy: Always
          image: cncamp/httpserver:v2.0-metrics
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: canary
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: canary

启动后端服务和生产VirtualService和DestinationRule。

kubectl apply -f canary.yaml -n canary
kubectl apply -f istio-specs.yaml -n canary

为了访问canary,这里再启动一个能执行linux命令的pod,toolbox。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: toolbox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: toolbox
  template:
    metadata:
      labels:
        app: toolbox
        access: "true"
    spec:
      containers:
        - name: toolbox
          image: centos
          command:
            - tail
            - -f
            - /dev/null

登录到toolbox中,通过curl命令(curl canary/hello -H "user: jesse")访问后端服务,如果header中有jesse字段值,那么就会访问到V2版本的服务,否则会访问V1版本的服务。执行结果如下所示

以上就是通过例子演示Istio中VirtualService、Gateway、DestinationRule的配置,从而完成流量管理。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐