代码改变世界

整合一套高性能网关Kong

前言

    相信大家对Api网关都比较的熟悉,我们之前的文章也介绍过ASP.NET Core的网关Ocelot,也介绍过Spring Cloud Gateway。说到网关的主要功能,其实总结起来就两个字"统一",无论是作为应用的入口、认证授权、熔断限流等等主要都是为了统一的地方做一些事情。今天我们介绍一款性能更高的网关Kong,相对于Ocelot或Gateway这些类型的网关来说,Kong的优势是具有更高的性能,主要因为Kong是基于Nginx+Lua为核心的,接下来我们就详细介绍一下。

概念介绍

    在使用Kong之前我们先来大致的介绍一下Kong是什么,Kong是基于OpenResty的开源网关。而OpenResty是基于Nginx与Lua的高性能Web平台。经常使用Nginx的同学们都知道,如果修改了Nginx的配置是需要重启Nginx的。而OpenResty让Nginx具备了动态编程的能力,使得Nginx成为了一个应用服务器软件,Kong正是基于OpenResty的,Nginx的性能不必多说,所以Kong可以理解为运行在Nginx上的高性能网关,在学习的过程中我们可以类比着Nginx进行了解。

Kong

说了这么多接下来我们大致介绍一下Kong自学三件套

Kong有一点做的还是比较好的,无论是GitHub还是官方文档介绍的都比较详细,而且比较通俗易懂,这里我们就不过多的介绍了,有兴趣的同学可以自行了解一下。介绍完了自学三件套之后,接下来我们了解一下搭建一套Kong的几个组成部分,总结起来就是三个Kong服务、Kong依赖的存储、Kong可视化界面,下面我们大致的介绍一下。

  • 首先是Kong服务,一套可以正常工作的Kong会包含两个对外提供服务的端口,一个是网关常规使用的端口,即对外提供访问的入口。另一个则是管理Kong的Admin端口,比如对Kong管理服务的增删改查以及Kong常用的Plugin管理以及一些常规的配置等,Kong的插件非常的丰富,基本上可以到达常规的一些操作比如限流、认证授权、链路跟踪、监控等都有而且形式非常的丰富。
  • 其次是Kong存储服务,因为在Kong上配置的转发服务、插件、环境变量、认证等相关的信息都是需要存储的,但是外部存储不是必须的,Kong可以将这些信息存储到进程内的缓存中,但是重启Kong之后这些配置将会丢失,因此在正常的使用过程中我们总会给他提供一个外部数据库来存储这些信息。可供Kong使用的存储数据库也有好几个选择分别是Mysql、MongoDB、Postgresql等。本次演示我们使用的是Postgresql,也是官方推荐的方式。
  • 最后是Kong的可视化UI,当然这个不是必须的。Kong提供了Admin管理接口的形式对服务和Plugin查看、新增、修改、删除等操作,但是有一个可视化的管理界面,无疑让有些操作变得更加简单清晰,而且这些可视化系统正式基于Kong的Admin接口开发的。可供选择的可视化UI也比较多,比较出名的有Konga、kong-ui、kong-admin-ui。本次演示我们选择的是Konga,也是推荐使用的最多的一个。
Konga

    Konga并非Kong官方出品的可视化UI,但是它是最流行的一个,也是使用最多的一个,目前最新版本是v0.6.3。Konga是基于Nodejs开发的,它的Github地址是https://github.com/pantsel/konga,同样的Konga的GitHub文档上介绍的也非常的详细。它的部署方式有两种,一个是直接克隆GitHub上的仓库上的代码通过npm的方式运行,另一种则是使用docker的方式部署。两种方式都非常的简单,本次我们选择docker的方式。

环境搭建

    关于Kong的搭建,官方网站给出了好几种部署方式可以基于Docker或K8S,也可以在Liunx操作系统Centos、Ubuntu、RHEL等都支持,目前最稳定版本为2.3.x,这些在官方文档上讲解的非常详细非常易懂,具体可参阅官方文档https://konghq.com/install/。如果想能快速的搭建起一套Kong+Postgresql+Konga的环境,docker-compose无疑是一个比较好的选择,接下来我们将演示用过这种方式快速搭建起一条完整的Kong环境。
    Kong官方GitHub有专门的docker-kong仓库地址为https://github.com/Kong/docker-kong,也已将这个仓库Clone下来,找到compose文件运行。但是我们这里还要整合可视化界面Konga,Konga并非官方出品,所以我要要修改一下compose文件整合进去Konga,完整的呈现如下所示

version: '3.3'
#创建kong_data卷
volumes:
  kong_data: {}
#创建kong-net网络
networks:
  kong-net:
    external: false

