平等的黑
首发于平等的黑
聊一聊微服务网关 kong 近期的模型变迁

聊一聊微服务网关 kong 近期的模型变迁

kong 是一个基于 Nginx/OpenResty 封装的开源网关,拥有文档详实,开箱既用的特性,在中小创业公司有不错的群众基础。本文讲一讲在折腾升级过程中的理解和感悟。

在 0.13.X 版本之前,kong 的核心域模型名为 API Object routes,长这个样子:

kong api-obj model

如果使用开源的 dashboard 进行配置,整个过程会更清晰:

这样配置一条转发规则和配置 nginx 类似。主流做法是,根据来源 uri 的前缀进行匹配,(也可以加上额外的 hosts 与 http-method 项作为条件)转发给后端对应服务的地址。


从 0.13.X 版本开始,kong 引入了 Service/Route Object,把 API Object 标记为 deprecated. 在当前的 0.14.X 版本虽然没有砍掉,但关于 API Object 部分的配置文档,直接被移除了。看一下 Service/Route 的定义:

乍看上去 Service/Route 模型就是把之前的 API Object 强行一分为二,在 gitter 讨论组里,kong 的维护人员说这是为了做到一个关注分离,让 Route 与 Service 可以一对一甚至多对一的灵活配置。不过即使以 API 的方式,也可以配置多个 API 项,通过转发到同一个服务地址做到路由与服务的多对一。这样的理由显然不具说服力。

不过不管怎么样,因为眼馋 kong 0.14.X 带来的新特性(尤其是几个内置插件 prometheus/zipkin) 还是要硬着头皮上了,在 docker 环境一般都需要类似 kongfig 的自动化小程序,把配置事先写到一个模板里,然后调用 kong-admin-api 咔咔咔的把路由、插件都塞进去初始化,达成使用 docker/rancher/kubernetes 最实用的好处 ------- 一键拉起

模板文件可以设计成下面这样子,在适当的地方定义一些方便描述与表达的语义。

apis:
  - name: cookie-api
    uris: /api/v1/cookie
    methods: GET,POST,HEAD,PUT
    upstream_url: http://service01:8080/api/v1/cookie
  - name: jar-api
    uris: /api/v1/jar
    upstream_url: http://service02:8080/api/v1/jar

plugins:
  - name: jwt
    plguin_type: jwt
    target_api: cookie-api
    config:
      uri_param_names: jwt
      secret_is_base64: false
  - name: ratelimiting-conf-01
    plguin_type: rate-limiting
    target_api: all
    config:
      second: 20
      minute: 100
      hour: 1800
      limit_by: consumer

现在需要把这个初始化小程序改成适配 Service/Route 的形式,深入细节的海洋,这里有很多让人蛋疼的地方。我随便列举一些,算是概括使用这套新版 API 需要注意的地方。

  • 需要先定义 Service, 再定义 Route。因为定义 Route 的过程中需要 apply 到一个 Service 上面。
  • Service/Route 均不支持批量删除(bulk delete) 需要遍历列表的 API 然后根据 id 单独删除。
  • Service 如果上面定义了 Route项,无法删除。必须先手动删除它的路由。
  • 列表 API 不会列出全部的路由,这里有个分页,需要用 offset 不停的递归到 offset=null 才算是理论上都遍历了一遍。
  • 通常来说,插件可以添加到一个 Service 或 Route 上面。但是要额外注意某些插件由于业务特性问题,只能添加到 Service。也有只能添加到 Route 的插件。

到此为止,没有感受到这套新模型带来的任何好处,只是把以前明显更简洁利落直达要害的模型,变得臃肿复杂。那就只能从新插件入手了,先将 prometheus 插件作用于 global (无需参数). 然后向 kong-url:8001/metrics/ 请求,可以取回这些指标。

