使用Kong和Konga管理微服务和API

Kong是Mashape开源的高性能高可用API网关和API服务管理层。自2015年在github开源后,普遍受到关注。它基于OpenResty,进行API管理,并提供了插件实现API的AOP。Kong在Mashape 管理了超过15,000 个API,为200,000开发者提供了每个月数十亿的请求支持。html

在微服务架构之下,服务被拆的很是零散,下降了耦合度的同时也给服务的统一管理增长了难度。如上图左所示,在旧的服务治理体系之下,鉴权,限流,日志,监控等通用功能须要在每一个服务中单独实现,这使得系统维护者没有一个全局的视图来统一管理这些功能。API 网关致力于解决的问题即是为微服务纳管这些通用的功能,在此基础上提升系统的可扩展性。如右图所示,微服务搭配上 API 网关,可使得服务自己更专一于本身的领域,很好地对服务调用者和服务提供者作了隔离。mysql

Kong 的插件机制是其高可扩展性的根源,Kong 能够很方便地为路由和服务提供各类插件,网关所须要的基本特性,Kong 都如数支持:nginx

云原生: 与平台无关,Kong能够从裸机运行到Kubernetes
动态路由: Kong 的背后是 OpenResty+Lua,因此从 OpenResty 继承了动态路由的特性
熔断
健康检查
日志: 能够记录经过 Kong 的 HTTP,TCP,UDP 请求和响应。
鉴权: 权限控制,IP 黑白名单,一样是 OpenResty 的特性
SSL: Setup a Specific SSL Certificate for an underlying service or API.
监控: Kong 提供了实时监控插件
认证: 如数支持 HMAC, JWT, Basic, OAuth2.0 等经常使用协议
限流
REST API: 经过 Rest API 进行配置管理,从繁琐的配置文件中解放
可用性: 自然支持分布式
高性能: 背靠非阻塞通讯的 nginx,性能自不用说
插件机制: 提供众多开箱即用的插件,且有易于扩展的自定义插件接口,用户可使用 Lua 自行开发插件git

1、安装

Kong支持多种安装方式,这里使用Docker安装,比较简单。github

1.1 安装Kong

使用数据库

1.建立一个Docker网络web

首先,咱们你须要建立一个自定义网络,这样的话多个容器之间可以相互发现和通信,网络名称能够随便命名。sql

docker network create kong-net

2.启动数据库mongodb

你可使用 PostgreSQL 或 Cassandra 做为你的存储。这里选择PostgreSQL做为Kong的数据库。docker

$ docker run -d --name kong-database \
               --network=kong-net \
               -p 5432:5432 \
               -e "POSTGRES_USER=kong" \
               -e "POSTGRES_DB=kong" \
               postgres:9.6

若是你但愿使用Cassandra。数据库

$ docker run -d --name kong-database \
               --network=kong-net \
               -p 9042:9042 \

3.准备数据库

如今让咱们经过启动一个短暂的Kong容器来准备咱们的数据库,这个容器将运行适当的迁移并死掉!

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

注意:上面的例子中,若是你使用的是 Cassandra,那么你应该更新 KONG_DATABASE 环境变量的值为 cassandra 。

4.启动Kong

当迁移已经运行而且数据库准备就绪后,启动一个Kong容器,它将链接到你的数据库容器。

$ docker run -d --name kong \
     --network=kong-net \
     -e "KONG_DATABASE=postgres" \
     -e "KONG_PG_HOST=kong-database" \
     -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
     -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
     -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
     -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
     -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
     -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
     -p 8000:8000 \
     -p 8443:8443 \
     -p 8001:8001 \
     -p 8444:8444 \
     kong:latest

5.测试Kong!

Kong’s admin API is exposed on port 8001 and the gateway on port 8000

$ curl -i http://localhost:8001/
$ curl -i http://localhost:8000/

更多见官方安装指南

1.2 安装Konga

当前KONG的社区版是没有dashboard的,可是付费的企业版是有带的,而且还有一些企业版才能使用的插件以及升级后的企业版插件。因此对于使用社区版的用户而言,排除本身去撸一个dashboard的这种选择,第三方开源的dashboard无疑是首选。当前GitHub上还在更新维护的dashboard有三个,分别是kong-dashboard,kongdash 和 konga。

