Skip to content

21案例:如何使用Kong进行网关业务化定制?

在上一课时中,我们对比了几款市面上流行的微服务网关,那么本课时我们就基于其中一款,也就是 Kong 来重点介绍微服务网关是如何搭建和实现的。

为什么使用 Kong

当我们对原有业务系统进行微服务改造时,客户端(包括移动端和 Web 端等)如何与各个微服务进行交互这个问题需要我们认真考虑,服务数量的增加会导致一些基础功能的实现变得困难,如认证授权、负载均衡和服务间通信管理。

但微服务网关所提供的访问限制、安全、流量控制、分析监控、日志、请求转发、合成和协议转换功能,可以使得开发者更加集中精力在各个业务服务的实现上,从而避免将大量时间花在考虑如何解决这些问题上。

在业内流行的微服务网关组件中,基于 Nginx 的Kong 表现突出。Kong 是 Mashape 开源的高性能、高可用 API 网关,也可以认为是 API 服务管理层。它可以通过插件扩展已有功能,这些插件(使用 Lua 编写)在 API 请求响应循环的生命周期中被执行。除此之外,Kong 本身还提供了包括 HTTP 基本认证、密钥认证、CORS、TCP、UDP、文件日志、API 请求限流、请求转发及 Nginx 监控等基本功能。

在用 Kong 进行实践之前,我们得先介绍一些 Kong 中常用的术语,因为这些术语在实践中会经常用得到。

  • Route:请求的转发规则,按照 Hostname 和 PATH,将请求转发给 Service。

  • Services:多个 Upstream 的集合,是 Route 的转发目标。

  • Consumer:API 的用户,记录用户信息。

  • Plugin:插件,可以是全局的,也可以绑定到 Service、Router 或者 Consumer。

  • Certificate:HTTPS 配置的证书。

  • SNI:域名与 Certificate 的绑定,指定了一个域名对应的 HTTPS 证书。

  • Upstream:上游对象用来表示虚拟主机名,拥有多个服务(目标)时,会对请求进行负载均衡。

  • Target:最终处理请求的 Backend 服务。

安装实践

Kong 支持多种安装方式,目前最新版本是 Kong 2.1,官方支持包括 Docker、K8s 等方式的安装:

Kong 的多种安装方式

除了官方提供的安装方式,还有社区提供的安装方式:Microsoft Azure、Kongverge 等,详细情况你可参见该网址:https://konghq.com/install/

为了方便,这里我们就选择基于 Docker 的方式安装,选择的 Kong 版本为 1.1.2。docker-compose.yml 中定义的镜像、依赖和参数如下所示:

java
version: "3.7"
services:
  kong:
    image: kong:1.1.2
    environment:
     - "KONG_DATABASE=postgres"
     - "KONG_PG_HOST=kong-database"
     - "KONG_CASSANDRA_CONTACT_POINTS=kong-database"
     - "KONG_PROXY_ACCESS_LOG=/dev/stdout"
     - "KONG_ADMIN_ACCESS_LOG=/dev/stdout"
     - "KONG_PROXY_ERROR_LOG=/dev/stderr"
     - "KONG_ADMIN_ERROR_LOG=/dev/stderr"
     - "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl"
    ports:
     - 8000:8000
     - 8443:8443
     - 8001:8001
     - 8444:8444
    networks:
     - kong-net
    depends_on:
      - kong-database
  konga:
    image: pantsel/konga
    environment:
     - "TOKEN_SECRET=blueskykong.com"
     - "NODE_ENV=production"
    ports:
     - 8080:1337
    networks:
     - kong-net
    depends_on:
      - kong-database
  kong-database:
    image: postgres:9.6
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=kong
      - POSTGRES_DB=kong
    networks:
      - kong-net
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /data/data/postgresql:/var/lib/postgresql/data
networks:
  kong-net:
    external: true

如上的 docker-compose.yml 会启动三个容器服务:Kong、Konga 和 Kong-database。这三个容器之间的通信需要增加 network 段,把容器放在同一个网段内,相关链接修改为容器名称来访问:

js
docker network create kong-net

所启动的三个容器服务,除了 Kong 之外的另两个服务:Konga 是 Kong 的 Dashboard,它是基于 JavaScript 的客户端管理工具 ,对外暴露的端口为 8080;Kong-database 是 Kong 的数据库服务,它用于存储配置信息,此处使用的是 Postgres。

