前面介绍了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.name | Gateway 名称 |
metadata.namespace | Gateway 命名空间 |
spec.selector | Gateway 使用填写的标签键值对匹配配置下发的边缘代理网关实例 |
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的配置,从而完成流量管理。