说道Kong的管理GUI,网上说的比较多的都是kong-dashboard,但目前最新版(v3.6.0)彷佛并未支持最新版本的Kong。而目前在github能找到star比较多的就是konga了。konga不只支持了Kong的最新版本(service和route的拆分新特性)同时支持管理员的权限控制和多个Kong链接池的管理。Konga因为自带了用户权限控制和Kong链接池管理,因此须要一些数据持久化处理。默认支持的数据库有mongodb、postgres、mysql。这里咱们选择的是PostgresSQL,缘由是KONG链接的数据库也是PGSQL,这样能够减小数据库的部署。

1.和之前同样,咱们须要经过启动一个短暂的容器来准备Konga的数据库。

$ docker run --rm \ 
    --network=kong-net \ 
    pantsel/konga -c prepare -a postgres -u postgresql://kong@kong-database:5432/konga_db

当迁移运行了,咱们能够启动 Konga

$ docker run -p 1337:1337 \
             --network=kong-net \
             -e "DB_ADAPTER=postgres" \
             -e "DB_HOST=kong-database" \
             -e "DB_USER=kong" \
             -e "DB_DATABASE=konga_db" \
             -e "KONGA_HOOK_TIMEOUT=120000" \
             -e "NODE_ENV=production" \
             --name konga \
             pantsel/konga

After a while, Konga will be available at:

http://<your-servers-public-ip-or-host>:1337

你须要注册一个管理员帐号

2、Konga操做

2.1 设置链接

登陆成功后,你将看到这个页面:

clipboard.png

此时,您须要手动建立与Kong的管理API的链接。

若是您打开链接(connections)页面,您会注意到与先前建立的Kong实例的链接已经存在但还没有激活。

clipboard.png

点击激活按钮。若是一切设置正确,Konga将链接到Kong,界面将充满各类酷感。

clipboard.png

2.2 核心概念

简要介绍一些核心的概念

Dashboard

clipboard.png

仪表板显示有关您当前链接的Kong实例,基础数据库和可用插件的基本信息。更多详细信息可在INFO页面中找到。

clipboard.png

Snapshots

快照功能容许您轻松地跨节点备份,恢复和移动Kong配置。您还能够安排Kong实例的自动快照。

clipboard.png

Settings

clipboard.png

设置页面提供了一种配置Konga并为用户账户设置基本ACL的简单方法。请记住,用户权限是全局设置的,并将用户账户称为实体。尚不支持单个用户ACL。

3、Konga建立服务(Services)和路由(Routes)

咱们将使用优秀的在线虚假API(由typicode提供的)进行测试和原型设计。

导航到服务页面并添加新服务。填写表格以下:

注意:url参数是一个简化参数,用于一次性添加protocol,host,port和path。另外不要把Services看成后端的具体API,要把它看成一个大的服务,该服务下面有多个API(endpoint or route)。因此建立服务的时候填上该服务的域名就好了。固然也能够是一个url(带Path的),这样每一个API(route)会路由到该path上。

clipboard.png

提交后,服务被建立,详情以下:

clipboard.png

The jsonplaceholder API 提供如下资源(假设咱们有这些API):
posts
comments
albums
photos
todos
users

咱们须要为这些资源建立路由。单击json-placeholder服务,选择routes选项卡并添加新路由。

clipboard.png

这里的 Path 就是具体业务API的路径(endpoint)。Hosts不设置会默认采用Services里的Host,可是一旦设置了,客户端请求该route的时候必须带上设置的host,且必须一致。、

若是Strip path设置为YES,这里的 Path 能够加一个前缀,如:/passport/users,但最终会映射到后端真实的API /users。Kong转发到后端服务的时候会把前缀/passport部分去掉。客户端调用API必须和Routes里的Path一致才行(/passport/users),不然会获得404,没法匹配。用户的请求是先匹配route,而后转发到service。

好比:
If your route has /my-service in the paths property, then
/my-service/foo will be routed upstream as /foo,
/my-service/bar will be routed upstream as /bar.

I’d recommend you to go through the definition of the Route Object in Kong to gain a better understanding.