这里要注意的是,在启动 Kong 容器之前,需要保持数据库的 Docker 容器在运行状态,并执行如下初始化数据库的操作:

js
docker run --rm \
     --network=kong-net \
     -e "KONG_DATABASE=postgres" \
     -e "KONG_PG_HOST=kong-database" \
     kong:latest kong migrations bootstrap

数据库初始化成功后,再次启动 docker-compose.yml 服务就可以了。我们看到 Kong 映射出多个端口,默认情况下,Kong 监听的端口为:

  • 8000。此端口是 Kong 用来监听来自客户端传入的 HTTP 请求,并将此请求转发到上游服务器;Kong 根据配置的路由规则转发到真实的后台服务地址。

  • 8443。此端口是 Kong 用来监听来自客户端传入的 HTTPS 请求,跟 8000 端口的功能类似,还会转发 HTTPS 请求。我们可以通过修改配置文件来禁用 HTTPS 的功能。

  • 8001。Kong 提供的管理 API 端口,通过此端口,管理者可以对 Kong 的监听服务进行配置,插件设置、API 的增删改查以及负载均衡等一系列的配置都是通过 8001 端口进行管理的。

  • 8444。通过此端口,管理者可以对 HTTPS 请求进行监控。

容器都启动好之后,下面我们来验证一下:

js
$ curl -i http://localhost:8001/
HTTP/1.1 200 OK
Date: Sat, 20 Jul 2019 08:39:08 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/1.1.2
Content-Length: 5785
...

本地访问 8001 端口,返回如上的结果,表示安装正确,可以正常使用 Kong。在浏览器中输入 http://localhost:8080 即可访问 Konga 的管理界面(如下图),如果你第一次登录使用,则需要创建管理员账号和密码。

Konga 的管理界面

至此,Kong 以及管理工具都已安装完成。下面我们将会通过创建服务、创建路由、安装插件等过程的讲解,进入 API Gateway 的具体实践。

1. 创建服务

正如我们在前面术语部分的介绍,服务 Services 是上游服务的抽象,可以是一个应用,或者具体某个接口。Kong 提供了管理接口,我们可以通过请求 8001 管理接口直接创建,也可以通过安装的管理界面,这二者的实现效果是一样的。

js
curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=aoho-blog' \
--data 'url=http://blueskykong.com/'

这里我们创建了一个服务名为 aoho-blog 的后端服务,指定转发的地址为 http://blueskykong.com。可以在管理界面中看到如下的记录:

Kong 服务列表

在创建服务的同时,我们还可以设置其中的一些参数,如 Retries(重试次数)、Connect timeout(连接的超时时间)、Write/Read timeout(写/读超时时间)等。

2. 创建路由

创建好服务之后,我们需要创建具体的 API 路由。路由是请求的转发规则,根据 Hostname 和 PATH,将请求转发。

js
curl -i -X POST \
--url http://localhost:8001/services/aoho-blog/routes \
--data 'hosts[]=blueskykong.com' \
--data 'paths[]=/api/blog'

如上所示,我们在 aoho-blog 中创建了一个访问 /api/blog 的路由,在管理界面可以看到相应的记录:

Kong 路由记录

创建好路由之后,我们就可以访问 /api/blog,如下图:

访问 /api/blog

Kong 默认通过 8000 端口处理代理的请求。成功的响应意味着 Kong 会将 http://localhost:8000 的请求转发到配置的 URL,并将响应转发给我们。需要注意的是,如果 API 暴露的地址与前面 Host 定义的地址(blueskykong.com)不一致,就需要在请求的 Headers 里面加入 Header,Kong 根据上面请求中定义的Header:Host,执行此操作。

创建了服务和路由之后,我们已经能够将客户端的请求转发到对应的服务,但微服务网关还承担了很多基础的功能,如安全认证、限流、分析监控等功能,因此还需要应用 Kong 的插件来实现这些功能。

安装 Kong 插件

请求到达 Kong 网关,我们可以在请求转发给服务端应用之前,应用 Kong 自带的插件对请求进行处理,如身份认证、API 限流、黑白名单校验和日志切面等。同时,我们也可以按照 Kong 的教程文档,定制属于自己的插件。这部分我们主要选择其中的三个插件示例应用,至于其余的插件应用,你可以参考这个网址:https://docs.konghq.com/hub/。

1. JWT 认证插件

