Istio Gateway与Kubernetes Ingress Controller对比

原文链接: https://yq.aliyun.com/articles/636511

在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              443/TCP        9h
new-nginx    NodePort    172.19.8.168            80:31904/TCP   4h
old-nginx    NodePort    172.19.12.148           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,比较简单地集成到自己项目的微服务开发中。

感谢师兄在本文写作过程中给予的指导:)

你可能感兴趣的:(Istio Gateway与Kubernetes Ingress Controller对比)