试试看

$ curl -i http://localhost:8000/users

一样,建立响应POST,PUT,PATCH和DELETE请求的第二个路由。

继续建立一个新用户并测试它:

$ curl -X POST \
       -H "Content-Type: application/json" \
       -d '{"name":"JohnDoe","username":"jdoe"}' \    
       http://localhost:8000/users

到目前为止,咱们成功部署了Kong&Konga并设法经过咱们的网关访问jsonplaceholder API。

4、Kong的工做原理

clipboard.png

clipboard.png

4.1 kong默认开放的端口

接受客户端流量的端口,proxy部分

  • :8000 http端口
  • :8443 https端口

admin API 端口 admin部分

  • :8001 http端口
  • :8444 https端口

4.2 Nginx配置 VS Kong配置

咱们来看一个典型的Nginx的配置对应在Kong上是怎么样的,下面是一个典型的Nginx配置

upstream passportUpstream {
        server localhost:8080 weight=100;
}
server {
        listen 80;
        location /hello {
        proxy_pass http://passportUpstream;
        }
}

下面咱们来看看其对应Kong中的配置

# 配置 upstream 
curl -X POST http://localhost:8001/upstreams 
    --data "name=passportUpstream" 

# 配置 target 
curl -X POST http://localhost:8001/upstreams/passport/targets 
    --data "target=localhost:8080" --data "weight=100" 

# 配置 service 
curl -X POST http://localhost:8001/services 
    --data "name=getUserInfo" --data "host=passportUpstream" 

# 配置 route 
curl -X POST http://localhost:8001/routes 
    --data "paths[]=/user"
    --data "service.id=8695cc65-16c1-43b1-95a1-5d30d0a50409" 

curl -X POST http://localhost:8001/routes 
    --data "hosts[]=*.example.com,test.com,*.abc.com" 
    --data "service.id=8695cc65-16c1-43b1-95a1-5d30d0a50409"

这一切配置都是经过其Http Restful API 来动态实现的,无需咱们在手动的 reload Nginx.conf 。开发的同窗看到这是否是感受到很幸福了。

在上述的配置中涉及到了几个概念:upstrean、target、service、route等概念,它们是Kong的几个核心概念,也是咱们在使用Kong Api 时常常打交道的,下面咱们就其几个核心概念作一下简单的说明。

4.3 Kong 关键术语/名词解析

Upstream
Upstream 对象表示虚拟主机名,可用于经过多个服务(目标)对传入请求进行负载均衡。例如:service.v1.xyz 为Service对象命名的上游host是service.v1.xyz对此服务的请求将代理到上游定义的目标。

Target
目标IP地址/主机名,其端口表示后端服务的实例。每一个上游均可以有多个target,而且能够动态添加Target。

因为上游维护Target的更改历史记录,所以没法删除或者修改Target。要禁用目标,请发布一个新的Targer weight=0,或者使用DELETE来完成相同的操做。

Service
顾名思义,服务实体是每一个上游服务的抽象。服务的示例是数据转换微服务,计费API等。

服务的主要属性是它的URL(其中,Kong应该代理流量),其能够被设置为单个串或经过指定其protocol, host,port和path。

服务与路由相关联(服务能够有许多与之关联的路由)。路由是Kong的入口点,并定义匹配客户端请求的规则。一旦匹配路由,Kong就会将请求代理到其关联的服务。

Route
路由实体定义规则以匹配客户端的请求。每一个Route与一个Service相关联,一个服务可能有多个与之关联的路由。与给定路由匹配的每一个请求都将代理到其关联的Service上。能够配置的字段有

  • hosts
  • paths
  • methods

Service 和 Route 的组合(以及它们之间的关注点分离)提供了一种强大的路由机制,经过它能够在Kong中定义细粒度的入口点,从而使基础架构路由到不一样上游服务。

Consumer
Consumer 对象表示服务的使用者或者用户。您能够依靠Kong做为主数据库存储,也能够将使用者列表与数据库映射,以保持Kong与现有的主数据存储之间的一致性。

Plugin
插件实体表示将在HTTP请求/响应生命周期期间执行的插件配置。它是如何为在Kong后面运行的服务添加功能的,例如身份验证或速率限制。