JWT(JSON Web Token)是一种流行的跨域身份验证解决方案。作为一个开放的标准(RFC 7519),JWT 定义了一种简洁的、自包含的方法用于通信双方之间以 JSON 对象的形式安全地传递信息。因为数字签名的存在,这些信息是可信的。

JWT 最大的优点就是能让业务无状态化,让 Token 作为业务请求的必须信息随着请求一并传输过来,服务端不用再去存储 session 信息,尤其是在分布式系统中。Kong 提供了 JWT 认证插件,用以验证包含 HS256 或 RS256 签名的 JWT 请求。每个消费者都将拥有 JWT 凭证(公钥和密钥),这些凭证必须用于签署其 JWT。JWT 令牌可以通过请求字符串、Cookie 或者认证头部传递,Kong 将会验证令牌的签名,通过则转发,否则直接丢弃请求。

我们在前面配置的路由基础上,增加 JWT 认证插件:

js
curl -X POST http://localhost:8001/routes/e33d6aeb-4f35-4219-86c2- a41e879eda36/plugins \
--data "name=jwt"

可以看到,在插件列表增加了相应的记录:

Kong 插件列表

在增加了 JWT 插件之后,就没法直接访问 /api/blog 接口了,接口返回 "message": "Unauthorized",提示客户端要访问的话则需要提供 JWT 的认证信息。因此,我们需要创建用户:

js
curl -i -X POST \
--url http://localhost:8001/consumers/  \
--data "username=aoho"

如上我们就创建了一个名为 aoho 的用户。

Kong 创建用户

创建好用户之后,需要获取用户 JWT 凭证,执行如下的调用:

dart
$ curl -i -X POST \
--url http://localhost:8001/consumers/aoho/jwt \
--header "Content-Type: application/x-www-form-urlencoded"
# 响应
{
    "rsa_public_key": null,
    "created_at": 1563566125,
    "consumer": {
        "id": "8c0e1ab4-8411-42fc-ab80-5eccf472d2fd"
    },
    "id": "1d69281d-5083-4db0-b42f-37b74e6d20ad",
    "algorithm": "HS256",
    "secret": "olsIeVjfVSF4RuQuylTMX4x53NDAOQyO",
    "key": "TOjHFM4m1qQuPPReb8BTWAYCdM38xi3C"
}

使用 key 和 secret 在 https://jwt.io 可以生成 JWT 凭证信息。在实际的使用过程中,我们通过编码实现,此处为了演示使用网页工具生成 Token。

生成 JWT Token

上图中画线部分即为 JWT 凭证的 key 和 secret。然后我们将生成的 Token,配置到请求的认证头部,再次执行请求:

带有 JWT 令牌的合法请求

可以看到,我们能够正常请求相应的 API 接口,JWT 认证插件应用成功。Kong 的 JWT 认证插件使用比较简单,但在实践过程中,我们还需要考虑如何跟自身的用户认证系统进行结合。

2. Prometheus 可视化监控

Prometheus 是一套开源的系统监控报警框架。它启发于 Google 的 BorgMon 监控系统,由工作在 SoundCloud 的员工在 2012 年作为社区开源项目进行开发,并于 2015 年正式发布。

作为新一代的监控框架,Prometheus 适用于记录时间序列数据,并且它还具有强大的多维度数据模型、灵活而强大的查询语句、易于管理和伸缩等特点。

Kong 官方提供的 Prometheus 插件,可用的 metric(指标)有如下:

  • 状态码。上游服务返回的 HTTP 状态码。

  • 时延柱状图。Kong 中的时延都将被记录,包括请求(完整请求的时延)、Kong(Kong 用来路由、验证和运行其他插件所花费的时间)和上游(上游服务所花费时间来响应请求)。

  • Bandwidth。流经 Kong 的总带宽(出口/入口)。

  • DB 可达性。Kong 节点是否能访问其 DB。

  • Connections。各种 Nginx 连接指标,如 Active、读取、写入和接收连接。

我们在 Service 为 aoho-blog 安装 Prometheus 插件:

js
curl -X POST http://localhost:8001/services/aoho-blog/plugins \
--data "name=prometheus"

从管理界面可以看到,我们已经成功将 Prometheus 插件绑定到 aoho-blog 服务上。

Prometheus 插件

通过访问/metrics接口返回收集度量数据:

