系列的第三篇文章,介绍基于openresty构建的高性能api网关Kong
说到Kong,其实是一个老牌的API网关了,想必玩微服务的圈子都会有所耳闻。Kong 早在2015年就已经发布(前身为Mashape),各类插件功能丰富,在api网关领域有着广泛的应用。因为发布的早,当时云原生的概念还没有大火,声明式配置方式没有普及,所以Kong还是基于restful接口来操纵网关的各类行为的。(最新的dbless模式已经支持了声明式配置方式)
能力
这里摘抄一段官方的对于api网关的介绍说明,其实不光是对于Kong,对于任意一个api网关来说,这些都应该是核心关注点
如果您正在为Web,移动或IoT(物联网)而构建应用,则您可能需要通用功能来运行实际的软件。 Kong可以充当微服务请求的网关(或sidecar),同时通过插件提供负载均衡、日志记录、身份验证、速率限制、报文转换等功能,从而为您提供帮助。
下图是传统模式和网关模式的对比图:
简单来说,就是通过网关模式,把认证、授权、日志、限速、监控、缓存等通用能力抽取出来,做在网关层,从而使得应用层只需要关注业务逻辑的实现而不需要关注这些非功能特性。从设计模式的角度来说,这个可以称之为关注点分离,或者说解耦。其实看过我之前两篇文章( Ambassador 和 Zuul )的同学可以看出来,网关做的事情都是差不多的。
架构
可以看到,Kong的设计一共分了5层
- 最上层是接口层,提供标准的restful的管理api,可以操纵Kong的各类对象,并且提供与CI/CD系统的集成
- 第二层是插件层,提供官方的或者第三方编写的各类lua插件,网关的各类核心能力,比如限速、认证、日志、监控等能力均是通过插件来实现。这部分Kong的能力还是比较完整的,网关所需要的核心插件都已经提供(有些插件有advanced版本,需要企业版才有)。如果有定制化的需求,也可以自己编写lua插件,可扩展性较好
- 第三层是集群与数据储存层:网关可以使用PostgreSQL或者Cassandra作为配置的持久化存储,只要有一个中心化的数据库集群,就可以保证各个Kong实例的配置同步(如果是dbless模式,则通过k8s来进行调度,使用configmap来统一配置各pod的配置文件),同时使用了缓存技术来保障高性能
- 第四层为openresty:openresty 基于 lua 语言开发。lua语言,简单来说是一种轻量小巧的脚本语言,Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数,这使得Lua在高性能编程领域中有着广泛的应用,像许多游戏(包括魔兽世界),里面都大量使用到了lua模块。openresty 就是使用nginx的 lua扩展模块 开发的高性能web平台,内部集成了大量精良的lua库,简单类比的话,如果nginx是jvm的话,openresty就是spring mvc框架,而kong就是一个使用openresty框架开发的应用
- 最底层nginx:这层相信不用太多介绍了,大名鼎鼎的反代软件nginx,异步非阻塞架构,性能的基石,kong所提供的各类功能,其实最终都是体现为对nginx行为和配置的操作
安装
Kong的安装方式比较多样,可以直接yum安装,或者docker安装,或者helm安装,都是支持的,具体可以参考官方安装文档。注意这边安装的均为单节点,非生产高可用安装,仅供实验环境验证:
docker安装
1.您需要创建一个自定义网络,以允许容器相互发现和通信。在此示例中kong-net是网络名称,您可以使用任何名称。
docker network create kong-net
2.启动数据库PostgreSQL
docker run -d --name kong-database --network=kong-net -p 5432:5432 -e "POSTGRES_USER=kong" -e "POSTGRES_DB=kong" -e "POSTGRES_PASSWORD=kong" postgres
3.准备数据库
docker run --rm --network=kong-net -e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" -e "KONG_PG_PASSWORD=kong" kong kong migrations bootstrap
4.启动kong
docker run -d --name kong --network=kong-net -e "KONG_DATABASE=postgres" -e "KONG_PG_HOST=kong-database" -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 kong
5.运行konga
注意DB_HOST为自己的ip地址
docker run -d -p 1337:1337 --network kong-net -e "TOKEN_SECRET=mark666" -e "DB_ADAPTER=postgres" -e "DB_HOST=10.234.2.204" -e "DB_PORT=5432:5432" -e "DB_USER=kong" -e "DB_PASSWORD=kong" -e "DB_DATABASE=kong_database" --name konga pantsel/konga
安装过程中使用到了三个镜像:
- postgresql:kong的配置信息数据库,restful模式下必须要安装,dbless模式下可以不装,生产环境下为保障HA需要安装集群,postgresql部署集群的方式较多,具体可自行百度,不在本文讨论范围。多个Kong实例连接到同一个数据库集群,就可以实现配置的同步。
- kong:kong网关应用
- konga:由于Kong的dashboard需要在企业版本中才会提供,所以这里安装社区的一个开源项目 konga,实现与 Kong的rest api进行交互。konga 自身也需要用到 postgresql 数据库,可以和kong使用同一个,也可以单独部署。
安装完成后启动 konga ,在 connection 中配置好kong 实例的url地址(默认的控制端口为8001),即可看到如下界面,说明安装成功:
此外,在1.1版本以后,由于支持了dbless模式,所以kong也可以直接部署为kubernetes ingress controller,方便GitOps,对云原生的支持更加友好。具体请参考官方文档
yum安装
若Kong部署在组织的边缘侧(例如DMZ区域),此时Kong一般是位于整个互联网流量的入口处,这里一般不会部署kubernetes集群,此外处于性能考虑,原生的安装方式可能是最合适的,所以这边介绍一下yum安装方式。
考虑到有些环境是无法上外网的,所以这里介绍一下离线安装方法:
1、安装postgresql(安装基线为postgresql12.2+kong2.0.2)
登陆postgresql官网下载12的离线安装包:https://yum.postgresql.org/12/redhat/rhel-7-x86_64/repoview/postgresqldbserver12.group.html
$ yum install libicu-50.2-3.el7.x86_64.rpm #10以上的postgres需要安装libicu依赖
$ yum install postgresql12-libs-12.2-1PGDG.rhel7.x86_64.rpm
$ yum install postgresql12-12.2-1PGDG.rhel7.x86_64.rpm
$ yum install postgresql12-server-12.2-1PGDG.rhel7.x86_64.rpm
$ /usr/pgsql-12/binpostgresql-12-setup initdb # 数据库初始化
$ systemctl enable postgresql-12 # 将postgresql添加至系统服务中
$ systemctl start postgresql-12 # 启动postgresql
调整/var/lib/pgsql/12/data
中的postgresql.conf
和pg_hba.conf
配置文件设置
postgresql.conf:寻找如下配置
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------
# - Connection Settings -
#listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost'; use '*' for all
# (change requires restart)
#port = 5432 # (change requires restart)
max_connections = 100 # (change requires restart)
#superuser_reserved_connections = 3 # (change requires restart)
#unix_socket_directories = '/var/run/postgresql, /tmp' # comma-separated list of directories
# (change requires restart)
#unix_socket_group = '' # (change requires restart)
#unix_socket_permissions = 0777 # begin with 0 to use octal notation
将listen_addresses
配置注释放开,并修改为'*',这样非本机的ip也可以连接数据库(生产环境建议设置固定ip)
此外注意max_connections
这个参数,若多个kong实例都连接了这个数据库,可能会有连接数不够的情况,建议参数设置为100*kong的实例数。
pg_hba.conf:寻找如下配置
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 ident
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all peer
host replication all 127.0.0.1/32 ident
host replication all ::1/128 ident
其中local是本机访问,即通过psql工具访问,认证模式peer
为对等模式,即使用当前系统用户的身份登陆postgres,例如postgres安装完之后会创建一个用户postgres,切换到这个用户,执行psql命令,会使用postgres这个用户登陆数据库,可以修改为md5模式,即使用密码登陆,ident模式暂时还没有研究。下面的两行分别为IPv4和IPv6的登陆ip控制,可根据自己的需求设置,地址符合CIDR格式即可。
执行psql登陆数据库,创建kong所需的用户及数据库并修改密码:
CREATE USER kong; CREATE DATABASE kong OWNER kong;
ALTER USER kong PASSWORD 'kong';
2、安装Kong
在Kong官网获取rpm安装包:https://docs.konghq.com/install/redhat/?_ga=2.163856379.559347509.1587004787-1507839573.1585882199
这个包比较简单,没有什么依赖,直接安装即可,安装完后执行
$ cp /etc/kong/kong.conf.default /etc/kong/kong.conf
并修改其中的数据库配置:(搜索关键字postgres)
database = postgres # Determines which of PostgreSQL or Cassandra
# this node will use as its datastore.
# Accepted values are `postgres`,
# `cassandra`, and `off`.
#pg_host = 22.196.66.214 # Host of the Postgres server.
#pg_port = 5432 # Port of the Postgres server.
#pg_timeout = 5000 # Defines the timeout (in ms), for connecting,
# reading and writing.
pg_user = kong # Postgres user.
pg_password = kong # Postgres user's password.
pg_database = kong # The database name to connect to.
修改admin_api网络访问配置:(为了让外部服务器可以访问admin api)
# for a description of the formats that Kong might accept in stream_listen.
admin_listen = 0.0.0.0:8001 reuseport backlog=16384, 127.0.0.1:8444 http2 ssl reuseport backlog=16384
# Comma-separated list of addresses and ports on
# which the Admin interface should listen.
# The Admin interface is the API allowing you to
# configure and manage Kong.
# Access to this interface should be *restricted*
# to Kong administrators *only*. This value accepts
# IPv4, IPv6, and hostnames.
#
# Some suffixes can be specified for each pair:
都搞好了之后,执行kong的数据库初始化操作并启动(注意如果是多个kong实例的话,只有第一个需要进行初始化操作,后面加入的不需要):
$ kong migrations bootstrap
$ kong start
一切正常的话,执行health命令可以查看一下当前kong实例的状态:
[root@dce304-cal-vm4 kong]# kong health
2020/04/18 02:34:13 [warn] 24276#0: *2 [lua] client.lua:576: init(): [dns-client] Invalid configuration, no valid nameservers found, context: ngx.timer
2020/04/18 02:34:13 [warn] 24276#0: *2 [lua] client.lua:576: init(): [dns-client] Invalid configuration, no valid nameservers found, context: ngx.timer
nginx.......running
Kong is healthy at /usr/local/kong
调用一下restful接口,显示输出一堆节点信息,说明kong当前状态正常
$ curl http://127.0.0.1:8001
日志:
kong的日志默认放在/usr/local/kong/logs
,其中access.log
是数据端口nginx(8000)的访问日志,admin_access.log
是管理端口nginx(8001)的访问日志,error.log
是kong应用的日志(自己写的插件,打印的kong日志都是显示在这里),日志相关的操作请访问:https://docs.konghq.com/2.0.x/logging/
插件及扩展
其实说到Kong,个人认为其最大的优势就是丰富的插件生态,大家可以看一下他们的官方pluing hub,应该说插件还是很多的,能满足绝大部分的需求(虽然说绝大部分都是Kong自己搞的...),从这点而言,Ambassador 和 Zuul 完全没有可比性(不存在生态一说),将来的 Envoy with wasm生态如果能起来,或可与之一战,可以静观后续发展。这里说说个人认为比较重要的几个插件:
- JWT:认证插件,用于验证JWT令牌身份,由于目前jwt在微服务认证体系中的广泛使用,基本是认证必备插件
- ACL:用于识别用户身份后的请求授权,没啥好说的,基本也是必备
- Rate Limiting:流量控制,仍然是没啥好说的,一个合格网关的必备
- Logging:日志,可以把请求和响应日志输出到本地或者某个http server,方便后续的分析或者告警
扩展开发
扩展能力也是一个优秀网关的关键考察点,这点上,zuul因为基于java(Spring Boot)开发,占了不少的优势,上手非常快。而Kong要开发扩展插件,就需要用到lua语言了。好在lua语言本身并不是太复杂,在有其他语言开发基础的情况下,基本花个半天时间就能掌握。主要的问题还是在于调试环境,Kong在windows或mac下的安装并不友好,windows是根本没有安装包,mac有安装包,但是本人在反复尝试下提示本人的osx版本过新,无法安装(-_-),网上也能看到安装过程中的各种版本依赖问题。
所以这里建议,为简化开发过程,最方便的还是使用docker方式进行安装,好处是不用关心各种头疼的版本依赖问题,且win和mac通吃。kong的源码工程安装于镜像的/usr/local/share/lua/5.1/kong
目录中,在plugins
目录下新建文件夹,就可以开发自己的插件了,具体的插件开发方法,请直接参考官方文档:https://docs.konghq.com/2.0.x/plugin-development/
把上面的目录mount到自己本地,就可以直接在图形界面中进行开发了,我这里使用的ide是vscode,安装了lua插件emmylua,显示效果如下,基本还是可以用的:
开发完之后,在容器中执行一下 kong reload命令,kong完成热重载,就可以直接看效果了。
修改 constants.lua
配置,加载自己开发的插件
local plugins = {
"jwt",
"acl",
"correlation-id",
"cors",
"oauth2",
"tcp-log",
"udp-log",
"file-log",
"http-log",
"key-auth",
"hmac-auth",
"basic-auth",
"ip-restriction",
"request-transformer",
"response-transformer",
"request-size-limiting",
"rate-limiting",
"response-ratelimiting",
"syslog",
"loggly",
"datadog",
"ldap-auth",
"statsd",
"bot-detection",
"aws-lambda",
"request-termination",
-- external plugins
"azure-functions",
"zipkin",
"pre-function",
"post-function",
"prometheus",
"proxy-cache",
"session",
"acme",
}
插件本身还可以自定义schema,可以方便的定义各种数据结构及校验规则,方便在插件中使用这些数据,具体的配置文档请参考下述链接:
https://docs.konghq.com/2.0.x/plugin-development/plugin-configuration/
参考资料
1、微服务API网关-Kong初探
2、Kong官方文档