原文:https://cloud.tencent.com/developer/article/1477672

1. 什么是Kong?

目前互联网后台架构一般是采用微服务,或者类似微服务的形式,应用的请求通常需要访问多个后台系统。如果让每一个后台系统都实现鉴权、限流、负载均衡、审计等基础功能是不合适的,通用的做法是把这些功能抽离出来放到网关层。Kong是目前最流行的网关平台。

Kong - The World’s Most Popular Open Source Microservice API Gateway and Platform.

  • Nginx = Http Server + Reversed Proxy + Load Balancer
  • Openresty = Nginx + Lua-nginx-module
  • Kong = Openresty + Customized Framework

2. 概念介绍

kong的API使用Restful风格,每个对象都是一个Object,其中最重要的两个对象是:

  • Service 代表一个后台服务
  • Route 是一条规则,告诉kong怎么把网关收到的请求发送到某个特定的后台服务,一个Service可以有多个Routes。

例如:主流http server都能根据不同的主机名,端口号,路径等信息把请求转发给不同的后台服务

kong 默认绑定4个端口:

  • :8000 用来接受用户的HTTP请求,并转发到后台系统
  • :8443 用来接受用户的HTTPS请求,并转发到后台系统
  • :8001 通过HTTP协议提供管理功能的API (Admin API)
  • :8444 通过HTTPS协议提供管理功能的API

这些端口可以在**/etc/kong/kong.conf**中修改,:8000 和 :8443 默认绑定0.0.0.0;:8001 和 :8444 默认绑定 127.0.0.1