services:
  #数据库运行完成之后需要执行kong进行初始化操作
  kong-migrations:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    command: kong migrations bootstrap
    depends_on:
      - db
    environment:
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
      KONG_PG_HOST: db
      KONG_PG_USER: ${KONG_PG_USER:-kong}
      KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
  #迁移过程依赖db
  kong-migrations-up:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    command: kong migrations up && kong migrations finish
    depends_on:
      - db
    environment:
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
      KONG_PG_HOST: db
      KONG_PG_USER: ${KONG_PG_USER:-kong}
      KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
  # kong服务
  kong:
    image: "${KONG_DOCKER_TAG:-kong:latest}"
    user: "${KONG_USER:-kong}"
    depends_on:
      - db
    environment:
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: '0.0.0.0:8001'
      KONG_CASSANDRA_CONTACT_POINTS: db
      KONG_DATABASE: postgres
      KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
      KONG_PG_HOST: db
      KONG_PG_USER: ${KONG_PG_USER:-kong}
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    networks:
      - kong-net
    #kong的端口,非https使用8000和8001
    ports:
      - "8000:8000/tcp"
      - "8001:8001/tcp"
      - "8443:8443/tcp"
      - "8444:8444/tcp"
    #健康检查
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 10s
      retries: 10
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure

  #konga可视化界面
  konga:
    image: pantsel/konga
    networks:
      - kong-net
    depends_on:
      - db
    ports:
      - "1337:1337/tcp"
    environment:
      TOKEN_SECRET: konga
      DB_ADAPTER: postgres
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: kong
      DB_PASSWORD: kong
      DB_DATABASE: kong
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
  # postgres数据库
  db:
    image: postgres:9.6
    environment:
      POSTGRES_DB: ${KONG_PG_DATABASE:-kong}
      POSTGRES_USER: ${KONG_PG_USER:-kong}
      POSTGRES_PASSWORD_FILE: /run/secrets/kong_postgres_password
    secrets:
      - kong_postgres_password
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "${KONG_PG_USER:-kong}"]
      interval: 30s
      timeout: 30s
      retries: 3
    restart: on-failure
    deploy:
      restart_policy:
        condition: on-failure
    stdin_open: true
    tty: true
    ports:
      - 5432:5432
    networks:
      - kong-net
    volumes:
      - kong_data:/var/lib/postgresql/data

# 用文件统一管理数据库密码
secrets:
  kong_postgres_password:
    file: ./POSTGRES_PASSWORD

通过docker-compose直接运行上面的yaml文件,便可以直接运行一套完整的kong项目,这里在强调一下上面说过GitHub上有官方专门提供的docker-kong仓库,直接Clone便可以直接得到运行Kong的yaml,但是本示例我们加入了Konga,如果需要可直接下载我修改后的包[点击下载👈]下载完成之后进入docker-compose.yml所在的文件夹,执行docker-compose up -d指令,首次的话会下载依赖的镜像文件可能稍微慢一点,出现如下界面的时候说明正常运行完成

完成之后打开http://localhost:8000/这个是kong提供转发服务的地址,出现如下运行结果

{
  "message": "no Route matched with those values"
}

因为我们没有配置任何路由规则,所以会提升匹配不到路由。然后我们在浏览器打开konga的地址http://localhost:1337/出现如下界面,则表示kong和konga运行成功

操作使用

通过上面的操作我们已经成功运行起来了Kong+Konga+Postgresql的运行环境,然后我们就可以使用这套网关完成服务的转发相关的操作,常用的方式有两种,一种是通过Konga直接配置,另一种则是直接通过Kong服务的8001端口访问管理的restful接口进行操作,当然Konga也是基于这套接口来操作的,接下来我们就来大致演示一下使用这两种方式完成相关操作。

通过Konga界面配置

打开Konga之后首次会让注册登录信息,注册完成之后会让完成Konga的配置连接操作,主要就是配置Kong的管理接口