将插件配置添加到服务时,客户端向该服务发出的每一个请求都将运行所述插件。若是某个特定消费者须要将插件调整为不一样的值,您能够经过建立一个单独的插件实例,经过service和consumer字段指定服务和消费者 。

对应关系
Upstream : target -> 1:n
Service:Upstream -> 1:1 or 1:0 (Service 能够直接指向具体的Target,至关于不作负载均衡)
Service : Route -> 1:n

Note: Client请求的流量经过Route指向与之相关的Service,若是配置插件的话就会做用插件,Service接到流量后给相应的Upstream的服务上面。

5、Kong API操做

5.1 配置服务

经过向Admin API发送HTTP请求来向Kong添加服务:

curl -i -X POST http://localhost:8001/services/ \
    -d 'name=foo-service' \
    -d 'url=http://foo-service.com'
HTTP/1.1 201 Created
...

{
    "connect_timeout": 60000,
    "created_at": 1515537771,
    "host": "foo-service.com",
    "id": "d54da06c-d69f-4910-8896-915c63c270cd",
    "name": "foo-service",
    "path": "/",
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1515537771,
    "write_timeout": 60000
}

这里注册一个名为“foo-service”的服务,该服务指向http://foo-service.com(上游)。

注意:url参数是一个简化参数,用于一次性添加protocol,host,port和path。

更多见官方指南

5.2 路由匹配规则

如今让咱们讨论Kong如何匹配针对路由的已配置host,path和methods属性(或字段)的请求。请注意,全部这三个字段都是可选的,但必须至少指定其中一个。

对于匹配路线的请求:

  • 请求必须包含全部已配置的字段
  • 请求中的字段值必须至少与其中一个配置值匹配(当字段配置接受一个或多个值时,请求只须要其中一个值被视为匹配)

这里思考一个问题,Kong route 中的host的做用是什么?有什么意义?哪些场景会用到设置多个host呢?

这是官方的解释:
Routing a request based on its Host header is the most straightforward way to proxy traffic through Kong, as this is the intended usage of the HTTP Host header. Kong makes it easy to do so via the hosts field of the API entity.

显然官方对host的说明,没有回答上面的问题,下面看下网络上对host的解释:

咱们知道Http请求头信息里面会带有一个Host字段,不少人不是很清楚这个字段具体的做用或者用法,包括我被不少人问过也曾经有些迷茫,这里具体扫盲下。

Host 是HTTP 1.1 协议中新增的一个请求头,主要用来实现虚拟主机技术。咱们知道一个IP地址能够对应多个域名,好比假设我有这么几个域名 www.qiniu.com,www.taobao.com和www.jd.com 而后在域名提供商那经过A记录或者CNAME记录的方式最终都和个人虚拟机服务器IP 111.111.111.111关联起来,那么我经过任何一个域名去访问最终解析到的都是IP 111.111.111.111。

可是仍是没有提到Host的概念,其实能够这样看,咱们的那台虚拟机111.111.111.111上面实际上是能够放很不少网站的(否则若是只能放一个网站的话就太不合理了,虚拟机那么多资源都浪费了),咱们能够把www.qiniu.com,www.taobao.com 和 www.jd.com 这些网站都假设那台虚拟机上面,可是这样会有一个问题,咱们每次访问这些域名其实都是解析到服务器IP 111.111.111.111,我怎么来区分每次根据域名显示出不一样的网站的内容呢,其实这就要用到请求头中Host的概念了,每一个Host能够看作是我在服务器111.111.111.111上面的一个站点,每次我用那些域名访问的时候都是会解析同一个虚拟机没错,可是我经过不一样的Host能够区分出我是访问这个虚拟机上的哪一个站点。

咱们再来看几个例子就完全明白了。考虑以下配置的路由:

{
    "hosts": ["example.com", "foo-service.com"],
    "paths": ["/foo", "/bar"],
    "methods": ["GET"]
}

下面咱们假设请求时不带Host

$ curl -v -H "Host: ''" -i http://localhost:8000/users/1