当然我们可以把Admin API作为一个服务通过kong的网关暴露出去,请参考[Kong API Loopback] (https://docs.konghq.com/1.1.x/secure-admin-api/#kong-api-loopback)不过要注意安全。

3. Cheat Sheet

3.1 文件路径

  • 配置文件:/etc/kong/kong.conf
  • 日志文件:/usr/local/kong/logs

3.2 基本启动停止

kong start [ -c /etc/kong/kong.conf ]
kong reload
kong stop

3.3 命令行参考

前面说过,Kong采用了Restful风格的API,所有对象都通过HTTP的不同方法去操作。比如 curl -X GET http://localhost:8111/{object}

其中/{object}是某个对象的在Admin API中的路径,下面以services为例:

方法

作用

url 路径

POST

添加一个service

/services

GET

查询service列表

/services

GET

查询某个特定service

/services/{name or id}

PATCH

更新service配置

/services/{name or id}

PUT

添加或更新serivce配置

/services/{name or id}

DELETE

删除service

/services/{name or id}

4. 实战

下面的内容以一个nodejs的server [node-demo] (https://github.com/4179e1/node-demo)为例,介绍kong的使用。

下文的配置中禁用了HTTPS,:8081 用来接受HTTP请求,:8111提供Admin API

4.1 Node Demo

这个服务默认绑定8080端口,提供若干API

$ curl http://127.0.0.1:8080/api?pretty
{
"api": [
{
"method": "GET, POST",
"desc": "GET demo",
"url": "http://127.0.0.1:8080/api/demo/headers"
},
{
"method": "GET",
"desc": "GET demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
},
{
"method": "PUT",
"desc": "PUT demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
},
{
"method": "DELETE",
"desc": "DELETE demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
},
{
"method": "POST",
"desc": "POST demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
}
]
}

其中/api/api/demo/headers会详细的打印请求参数

$ curl http://127.0.0.1:8080/api/demo/headers?pretty
{
"result": true,
"hostname": "localhost",
"headers": {
"host": "127.0.0.1:8080",
"user-agent": "curl/7.58.0",
"accept": "*/*"
},
"query": {
"pretty": ""
},
"body": {}
}

在下面的例子中,我们尝试把网关的/api转发到 nodedemo 的 /api,也就是访问网关地址http://127.0.0.1/api的时候,把对应的请求转发到nodedemo 的http://127.0.0.1:8080/api

4.2 添加service 和 route

下面这条命令添加了nodedemo.service这个service,凡是访问这个服务的请求都会被反向代理到http://127.0.0.1:8080这个地址

curl -i -X POST -H 'Content-Type: application/json' --url http://localhost:8111/services/ --data '{"name": "nodedemo.service", "url" : "http://127.0.0.1:8080"}'
  • 我习惯给object的名字带上类型(.service),这样交叉引用的时候不容易混淆
  • kong的Admin API支持urlencode参数,但我更习惯使用json

定义service之后,我们还得声明路由,把哪些特定的请求发送到这个service,这里我们配置的规则是“所有以/api开始的路径都转发给nodedemo.servce”:

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service/routes -d '{"name": "nodedemo.route", "paths" : ["/api"], "strip_path": false }'

其中strip_path: false表示请求后端服务时,同时还会把/api 这个前缀带上,更多细节见后文[Tips]部分。

4.3 测试

可以看到这个请求已经转发给nodedemo,并且网关自动添加了几个headres

# curl http://127.0.0.1/api/demo/headers?pretty
{
"result": true,
"hostname": "localhost",
"headers": {
"host": "127.0.0.1:8080",
"connection": "keep-alive",
"x-forwarded-for": "127.0.0.1",
"x-forwarded-proto": "http",
"x-forwarded-host": "127.0.0.1",
"x-forwarded-port": "80",
"x-real-ip": "127.0.0.1",
"user-agent": "curl/7.58.0",
"accept": "*/*",
"proxy-connection": "Keep-Alive"
},
"query": {
"pretty": ""
},
"body": {}
}

4.4 负载均衡

我们知道,nginx是能通过upstream支持多个后端server的负载均衡的,kong中要怎么做呢?

4.4.1 创建一个upstream对象

下面创建一个叫nodedemo.upstream的对象

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams -d '{"name":"nodedemo.upstream"}'

4.4.2 为upstream添加target

刚才创建的upstream对象是空的,还需要给他添加实际的后端server地址,kong中称为target,实际就是host:port的组合,其中weight用于round robin的加权平均。

为了看到负载均衡的效果,我们可以再起一个nodedmo运行在8088端口:PORT=8088 node server.js

下面把两个nodedemo添加到upstream中

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams/nodedemo.upstream/targets -d '{"target": "127.0.0.1:8080", "weight": 100}'
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams/nodedemo.upstream/targets -d '{"target": "127.0.0.1:8088", "weight": 100}'

4.4.3 把service的url指向upstream

我们创建nodedemo.service的时候,url是直接指向http://127.0.0.1:8080的后端地址,此时把它改为upstream即可

curl -X PATCH -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service --data '{ "url": "http://nodedemo.upstream" }'

访问http://localhost/api/demo/headers 数次可以发现轮询了不同的后端。

4.5 插件

kong 自带了很多插件,我们让我们给nodedemo启用一个试试,request-transformer可以修改请求内容,这里我们给请求添加一个header Hello: world

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service/plugins -d '{"name":"request-transformer","config":{"add":{"headers":["Hello: world"]}}}'

再次请求api,可以发现这个header已经有了

# curl http://127.0.0.1/api/demo/headers?pretty
{
"result": true,
"hostname": "localhost",
"headers": {
"host": "127.0.0.1:8080",
"connection": "keep-alive",
"x-forwarded-for": "127.0.0.1",
"x-forwarded-proto": "http",
"x-forwarded-host": "127.0.0.1",
"x-forwarded-port": "80",
"x-real-ip": "127.0.0.1",
"user-agent": "curl/7.58.0",
"accept": "*/*",
"hello": "world" # <==== here
},
"query": {
"pretty": ""
},
"body": {}
}

plugins也可以是全局的,Admin API调用的时候地址改为 /plugins 即可,如果同一个插件在全局和某个单独的service都配置了,以service的为准。

5. Tips

5.1 静态文件要放哪里?

Q:呃……我想用kong来搭建一个前后端分离的网站,我的静态文件要放哪?

A: 尽管kong是基于nginx的,但是作为一个API 网关,它对静态文件的支持不是特别友好,有两种方式:

  1. 修改kong的配置模版,详情请参考[Serving both a website and your APIs from Kong] (https://docs.konghq.com/1.2.x/configuration/)
  2. 后端再起一个http server,把静态文件都放到这个http server中,并配置对应的路由。

5.2 好多对象都有跟path相关的参数,它们都是干什么的?

我用到的path有几个

  1. route中的paths参数,表示符合这些请求路径要发到route对应的service中
  2. route中的strip_path 参数,决定kong转发给后端的时候是否保留源请求用于路由匹配的路径
  3. service中的path参数,上面的例子没有配置,默认为null,kong转发请求时会把这个作为前缀加上

假设网关以/api为路由把请求转发给nodedemo(即route.paths = ['/api']),它们的组合关系如下:

strip_path

service.path

请求地址

网关实际访问后端地址

true

null 或者 /

http://127.0.0.1/api/demo

http://127.0.0.1:8080/demo

true

/test

http://127.0.0.1/api/demo

http://127.0.0.1:8080/test/demo

false

null 或者 /

http://127.0.0.1/api/demo

http://127.0.0.1:8080/api/demo

false

/test

http://127.0.0.1/api/demo

http://127.0.0.1:8080/test/api/demo

以最后一行为例,相当于访问 http://127.0.0.1/api/demo 时,实际访问的是/test/api/demo,也就是把 service.path (/test)跟实际请求的路径(/api/demo)拼接起来发给后端。

5.3 wildcard域名匹配

kong同样可以基于域名把请求转发到不同的服务,比如a.example.com转发到服务A,b.example.com转发到服务B;同时kong还支持通配域名,比如*.example.com转发到服务C。

我遇到一个坑是这样的:在已经配置a.example.comb.example.com这两个路由转发的前提下,我遇到的一个坑是这样的:我还有一个服务C,需要让*.example.com/c/ (包括a.example.com/cb.example.com/c)都转发到服务C。略一思索,我给服务C的Hosts添加了一个通配地址*.example.com,然而这并不work,a.example.com/c/b.example.com/c/并不能匹配到这个路由,但是c.example.com/c/等没有配置过路由的域名可以匹配。

最终解决方案是,需要同时添加a.example.comb.example.com*.example.com三个域名,类似这样:

{
"hosts": [
"a.example.com",
"b.example.com",
"*.example.com"
],
}

我估计这不是kong的问题,大概nginx本来就是这样的,未验证

5.4 Log rotate

网关访问量太大,日志要把硬盘写满了怎么办?上logroate,添加一个配置文件/etc/logroate.d/kong,内容如下:

/usr/local/kong/logs/*.log {
daily
missingok
rotate 3
compress
delaycompress
notifempty
create 640 root root
sharedscripts
postrotate
[ -f /usr/local/kong/pids/nginx.pid ] && kill -USR1 `cat /usr/local/kong/pids/nginx.pid`
endscript
}

这个文件是从CentOS自带的/etc/logrotate.d/nginx 修改而来的,保留最近3天的日志(rotate 3)

5.5 kong有GUI吗?

kong 社区版没有GUI组件,但是有一个第三方的[konga] (https://github.com/pantsel/konga)。

6. Reference

  • [微服务与API网关(上): 为什么需要API网关?] (http://blog.didispace.com/hzf-ms-apigateway-1/)
  • [ 微服务与API 网关(下)- Kong能为我们做什么?] (http://blog.didispace.com/hzf-ms-apigateway-2/)
  • [Documentation for Kong] (https://docs.konghq.com/)

最新文章

  1. HTML5新标签&lt;canvas&gt;
  2. iOS-钥匙串中证书全部失效(证书的签发者无效)的解决办法
  3. petset翻译
  4. JS Encode and Decode URL
  5. 优雅地在markdown插入图片
  6. Winform开发框架之权限管理系统功能介绍
  7. AngularJS directive 指令相关记录
  8. 【转】Html页面结构范例
  9. javascript中IE与ff的区别
  10. 浅议tomcat与classloader
  11. 4.关于phpstudy for linux 的安装(LNMP)更好的环境请看8.LNMP环境
  12. Linux chmod权限管理需要小心的地方
  13. webservice04#对象与xml转换-jaxb#Stax解析xml#新建修改xml
  14. mysql 创建用户
  15. Jarvis OJ 一些简单的re刷题记录和脚本
  16. Jquery easyUI datagrid遇到空行做判断
  17. 05、transformation操作开发实战
  18. 创成汇丨投脑风暴&#183;创心不止|路演日 第2期,寻IT创业者
  19. [python]windows截图
  20. 译: 5. RabbitMQ Spring AMQP 之 Topic 主题

热门文章

  1. 5种智能指针指向数组的方法| 5 methods for c++ shared_ptr point to an array
  2. Elasticsearch从入门到放弃:索引基本使用方法
  3. iOS-基于TCP连接&lt;Scoket-客户端&gt;
  4. gradle+shell实现自动系统签名
  5. Java反射02 : Class对象获取的三种方式和通过反射实例化对象的两种方式
  6. python数据库模块
  7. Postsql 修改字段长度和类型
  8. 43-安装 Docker Machine
  9. swoole1--搭建echo服务器
  10. Python-1-Day