配置成功之后左侧菜单栏会出现配置相关的操作比如Service、Route、Target相关如下图所示
如果只是配置类似代理的功能,即通过网关转发到一个真实地址的场景,那么只需要添加Service,并且配置Route即可,首先添加Service
然后添加Route信息
当然这是最简单的方式去实现类似反向代理的操作,有的时候其实我们需要的是不仅仅能够实现转发,而且还能实现负载均衡的场景。这时候的请求模式就是一个高可用和负载均衡的模式,这个时候仅仅通过配置Service和Route是不够的,我们需要的是Nginx的Upstream的情况,一个Upstream对应多个真实的后端地址。
首先配置Upstreams,由于kong本质处理请求还是通过nginx进行的,所以概念可以直接和nginx类比过来,其实就是是类似配置一个转发操作,这个操作既可以是一个服务地址,也可以是一组服务集合
然后对这个upstream的信息进行配置,除了Name必填其他都可以选填
如果选择了header或cookie策略,就要为对应的策略设置具体的值,以header为例
健康检查相关分为主动检测和被动检测。主动监测是kong定时像Target发送请求探测,根据探测的结果判断服务是否健康。被动检测是通过拦截外部请求到Target,然后根据Target的响应状态判断转发的节点是否健康
下图为被动检查的相关配置,其实无论是主动监测还是被动检测,他们的概念都是一样的只是方式不一样,一个是Kong主动探测,一个是根据外部请求拦截真实服务节点的返回状态判断。它们判断的依据和Nginx健康检查的判断逻辑也是一致的,主要通过http状态码、tpc连接是否正常、请求是否超时为主要评估依据。
Upstream除了Name之外其他的配置都是选填的,配置完成直接点击保存即可,这样Upstream就配置完成了。接下来就是为Upstreams配置Target。Target等同于Nginx上在Upstream节点内配置真实的转发节点地址,如果用一句话描述Upstream与Target代表的含义的话,可以理解为Upstream是一组可以提供相同服务的虚拟节点,而Target是这一组真实节点中的一个,是真实提供服务的节点之一。
Target的配置也很简单,一个是配置Target的地址,另一个是当前这个Target的权重。上面咱们说过,一个Upstream是一组Target的集合,所以每个Target配置都可以配置对应的权重,咱们说的权重其实一种优先级的概念,每一次请求优先级比较高的会优先被访问到。如果真是的服务器配置基本上一致的话,那么权重理应是一样的,如果存在弱机的情况,那么弱机的权重应该配置的稍微低一些。
接下来就是添加Service,Service在Kong网关里的概念就是一个服务,比如我要访问商品服务、订单服务、优惠券服务,而Service正是表示着这种概念。如果说Upstreams代表着一组真实的服务,那么Service就是Upstream的门面,通过Service才能访问到Upstream。
配置Service主要配置Name和访问地址即可,这里的访问地址在Konga里有两种配置形式,一个是直接配置Url,另一个是配置protocol、host、port、path。比如上图的Url属性等同于下面的 协议+主机+端口+路径,均是针对Upstream的访问配置,和具体访问路由属于不同概念,如果是通过Service访问Upstream的场景,那么Host就是是Upstream的名称或真实的访问目标地址名称
接下来就是配置Route,上面咱们说到了Service和Upstream的概念,那么Route起到的作用就是,当我访问了Service那么我通过什么样的路由规则才能访问到Upstream,比如特定的Host头信息或者路径信息等。在Konga里,通过Service列表界面点击具体Service进入ServiceDetail界面配置Route
点击ADD ROUTE会弹出添加Route信息的弹窗,主要配置Name、Host Paths Strip Path具体含义可看截图
这里咱们重点说一下Strip Path,它代表的含义是如果通过一个路径访问到了真实的服务地址,那么在访问真实的服务地址的时候是否要带上这个Path信息,还是这个Path只是为了提供访问到具体服务的一个标识。比如真实的OrderService服务里获取订单详情的地址为http://localhost:5001/order/get/1,我们在通过网关访问OrderService的时候地址是http://localhost:8000/orderservice/order/get/1,这种情况Path里的orderservice对我来说只是为了能访问到OrderService的一个路径标识,真实服务OrderService的时候并不需要,这个时候我就可以设置Strip Path的值为true,这样在转发地址的时候就不会在Url上带上orderservice这个标识了。到这里,关于通过Konga配置的常用操作介绍的就差不多了,截图上也标注了常用字段的含义,想更详细的了解还需要自己搭建一套Konga实操一下。

通过Kong的Admin接口配置

    上面我们介绍了通过Konga的方式配置Kong的服务,我们开始的时候也说过,Konga也是通过Kong Admin API的接口完成对服务的增删改查的操作的,Kong Admin API是Kong自带的管理接口,通过这些接口我们可以通过更原生的方式去操作Kong。而且如果你通过Konga配置的时候不太了解哪些字段是必须的,或者需要理解每个字段详细代表的含义,这时候就可以通过原生的接口了解,这样有助于更深刻的了解。Admin API的官方文档地址位于https://docs.konghq.com/gateway-oss/2.3.x/admin-api/文档介绍的非常的详细,接下来我们就大致的介绍Admin API的一些常规操作。

  • 默认情况Admin API的监听端口是8001,如果是https的话默认为8444
  • API可接收的内容格式即ContentType为application/json、application/x-www-form-urlencoded、multipart/form-data
  • Kong Admin API是标准的Restful风格的接口,这对于我们操作来说非常的便捷

