在Kubernetes环境中,Kubernetes Ingress用于配置需要在集群外部公开的服务。但是在Istio服务网格中,更好的方法是使用新的配置模型,即Istio Gateway。Gateway允许将Istio流量管理的功能应用于进入集群的流量。
二者在支持的功能上的对比,如下表所示
Istio Gateway | 阿里云Ingress Controller | NGINX Ingress Controller | |
---|---|---|---|
根据HTTP Header选择路由规则 | 支持 | 仅支持单个Header,不支持多个Header组合 | 不支持 |
Header规则支持正则表达式 | 支持 | 支持 | 不支持 |
服务之间设置权重拆分流量 | 支持 | 支持 | 不支持 |
Header和权重规则组合使用 | 支持 | 支持 | 不支持 |
路由规则检查 | 支持 | 不支持 | 不支持 |
路由规则粒度 | service下的不同pod | service | service |
支持的协议 | HTTP1.1/HTTP2/gRPC/TCP/Websockets/MongoDB | HTTP1.1/HTTP2/gRPC/TCP/Websockets | HTTP1.1/HTTP2/gRPC/TCP/Websockets |
下面我们看下使用Istio Gateway实现灰度发布时,与Ingress在使用上的差异
Istio Gateway实现灰度发布
环境准备
创建Kubernetes集群
阿里云容器服务Kubernetes 1.10.4目前已经上线,可以通过容器服务管理控制台非常方便地快速创建 Kubernetes 集群。具体过程可以参考这里
部署istio
阿里云容器服务在应用目录
目前支持istio快速部署,具体过程可以参考这里
部署用于灰度发布的两个服务
old service
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: old-nginx
spec:
replicas: 2
selector:
matchLabels:
run: old-nginx
template:
metadata:
labels:
run: old-nginx
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/xianlu/old-nginx
imagePullPolicy: Always
name: old-nginx
ports:
- containerPort: 80
protocol: TCP
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: old-nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: old-nginx
sessionAffinity: None
type: NodePort
new service
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: new-nginx
spec:
replicas: 1
selector:
matchLabels:
run: new-nginx
template:
metadata:
labels:
run: new-nginx
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/xianlu/new-nginx
imagePullPolicy: Always
name: new-nginx
ports:
- containerPort: 80
protocol: TCP
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: new-nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: new-nginx
sessionAffinity: None
type: NodePort
部署后效果如下图
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.19.0.1 <none> 443/TCP 9h
new-nginx NodePort 172.19.8.168 <none> 80:31904/TCP 4h
old-nginx NodePort 172.19.12.148 <none> 80:31545/TCP 4h
测试两个服务
$ kubectl run -it --rm bash --image=appropriate/curl --restart=Never curl 172.19.8.168
new
$ kubectl run -it --rm bash --image=appropriate/curl --restart=Never curl 172.19.12.148
old
创建Gateway对象
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: helloworld-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
确定Istio入口IP和port
$ kubectl get svc istio-ingressgateway -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 172.19.14.182 47.1x6.xx.41 80:31380/TCP,443:31390/TCP,31400:31400/... 10h
确认EXTERNAL-IP
设置了值
export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http")].port}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
发布场景
满足特定规则的客户端才能访问new service
例如,我们希望请求头中带有user且值为new的客户端请求才能访问new service
我们可以创建一个对应的VirtualService
为通过Gateway
进入的流量配置路由
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: helloworld
spec:
gateways:
- helloworld-gateway
hosts:
- '*'
http:
- match:
- headers:
user:
exact: new
route:
- destination:
host: new-nginx
port:
number: 80
- route:
- destination:
host: old-nginx
port:
number: 80
客户端测试
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
在满足特定规则的客户端请求中会有一定比例访问到new service
例如,我们希望请求头中带有user且值为new的客户端请求有50%的比例访问new service,那么我们可以创建一个这样的VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: helloworld
spec:
gateways:
- helloworld-gateway
hosts:
- '*'
http:
- match:
- headers:
user:
exact: new
route:
- destination:
host: new-nginx
port:
number: 80
weight: 50
- destination:
host: old-nginx
port:
number: 80
weight: 50
- route:
- destination:
host: old-nginx
port:
number: 80
客户端测试
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
new
$ curl -H "user: new" http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
客户端请求中随机会有一定比例访问到new service
例如,我们仅仅希望20%的客户端请求访问new service,那么我们可以创建一个这样的VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: helloworld
spec:
gateways:
- helloworld-gateway
hosts:
- '*'
http:
- route:
- destination:
host: new-nginx
port:
number: 80
weight: 20
- destination:
host: old-nginx
port:
number: 80
weight: 80
客户端测试
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
old
$ curl http://$INGRESS_HOST:$INGRESS_PORT
new
与阿里云容器服务Kubernetes Ingress Controller使用上的对比
阿里云容器服务Kubernetes Ingress Controller提供的灰度发布功能参考这里
因为受限于Kubernetes对Ingress资源的描述,Ingress Controller只能通过各种annotation表达式来支持http路由特性
但是Istio Gateway是通过Custom Resource Definition(CRD)的方式定义一种新的资源,相比之下具有更多优势:
- 语义规则清晰,更有约束,与Ingress的annotation纯文本定义相比更容易检查错误
- 各种规则可以自由组合,比如,第一种灰度场景,如果我们希望客户端请求header中同时有user=new和foo=bar才可以访问new service时,我们可以将
VirtualService
修改如下:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: helloworld
spec:
gateways:
- helloworld-gateway
hosts:
- '*'
http:
- match:
- headers:
user:
exact: new
foo:
exact: bar
route:
- destination:
host: new-nginx
port:
number: 80
weight: 50
- destination:
host: old-nginx
port:
number: 80
weight: 50
- route:
- destination:
host: old-nginx
port:
number: 80
这种场景,目前Ingress就无法支持
总结
我们可以利用阿里云Kubernetes容器服务,快速搭建一套用于连接、管理以及安全化微服务的开放平台Istio,为应用引入和配置多个相关服务。本文通过几个灰度发布的场景来体验Istio Gateway带来的Ingress功能,并同Kubernetes Ingress做了功能上的对比。
欢迎大家使用阿里云上的容器服务,快速搭建微服务的开放治理平台Istio,比较简单地集成到自己项目的微服务开发中。
感谢师兄在本文写作过程中给予的指导:)