Gloo 是一个设计很新颖的 api gateway,功能和支持的平台很丰富,文档也非常好,架构设计、基本概念等介绍的很清楚。设计架构如下:
基本概念:
Gloo 提供了一个名为 glooctl 的工具,用来管理 gloo ,用 brew 直接安装:
brew install solo-io/tap/glooctl.
# 或者
curl -sL https://run.solo.io/gloo/install | sh
直接用 glooctl 部署 gloo ,默认安装在名为 gloo-system 的 namespace 中,用-n
修改默认的 namespace,例如“glooctl install gateway -n my-namespace”。
gloo有三种部署方式,分别是Gloo Gateway、 Gloo Ingress Controller、Gloo Knative Cluster Ingress,官方文档建议使用gateway的方式,三种方式部署命令如下:
glooctl install gateway (推荐方式,可以应用gloo提供的特性,glooctl使用helm安装gloo)
glooctl install ingress (兼容kubernete的ingress)
glooctl install knative (兼容knative应用)
安装的过程会访问需要翻Q才能访问的 https://storage.googleapis.com/solo-public-helm/charts/gloo-0.13.28.tgz,与此同时又要访问kuberetes。
这里的操作端mac上开启了全局VPN,访问内部网中的kubernetes 10.10.173.203,需要进行路由设置:
开启vpn后,默认路由是通过vpn的网卡:
$ route get 10.10.173.203
route to: 10.10.173.203
destination: default
mask: default
interface: utun3
flags: <UP,DONE,CLONING,STATIC>
recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire
0 0 0 0 0 0 1280 0
修改为经过本地网卡en0,参考mac设置路由表:
$ route -n add -net 10.10.0.0 -netmask 255.255.0.0 172.16.111.254
gloo默认安装在gloo-system中:
$ kubectl -n gloo-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gateway-proxy LoadBalancer 172.16.46.252 <pending> 80:31403/TCP,443:32649/TCP 2m47s
gloo ClusterIP 172.16.126.174 <none> 9977/TCP 2m48s
$ kubectl -n gloo-system get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
discovery 1 1 1 1 2m50s
gateway 1 1 1 1 2m50s
gateway-proxy 1 1 1 1 2m50s
gloo 1 1 1 1 2m50s
删除方法:
glooctl uninstall
安装helm命令:
$ brew install kubernetes-helm
$ helm init
$ helm repo add gloo https://storage.googleapis.com/solo-public-helm
用helm搜索gloo:
$ helm search gloo/gloo --versions
NAME CHART VERSION APP VERSION DESCRIPTION
gloo/gloo 0.13.28 Gloo Helm chart for Kubernetes
gloo/gloo 0.13.27 Gloo Helm chart for Kubernetes
gloo/gloo 0.13.26 Gloo Helm chart for Kubernetes
gloo/gloo 0.13.25 Gloo Helm chart for Kubernetes
gloo/gloo 0.13.24 Gloo Helm chart for Kubernetes
...
部署方法:
$ helm install gloo/gloo --name gloo-0-7-6 --namespace my-namespace
将helm charts获取到本地的方法:
$ helm fetch gloo/gloo
gloo-0.13.28.tgz
先在kubernetes集群中部署一个应用,这是gloo文档中给出的应用,部署在default namespace中:
$ kubectl apply \
--filename https://raw.githubusercontent.com/solo-io/gloo/master/example/petstore/petstore.yaml
Ingress方式就是直接识别Kubernetes的ingress,将它们转换成envoy中的配置。
创建ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: petstore-ingress
annotations:
kubernetes.io/ingress.class: gloo
spec:
rules:
- host: gloo.example.com
http:
paths:
- path: /.*
backend:
serviceName: petstore
servicePort: 8080
用proxy命令获得代理服务地址:
$ glooctl proxy url --name ingress-proxy
http://192.168.64.46:30949
Gateway方式中,gloo自定义的CRD完全取代了kubernetes的ingress,使用gateway方式,需要用glooctl install gateway部署gloo。
用下面的命令查看所有的upstream,也就是集群中的所有pod和它们的服务端口:
$ glooctl get upstreams
+-------------------------------+------------+----------+--------------------------------+
| UPSTREAM | TYPE | STATUS | DETAILS |
+-------------------------------+------------+----------+--------------------------------+
| amb-demo-httpbin-80 | Kubernetes | Accepted | svc name: httpbin |
| | | | svc namespace: amb-demo |
| | | | port: 80 |
| default-petstore-8080 | Kubernetes | Accepted | svc name: petstore |
| | | | svc namespace: default |
| | | | port: 8080 |
| | | | REST service: |
| | | | functions: |
| | | | - addPet |
| | | | - deletePet |
| | | | - findPetById |
| | | | - findPets |
| | | | |
...
注意 default-petstore-8080 的DETAILS
明显不同,其中有一段 REST Service 和 functions 信息:
REST service:
functions:
- addPet
- deletePet
- findPetById
- findPets
Petstore的部署方式和其它应用没有区别,这些信息是从petstore的/swagger.json中发现的,在yaml文件中可以看到:
$ glooctl get upstream default-petstore-8080 --output yaml
...
serviceSpec:
rest:
swaggerInfo:
url: http://petstore.default.svc.cluster.local:8080/swagger.json
...
自动从swagger文件中发现应用支持的接口,这个设计非常非常赞!除了swagger,gloo还支持gRPC reflection。
为指定的upstream配置转发路径,将路径 /sample-route-1 转发到 default-petstore-8080 ,并且将路径前缀修改为 /api/pets。
$ glooctl add route \
--path-exact /sample-route-1 \
--dest-name default-petstore-8080 \
--prefix-rewrite /api/pets
creating virtualservice default with default domain *
+-----------------+--------------+---------+------+---------+---------+--------------------------------+
| VIRTUAL SERVICE | DISPLAY NAME | DOMAINS | SSL | STATUS | PLUGINS | ROUTES |
+-----------------+--------------+---------+------+---------+---------+--------------------------------+
| default | | * | none | Pending | | /sample-route-1 -> |
| | | | | | | default-petstore-8080 |
+-----------------+--------------+---------+------+---------+---------+--------------------------------+
设置路径转发实质是创建一个对应的virtual service, virtual service 中不仅有 routes ,还有 domains 、ssl、plugins字段,没有指定域名的路径转发位于 default virtualservice 中,domains 字段是*
:
$ glooctl get virtualservice
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
| VIRTUAL SERVICE | DISPLAY NAME | DOMAINS | SSL | STATUS | PLUGINS | ROUTES |
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
| default | | * | none | Accepted | | /sample-route-1 -> |
| | | | | | | default-petstore-8080 |
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
$ glooctl get virtualservice --output yaml (yaml格式输出)
...
用proxy命令获取代理服务地址:
$ glooctl proxy url
http://10.10.173.203:31403
$ curl http://10.10.173.203:31403/sample-route-1
[{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]
创建另一个路径转发,观察 prefix-rewrite 的作用:
$ glooctl add route \
--path-exact /isecho \
--dest-name demo-echo-echo-80 \
--prefix-rewrite /prefix/a
selected virtualservice default for route
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
| VIRTUAL SERVICE | DISPLAY NAME | DOMAINS | SSL | STATUS | PLUGINS | ROUTES |
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
| default | | * | none | Accepted | | /echo -> |
| | | | | | | demo-echo-echo-echo-80 |
| | | | | | | /sample-route-1 -> |
| | | | | | | default-petstore-8080 |
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
echo应用将收到的请求信息回显,可以看到echo应用收到的请求的uri为/prefix/a
,而不是echo
:
$ curl http://10.10.173.203:31403/echo
Hostname: echo-7df87d5c6d-s4vhq
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.3 - lua: 10008
Request Information:
client_address=172.16.128.136
method=GET
real path=/prefix/a
query=
request_version=1.1
request_uri=http://10.10.173.203:8080/prefix/a
...
注意 在添加路径的过程中遇到一个比较严重的问题:添加的转发信息没有被及时刷新到envoy中,将容器quay.io/solo-io/gloo-envoy-wrapper删除重建后,envoy中才有了相关配置,这个问题需要特别注意。
upstream default-petstore-8080 实现了四个函数,这些函数可能是应用的API接口也可能是函数服务,gloo 通过 swagger 或者 gRPC reflection 自动发现应用支持的函数:
$ glooctl get upstream default-petstore-8080
+-----------------------+------------+----------+-------------------------+
| UPSTREAM | TYPE | STATUS | DETAILS |
+-----------------------+------------+----------+-------------------------+
| default-petstore-8080 | Kubernetes | Accepted | svc name: petstore |
| | | | svc namespace: default |
| | | | port: 8080 |
| | | | REST service: |
| | | | functions: |
| | | | - addPet |
| | | | - deletePet |
| | | | - findPetById |
| | | | - findPets |
| | | | |
+-----------------------+------------+----------+-------------------------+
在 upstream 中找到 findPetById 的定义,这个函数用 GET 方法, uri 是 /api/pets/{{id}},id 为输入参数:
$ glooctl get upstream default-petstore-8080 -o yaml
...
findPetById:
body: {}
headers:
:method:
text: GET
:path:
text: /api/pets/{{ default(id, "") }}
content-length:
text: "0"
content-type: {}
transfer-encoding: {}
...
1、配置一条路由,将请求转发给函数 findPetById,传入参数默认是 body 中的 json 字符串:
$ glooctl add route \
--path-exact /petstore/findPet \
--dest-name default-petstore-8080 \
--rest-function-name findPetById
selected virtualservice default for route
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
| VIRTUAL SERVICE | DISPLAY NAME | DOMAINS | SSL | STATUS | PLUGINS | ROUTES |
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
| default | | * | none | Accepted | | /petstore/findPet -> |
| | | | | | | default-petstore-8080 |
| | | | | | | ... |
+-----------------+--------------+---------+------+----------+---------+--------------------------------+
通过gloo调用函数,body中包含json格式的传入参数,gloo将参数提取后转发fucntion:
$ curl http://10.10.173.203:31403/petstore/findPet -d '{"id": 1}'
{"id":1,"name":"Dog","status":"available"}
$ curl http://10.10.173.203:31403/petstore/findPet -d '{"id": 2}'
{"id":2,"name":"Cat","status":"pending"}
2、同样转发给函数 findPetById ,传入参数从uri中提取,–rest-parameters 指定提取方法:
$ glooctl add route \
--path-prefix /petstore/findWithId/ \
--dest-name default-petstore-8080 \
--rest-function-name findPetById \
--rest-parameters ':path=/petstore/findWithId/{id}'
这时可以用下面的方式调用:
$ curl http://10.10.173.203:31403/petstore/findWithId/1
{"id":1,"name":"Dog","status":"available"}
$ curl http://10.10.173.203:31403/petstore/findWithId/2
{"id":2,"name":"Cat","status":"pending"}
Gloo 自身定位是一个支持 kubernetes、aws、azure 等平台的独立网关,将请求代理到 kubernetes 集群外部不是问题。只需创建一个对应集群外部服务的 upstream:
$ glooctl create upstream static jsonplaceholder-80 --static-hosts jsonplaceholder.typicode.com:80
+--------------------+--------+---------+---------------------------------+
| UPSTREAM | TYPE | STATUS | DETAILS |
+--------------------+--------+---------+---------------------------------+
| jsonplaceholder-80 | Static | Pending | hosts: |
| | | | - |
| | | | jsonplaceholder.typicode.com:80 |
| | | | |
+--------------------+--------+---------+---------------------------------+
接下来的操作和上一节类似,创建指向该upstream的路由即可,例如:
$ glooctl add route \
--dest-name jsonplaceholder-80 \
--path-exact /api/posts \
--prefix-rewrite /posts
前面用 glooctl 创建的 virtual service 存放在 kubernetes 集群中,是一个类型为 VirtualService
的 CRD,包含很多配置项:
$ kubectl get virtualservices default --namespace gloo-system -o yaml |less
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
creationTimestamp: "2019-05-15T10:02:25Z"
generation: 1
name: default
namespace: gloo-system
...
路由匹配规则见 Advanced Route Matching 、路由动作见 Advanced Route Actions (可以为一组目标,设置不同的权重)。Gloo route 支持下列 插件:transformations(内容修改)、faults(注入一定比例的错误返回,用于测试)、prefixRewrite(uri改写)、timeout(超时设置)、retries(重试次数)、extensions。
Gloo 是一个值得跟进的项目,它的使用感受非常非常好,如果有能力和时间可以参与这个项目,美中不足的是一些高级功能只有企业版支持。