Kong动态负载均衡
- 一、背景
- 二、通过docker 安装 Kong
- 三、分布式API网关存在的意义
- 四、Kong 的相关特性
- 五、Kong 体系结构
- 六、Kong 工作流程
- 七、从 nginx 配置到 Kong 配置
- 7.1、Kong 核心四对象
- 7.2、四对象关系
- 八、插件机制
- 九、Kong 网关插件
- 十、使用konga
- 10.1、实现一个负载均衡器
- 10.2、实现黑白名单
- 10.3、实现限流
- 10.4、实现鉴权验证
- 总结
- 后言
一、背景
Kong 是一款基于 openresty 编写的高可用、易扩展的开源 API Gateway 项目。
Kong 支持两种工作模式:一种是不使用数据库;另一种是使用数据库;可用的数据库为 PostgreSQL、Cassandra(分布式NoSQL 数据库)。
官方网站
官方文档
项目地址
中文文档:基于1.1.x版本
二、通过docker 安装 Kong
(1)创建 Kong 网络。创建自定义 Docker 网络以允许容器相互发现和通信:
sudo docker network create kong-net
(2)安装 PostgreSQL。
# 创建 PostgreSQL 容器
sudo docker run -d --name kong-database --network=kong-net -p 5432:5432 -e "POSTGRES_USER=kong" -e "POSTGRES_DB=kong" -e "POSTGRES_PASSWORD=kong" --restart always postgres:9.6
# 使用 Kong 容器运行进行数据库初始化
sudo docker run --rm --network=kong-net -e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" -e "KONG_PG_USER=kong" -e "KONG_PG_PASSWORD=kong" -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" kong:2.5.0 kong migrations bootstrap
(3)创建 Kong 容器。
# -u -root: Kong 启动过程中,需要权限创建一些文件和文件夹
sudo docker run -d --name kong --network=kong-net -u root -e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" -e "KONG_PG_USER=kong" -e "KONG_PG_PASSWORD=kong" -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 --restart always kong:2.5.0
(4)搭建和运行Konga。Konga 是 Kong 的可视化 API 操作工具。
# 复用上面的数据库容器
sudo docker run --rm --network=kong-net pantsel/konga:0.14.9 -c prepare -a postgres -u postgresql://kong:kong@kong-database/konga
# 运行 Konga 容器
sudo docker run -d -p 1337:1337 --network=kong-net -e "DB_ADAPTER=postgres" -e "DB_URI=postgresql://kong:kong@kong-database/konga" -e "NODE_ENV=production" --name konga pantsel/konga:0.14.9
(5)测试 Kong 和 Konga。
# 输入下列命令,若返回一个大的 json 串,说明安装成功
curl http://localhost:8001
# 浏览器运行
http://192.168.0.102:1337
三、分布式API网关存在的意义
在微服务架构之下,服务被拆的非常零散,降低了耦合度的同时也给服务的统一管理增加了难度。在旧的服务治理体系之下,鉴权,限流,日志,监控等通用功能需要在每个服务中单独实现,这使得系统维护者没有一个全局的视图来统一管理这些功能。API 网关致力于解决的问题便是为微服务纳管这些通用的功能,在此基础上提高系统的可扩展性。微服务搭配上 API网关,可以使得服务本身更专注于自己的领域,很好地对服务调用者和服务提供者做了隔离。
四、Kong 的相关特性
- 云原生:与平台无关,Kong 可以从裸机运行到 Kubernetes。
- 动态路由:Kong 的背后是 OpenResty,所以从 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 的非阻塞 io 模型。
- 插件机制:提供众多开箱即用的插件,且有易于扩展的自定义插件接口,用户可以使用 Lua 自行开发插件。
五、Kong 体系结构
- Kong 核心基于 Openresty 构建,实现了请求/响应的 lua 处理化。
- Kong 插件拦截请求/响应,等价于拦截器,实现请求/响应的AOP 处理。
- Kong Restful 管理 API 提供了 API / API 消费者 / 插件的管理。
- 数据中心用于存储 Kong 集群节点信息、API、消费者、插件等信息,目前提供了 PostgreSQL 和 Cassandra支持,如果需要高可用建议使用 Cassandra。
- Kong 集群中的节点通过 gossip 协议(redis cluster)自动发现其他节点,当通过一个 Kong 节点的管理 API 进行一些变更时也会通知其他节点。每个 Kong 节点的配置信息是会缓存的,如插件,那么当在某一个 Kong 节点修改了插件配置时,需要通知其他节点配置的变更。
六、Kong 工作流程
API 网关可以通过实现一些中间组件来解决一些问题,这些中间组件的功能就不需要到每个微服务中实现了,这样不同的团队可使用不同的方式来实现了不同的微服务。
API 网关不仅可以帮你解决 API 的管理部分,而且还可以解决下面两件事情:
- 分析(Analytics)。API 网关可以和你的分析基础设施保持透明的交互和通信,因为 API 网关是每个请求(request)的入口,必经地。API 网关可以看到所有的数据,可以知道经过你的 service 的流量。这就相当于你有一个集中的地方,你可以把所有的这些信息 push 到你的监控或分析工具,比如 Kibana 或者 Splunk。
- 自动化(Automation)。网关有助于自动化部署,还可以实现自动化登入验证。 什么是登入呢? 如果你有 API,并且你希望有身份验证,你可能需要一些功能可以允许用户为该API 创建登入凭据(credentials)然后开始使用(消费)API。 开发人员门户网站或你的文档中心等都可以与网关集
成来配置这些凭据(credentials),这样就不用从头开始构建一些功能了。
Kong 默认是缺失如 API 级别的超时、重试、fallback 策略、缓存、API 聚合、AB 测试等功能,这些功能插件需要企业开发人员通过 Lua 语言进行定制和扩展。
init_by_lua_block {
kong = require 'kong'
kong.init() -- 完成 Kong 的初始化,路由创建,插件预加载等
}
init_worker_by_lua_block {
kong.init_worker() -- 初始化 Kong 事件,worker之间的事件,由 worker_events 来处理, cluster 节点之间的事件,由 cluster_events 来处理,缓存机制
}
upstream kong_upstream {
server 0.0.0.1;
balancer_by_lua_block {
kong.balancer() --负载均衡
}
keepalive 60;
}
server {
server_name kong;
listen 0.0.0.0:8000 reuseport backlog=16384;
listen 0.0.0.0:8443 ssl http2 reuseport backlog=16384;
rewrite_by_lua_block {
kong.rewrite() --插件生效策略的筛选,并执行对应的操作,只能处理全局插件(kong插件级别,全局(作用于所有请求),route(作用于当前路由),service(作用于匹配到当前service的所有请求)),路由匹配未开始。
}
access_by_lua_block {
kong.access() --1.完成路由匹配,2.确认加载的插件(并加入缓存) 3.进入balancer阶段
}
header_filter_by_lua_block {
kong.header_filter() --遍历在缓存中的插件列表,并执行
}
body_filter_by_lua_block {
kong.body_filter() --遍历在缓存中的插件列表,并执行
}
log_by_lua_block {
kong.log() --遍历在缓存中的插件列表,并执行
}
location / {
proxy_pass $upstream_scheme://kong_upstream$upstream_uri;
}
}
七、从 nginx 配置到 Kong 配置
nginx:
upstream my_upstream {
server 192.168.0.106:3000 weight=100;
}
server {
listen 80;
location /hello {
proxy_pass my_upstream;
}
}
Kong:
# 配置 upstream
curl -X POST http://localhost:8001/upstreams --data "name=mark_upstream"
# 配置 target
curl -X POST http://localhost:8001/upstreams/mark_upstream/tar gets --data "target=192.168.31.91:8888" --data "weight=100"
curl -X POST http://localhost:8001/upstreams/mark_upstream/tar gets --data "target=192.168.31.91:9999" --data "weight=100"
# 配置 service
curl -X POST http://localhost:8001/services --data "name=hello" --data "host=mark_upstream"
# 配置 route
curl -X POST http://localhost:8001/routes --data "paths[]=/hello" --data "service.id=15722364-296f-4624-9026-ceff2d2166f2"
7.1、Kong 核心四对象
Kong 涉及到 upstream,target,service,route 概念。
- upstream 是对上游服务器的抽象。
- target 代表了一个物理服务,是 ip + port 的抽象。
- service 是抽象层面的服务,他可以直接映射到一个物理服务(host 指向 ip + port),也可以指向一个 upstream 来做到负载均衡。
- route 是路由的抽象,他负责将实际的 request 映射到service。
consumer 是使用 service 的用户,可以为 consumer 添加plugin 插件,从而定义 consumer 的请求行为。
7.2、四对象关系
- upstream 和 target :1 : n。
- service 和 upstream :1 : 1 或 1 : 0;(service 也可以直接指向具体的 target,相当于不做负载均衡)。
- service 和 route:1 : n。
八、插件机制
Kong 的插件机制是 Kong 最核心的功能;Kong 要解决的问题是承担所有服务共同需要的那部分功能;插件可能是作用全局的,也可能是作用局部的,比如大部分插件是作用在 service 或route 上。
Kong 的插件列表
# 为 hello 服务添加 50/s 的限流,作用在 service 上
curl -X POST http://localhost:8001/services/hello/plugins \
--data "name=rate-limiting" \
--data "config.second=50"
# 为某个 routeId 添加 50/s 的限流,作用在 routes 上
curl -X POST http://localhost:8001/routes/{routeId}/plugins \
--data "name=rate-limiting" \
--data "config.second=50"
九、Kong 网关插件
- 身份认证插件:Kong提供了 Basic Authentication、Key authentication、OAuth2.0 Authentication、HMAC authentication、JWT、LDAP authentication 认证实现。
- 安全控制插件:ACL(访问控制)、CORS(跨域资源共享)、动态 SSL、IP限制、爬虫检测实现。
- 流量控制插件:请求限流(基于请求计数限流)、上游响应限流(根据 upstream 响应计数限流)、请求大小限制。限流支持本地、Redis 和集群限流模式。
- 分析监控插件:Galileo(记录请求和响应数据,实现 API 分析)、Datadog(记录 API Metric 如请求次数、请求大小、响应状态和延迟,可视化 API Metric)、Runscope(记录请求和响应数据,实现 API 性能测试和监控)。
- 协议转换插件:请求转换(在转发到 upstream 之前修改请求)、响应转换(在 upstream 响应返回给客户端之前修改响应)。
- 日志应用插件:TCP、UDP、HTTP、File、Syslog、StatsD、Loggly 等。
十、使用konga
10.1、实现一个负载均衡器
输入IP和端口号即可进入konga。比如:192.168.0.106:1337
(1)第一次进入需要注册一个用户。
(2)创建完用户后需要登录。
(2)创建connections。
(3)创建完成后激活。
(4)先使用openresty创建三个后台服务器:创建两个文件夹conf和logs,在conf文件夹下创建nginx.conf文件并输入以下内容:
worker_processes 2;
events {
worker_connections 10240;
}
http {
server {
listen 7001;
location / {
content_by_lua_block {
ngx.say(7001,"\t",ngx.var.remote_addr)
}
}
}
server {
listen 7002;
location / {
content_by_lua_block {
ngx.say(7002,"\t",ngx.var.remote_addr)
}
}
}
server {
listen 7003;
location / {
content_by_lua_block {
ngx.say(7003,"\t",ngx.var.remote_addr)
}
}
}
}
(5)启动后台服务器。
openresty -p . -c conf/nginx.conf
查看:
ps aux | grep nginx
(6)konga中创建upstream。
然后直接点击提交。
(7)创建tagets。
(8)创建service。
然后点击提交。
(9)创建route。
然后点击提交。
(10)现在配置完成了7001,可以测试一下访问了。网页输入IP和端口。
此时会发现,网页返回的IP地址是docker容器的IP地址,客户端的IP地址丢失了。其实kong已经把IP地址传过去了,只是我们还没有使用而已。它在头信息里面,需要自己提取出来。
可以把nginx.conf修改一下:
worker_processes 2;
events {
worker_connections 10240;
}
http {
server {
listen 7001;
location / {
content_by_lua_block {
local header = ngx.req.get_headers()
local cjson = require "cjson"
ngx.say(7001,"\t",ngx.var.remote_addr)
ngx.say(cjson.encode(header))
}
}
}
server {
listen 7002;
location / {
content_by_lua_block {
ngx.say(7002,"\t",ngx.var.remote_addr)
}
}
}
server {
listen 7003;
location / {
content_by_lua_block {
ngx.say(7003,"\t",ngx.var.remote_addr)
}
}
}
}
然后重写加载一下:
openresty -p . -s reload
重新访问一下就可以看到真正的客户端IP:
(11)上面已经添加了一个taget,为了演示负载均衡,继续添加创建两个tagets:7002和7003。
(12)然后在网址输入IP和端口,每次访问的返回信息就有改变了。至此实现一个负载均衡器。
10.2、实现黑白名单
有了前面的基础,我们可以在service上加上黑白名单的插件。插件是 ip restriction。
加完之后,再次访问就会出现如下信息:
要移出黑名单直接叉掉就可以。
10.3、实现限流
限流可以是限制次数和限制大小。限流使用的插件是rate limiting。
限流效果:
10.4、实现鉴权验证
(1)先创建consumer。
(2)然后创建用户和密码:
(3)添加验证插件,basic auth。
(4)浏览器访问时就会弹出登录验证。
总结
-
kong是openresty的一个应用,开箱即用,可以不开发只使用。kong的功能基本涵盖不部分问题。
-
kong重要的特性:抽象对象,不需要写nginx配置;插件机制,使用一个功能,直接用插件应用在对象上(service、route、consume)就行。
-
openresty如何解决问题:在nginx的关键阶段嵌入lua;openresty实现了cosocket,通过协程黏合异步事件回调,可以写同步的代码。
-
kong可以是我们不需要写复杂的nginx配置文件,它抽象了四个对象,这几个对象的关系需要清楚;而且kong提供了丰富的插件。
后言
本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接查看详细的服务:C/C++服务器开发 。