本文目录结构
Kong目前的定位是一个网关组件,非常适合在现代的微服务下进行集成。尽管很多技术网友声称Kong是完全基于Nginx,但是Kong官方纠正了这一不太准确的说法,官方给出的描述是:Kong是运行在Nginx中的Lua应用程序,通过OpenResty的lua-nginx-module模块实现,并且与OpenResty一起发布,这为可插拔的体系结构奠定了基础。在这种体系结构中,可以在运行时启用和执行Lua脚本(称为“插件”)。kong的核心是实现数据库抽象、路由和插件管理。插件可以存在于不同的代码库中,并且可以通过几行代码将限流、降级、鉴权、流量监控等功能插件注入到请求生命周期的任何地方,Kong支持非常丰富的现成插件,并且支持自行开发插件。
Kong很多概念确实和Nginx非常类似,实际上笔者认为在功能实现上Kong确实是基于Nginx进行封装的, 当你启动Kong后你可以直接通过ps aux|grep nginx看到实际的Nginx进程在运行。Kong核心思想是通过面向对象的方式将Nginx的基础功能以及加强的其它功能进行良好地架构化(参考插件式微内核架构),包装后通过对外提供Admin-API的Restful接口的方式进行配置。
Kong的定位是应用层面的网关中间件实现,而代理转发功能是网关组件角色的基本功能,实际上Kong的核心功能就是代理转发,其它功能都是基于代理转发的生命周期上进行插件式地拦截处理。本节我们就通过一个简单的代理转发来快速入门Kong。在这之前请确保你已经安装好Kong并启动成功。
学习Kong代理转发前先理解Kong几个核心概念:
Client:指请求Kong网关的发起请求的客户端,可能是curl或Postman,也可能是微服务应用。
Service:Kong核心服务实体对象,指上游后端服务,对应Nginx upstream配置的后端服务条目,或者IPVS的Real Server的概念。
Upstream Service:Kong核心服务组实体对象,对一组Service的抽象,对应Nginx的upstream的概念和功能。
Route:Kong核心路由规则实体对象,对应Nginx的location配置url规则的功能和概念,Kong除了支持url规则配置外还提供了其它方式的规则配置。
Plugin:Kong核心插件实体对象。可以通过Admin API配置全局或特定的路由和服务的插件,通过在代理转发不同生命周期节点上嵌入业务逻辑,例如鉴权,限流等。
看到这里,第一感受应该就是Kong将Nginx的功能进行对象化、丰富化。下面我们开始使用Kong。
准备部署虚拟机节点,笔者节点清单如下:
hostname |
ip |
role |
node0 |
10.68.212.100 |
Kong、PostgreSQL |
node1 |
10.68.212.101 |
后端服务1 |
node2 |
10.68.212.102 |
后端服务2 |
# node1、node2安装apache httpd服务,方便后面测试代理转发用:
# 通过yum安装httpd
yum install httpd -y
# httpd服务默认配置文件,可根据需要进行配置,这里我们使用默认配置
# vi /etc/httpd/conf/httpd.conf,将默认监听80的端口改为8080:
Listen 8080
# 启动httpd服务
service httpd restart
# httpd默认将网站的根页面指向/var/www/html/index.html,
# node1我们vi /var/www/html/index.html,输入以下内容:
from node1
# node2我们vi /var/www/html/index.html,输入以下内容:
from node2
# 关闭防火墙
systemctl stop firewalld
# 浏览器访问验证服务可用:
http://10.68.212.101:8080/
http://10.68.212.102:8080/
本文从下面开始没有明确说明情况下默认所有命令都在node0节点上执行。
# 添加node1服务
curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=node1-httpd-service' \
--data 'url=http://10.68.212.101:8080'
# url规则/node1-httpd开头的流量转发到node1-httpd-service服务
curl -i -X POST \
--url http://localhost:8001/services/node1-httpd-service/routes \
--data 'name=node1-httpd-route'
--data 'paths[]=/node1-httpd'
curl -X GET http://localhost:8000/node1-httpd
从上图中可以看到正常返回了node1部署的httpd服务的index.html页面内容。
注意,Kong默认在8000端口上监听来自客户端的流量请求。具体其它端口监听说明如下:
8000 Kong在该端口上侦听来自客户端的传入HTTP流量,并将其转发到上游服务。
8443Kong在其上侦听传入的HTTPS流量。此端口具有与端口类似的行为:8000,除了它仅期望HTTPS通信。可以通过配置文件禁用此端口。
8001用于配置Kong侦听的Admin API。
8444 Admin API在其上侦听HTTPS流量。
Kong通过路由(Route)规则对象来实现代理转发的规则配置。路由规则是Kong接收外部客户请求的第一入口,基本所有的客户端请求到达Kong默认监听的8000端口后,内部通过配置的路由规则和路由规则匹配策略进行查找匹配,将客户端请求转发到匹配规则的后端服务,如果是一组upstream后端服务时,则会根据配置的负载均衡算法进行负载。如果没有找到任何匹配的路由转发规则时,则返回:{"message":"no Route matched with those values"}。
Kong除了支持http协议的代理转发外,还支持tcp、grpc等协议,这里我们重点讨论http协议的路由转发规则配置。路由规则的配置等同Nginx的location配置的概念,Kong将其包装为对象的形式对外提供Restful-API,同时丰富了路由配置的配置项。
http协议的情况下Kong支持以下路由转发规则配置项:
methods:例如指定GET、POST、PUT、DELETE,一般和其它规则组合使用。
hosts:通过hosts的方法来配置路由转发规则,通过请求头携带Host的方式来指定。
headers:通过http协议的header参数来配置路由转发规则。
paths:通过http协议的url来配置路由转发规则,支持正则表达式。
snis:ssl安全协议下,可以通过 Server Name Indication来配置路由转发规则。
1、可以为单个后端服务Service或upstream(负载均衡部分会介绍)配置多个路由规则,例如前面的两次curl请求。
2、单个后端服务Service或upstream支持多个配置项组合配置,例如同时指定methods、paths、headers;
3、当配置多个配置项时,客户端请求必须满足单个ruote包含的全部配置项的要求,配置项属于AND布尔逻辑。
4、每个配置项的值为数组时,表示支持配置多个,单个配置项多个配置值时只需要满足其中一个即可,配置项值属于OR布尔逻辑。
5、当同一个服务配置了多个路由规则时,Kong启动默认查找优先级策略,规则配置项最多的路由将优先匹配。
# 列出服务名称为node1-httpd-service的所有路由规则
curl -i -X GET http://localhost:8001/services/node1-httpd-service/routes/
# 为名称为node1-httpd-service的服务新增路由规则route1
curl -i -X POST http://localhost:8001/services/node1-httpd-service/routes/ \
-H 'Content-Type: application/json' \
-d '{"name":"route1","hosts":["node1.com", "node1-httpd.com"],"methods":["GET", "POST"],"paths":["/node1", "/node1-httpd"]}'
# 为名称为node1-httpd-service的服务新增路由规则route2
curl -i -X POST http://localhost:8001/services/node1-httpd-service/routes/ \
-H 'Content-Type: application/json' \
-d '{"name":"route2","headers":{"version":["v1"]}}'
# 如果要删除某个路由规则可以执行以下请求
curl -i -X DELETE \
http://localhost:8001/services/node1-httpd-service/routes/
最佳实践建议:在Kong中任何对象基本都有name属性,在新增时强烈建议指定name,这样做好处是可以防止重复创建,如果不指定name,多次执行会创建多条配置相同但id不同的数据(个人认为这是Kong的设计缺点)。需要注意name是全局范围的,需要保证全局唯一(个人认为这也是Kong的设计缺点)。除此之外当我们通过curl删除Service服务时,Kong内部是不会自动删除该服务的其它关联对象的,删除服务对象时要手工删除该服务关联的路由规则、认证等数据。删除关联对象可以查阅官方API文档:
https://docs.konghq.com/2.0.x/admin-api/#delete-service
为了方便理解,将前面配置的两个路由规则合并在一起进行json格式化:
# route1
{
"name":"route1",
"hosts":[
"node1.com",
"node1-httpd.com"
],
"methods":[
"GET",
"POST"
],
"paths":[
"/node1",
"/node1-httpd"
]
}
# route2
{
"name":"route2",
"headers":{
"version":[
"v1"
]
}
}
# 匹配route2路由规则
curl -i -X GET \
--url http://localhost:8000/ \
--header 'version: v1'
# 不匹配任何路由规则
curl -i -X GET \
--url http://localhost:8000/ \
--header 'version: v2'
# 匹配route1路由规则
curl -i -X GET \
--url http://localhost:8000/node1 \
--header 'Host: node1.com'
# 同时满足route1、route2路由规则,根据优先级这里将匹配到route2
curl -i -X GET \
--url http://localhost:8000/node1 \
--header 'Host: node1.com' \
--header 'version: v2'
# 虽然匹配route2路由规则的methods、paths,但是缺少hosts所以不匹配
curl -i -X GET \
--url http://localhost:8000/node1
# paths正则示例,正则需要通过URL进行编码转义特殊符号
curl -i -X POST http://localhost:8001/services/node1-httpd-service/routes/ \
--data "name=route3" \
--data-urlencode "paths[]=/node1/\d+"
# paths正则指定优先级示例,正则需要通过URL进行编码转义特殊符号
curl -i -X POST http://localhost:8001/services/node1-httpd-service/routes/ \
--data "name=route4" \
--data "regex_priority=100" \
--data-urlencode "paths[]=/node1/status/\d+"
# 通过strip_path标记指定代理转发时是否去掉paths部分,true:去掉,false:不去掉,默认为true,正则时也是一样的。
curl -i -X POST http://localhost:8001/services/node1-httpd-service/routes/ \
--data "name=route5" \
--data "name=paths[]=/node1/abc" \
--data "strip_path=true"
# 示例说明
http://localhost:8000/node1/abc
strip_path为true时代理后url为:
http://node1:8080
strip_path为false时代理后url为:
http://node1:8080/node1/abc
关于更多Kong代理转发功能可以直接查阅官方文档:
https://docs.konghq.com/2.0.x/proxy/
当代理转发的后端服务是一组集群服务时,则需要配套负载均衡来实现代理转发。Kong支持外部DNS和upstream内置的环形平衡器(ring-balancer)两种方式实现负载均衡。这里我们主要介绍upstream的方式。Kong upstream负载均衡等同Nginx的upstream的概念,只是Kong将其包装为对象的方式对外提供Restful-API进行配置。每个upstream都有属于自己的环形平衡器(ring-balancer),环形平衡器实现负载均衡的原理是根据配置的负载均衡算法在初始化时一次性将1-slots(可配置,默认10000,推荐值为后端服务数量 * 100)插槽随机设置在环形上(例如环形链表),后续只需要通过简单的顺序读取环形链表(node.next)即可实现负载均衡效果,slots值和后端服务对应关系可以理解为:插槽位置 % 后端服务数量 = 本次转发的后端服务标号)。下面我们来实战一个例子。
# 列出当前配置的所有服务
curl -i -X GET \
--url http://localhost:8001/services/
# 添加node2节点的httpd服务
# 快速入门章节我们添加了node1节点的httpd服务
curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=node2-httpd-service' \
--data 'url=http://10.68.212.102:8080'
# 可以通过下面请求删除服务
# 删除前需要手动删除关联对象,否则提示类似 an existing 'routes' entity references this 'services' entity ....
curl -i -X DELETE \
--url http://localhost:8001/services/
# 查询服务列表
curl -i -X GET \
--url http://localhost:8001/services
curl -X POST http://localhost:8001/upstreams \
--data "name=httpd-upstream" \
# 指定负载均衡算法(默认)
--data "algorithm=round-robin" \
# 指定插槽数量,默认10000
--data "slots=200"
关于创建upstream对象更多说明可以查阅官方文档:
https://docs.konghq.com/2.0.x/admin-api/#add-upstream
curl -X POST http://localhost:8001/upstreams/httpd-upstream/targets \
--data "target=10.68.212.101:8080" \
--data "weight=100"
curl -X POST http://localhost:8001/upstreams/httpd-upstream/targets \
--data "target=10.68.212.102:8080" \
--data "weight=100"
# upstream也是需要配置为服务对象,才能于route进行关联
curl -X POST http://localhost:8001/services/ \
--data "name=httpd-upstream-service" \
--data "host=httpd-upstream" \
--data "path=/"
curl -i -X POST http://localhost:8001/services/httpd-upstream-service/routes/ \
-H 'Content-Type: application/json' \
-d '{"name":"httpd-upstream-service-route1","hosts":["httpd-service.com"],"paths":["/httpd-service"]}'
# 循环发起100个请求,可以看到轮询输出from node1和from node2
for((a=1;a<100;a++)); do curl -i -X GET \
--url http://localhost:8000/httpd-service \
--header 'Host: httpd-service.com' ;done
负载均衡技术解决的是代理后端服务高并发过载的问题,而健康检查技术解决的是代理后端服务可用性的问题。代理转发、负载均衡、健康检查通常是网关或负载均衡器的基本功能要求。Kong支持两种健康检查:主动健康检查、被动健康检查。
通过定期请求后端服务的某个HTTP端口,根据返回是否TCP错误,超时或HTTP状态代码来判断服务是否可用,并依据配置的信息来决定如何更新内部健康计数器。大致原理如下:
1、如果返回的状态码是配置为“健康”的状态码,它将为目标增加“成功”计数器,并清除其所有其他计数器。
2、如果连接失败,它将为目标增加“ TCP failures”计数器,并清除“ Successes”计数器。
3、如果超时,它将增加目标的“超时”计数器,并清除“成功”计数器。
4、如果返回的状态码是配置为“不健康”的状态码,它将为目标增加“ HTTP失败”计数器,并清除“成功”计数器。
5、如果“ TCP失败”,“ HTTP失败”或“超时”计数器中的任何一个达到配置的阈值,则目标将被标记为不正常。
6、Kong可以通过配置后端目标服务最少可用的“weight”比例值(threshold)来指示Kong是否返回上游服务为:503 Service Unavailable
7、运行状况检查仅对活动目标进行操作,而不会在Kong数据库中修改目标的活动状态。
8、不健康的目标不会从负载均衡器中删除,因此在使用哈希算法时(不会被忽略)不会对均衡器的布局产生任何影响。
# 创建upstream对象
curl -i -X POST http://localhost:8001/upstreams \
-H 'Content-Type: application/json' \
-d '{
# upstream名称
"name":"httpd-health-upstream",
# 轮询算法
"algorithm": "round-robin",
# 健康检查配置
"healthchecks":{
# HTTP协议
"type": "http",
# 主动健康检查
"active":{
# 每次同时检查2个后端服务
"concurrency":2,
# 健康配置
"healthy":{
# 健康http状态配置
"http_statuses":[200,302],
# 对健康的后端服务检查间隔,单位:s
"interval":10,
# 符合http_statuses次数,则认为健康
"successes":1
},
# 后端服务http端点
"http_path":"/",
# 超时时间,单位:s,超时后不健康计数器加1
"timeout":10,
# 不健康配置
"unhealthy":{
# 返回http_statuses为不健康的次数,达到则认为不健康
"http_failures":2,
# 不健康的http_status
"http_statuses":[
429,404,500,501,502,503,504,50
],
# 对不健康的后端服务检查间隔,单位:s
"interval":10,
# TCP错误的次数,达到则认为不健康
"tcp_failures":2,
# timeout超时的次数,达到则认为不健康
"timeouts":2
}
},
# 最少可用的“weight”百分比
"threshold":0
}
}'
假设配置了的上游healthchecks.threshold=55。它有5个目标,每个目标都有weight=100,因此环形平衡器的总重量为500。当故障开始发生时,第一个目标的断路器跳闸。现在认为它不健康。这意味着在环形平衡器中,现在容量的20%不健康(500个重量中的100个重量)。该阈值仍高于55的阈值,因此其余目标将为失败目标的流量提供服务。当第二次失败发生时,另一个目标失败,另外100磅不健康而丢失。现在,环形平衡器以其容量的60%运行,但仍在配置的阈值内。如果我们假设这2个故障是由于系统过载而发生的,那么现在我们可以假设剩下的60%也将无法应付满负荷,并且很快第三个节点将发生故障,从而将正常容量减少到40%。此时,上游健康状况将小于其阈值,并且自身将被标记为不健康。一旦进入不正常状态,上游只会返回错误。这可使目标/服务从遇到的级联故障中恢复。一旦目标开始恢复并且上游的可用容量再次超过阈值,则环形平衡器的运行状况将自动更新。
根据当前代理转发请求的响应状态来判断该服务是否可用,类似断路器。被动健康检查是根据(HTTP / HTTPS / TCP)代理转发请求时对响应情况的检查,当目标服务无响应时,被动健康检查器将其标记为不健康,环形平衡器将开始跳过此目标,因此将不再有流量路由到该目标。当解决目标服务问题并准备再次接收流量时,可以通过Admin API端点手动通知运行状况检查器要求其再次启用目标服务。
curl -i -X POST \
http://localhost:8001/upstreams/my_upstream/targets/10.68.212.102:8080/healthy
curl -i -X POST http://localhost:8001/upstreams \
-H 'Content-Type: application/json' \
-d '{"name":"httpd-health-upstream",
# 轮询算法
"algorithm":"round-robin",
# 健康检查配置
"healthchecks":{
# 被动检查配置
"passive":{
# 不健康检查
"unhealthy":{
# 代理转发过程TCP错误次数
"http_failures":2,
# 代理转发过程TCP错误状态次数
"http_statuses":[
429,500,503
],
"tcp_failures":2,
"timeouts":2
},
"type":"http",
"healthy":{
# http_statuses响应健康状态次数
"successes":1,
"http_statuses":[
200,201,202,203,204,205,206,207,226,300,301,302,303,304,305,306,307,308
]
}
}
}
}'
1、主动健康检查可以在目标再次恢复健康后自动重新启用环形平衡器中的目标服务,被动健康检查不能。
2、被动健康检查通常更快识别到不健康的目标服务,而主动健康检查识别时间为:间隔时间 * 失败次数。
3、被动健康检查不会对目标产生额外的流量,主动健康检查则相反。
4、主动健康检查要求目标服务创建一个HTTP端口,被动运行状况检查不需要这种配置。
最佳实践建议:
可以组合两种模式,例如,可以启用被动健康检查来识别不健康的目标服务,然后主动健康检查只监测不健康的目标服务。下面是配置示例:
{
"healthchecks":{
# ........省略其它配置
"active":{
# ........省略其它配置
"healthy":{
# 间隔设置为0表示不检查健康的目标服务
"interval":0
},
# ........省略其它配置
"unhealthy":{
# 间隔设置不为0表示检查不健康的目标服务
"interval":10
}
}
}
}
对于网关来说,除了支持基本的代理转发功能外,保护服务的安全也是一项基本需求。Kong通过插件的方式提供服务鉴权的支持,不同的服务鉴权可以通过不同的插件来实现。关于插件可以查阅官方提供的插件支持说明:https://docs.konghq.com/hub/。
如果没有找到满足业务需求的插件,也可以自行开发,这是Kong最大的特点,有需要时可以好好利用这点来扩展自己的业务需求。下面我们来看下Kong基本最简单的服务鉴权是如何实现的。
#创建服务
curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=mockbin-service' \
--data 'url=http://mockbin.org/request'
#创建路由
curl -i -X POST \
--url http://localhost:8001/services/mockbin-service/routes \
--data 'name=mockbin-service-route' \
--data 'paths[]=/auth-sample'
# 测试路由(直接可以返回html页面)
curl -X GET http://localhost:8000/auth-sample
# 启用服务key-auth身份认证插件
curl -i -X POST \
--url http://localhost:8001/services/mockbin-service/plugins/ \
# 表示启动key-auth认证插件
--data 'name=key-auth'
# 或者启用路由key-auth身份认证插件
curl -i -X POST \
--url http://localhost:8001/routes/mockbin-service-route/plugins/ \
# 表示启动key-auth认证插件
--data 'name=key-auth'
# 测试路由:
# 应该收到无权访问提示:{"message":"No API key found in request"}
curl -X GET http://localhost:8000/auth-sample
# 创建消费者对象
curl -d "username=lazy" http://localhost:8001/consumers/
# 为消费者lazy生成一个apikey
curl -X POST http://localhost:8001/consumers/lazy/key-auth -d ''
# 测试访问,现在应该有权限访问了
curl -X GET http://localhost:8000/auth-sample \
--header 'apikey: C6tYGtoekPU4flgNTg6FJPx6bC5K6HFn'
下面给大伙抛出几个思考问题,这些问题有空笔者会写一篇文章进行解答:
1、Kong在实际业务中使用场景,例如如何和普通SpringBoot项目进行集成?或者如何跟Spring Cloud进行集成,能实现自动注册的功能吗?或者说Kong能否替代类似eruka这样的注册中心组件?
2、github是否有现成的Kong 界面化开源界面?
3、Kong使用PostgreSQL作为数据信息存储时,会影响高并发性能吗?
---------- 正文结束 ----------
长按扫码关注微信公众号
Java软件编程之家