注意观察请求头,* Connected to localhost (127.0.0.1) port 8000 (#0);客户端和kong创建了tcp链接,可是host是空的,因此Kong匹配不到上面的route。

$ curl -v -H "Host: ''" -i http://localhost:8000/users/1
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /users/1 HTTP/1.1
> Host: ''
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 404 Not Found
HTTP/1.1 404 Not Found
< Date: Thu, 19 Sep 2019 09:49:49 GMT
Date: Thu, 19 Sep 2019 09:49:49 GMT
< Content-Type: application/json; charset=utf-8
Content-Type: application/json; charset=utf-8
< Connection: keep-alive
Connection: keep-alive
< Content-Length: 48
Content-Length: 48
< Server: kong/1.3.0
Server: kong/1.3.0

< 
* Connection #0 to host localhost left intact
{"message":"no Route matched with those values"}

让咱们看下设置了正确Host的请求:

curl -v -H "Host: example.com" -i http://localhost:8000/users/1

此次kong匹配到了route,因此拿到了数据。

$ curl -v -H "Host: example.com" -i http://localhost:8000/users/1
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /users/1 HTTP/1.1
> Host: example.com
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
Content-Type: application/json; charset=utf-8
< Content-Length: 509
Content-Length: 509
< Connection: keep-alive
Connection: keep-alive
< Date: Thu, 19 Sep 2019 10:13:05 GMT
Date: Thu, 19 Sep 2019 10:13:05 GMT
< Set-Cookie: __cfduid=d20158b06862bc2fa86521fef7e966e771568887985; expires=Fri, 18-Sep-20 10:13:05 GMT; path=/; domain=.typicode.com; HttpOnly
Set-Cookie: __cfduid=d20158b06862bc2fa86521fef7e966e771568887985; expires=Fri, 18-Sep-20 10:13:05 GMT; path=/; domain=.typicode.com; HttpOnly
< X-Powered-By: Express
X-Powered-By: Express
< Vary: Origin, Accept-Encoding
Vary: Origin, Accept-Encoding
< Access-Control-Allow-Credentials: true
Access-Control-Allow-Credentials: true
< Cache-Control: public, max-age=14400
Cache-Control: public, max-age=14400
< Pragma: no-cache
Pragma: no-cache
< Expires: Thu, 19 Sep 2019 14:13:05 GMT
Expires: Thu, 19 Sep 2019 14:13:05 GMT
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< Etag: W/"1fd-+2Y3G3w049iSZtw5t1mzSnunngE"
Etag: W/"1fd-+2Y3G3w049iSZtw5t1mzSnunngE"
< Via: kong/1.3.0
Via: kong/1.3.0
< CF-Cache-Status: MISS
CF-Cache-Status: MISS
< Accept-Ranges: bytes
Accept-Ranges: bytes
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
Server: cloudflare
< CF-RAY: 518ac8f28e0bd362-LAX
CF-RAY: 518ac8f28e0bd362-LAX
< X-Kong-Upstream-Latency: 981
X-Kong-Upstream-Latency: 981
< X-Kong-Proxy-Latency: 0
X-Kong-Proxy-Latency: 0

< 
{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "Sincere@april.biz",
  "address": {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
      "lat": "-37.3159",
      "lng": "81.1496"
    }
  },
  "phone": "1-770-736-8031 x56442",
  "website": "hildegard.org",
  "company": {
    "name": "Romaguera-Crona",
    "catchPhrase": "Multi-layered client-server neural-net",
    "bs": "harness real-time e-markets"
  }
* Connection #0 to host localhost left intact
}

因此Host的做用是上面,如今彻底搞清楚了吧。

参考

https://docs.konghq.com/insta...
https://docs.konghq.com/0.14....
https://pantsel.github.io/konga/
https://ajaysreedhar.github.i...
https://discuss.konghq.com/t/...
https://zhuanlan.zhihu.com/p/...
https://www.lijiaocn.com/%E9%...
https://keyla.vip/kong/route/
https://www.li-rui.top/2019/0...
https://www.linzepeng.com/201...
https://medium.com/@tselentis...
https://www.cnkirito.moe/kong...
https://www.li-rui.top/2019/0...
https://www.qingtingip.com/h_...
http://www.102no.com/archives...

相关文章
相关标签/搜索