js
$ curl -i http://localhost:8001/metrics
HTTP/1.1 200 OK
Server: openresty/1.13.6.2
Date: Sun, 21 Jul 2019 09:48:42 GMT
Content-Type: text/plain; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: *
kong_bandwidth{type="egress",service="aoho-blog"} 178718
kong_bandwidth{type="ingress",service="aoho-blog"} 1799
kong_datastore_reachable 1
kong_http_status{code="200",service="aoho-blog"} 4
kong_http_status{code="401",service="aoho-blog"} 1
kong_latency_bucket{type="kong",service="aoho-blog",le="00005.0"} 1
kong_latency_bucket{type="kong",service="aoho-blog",le="00007.0"} 1
...
kong_latency_bucket{type="upstream",service="aoho-blog",le="00300.0"} 4
kong_latency_bucket{type="upstream",service="aoho-blog",le="00400.0"} 4
...
kong_latency_count{type="kong",service="aoho-blog"} 5
kong_latency_count{type="request",service="aoho-blog"} 5
kong_latency_count{type="upstream",service="aoho-blog"} 4
kong_latency_sum{type="kong",service="aoho-blog"} 409
kong_latency_sum{type="request",service="aoho-blog"} 1497
kong_latency_sum{type="upstream",service="aoho-blog"} 1047
kong_nginx_http_current_connections{state="accepted"} 2691
kong_nginx_http_current_connections{state="active"} 2
kong_nginx_http_current_connections{state="handled"} 2691
kong_nginx_http_current_connections{state="reading"} 0
kong_nginx_http_current_connections{state="total"} 2637
kong_nginx_http_current_connections{state="waiting"} 1
kong_nginx_http_current_connections{state="writing"} 1
kong_nginx_metric_errors_total 0

返回的响应太长,有省略,从响应可以看到 Prometheus 插件提供的 metric 都有体现。Prometheus 插件导出的度量标准,可以在 Grafana(一个跨平台的开源的度量分析和可视化工具)中绘制,"Prometheus + Grafana" 的组合是目前较为流行的监控系统。

3. 链路追踪 Zipkin 插件

Zipkin 是由 Twitter 开源的分布式实时链路追踪组件。 Zipkin 收集来自各个异构系统的实时监控数据,用来追踪与分析微服务架构下的请求,应用系统则需要向 Zipkin 报告链路信息。Kong 的 Zipkin 插件将 Kong 作为 zipkin-client,zipkin-client 组装好 Zipkin 需要的数据包发送到 zipkin-server。Zipkin 插件会将请求打上如下标签,并推送到 Zipkin 服务端:

  • span.kind (sent to Zipkin as "kind")

  • http.method

  • http.status_code

  • http.url

  • peer.ipv4

  • peer.ipv6

  • peer.port

  • peer.hostname

  • peer.service

关于链路追踪和 Zipkin 的具体信息,到后面的链路追踪课时我们会详细讲解,本课时我们就旨在介绍如何在 Kong 中使用 Zipkin 插件追踪所有请求的链路。

首先开启 Zipkin 插件,将插件绑定到路由上(这里可以绑定为全局的插件)。

js
curl -X POST http://kong:8001/routes/e33d6aeb-4f35-4219- 86c2-a41e879eda36/plugins \
    --data "name=zipkin"  \
    --data "config.http_endpoint=http://localhost:9411/api/v2/spans" \
    --data "config.sample_ratio=1"

如上配置了 Zipkin Collector 的地址和采样率,为了测试效果明显,设置采样率为 100%,但在实际生产环境中还是要谨慎使用 100% 的采样率配置,采样率对系统吞吐量会有影响。

Zipkin 插件

可以看到,Zipkin 插件已经应用到指定的路由上。下面我们将会执行请求 /api/blog 接口,打开 http://localhost:9411 界面如下:

Zipkin 链路追踪

这时 Zipkin 已经将请求记录,我们可以点开查看其链路详情:

链路详情

从链路调用可以知道,请求到达 Kong 之后,都经历了哪些服务和 Span,以及每个 Span 所花费的时间等信息。

小结

本课时我们重点介绍了微服务网关 Kong 的相关概念和安装实践,并在此基础上安装实践了其中具有代表性的三个 Kong 插件:JWT 认证插件、Prometheus 可视化监控和链路追踪 Zipkin 插件。Kong 官方对自身的定位也是适用于混合云平台的下一代 API 管理平台,其在功能方面很强大。因此,基于 Kong 的丰富生态和配套的工具,我们可以快速构建一个微服务网关,作为服务端的统一入口。

学习完本课时,你觉得还需要自定义哪些 Kong 网关的插件,欢迎你在留言区和我分享。