# HELP kong_datastore_reachable Datastore reachable from Kong, 0 is unreachable
# TYPE kong_datastore_reachable gauge
kong_datastore_reachable 1
# HELP kong_nginx_http_current_connections Number of HTTP connections
# TYPE kong_nginx_http_current_connections gauge
kong_nginx_http_current_connections{state="accepted"} 1
kong_nginx_http_current_connections{state="active"} 1
kong_nginx_http_current_connections{state="handled"} 1
kong_nginx_http_current_connections{state="reading"} 0
kong_nginx_http_current_connections{state="total"} 1
kong_nginx_http_current_connections{state="waiting"} 0
kong_nginx_http_current_connections{state="writing"} 1
# HELP kong_nginx_metric_errors_total Number of nginx-lua-prometheus errors
# TYPE kong_nginx_metric_errors_total counter
kong_nginx_metric_errors_total 0

有了这些现成的指标,用 kong 作关键字搜索一下 grafana dashboard,找到一个官方提供的面板。直接导入集成,一个可以一览网关所有请求统计的监控面板就有了。

利用 prometheus 插件现有的指标,也可以达成很多定制化的展示,比如:

  • 以服务为粒度的总请求数统计,状态码统计。尤其是 5xx 这种排序后做一个 table,以直觉工程的形式迅速定位后面微服务的接口异常。
  • 以服务为粒度的 latency 分布
  • 定制一些 SLA 进行性能观测。

再来一个 zipkin 插件的例子,首先在终端用 docker 一行拉起 zipkin-server

docker run -d -p 9411:9411 openzipkin/zipkin

然后调用 kong-admin-api 或者在 dashboard 上面直接配置 zipkin 插件,采样率千分之一,指向 zipkin-server 的服务地址 (docker-mac 因为在 VM 运行,需要通过特殊的内置域名解析到本机地址)

请求接口数次,就可以在 zipkin 的 UI 页面跟踪到本次请求的 tracing 信息。



直到这里,kong 进行模型变迁的目的渐渐明朗了。kong 的功能设计在向 service mesh 靠拢。

在反向代理视角下,之前的 API object 是一个简洁利落的优秀建模。这种模式随着服务化发展会暴露出很多局限性。因为服务化的场景,关注点都是服务本身,作为基础设施的网关,将 API Object 一分为二,分离明确出 Service 这个概念,就很自然了。

提起 service mesh,主流的声音还是 istio/conduit 这些实现。它们思路相似,通过 SideCar 的设计,让一个 native 语言编写的高性能代理服务(e.g. Envoy) 跟随容器启动,通过变更 iptables 规则把容器进程的网络包转发给代理进程。代理进程结合容器管理平台的API,知晓服务的启停细节和服务发现配置,进行最终的网络调用。

可以说 kong 走了一条非主流的 service mesh 之路。以自己为中心,以 Service 模型为基本元素,结合插件系统来实现对微服务非侵入式的一系列加成 monitor tracing logging health-check 而这些插件基本都要面向 Service 作为粒度,上面两个插件里 prometheus 的指标会被注入 service="service_name" 的label,zipkin上报数据里也是按照 service name区分开来(这里不能是 api-name,想象一下监控面板里充斥着 xx-api-oauth, xx-api-no-auth, xx-api-for-test xx-api-for-admin 这些其实 backend 指向同一个服务的混乱 )

其实,kong 这个模式是有不少局限性的,比如说

  • Service 模型跟容器平台如何集成与保持同步,是否能做到主流容器管理平台向 kong 自动注册。
  • 调用链下游的服务难以被这种模式 cover 到。通过插件很容易能拦截 ingress 流量。但是下游服务如果直连调用其他子服务,kong 就没法感知了。除非在微服务环境的内部再来一个 kong 当一个 restful 总线。
  • 协议支持: kong 作为一个 L7 网关,只能代理 http 流量,遇到基于 tcp 的 rpc 就抓瞎了。

当然这种模式也有它的好处,简单容易配置部署。只需要一套 Service/Route/Plugin 的配置文件初始化进去,简单的服务化体系就成型了。颇有四两拨千斤的意味。

编辑于 2018-08-02

文章被以下专栏收录