由于Admin API官方文档介绍的非常详细了,这里咱们就不一一的介绍了,咱们这里简单的介绍一下有代表性的接口和一些注意相关的操作。强烈建议,学习Kong的话一定要看这个文档,这样的话能解决很多的困惑。首先介绍Service的List的接口,这个接口是返回注册在Kong上的所有Service

  • 请求方式是curl http://localhost:8001/services
  • 成功的状态码为HTTP 200 OK
  • 返回的json格式代表的含义,文档中介绍的也非常详细
{
    "data": [{
        "id": "a5fb8d9b-a99d-40e9-9d35-72d42a62d83a",
        "created_at": 1422386534,
        "updated_at": 1422386534,
        "name": "my-service",
        "retries": 5,
        "protocol": "http",
        "host": "example.com",
        "port": 80,
        "path": "/some_api",
        "connect_timeout": 60000,
        "write_timeout": 60000,
        "read_timeout": 60000,
        "tags": ["user-level", "low-priority"],
        "client_certificate": {"id":"51e77dc2-8f3e-4afa-9d0e-0e3bbbcfd515"},
        "tls_verify": true,
        "tls_verify_depth": null,
        "ca_certificates": ["4e3ad2e4-0bc4-4638-8e34-c84a417ba39b", "51e77dc2-8f3e-4afa-9d0e-0e3bbbcfd515"]
    }],
    "next": "http://localhost:8001/services?offset=6378122c-a0a1-438d-a5c6-efabae9fb969"
}

data为数据结果,next是下一批数据查询的地址。这一点做得还是考虑的比较周全的,如果Service比较多的话,一次性返回不是一个明智的操作,通过判断next的方式可以知道是否存在分页的情况,根据判断状态码可以知道请求是否成功。
其次Service还有一个检索接口,它主要用于返回指定的Service信息,它的请求方式是curl http://localhost:8001/services/{service name or id},如果返回的状态码为404则说明服务没有被注册过,如果服务存在则状态码为200并返回服务详情的json。
还有一个比较实用的接口是Update Or Create Service翻译过来就是修改或创建,即如果Service存在则执行更新操作,如果不存在则直接创建一个Service。试着想一下,如果不存在这个接口,我们每次注册Service之前还要调用一下检索接口判断一下Service是否被注册过。
请求方式是curl -X PUT -H "Content-Type: application/json" -d '{}' "http://localhost:8001/services/{service name or id}"它的请求数据格式和Add的是一样的只是请求方式是PUT它的数据格式是

{
    "id": "9748f662-7711-4a90-8186-dc02f10eb0f5",
    "created_at": 1422386534,
    "updated_at": 1422386534,
    "name": "my-service",
    "retries": 5,
    "protocol": "http",
    "host": "example.com",
    "port": 80,
    "path": "/some_api",
    "connect_timeout": 60000,
    "write_timeout": 60000,
    "read_timeout": 60000,
    "tags": ["user-level", "low-priority"],
    "client_certificate": {"id":"4e3ad2e4-0bc4-4638-8e34-c84a417ba39b"},
    "tls_verify": true,
    "tls_verify_depth": null,
    "ca_certificates": ["4e3ad2e4-0bc4-4638-8e34-c84a417ba39b", "51e77dc2-8f3e-4afa-9d0e-0e3bbbcfd515"]
}'

它返回的结果是HTTP 201 Created or HTTP 200 OK

这里需要特别注意的是Add相关的接口返回的Http是HTTP 201 Created

    我们就通过关于Service的操作,大致介绍一下关于Admin API的大致操作方式,关于每一个Service、Route、Upstream、Target的操作都有完整的增删改查接口,咱们上面介绍的是除了这些之外实用操作的接口。这里还需要注意的是有些操作是有关联关系的,比如Service和Route的操作,Upstream和Target的操作,因此在实际通过接口开发的过程中要注意对Service的Id或Upstream的Id的保存。

因为Kong对注册中心这些比如Consul、Eureka、Nacos这种支持的并不是很完善,虽然Kong支持自己编写Lua脚本的方式完成这些操作,但是这些编写成本还是比较高的,所以这个时候Admin API就显得格外的实用。

总结

    本次我们主要讲解了对Kong的大致入门操作,相信很多同学都听说过或者用过它,我个人觉得Kong这种级别的组件很多时候能了解它的大致场景和结构并能搭建出来一套可运行的环境,那么入门就已经完成一半了。我觉得Kong做的比较好的一点是,它的学习文档和GitHub仓库上的一些操作都非常的完整而且很详细。我每次写这种类型的文章其实初衷都非常的简单,就是能让对这些东西有兴趣的同学,能通过这篇文章入门,算是能有一个好的开始吧。

👇欢迎扫码关注我的公众号👇
posted @ 2021-04-12 08:05  yi念之间  阅读(4900)  评论(12编辑  收藏  举报