Linux Note / Middleware

Kong API Gateway

Einic Yeo · 10月16日 · 2019年 · · · ·

一、什么是API网关

在微服务的架构中,一个应用可能背拆分许多个小的服务系统,这些小系统可能自成一体,有自己的硬件资源、数据库、框架,甚至连语言都各不相同,这些小系统通常以Restfull API风格的借口提供给前端或者其他系统调用,一个应用可能会被拆分上百上千个API,管理起来极其不变,这个时候出现了API网关,API网关提供了一个统一的入口,将流量转发给对应的后端取得数据后再一次返回,并且在次基础上提供各种认证鉴权流控等等功能让后端专心于自己的业务。

使用API网关带来一下几个好处:

  • 统一API入口
  • 隔离后端
  • 认证鉴权流控
  • 负载均衡
  • 降低后端开发对API安全性的考虑

其实无论是否是微服务的架构,只要用到了认证鉴权流控功能,我觉得都应该使用API网关,这将大大减低后端开发的成本和速度,未来扩张也方便。

目前各大云厂商都可用了商用的API网关,例如阿里云,功能强大,性能稳定,如果应用于云上的生产环境,也可以考虑。

当然开源社区中也提供几种开源的方案

名称说明
TykTyk是一个开放源码的API网关,它是快速、可扩展和现代的。Tyk提供了一个API管理平台,其中包括API网关、API分析、开发人员门户和API管理面板。Try 是一个基于Go实现的网关服务。
KongKong是一个可扩展的开放源码API Layer(也称为API网关或API中间件)。Kong 在任何RESTful API的前面运行,通过插件扩展,它提供了超越核心平台版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!的额外功能和服务。
OrangeOrange和Kong类似也是基于OpenResty的一个API网关程序,是由国人开发的。
ZuulZuul是一种提供动态路由、监视、弹性、安全性等功能的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。
apiaxleNodejs 实现的一个 API 网关。
api-umbrellaRuby 实现的一个 API 网关。

在以上的产品中,个人还是比较推荐是用kong的,kong基于nginx,使用了lua语言,这将大大提供了性能和稳定性。

二、为什么是 Kong

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

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

上面这些特性中,反复提及了 Kong 背后的 OpenResty,实际上,使用 Kong 之后,Nginx 可以完全摒弃,Kong 的功能是 Nginx 的父集。

三、安装Kong

Kong支持PostgreSQL 9.5+ 和Cassandra 3.xx 作为其数据存储。所以在暗转Kong之前我们需要先安装配置好数据库,并且给Kong创建好数据库和用户名等等

数据库的安装不在此文中体现了。

建议使用PostgreSQL,这个是一个性能非常好非常优秀的开源数据库,我们新的项目也开始逐步使用这个数据库

Kong提供了丰富的安装方式,你可以基于docker来安装kong,也可以使用包管理器来安装它,当然也可以通过源码编译安装。

本文使用的资源如下:

  • 云平台:阿里云
  • 系统:centos 7.4
  • Kong版本:1.4.0
  • OpenResty 版本: 1.15.8.2
  • Luarocks 版本:3.2.1
  • postgresql 版本:11.5

1、通过包管理器来安装

通过包管理器来安装极其简单,执行一下命令即可。

[[email protected] ~]# yum install https://bintray.com/kong/kong-community-edition-rpm/download_file?file_path=centos/7/kong-community-edition-1.4.0.el7.noarch.rpm

检查一下都安装到哪个目录了。

[[email protected] ~]# whereis kong
kong: /etc/kong /usr/local/bin/kong /usr/local/kong

如上,/ect/kong目录为配置文件目录,安装后会有一个官方的默认配置文件kong.conf.default 复制这个文件为kong.conf即可启动Kong.

/usr/local/kong为Kong的运行目录,Kong启动后会生成nginx的配置文件放在此目录,当然缓存文件也会存放在这个目录中。

Kong的配置文件中,如果没有启用某一项目配置,那么Kong会使用其默认的配置文件.

Kong基于openresty,所以通过包管理器来安装也会安装openresty,安装目录为/usr/local/openresty/

基本上我们不需要去修改这个目录,如果需要修改nginx的配置,我们可以通过Kong的配置文件注入nginx配置,Kong在启动的时候会生成nginx的配置文件。

2、通过编译来安装

pass

四、启动Kong

Kong提供了CLI以此来控制Kong的启动停止等等的操作,我们可以通过kong --help命令来查看CLI提供了哪些功能,如下:

[[email protected] ~]# kong --help
No such command: --help

Usage: kong COMMAND [OPTIONS]

The available commands are:
 check
 health
 migrations
 prepare
 quit
 reload
 restart
 roar
 start
 stop
 version

Options:
 --v              verbose
 --vv             debug

对应的参数功能如下:

参数功能
check检查配置文件
health检查节点的健康状态
migrations迁移数据库,再第一次配置Kong时候必须要运行此命令,用来初始化数据库..
数据库的信息保存再配置文件中,所以使用此命令需要通过-c指定配置文件
prepare在配置的前缀目录中准备Kong前缀。这个命令可以用于从nginx二进制文件启动Kong而不使用’kong start’
quit优雅的停止Kong,在退出之前会先处理完已经接受到的请求
reload重载配置文件
restart重启Kong
roar这个参数我也不知道干什么的,可能只是打印出吉祥物?
start启动Kong,通过-c来指定配置文件
stop停止运行Kong
version查看版本
–v以上任意参数都可以加张-v选项,此选修会打印出信息。
–vv以上任意参数都可以加张-v选项,此选修会打印出更为丰富的debug信息。

现在,我们来启动Kong,因为是第一次启动,所以需要先运行迁移命令,以初始化数据库。

[[email protected] ~]$ kong migrations list -c /etc/kong/kong.conf -v
2019/10/16 14:19:44 [verbose] Kong: 1.4.0
2019/10/16 14:19:44 [verbose] reading config file at /etc/kong/kong.conf
2019/10/16 14:19:44 [verbose] prefix in use: /usr/local/kong
2019/10/16 14:19:44 [info] No migrations have been run yet for database 'kong'
....

如上,在migrations参数中,还可以跟上listupreset的参数,其中,list是显示当前迁移的信息,up参数查询缺少的部分,并且修复、r版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!eset的重置所有的迁移

迁移成功之后可以使用kong start来启动Kong

[[email protected] ~]# kong start -c /etc/kong/kong.conf --vv
2019/10/16 14:25:49 [verbose] Kong: 1.4.0
2019/10/16 14:25:49 [debug] ngx_lua: 10013
2019/10/16 14:25:49 [debug] nginx: 1013006
2019/10/16 14:25:49 [debug] Lua: LuaJIT 2.1.0-beta3
2019/10/16 14:25:49 [verbose] reading config file at /etc/kong/kong.conf
2019/10/16 14:25:49 [debug] reading environment variables
2019/10/16 14:25:49 [debug] admin_access_log = "logs/admin_access.log"
2019/10/16 14:25:49 [debug] admin_error_log = "logs/error.log"
2019/10/16 14:25:49 [debug] admin_listen = {"127.0.0.1:8001","127.0.0.1:8444 ssl"}
2019/10/16 14:25:49 [debug] anonymous_reports = true
2019/10/16 14:25:49 [debug] cassandra_consistency = "ONE"
2019/10/16 14:25:49 [debug] cassandra_contact_points = {"127.0.0.1"}
2019/10/16 14:25:49 [debug] cassandra_data_centers = {"dc1:2","dc2:3"}
2019/10/16 14:25:49 [debug] cassandra_keyspace = "kong"
2019/10/16 14:25:49 [debug] cassandra_lb_policy = "RoundRobin"
2019/10/16 14:25:49 [debug] cassandra_port = 9042
2019/10/16 14:25:49 [debug] cassandra_repl_factor = 1
2019/10/16 14:25:49 [debug] cassandra_repl_strategy = "SimpleStrategy"
2019/10/16 14:25:49 [debug] cassandra_schema_consensus_timeout = 10000
2019/10/16 14:25:49 [debug] cassandra_ssl = false
2019/10/16 14:25:49 [debug] cassandra_ssl_verify = false
2019/10/16 14:25:49 [debug] cassandra_timeout = 5000
2019/10/16 14:25:49 [debug] cassandra_username = "kong"
2019/10/16 14:25:49 [debug] client_body_buffer_size = "8k"
2019/10/16 14:25:49 [debug] client_max_body_size = "0"
2019/10/16 14:25:49 [debug] client_ssl = false
2019/10/16 14:25:49 [debug] custom_plugins = {}
2019/10/16 14:25:49 [debug] database = "postgres"
2019/10/16 14:25:49 [debug] db_cache_ttl = 0
2019/10/16 14:25:49 [debug] db_resurrect_ttl = 30
2019/10/16 14:25:49 [debug] db_update_frequency = 5
2019/10/16 14:25:49 [debug] db_update_propagation = 0
2019/10/16 14:25:49 [debug] dns_error_ttl = 1
2019/10/16 14:25:49 [debug] dns_hostsfile = "/etc/hosts"
2019/10/16 14:25:49 [debug] dns_no_sync = false
2019/10/16 14:25:49 [debug] dns_not_found_ttl = 30
2019/10/16 14:25:49 [debug] dns_order = {"LAST","SRV","A","CNAME"}
2019/10/16 14:25:49 [debug] dns_resolver = {}
2019/10/16 14:25:49 [debug] dns_stale_ttl = 4
2019/10/16 14:25:49 [debug] error_default_type = "text/plain"
2019/10/16 14:25:49 [debug] headers = {"server_tokens","latency_tokens"}
2019/10/16 14:25:49 [debug] log_level = "notice"
2019/10/16 14:25:49 [debug] lua_package_cpath = ""
2019/10/16 14:25:49 [debug] lua_package_path = "./?.lua;./?/init.lua;"
2019/10/16 14:25:49 [debug] lua_socket_pool_size = 30
2019/10/16 14:25:49 [debug] lua_ssl_verify_depth = 1
2019/10/16 14:25:49 [debug] mem_cache_size = "128m"
2019/10/16 14:25:49 [debug] nginx_admin_directives = {}
2019/10/16 14:25:49 [debug] nginx_daemon = "on"
2019/10/16 14:25:49 [debug] nginx_http_directives = {}
2019/10/16 14:25:49 [debug] nginx_optimizations = true
2019/10/16 14:25:49 [debug] nginx_proxy_directives = {}
2019/10/16 14:25:49 [debug] nginx_user = "nobody nobody"
2019/10/16 14:25:49 [debug] nginx_worker_processes = "auto"
2019/10/16 14:25:49 [debug] pg_database = "kong"
2019/10/16 14:25:49 [debug] pg_host = "192.168.0.219"
2019/10/16 14:25:49 [debug] pg_password = "******"
2019/10/16 14:25:49 [debug] pg_port = 5432
2019/10/16 14:25:49 [debug] pg_ssl = false
2019/10/16 14:25:49 [debug] pg_ssl_verify = false
2019/10/16 14:25:49 [debug] pg_user = "kong"
2019/10/16 14:25:49 [debug] plugins = {"bundled"}
2019/10/16 14:25:49 [debug] prefix = "/usr/local/kong/"
2019/10/16 14:25:49 [debug] proxy_access_log = "logs/access.log"
2019/10/16 14:25:49 [debug] proxy_error_log = "logs/error.log"
2019/10/16 14:25:49 [debug] proxy_listen = {"0.0.0.0:8000","0.0.0.0:8443 ssl"}
2019/10/16 14:25:49 [debug] real_ip_header = "X-Real-IP"
2019/10/16 14:25:49 [debug] real_ip_recursive = "off"
2019/10/16 14:25:49 [debug] ssl_cipher_suite = "modern"
2019/10/16 14:25:49 [debug] ssl_ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
2019/10/16 14:25:49 [debug] trusted_ips = {}
2019/10/16 14:25:49 [debug] upstream_keepalive = 60
2019/10/16 14:25:49 [verbose] prefix in use: /usr/local/kong
2019/10/16 14:25:49 [verbose] preparing nginx prefix directory at /usr/local/kong
2019/10/16 14:25:49 [verbose] SSL enabled, no custom certificate set: using default certificate
2019/10/16 14:25:49 [verbose] generating default SSL certificate and key
2019/10/16 14:25:49 [verbose] Admin SSL enabled, no custom certificate set: using default certificate
2019/10/16 14:25:49 [verbose] generating admin SSL certificate and key
2019/10/16 14:25:49 [warn] ulimit is currently set to "1024". For better performance set it to at least "4096" using "ulimit -n"
2019/10/16 14:25:50 [debug] searching for OpenResty 'nginx' executable
2019/10/16 14:25:50 [debug] /usr/local/openresty/nginx/sbin/nginx -v: 'nginx version: openresty/1.15.8.2'
2019/10/16 14:25:50 [debug] found OpenResty 'nginx' executable at /usr/local/openresty/nginx/sbin/nginx
2019/10/16 14:25:50 [debug] testing nginx configuration: KONG_NGINX_CONF_CHECK=true /usr/local/openresty/nginx/sbin/nginx -t -p /usr/local/kong -c nginx.conf
2019/10/16 14:25:50 [debug] searching for OpenResty 'nginx' executable
2019/10/16 14:25:50 [debug] /usr/local/openresty/nginx/sbin/nginx -v: 'nginx version: openresty/1.15.8.2'
2019/10/16 14:25:50 [debug] found OpenResty 'nginx' executable at /usr/local/openresty/nginx/sbin/nginx
2019/10/16 14:25:50 [debug] sending signal to pid at: /usr/local/kong/pids/nginx.pid
2019/10/16 14:25:50 [debug] kill -0 `cat /usr/local/kong/pids/nginx.pid` >/dev/null 2>&1
2019/10/16 14:25:50 [debug] starting nginx: /usr/local/openresty/nginx/sbin/nginx -p /usr/local/kong -c nginx.conf
2019/10/16 14:25:50 [debug] nginx started
2019/10/16 14:25:50 [info] Kong started

指定了--vv之后会打印出启动的debug信息,启动成功之后会自动再后台以daemon的方式运行。

启动之后我们看一下Kong默认监听了那些端口

[[email protected] ~]# netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      775/sshd            
tcp        0      0 0.0.0.0:5432            0.0.0.0:*               LISTEN      2084/postmaster     
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      730/master          
tcp        0      0 0.0.0.0:8443            0.0.0.0:*               LISTEN      2369/nginx: master  
tcp        0      0 127.0.0.1:8444          0.0.0.0:*               LISTEN      2369/nginx: master  
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      2369/nginx: master  
tcp        0      0 127.0.0.1:8001          0.0.0.0:*               LISTEN      2369/nginx: master  
tcp        0      0 127.0.0.1:32001         0.0.0.0:*               LISTEN      1067/java           
tcp6       0      0 :::22                   :::*                    LISTEN      775/sshd            
tcp6       0      0 ::1:25                  :::*                    LISTEN      730/master          

如上,监听了4个端口,分别是:

  • 8000:API网关http代理端口
  • 8443:API网关https代理端口
  • 8001:Kong管理API的http端口
  • 8444:Kong管理API的https端口

再观察一下,启动的进程名称均为nginx

Kong的默认运行目录在/usr/local/kong,我们看一下这个目录有什么东西:

[[email protected] kong]# ls -l
总用量 100
drwx------ 2 nobody root  4096 10月  23 14:25 client_body_temp
-rw-r--r-- 1 root   root 55501 10月  22 06:52 COPYRIGHT
drwx------ 2 nobody root  4096 10月  23 14:25 fastcgi_temp
drwxr-xr-x 2 root   root  4096 10月  23 14:25 logs
-rw-r--r-- 1 root   root   219 10月  23 14:25 nginx.conf
-rw-r--r-- 1 root   root  5249 10月  23 14:25 nginx-kong.conf
drwxr-xr-x 2 root   root  4096 10月  23 14:25 pids
drwx------ 2 nobody root  4096 10月  23 14:25 proxy_temp
drwx------ 2 nobody root  4096 10月  23 14:25 scgi_temp
drwxr-xr-x 2 root   root  4096 10月  23 14:25 ssl
drwx------ 2 nobody root  4096 10月  23 14:25 uwsgi_temp

是不是很熟悉?这个目录和nginx的工作目录非常像。

其中,里面的nginx.conf 和 nginx-kong.conf 就是Kong在启动的时候自动生成的nginx配置文件啦。

自此,Kong API网关安装启动成功,当然你可以通过CLI来停止Kong等等的操作。

五、配置文件详解

Kong配置文件是Kong服务的核心文件,它配置了Kong以怎么的方式运行,并且依赖于这个配置生成Nginx的配置文件,本文通过解读Kong配置文件,以了解Kong的运行和配置。

在成功安装Kong以后,会有一个名为kong.conf.default默认的配置文件示例,如果是通过包管理器安装的,通常位于/etc/kong/kong.conf.default,我们要将其复制为kong.conf以便于我们修改使用他。

在Kong的配置文件中,约定了以下的几条规则:

  • 配置文件中以#开头的行均为注释行,程序不会读取这些内容。
  • 在官方提供的默认配置文件中,以#开头的有值的配置项目均为默认配置。
  • 所有的配置项,均可以在系统环境变量中配置,但是必须要加上KONG_为前缀。
  • 值为布尔型的配置,可以使用on/off或者true/false
  • 值为列表的,必须使用半角逗号分割。

Kong的配置,大概分为几种,分别是:

  • 常规配置:配置服务运行目录,插件加载,日志等等
  • NGINX配置:配置Nginx注入,例如监听IP和端口配置等等,用于Kong在启动的时候生成Nginx配置文件
  • 数据库存储配置:配数据库类型,地址、用户名密码等等信息
  • 数据库缓存配置:配置数据的缓存规则,Kong会缓存诸如API信息、用户、凭证等信息,以减少访问数据库次数提高性能
  • DNS解析器配置:默认情况会使用系统设置,如hostsresolv.conf的配置,你也可以通过DNS的解析器配置来修改
  • 其他杂项配置:继承自lua-nginx模块的其他设置允许更多的灵活性和高级用法。

下面我们一个模块一个模块解释一下各项的配置。

1、常规配置

在常规配置中,主要是控制Kong一些运行时的一些配置,主要有如下配置:

配置项版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!默认值说明
prefix/usr/local/kong/配置Kong的工作目录,相当于Nginx的工作目录,这个目录存放运行时的临时文件和日志,包括Kong启动的时候自动生成的Nginx的配置文件。
每一个Kong经常必须有一个单独的工作目录
log_levelnoticeNginx的日志级别。日志存放/logs/error.log
pro版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!xy_access_loglogs/access.log代理端口请求的日志文件,可以设置为off来关闭日志的记录,也可以通过设置绝对路径也可以设置相对路径。
如果设置了相对路径,则日志文件会保存在的目录下
proxy_error_loglogs/error.log代理端口请求的错误日志文件,可以设置为off来关闭日志的记录,也可以通过设置绝对路径也可以设置相对路径。
如果设置了相对路径,则日志文件会保存在的目录下
admin_access_loglogs/admin_access.logKong管理的API端口请求的日志文件,可以设置为off来关闭日志的记录,也可以通过设置绝对路径也可以设置相对路径。
如果设置了相对路径,则日志文件会保存在的目录下
admin_error_loglogs/error.logKong管理的API端口请求的错误日志文件,可以设置为off来关闭日志的记录,也可以通过设置绝对路径也可以设置相对路径。
如果设置了相对路径,则日志文件会保存在的目录下
pluginsbundledKong启动的时候加载的插件,如果多个必须要使用半角逗号分割。默认情况下,只有捆绑官方发行版本的插件通过 bundled 这个值来加载。
加载插件只是Kong在启动的时候载入插件的代码,但是并不会使用它,如果要使用他,还必须要通过管理API来配置
当然,如果你不想加载任何插件,可以使用off来关闭它,值得强调的一点bundled值可以和其他插件名称一起使用,bundled并不是一个插件名称,它代表了随官方发行的所有插件。
anonymous_reportson如果Kong进程发生了错误,会以匿名的方式将错误提交给Kong官方, 以帮助改善Kong。

在常规的配置中,主要配置了Kong运行的目录日志等信息。

无论如何,配置的文件或者目录Kong必须要用权限访问,否则会报错。

2、Nginx注入配置

Kong基于Nginx,当然需要配置Nginx来满足Kong的运行要求,Kong提供了Nginx的注入配置,使得我们更轻松控制。

在Nginx注入配置中,有如下配置:

配置项默认值说明
proxy_listen0.0.0.0:8000, 0.0.0.0:8443 ssl配置Kong代理监听的地址和端口,这个是Kong的入口,API调用都将通过这个端口请求。这个配置和Ngxin中的配置一致,通过SSL可以指定接受https的请求
支持IPv4和IPv6
admin_listen127.0.0.1:8001, 127.0.0.1:8444 ssl配置Kong的管理API监听的端口,和proxy_listen配置一样,但是这个配置不建议监听在公网IP上。
nginx_usernobody nobody配置Nginx的用户名和用户组,和Nginx的配置规则一样
nginx_worker_processesauto设置Nginx的进程书,通常等于CPU核心数
nginx_daemonon是否以daemon的方式运行Ngxin
mem_cache_size128m内存的缓存大小,可以使用km为单位
ssl_cipher_suitemodern定义Nginx提供的TLS密码,可以配置的值有:modern,intermediateoldcustom.
ssl_ciphers定义Nginx提供的TLS密码的列表,参考Nginx的配置
ssl_cert配置SSL证书的crt路径,必须是要绝对路径
ssl_cert_key设置SSL证书的key文件,必须是绝对路径
client_ssloff…..
client_ssl_cert…..
client_ssl_cert_key…..
admin_ssl_cert…..
admin_ssl_cert_key…..
headersserver_tokens, latency_tokens设置再相应客户端时候应该注入的头部,可以设置的值如下:
– server_tokens: 注入’Via’和’Server’头部.
– latency_tokens: 注入’X-Kong-Proxy-Latency’和’X-Kong-Upstream-Latency’ 头部.
– X-Kong-<header-name>: 只有在适当的时候注入特定的头部
这个配置可以被设置为off。当然,即便设置了off以后,插件依然可以注入头部
trusted_ips定义可信的IP地址段,通常不建议在此处限制请求,应该再插件中过滤
real_ip_headerX-Real-IP获取客户端真实的IP,将值通过同步的形式传递给后端
real_ip_recursiveoff这个值在Nginx配置中设置了同名的ngx_http_realip_module指令
client_max_body_size0配置Nginx接受客户端最大的body长度,如果超过此配置 将返回413。
设置为0则不检查长度
client_body_buffer_size8k设置读取缓冲区大小,如果超过内存缓冲区大小,那么NGINX会缓存在磁盘中,降低性能。
error_default_typetext/plain当请求’ Accept ‘头丢失,Nginx返回请求错误时使用的默认MIME类型。可以配置的值为:
text/plain,text/htmlapplication/jsonapplication/xml.

在Nginx注入配置中,配置了Nginx的基本的参数,这些参数大部分和NGINX的配置值是一样的,可以通过Nginx的配置文档了解一下。

3、数据库存储配置

数据库配置的模块配置数据库相关的连接信息等等。主要有如下配置:

配置项默认值说明
databasepostgres设置数据库类型,Kong支持两种数据库,一种是postgres,一种是cassandra
PostgreSQL配置如果database设置为postgres以下配置生效
pg_host127.0.0.1设置PostgreSQL的连接地址
pg_port5432设置PostgreSQL的端口
pg_userkong设置PostgreSQL的用户名
pg_password设置PostgreSQL的密码
pg_databasekong设置数据库名称
pg_ssloff是否开启ssl连接
pg_ssl_verifyoff如果启用了’ pg_ssl ‘,则切换服务器证书验证。
cassandra配置如果database设置为cassandra以下配置生效
cassandra_contact_points127.0.0.1…..
cassandra_port9042…..
cassandra_keyspacekong…..
cassandra_timeout5000…..
cassandra_ssloff…..
cassandra_ssl_verifyoff…..
cassandra_usernamekong…..
cassandra_password…..
cassandra_consistencyONE…..
cassandra_lb_policyRoundRobin…..
cassandra_local_datacenter…..
cassandra_repl_strategySimpleStrategy…..
cassandra_repl_factor1…..
cassandra_data_centersdc1:2,dc2:3…..
cassandra_schema_consensus_timeout10000…..

推荐使用PostgreSQL数据库作为生产环境的存储,PostgreSQL具有良好的性能和稳定性,是一个非常优秀的开源数据库。

4、数据库缓存配置

在上一节中,配置了Kong持久化存储,显然如果每次的请求都需要去查询数据库中的相关信息那无疑是非常消耗资源,性能和稳定性也会大大降低,作为一个API网关肯定是不能忍的,解决这个问题的办法就是缓存,Kong将数据缓存在内存中,这样会大大提高性能,本节介绍Kong的缓存配置。

配置项默认值说明
db_update_frequency5节点更新数据库的时间,以秒为单位。
这个配置设置了节点查询数据库的时间,假如有3台Kong服务器节点ABC,如果再A节点增加了一个API网关,那么B和C节点最多需要等待db_update_frequency时间才能被更新到。
db_update_propagation0数据库节点的更新时间。
如果使用了Cassandra数据库集群,那么如果数据库有更新,最多需要db_update_propagation时间来同步所有的数据库副本。
如果使用PostgreSQL或者单数据库,这个值可以被设置为0
db_cache_ttl0缓存生效时间,单位秒。如果设置为0表示永不过期
Kong从数据库中读取数据并且缓存,在ttl过期后会删除这个缓存然后再一次读取数据库并缓存
db_resurrect_ttl30缓存刷新时间,单位秒。当数据存储中的陈旧实体无法刷新时(例如,数据存储不可访问),应该对其进行恢复。当这个TTL过期时,将尝试刷新陈旧的实体。

5、DNS解析器配置

默认情况下,DNS解析器将使用标准配置文件/etc/hosts/etc/resolv.conf。如果设置了环境变量LOCALDOMAINRES_OPTIONS,那么后一个文件中的设置将被覆盖。

配置项默认值说明
dns_resolver配置DNS服务器列表,用半角逗号分割,每个条目使用ip[:port]的格式,这个配置仅提供给Kong使用,不会覆盖节点系统的配置,如果没有配置则使用系统的设置。接受IPv4和IPv6的地址。
dns_hostsfile/etc/hosts配置Kong的hosts文件,这个配置同样仅提供给Kong使用,不会覆盖节点系统的配置。
需要说明的是这个文件仅读取一次,读取的内容会缓存再内存中,如果修改了此文件,必须要重启Kong才能生效。
dns_orderLAST,SRV,A,CNAME解析不同记录类型的顺序。“LAST”类型表示最后一次成功查找的类型(用于指定的名称)
dns_stale_ttl4配置DNS记录缓存过期时间
dns_not_found_ttl30这个配置值不知道该如何理解??
dns_error_ttl1…..
dns_no_syncoff如果启用了该项,那么在DNS缓存过期之后,每一次请求都会发起DNS查询。在禁用此项时,那么相同的域名多次请求会同步到一个版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!查询中共享返回值。

在DNS配置中,我们基本上不需要更改,官网的配置给出了最优的配置。如果我们需要在host文件中定义后端绑定的域名,一定要在编辑hosts文件后重载Kong的配置,或者重启Kong,无论hosts的文件是否是/etc/hosts,否则都不会生效的。

6、其他杂项配置

杂项配置基本上关于LUA的配置,如果不熟悉请不要修改,按照官方默认即可。

配置项默认值说明
lua_ssl_trusted_certificate….
lua_ssl_verify_depth1….
lua_package_path./?.lua;./?/init.lua;….
lua_package_cpath….
lua_socket_pool_size30….

六、 API 管理详解

安装好了Kong,那么如何使用和管理它呢?Kong附带了一个管理的API接口,我们通过这个API接口来管理所有的API以及其他的资源,这个接口具有最高的权限,所以在生产环境中我们要特别注意这个接口的权限,通常我们不会将这个接口暴露在外网中。

如果Kong是以集群的状态的运行的,那么你只需要将管理API的请求发送到其中的一个节点中,Kong会自动同步信息到其他的节点。Kong默认监听80018444两个端口用接受管理API的请求,8001为http端口,8444为https端口

Kong支持application/x-www-form-urlencodedapplication/json两种类型,POST的数据我们需要以json的格式发送。

Kong大概有以下几个管理对象:

  • 节点信息
  • 服务
  • 路由
  • 用户
  • 插件
  • 证书
  • SNI
  • 上游信息
  • 目标

在Kong0.13.0版本以前,有API对象,但是从0.13.0之后开始逐步抛弃API对象,使用路由和服务组合来取代API对象,这提高了Kong的灵活性。基于此本文也将不介绍有关API对象的操作

本文约定:

  • 我使用的是Kong_1.4.0版本
  • 节点管理地址为 http://192.168.0.184:8001
  • 使用curl请求接口,并且将得到的结果通过管道送给python进行格式化

例如:

[email protected]:~$curl -s http://192.168.0.184:8001/status | python -m json.tool
{
    "database": {
        "reachable": true
    },
    "server": {
        "connections_accepted": 112,
        "connections_active": 1,
        "connections_handled": 112,
        "connections_reading": 0,
        "connections_waiting": 0,
        "connections_writing": 1,
        "total_requests": 65
    }
}

在以上的示例中,-s参数表示静默模式,curl将不输入信息,这样我们就可以不打印我们不需要关注的信息。

然后将得到的结果通过管道送给python进行格式化,这样打印出来的就是格式化的json,方便我们查看。

下面我们一起看一下每一种对象的操作


1、查看节点信息

接口信息:

接口名称查看节点详细信息
请求端点/
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s http://192.168.0.184:8001 | python -m json.tool

返回值:

查询节点详细信息返回的信息非常多,这里就显示了。

2、查看节点状态

这个接口查询节点的状态,主要是显示nginx进程处理连接的情况,以及数据库连接的情况。

如果Kong是以集群的方式运行,那么如果要查看其他节点的情况,必须要要一个一个访问节点的此接口。

因为Kong是基于nginx的,你也可以直接使用Nginx的监控工具。

接口信息:

接口名称查看节点状态
请求端点/status
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s http://192.168.0.184:8001/status | python -m json.tool

返回值:

{
    "database": {
        "reachable": true
    },
    "server": {
        "connections_accepted": 111,
        "connections_active": 1,
        "connections_handled": 111,
        "connections_reading": 0,
        "connections_waiting": 0,
        "connections_writing": 1,
        "total_requests": 64
    }
}

在返回的数据中,各个值的含义如下::

  • total_requests:客户端请求的总数。
  • connections_active:当前活动客户端连接数,包括等待连接。
  • connections_accepted:已接受的客户端连接总数。
  • connections_handled:已处理连接的总数。通常,参数值与accept相同,除非已达到某些资源限制。
  • connections_reading:Kong正在读取请求标头的当前连接数。
  • connections_writing:nginx将响应写回客户端的当前连接数。
  • connections_waiting:等待请求的当前空闲客户端连接数。

服务是每一个后端真实接口的抽象,它与路由关联,客户端发起请求,如果路由匹配到了,那么会将这个请求代理到与匹配路由相关联的服务中。

3、添加服务

接口信息:

接口名称添加服务
请求端点/services/
请求方法POST
返回状态HTTP 201 Created

请求参数:

参数名类型默认值是否必须说明
namestring服务名称,全局唯一
protocolstringhttp和上游通讯的协议取值httphttps
hoststring上游服务器的主机
portint80上游服务器的端口
pathstring上游服务器请求中的路径,必须以/开头
retriesint5代理失败时要执行的重试次数
connect_timeoutint60000与上游连接的超时时间,单位毫秒
write_timeoutint60000向上游发送请求两次连续写操作的超时时间 ,单位毫秒
read_timeoutint60000用于向上游服务器发送请求的两次连续读取操作之间的超时 ,单位毫秒

请求示例:

curl -s -X POST --url http://192.168.0.184:8001/services/ \
-d 'name=infvie_server' \
-d 'protocol=http' \
-d 'host=www.baidu.com'\
| python -m json.tool

返回值:

{
    "connect_timeout": 60000,
    "created_at": 1537924532,
    "host": "www.baidu.com",
    "id": "27f30248-fef1-4ddc-9fdc-4ca73f354c64",
    "name": "infvie_server",
    "path": null,
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1537924532,
    "write_timeout": 60000
}

如果在创建服务的时候没有指定name,那么Kong并不会自动创建name,但是会创建UUID形式的ID,Kong在调用、匹配等等的操作都是可以基于这个ID,所以这个ID绝对是全局唯一的。

在其他的对象管理中,name字段可能是必须的,通常这个name资源也是全局唯一的,即便如此,Kong也会创建ID字段,也可以通过这个ID字段来匹配。

在服务对象中,能组合起来成为上游服务也是唯一的,也就是说,在一个服务中无法同时存在 httphttps,如果上游提供http和https服务,同时也需要Kong代理它们的话,那必须要设置两个服务。

4、查询服务

接口信息:

接口名称查询服务
请求端点/services/{name or id}
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s --url http://192.168.0.184:8001/services/27f30248-fef1-4ddc-9fdc-4ca73f354c64 | python -m json.tool

返回值:

{
    "connect_timeout": 60000,
    "created_at": 1537986798,
    "host": "www.baidu.com",
    "id": "27f30248-fef1-4ddc-9fdc-4ca73f354c64",
    "name": "infvie_server",
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1537986798,
    "write_timeout": 60000
}

查询服务的接口我们可以通过id来查询,也可以通过name来查询,在创建服务的时候可以不指定name字段,在系统中显示为null,这种服务就无法通过指定name来查询了,必须要使用id来查询,所以,强烈建议在创建服务的时候指定name,这是一个好习惯。

5、查询所有服务

接口信息:

接口名称查询所有服务
请求端点/services/
请求方法GET
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
offsetstring分页偏移,用于定义列表中的唯一
sizeint100每页返回的对象的数量

请求示例:

curl -s --url http://192.168.0.184:8001/services/?size=1 | python -m json.tool

返回值:

{
    "data": [
        {
            "connect_timeout": 60000,
            "created_at": 1537986798,
            "host": "www.baidu.com",
            "id": "27f30248-fef1-4ddc-9fdc-4ca73f354c64",
            "name": "infvie_server",
            "path": null,
            "port": 80,
            "protocol": "http",
            "read_timeout": 60000,
            "retries": 5,
            "updated_at": 1537986798,
            "write_timeout": 60000
        }
    ],
    "next": "/services?offset=WyIyN2YzMDI0OC1mZWYxLTRkZGMtOWZkYy00Y2E3M2YzNTRjNjQiXQ",
    "offset": "WyIyN2YzMDI0OC1mZWYxLTRkZGMtOWZkYy00Y2E3M2YzNTRjNjQiXQ"
}

在上面的请求示例中,我们带了一个size的参数来限定每一页的数量,在返回的结果中有两个字段,next表示下一页的端点,offset是本页的偏移。

当然,如果你在读取下一页的时候还需要限定返回的数据,还是依然要使用size的参数

6、更新服务

接口信息:

接口名称查询所有服务
请求端点/services/{name or id}
请求方法PATCH
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
namestring服务名称,全局唯一
protocolstringhttp和上游通讯的协议取值httphttps
hoststring上游服务器的主机
portint80上游服务器的端口
pathstring上游服务器请求中的路径,必须以/开头
retriesint5代理失败时要执行的重试次数
connect_timeoutint60000与上游连接的超时时间,单位毫秒
write_timeoutint60000向上游发送请求两次连续写操作的超时时间 ,单位毫秒
read_timeoutint60000用于向上游服务器发送请求的两次连续读取操作之间的超时 ,单位毫秒

请求示例:

curl -s -X PATCH --url http://192.168.0.184:8001/services/infvie_server \
-d 'name=infvie_server_patch' \
-d 'protocol=http' \
-d 'host=www.baidu.com' \
 | python -m json.tool

返回值:

{
    "connect_timeout": 60000,
    "created_at": 1537986798,
    "host": "www.baidu.com",
    "id": "27f30248-fef1-4ddc-9fdc-4ca73f354c64",
    "name": "infvie_server_patch",
    "path": null,
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1537989418,
    "write_timeout": 60000
}

和查看服务的接口一样,接入点是需要带上id或者name的,只不过方法变成了PATCH,参数和添加的参数一样。

在上面的示例中,我修改了一个服务的name,从返回值中可以可能出来id是不会变化的。

7、更新或者创建服务

接口信息:

接口名称更新或者创建服务
请求端点/services/{name or id}
请求方法PUT
返回状态HTTP 201 Created or HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
namestring服务名称,全局唯一
protocolstringhttp和上游通讯的协议取值httphttps
hoststring上游服务器的主机
portint80上游服务器的端口
pathstring上游服务器请求中的路径,必须以/开头
retriesint5代理失败时要执行的重试次数
connect_timeoutint60000与上游连接的超时时间,单位毫秒
write_timeoutint60000向上游发送请求两次连续写操作的超时时间 ,单位毫秒
read_timeoutint60000用于向上游服务器发送请求的两次连续读取操作之间的超时 ,单位毫秒

请求示例:

curl -s -X PUT --url http://192.168.0.184:8001/services/infvie_server_put \
-d 'name=infvie_server_patch' \
-d 'protocol=http' \
-d 'host=www.baidu.com' \
 | python -m json.tool

返回值:

{
    "connect_timeout": 60000,
    "created_at": 1537991524,
    "host": "www.baidu.com",
    "id": "7242306f-3a55-46b5-9cda-cfb6c25a421c",
    "name": "infvie_server_put",
    "path": null,
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1537991524,
    "write_timeout": 60000
}

这个接口是更新或者创建一个服务,接入点和查询、更新是一样的,不一样的是用了PUT的方法。

此接口带了{name or id},Kong会根据{name or id}的信息查询数据库,如果对应的数据存在,那么更新,如果对于的数据不存在,那么就新增。

在更新服务的接口中,我们也是可以带{name or id},无论是带了id还是name,服务中的name字段都是能被更新到的,但是这个接口就有点不一样了。

有以下几种情况:

1.如果这个接口带的是name,例如/services/infvie_server Kong会判断name为”infvie_server“的服务存不存在,如果存在,则更新,这个时候POST到Kong的数据中name字段无效,Kong并不会修改。 如果不存在”infvie_server“这个服务存,那么会创建name为”infvie_server”的服务,并且Kong会生成UUID形式的id,此时POST上来的数据中的name字段依然无效。

2.如果这个接口带的是id,例如/services/ca4f16bc-1562-4ab8-b201-131c7ac393f0 Kong会判断id为”ca4f16bc-1562-4ab8-b201-131c7ac393f0″的服务是否存在,如果存在,那么更新它,这个时候如果POST上来的数据中有name字段,那么Kong会更新name。 如果id为”ca4f16bc-1562-4ab8-b201-131c7ac393f0″的服务不存在,那么Kong会创建name为”ca4f16bc-1562-4ab8-b201-131c7ac393f0″的服务,并且Kong会生成UUID形式的id,这个时候POST上来的数据中如果有name字段,那么这个字段无效。

8、删除服务

接口信息:

接口名称删除服务
请求端点/services/{name or id}
请求方法DELETE
返回状态HTTP 204 No Content

请求参数:

请求示例:

curl -i  -X DELETE --url http://192.168.0.184:8001/services/b6094754-07da-4c31-bb95-0a7caf5e6c0b

返回值:

此接口没有返回值


路由用来匹配客户端请求的规则,每一个路由都要与一个服务相关联,当一个请求到达Kong的时候,会先给路由匹配,如果匹配成功,那么会将请求转发给服务,服务再去后端请求数据。所以路由是Kong的入口。

在Kong0.13.0之前的版本有API实体,因为API实体使用用起来并不方便,所以将API实体拆分成路由和服务,这样提供了最大的自由度。

Kong的一个API实体必须是路由和服务的组合。

9、添加路由

接口信息:

接口名称添加路由
请求端点/routes/
请求方法POST
返回状态HTTP 201 Created

请求参数:

参数名类型默认值是否必须说明
protocolsstring or list[“http”, “https”]此路由允许的协议,取值httphttps
methodsstring or listnull*否此路由允许的方法
hostsstring or listnull*否此路由允许的域名
pathsstring or listnull*否此路由匹配的path
strip_pathbooltrue匹配到path时,是否删除匹配到的前缀
preserve_hostboolfalse匹配到hosts时,使用请求头部的值为域名向后端发起请求,请求的头部为”host”,例如”host:api.abc.com”
servicestring关联的服务id。
这里需要特别注意,如果是以表达的形式发送的,需要以 service.id=<service_id>形式发送,如果是json,需要以"service":{"id":"<service_id>"}形式发送

请求示例:

curl -s -X POST --url http://192.168.0.184:8001/routes \
-d 'protocols=http' \
-d 'methods=GET'  \
-d 'paths=/weather' \
-d 'service.id=43921b23-65fc-4722-a4e0-99bf84e26593' \
| python -m json.tool

返回值:

{
    "created_at": 1538089234,
    "hosts": null,
    "id": "cce1a279-d05a-4faa-8c10-1f9d27b881c9",
    "methods": [
        "GET"
    ],
    "paths": [
        "/weather"
    ],
    "preserve_host": false,
    "protocols": [
        "http"
    ],
    "regex_priority": 0,
    "service": {
        "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
    },
    "strip_path": true,
    "updated_at": 1538089234
}

在上面的示例中,我们创建了一个路由并且关联了一个服务,在创建路由的时候并没有指定hosts,路由匹配到host的时候会允许所有,因为默认值为null。当然如果不指定其他的也是一样的。

值得注意的是,methodshostspaths这三个参数必须要指定一个,否则无法创建路由。

路由和服务组合的规则相对比较复杂,我们将会重新开一篇文章来专门讲解这个,本文仅仅关注管理API的使用。

10、查看路由

接口信息:

接口名称查看路由
请求端点/routes/{id}
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s http://192.168.0.184:8001/routes/6c6b7863-9a05-4d51-bf7e-8e4e5866a131 | python -m json.tool

返回值:

{
    "created_at": 1538089668,
    "id": "6c6b7863-9a05-4d51-bf7e-8e4e5866a131",
    "paths": [
        "/weather"
    ],
    "preserve_host": false,
    "protocols": [
        "http",
        "https"
    ],
    "regex_priority": 0,
    "service": {
        "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
    },
    "strip_path": true,
    "updated_at": 1538089668
}

路由中没有name字段,所以只能通过ID来查看。

11、查询所有路由

接口信息:

接口名称查询所有路由
请求端点/routes
请求方法GET
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
offsetstring分页偏移,用于定义列表中的唯一
sizeint100每页返回的对象的数量

请求示例:

curl -s --url http://192.168.0.184:8001/routes/?size=1 | python -m json.tool

返回值:

{
    "data": [
        {
            "created_at": 1538004899,
            "hosts": [],
            "id": "19377681-ba1c-43dc-9eb6-ff117467ce96",
            "methods": [
                "GET",
                "POST"
            ],
            "paths": [],
            "preserve_host": false,
            "protocols": [
                "http",
                "https"
            ],
            "regex_priority": 0,
            "service": {
                "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
            },
            "strip_path": true,
            "updated_at": 1538071225
        }
    ],
    "next": "/routes?offset=WyIxOTM3NzY4MS1iYTFjLTQzZGMtOWViNi1mZjExNzQ2N2NlOTYiXQ",
    "offset": "WyIxOTM3NzY4MS1iYTFjLTQzZGMtOWViNi1mZjExNzQ2N2NlOTYiXQ"
}

在上面的请求示例中,我们带了一个size的参数来限定每一页的数量,在返回的结果中有两个字段,next表示下一页的端点,offset是本页的偏移。

当然,如果你在读取下一页的时候还需要限定返回的数据,还是依然要使用size的参数

12、更新路由

接口信息:

接口名称更新路由
请求端点/routes/{id}
请求方法PATCH
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
protocolsstring or list[“http”, “https”]此路由允许的协议,取值httphttps
methodsstring or listnull*否此路由允许的方法
hostsstring or listnull*否此路由允许的域名
pathsstring or listnull*否此路由匹配的path
strip_pathbooltrue匹配到path时,是否删除匹配到的前缀
preserve_hostboolfalse匹配到hosts时,使用请求头部的值为域名向后端发起请求,请求的头部为”host”,例如”host:api.abc.com”
servicestring关联的服务id。
这里需要特别注意,如果是以表达的形式发送的,需要以 service.id=<service_id>形式发送,如果是json,需要以"service":{"id":"<service_id>"}形式发送

请求示例:

curl -s -X PATCH --url http://192.168.0.184:8001/routes/6c6b7863-9a05-4d51-bf7e-8e4e5866a131 \
-d 'protocols=http' \
-d 'methods=GET'  \
-d 'paths=/weather' \
-d 'service.id=43921b23-65fc-4722-a4e0-99bf84e26593' \
| python -m json.tool

返回值:

{
    "created_at": 1538089668,
    "hosts": null,
    "id": "6c6b7863-9a05-4d51-bf7e-8e4e5866a131",
    "methods": [
        "GET"
    ],
    "paths": [
        "/weather"
    ],
    "preserve_host": false,
    "protocols": [
        "http"
    ],
    "regex_priority": 0,
    "service": {
        "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
    },
    "strip_path": true,
    "updated_at": 1538090658
}

此接口的参数和添加路由的参数一样,接入点带了路由的id,路由没有name字段,所有的匹配都是以ID匹配的。

13、更新或者添加路由

接口信息:

接口名称更新或者添加路由
请求端点/routes/{id}
请求方法PUT
返回状态HTTP 201 Created or HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
protocolsstring or list[“http”, “https”]此路由允许的协议,取值httphttps
methodsstring or listnull*否此路由允许的方法
hostsstring or listnull*否此路由允许的域名
pathsstring or listnull*否此路由匹配的path
strip_pathbooltrue匹配到path时,是否删除匹配到的前缀
preserve_hostboolfalse匹配到hosts时,使用请求头部的值为域名向后端发起请求,请求的头部为”host”,例如”host:api.abc.com”
servicestring关联的服务id。
这里需要特别注意,如果是以表达的形式发送的,需要以 service.id=<service_id>形式发送,如果是json,需要以"service":{"id":"<service_id>"}形式发送

请求示例:

curl -s -X PUT --url http://192.168.0.184:8001/routes/6c6b7863-9a05-4d51-bf7e-2962c1d6b0e6 \
-d 'protocols=http' \
-d 'methods=GET'  \
-d 'paths=/weather' \
-d 'service.id=43921b23-65fc-4722-a4e0-99bf84e26593' \
| python -m json.tool

返回值:

{
    "created_at": 1538091038,
    "hosts": null,
    "id": "6c6b7863-9a05-4d51-bf7e-2962c1d6b0e6",
    "methods": [
        "GET"
    ],
    "paths": [
        "/weather"
    ],
    "preserve_host": false,
    "protocols": [
        "http"
    ],
    "regex_priority": 0,
    "service": {
        "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
    },
    "strip_path": true,
    "updated_at": 1538091038
}

此接口的参数和添加路由的参数一样,接入点带了路由的id,路由没有name字段,所有的匹配都是以ID匹配的。

和服务的更新创建接口差不多,只不过路由没有name的字段,所以匹配均是按照id来匹配,所以规则比较简单:如果id存在则更新,如果id不存在则添加。

但是,此处的ID必须是UUID形式的ID,否则添加不成功。

14、查看和服务关联的路由

接口信息:

接口名称查看和服务关联的路由
请求端点/services/{service name or id}/routes
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s http://192.168.0.184:8001/services/wechat/routes | python -m json.tool

返回值:

{
    "data": [
        {
            "created_at": 1538089623,
            "id": "cdfee2bc-a5eb-4f80-96f5-64bfe3b85507",
            "methods": [
                "GET"
            ],
            "preserve_host": false,
            "protocols": [
                "http",
                "https"
            ],
            "regex_priority": 0,
            "service": {
                "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
            },
            "strip_path": true,
            "updated_at": 1538089623
        },
        {
            "created_at": 1538089397,
            "id": "f8ef8876-9681-4629-a2ee-d7fac8a8094a",
            "methods": [
                "GET"
            ],
            "paths": [
                "/weather"
            ],
            "preserve_host": false,
            "protocols": [
                "http",
                "https"
            ],
            "regex_priority": 0,
            "service": {
                "id": "43921b23-65fc-4722-a4e0-99bf84e26593"
            },
            "strip_path": true,
            "updated_at": 1538089397
        }
    ],
    "next": null
}

15、查看和路由关联的服务

接口信息:

接口名称查看和路由关联的服务
请求端点/routes/{route id}/service
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s http://192.168.0.184:8001/routes/f8ef8876-9681-4629-a2ee-d7fac8a8094a/service | python -m json.tool

返回值:

{
    "connect_timeout": 60000,
    "created_at": 1538000258,
    "host": "t.weather.sojson.com",
    "id": "43921b23-65fc-4722-a4e0-99bf84e26593",
    "name": "wechat",
    "path": "/api/weather/city/",
    "port": 80,
    "protocol": "http",
    "read_timeout": 60000,
    "retries": 5,
    "updated_at": 1538005796,
    "write_timeout": 60000
}

16、删除路由

接口信息:

接口名称删除路由
请求端点/routes/{id}
请求方法DELETE
返回状态HTTP 204 No Content

请求参数:

请求示例:

curl -s http://192.168.0.184:8001/routes/f8ef8876-9681-4629-a2ee-d7fac8a8094a/service | python -m json.tool

返回值:

此接口没有返回值


17、添加用户

接口信息:

接口名称添加用户
请求端点/consumers/
请求方法POST
返回状态HTTP 201 Created

请求参数:

参数名类型默认值是否必须说明
usernamestringnull*否全局唯一的用户名
custom_idstringnull*否全局唯一的用户ID

请求示例:

curl -s -X POST --url http://192.168.0.184:8001/consumers  -d 'username=infvie' | python -m json.tool

返回值:

{
    "created_at": 1538126090,
    "custom_id": null,
    "id": "376a9ccf-7d10-45a7-a956-77eb129d8ff0",
    "username": "infvie"
}

在上面示例中,我们指定了一个username来创建一个用户,从返回值看出,如果没有指定参数,那么默认值为空。

在调用这个接口,必须要指定username 和 custom_id其中一个参数,不能同时不指定。

在创建用户的时候,系统都将会生成一个UUID的形式的唯一ID。

18、查询用户

接口信息:

接口名称查询用户
请求端点/consumers/{username or id}
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s http://192.168.0.184:8001/consumers/infvie | python -m json.tool

返回值:

{
    "created_at": 1538126090,
    "id": "376a9ccf-7d10-45a7-a956-77eb129d8ff0",
    "username": "infvie"
}

查询用户只能通过系统ID和username来查询,无法通过custom_id查询

19、查询所有用户

接口信息:

接口名称查询所有用户
请求端点/consumers
请求方法GET
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
offsetstring分页偏移,用于定义列表中的唯一
sizeint100每页返回的对象的数量

请求示例:

curl -s http://192.168.0.184:8001/consumers?size=1 | python -m json.tool

返回值:

{
    "data": [
        {
            "created_at": 1538126090,
            "custom_id": null,
            "id": "376a9ccf-7d10-45a7-a956-77eb129d8ff0",
            "username": "infvie"
        }
    ],
    "next": "/consumers?offset=WyIzNzZhOWNjZi03ZDEwLTQ1YTctYTk1Ni03N2ViMTI5ZDhmZjAiXQ",
    "offset": "WyIzNzZhOWNjZi03ZDEwLTQ1YTctYTk1Ni03N2ViMTI5ZDhmZjAiXQ"
}

在上面的请求示例中,我们带了一个size的参数来限定每一页的数量,在返回的结果中有两个字段,next表示下一页的端点,offset是本页的偏移。

当然,如果你在读取下一页的时候还需要限定返回的数据,还是依然要使用size的参数

20、更新用户

接口信息:

接口名称更新用户
请求端点/consumers/{username or id}
请求方法PATCH
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
usernamestringnull*否全局唯一的用户名
custom_idstringnull*否全局唯一的用户ID

请求示例:

curl -s -X PATCH --url http://192.168.0.184:8001/consumers/376a9ccf-7d10-45a7-a956-77eb129d8ff0 -d "custom_id=infvie123456789" | python -m json.tool

返回值:

{
    "created_at": 1538126090,
    "custom_id": "infvie123456789",
    "id": "376a9ccf-7d10-45a7-a956-77eb129d8ff0",
    "username": "infvie"
}

21、更新或者添加用户

接口信息:

接口名称更新或者添加用户
请求端点/consumers/{username or id}
请求方法PUT
返回状态HTTP 201 Created or HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
usernamestringnull*否全局唯一的用户名
custom_idstringnull*否全局唯一的用户ID

请求示例:

curl -s -X PUT --url http://192.168.0.184:8001/consumers/376a9ccf-7d10-45a7-a956-77eb129d8ff0 -d "custom_id=infvie123456789" | python -m json.tool

返回值:

{
    "created_at": 1538127246,
    "custom_id": "infvie123456789",
    "id": "376a9ccf-7d10-45a7-a956-77eb129d8ff0",
    "username": null
}

这个接口和更新添加服务的接口类似,当{username or id}属性具有UUID的结构时,插入/更新的Consumer将由其标识id。否则它将由它识别username

22、删除用户

接口信息:

接口名称删除用户
请求端点/consumers/{username or id}
请求方法DELETE
返回状态HTTP 204 No Content

请求参数:

请求示例:

curl -i -X DELETE --url http://192.168.0.184:8001/consumers/376a9ccf-7d10-45a7-a956-77eb129d8ff0 

返回值:

此操作没有返回值


Kong提供了一个插件的功能,你可以通过配置指定某个服务或者路由或者用户启用某个插,插件始终只运行一次,所以对于不同的实体配置相同插件时就有优先级的概念。

多次配置插件的优先级如下:

  • 在以下组合上配置的插件:路由,服务和使用者。 (消费者意味着必须对请求进行身份验证)。
  • 在Route和Consumer的组合上配置的插件。 (消费者意味着必须对请求进行身份验证)。
  • 在服务和使用者的组合上配置的插件。 (消费者意味着必须对请求进行身份验证)。
  • 在路由和服务的组合上配置的插件。
  • 在Consumer上配置的插件。 (消费者意味着必须对请求进行身份验证)。
  • 在路由上配置的插件。
  • 在服务上配置的插件。
  • 配置为全局运行的插件。

从以上的优先级可以看出全局配置的插件优先级最小,而越具体的组合优先级越高。

23、添加插件

可以通过以下几种不同的方式添加插件:

  • 对于每个Service/Route和consumer。不要设置consumer_id和设置service_id或route_id。
  • 适用于每个Service/Route和特定consumer。只有设定consumer_id。
  • 适用于每个consumer和特定Service。仅设置service_id(警告:某些插件只允许设置route_id)
  • 对于每个consumer和特定的Route。仅设置route_id(警告:某些插件只允许设置service_id)
  • 对于特定的Service/Route和consumer。设置两个service_id/ route_id和consumer_id。

并非所有插件都允许指定consumer_id。检查插件文档。

接口信息:

接口名称添加插件
请求端点/plugins/
请求方法POST
返回状态HTTP 201 Created

请求参数:

参数名类型默认值是否必须说明
namestring要添加的插件的名称。目前,插件必须分别安装在每个Kong实例中。
consumer_idstringnull使用者的唯一标识符,用于覆盖传入请求中此特定使用者的现有设置。
service_idstringnull服务的唯一标识符,用于覆盖传入请求中此特定服务的现有设置。
route_idstringnull路由的唯一标识符,它覆盖传入请求中此特定路由的现有设置。
config.{property}stringnull插件的配置属性,可以在Kong Hub的插件文档页面找到。
enabledbooltrue是否应用插件。默认值:true。

请求示例:

curl -s -X POST --url http://192.168.0.184:8001/plugins/ \
-d 'name=basic-auth' \
-d 'route_id=d1a10507-ea15-4c61-9d8c-7f10ebc79ecb' \
| python -m json.tool

返回值:

{
    "config": {
        "anonymous": "",
        "hide_credentials": false
    },
    "created_at": 1540102452000,
    "enabled": true,
    "id": "900aeaa3-0a47-49a1-9fea-649e6c90ab7f",
    "name": "basic-auth",
    "route_id": "d1a10507-ea15-4c61-9d8c-7f10ebc79ecb"
}

在上面的请求示例中,我们在route上添加了basic-auth插件,这个插件用于认证,通过http的头部带入用户名和密码信息进行认证。

当然,启用插件后,我们要对user设置好basic-auth的凭证,否则访问不了,并且返回如下信息:

{
"message": "Unauthorized"
}

24、查询插件

接口信息:

接口名称查询插件
请求端点/plugins/{id}
请求方法GET
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
idstring要检索的插件的唯一标识符

请求示例:

curl -s  --url http://192.168.0.184:8001/plugins/900aeaa3-0a47-49a1-9fea-649e6c90ab7f  | python -m json.tool

返回值:

{
    "config": {
        "anonymous": "",
        "hide_credentials": false
    },
    "created_at": 1540102452000,
    "enabled": true,
    "id": "900aeaa3-0a47-49a1-9fea-649e6c90ab7f",
    "name": "basic-auth",
    "route_id": "d1a10507-ea15-4c61-9d8c-7f10ebc79ecb"
}

25、查询所有插件

接口信息:

接口名称查询所有插件
请求端点/plugins/
请求方法GET
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
idstring通过id查询
namestring通过name查
service_idstring通过service_id查
route_idstring通过iroute_id查
consumer_idstring通过consumer_id查
offsetstring分页偏移,用于定义列表中的唯一
sizeint100每页返回的对象的数量

请求示例:

curl -s --url http://192.168.0.184:8001/plugins/?size=1 | python -m json.tool

返回值:

{
    "data": [
        {
            "config": {
                "anonymous": "",
                "hide_credentials": false
            },
            "created_at": 1540102452000,
            "enabled": true,
            "id": "900aeaa3-0a47-49a1-9fea-649e6c90ab7f",
            "name": "basic-auth",
            "route_id": "d1a10507-ea15-4c61-9d8c-7f10ebc79ecb"
        }
    ],
    "total": 1
}

在上面的请求示例中,我们带了一个size的参数来限定每一页的数量,在返回的结果中有两个字段,next表示下一页的端点,offset是本页的偏移。

当然,如果你在读取下一页的时候还需要限定返回的数据,还是依然要使用size的参数

26、更新插件

接口信息:

接口名称更新插件
请求端点/plugins/{plugin id}
请求方法PATCH
返回状态HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
plugin idstring要更新的插件配置的唯一标识符
namestringnull要添加的插件的名称要添加的插件的名称
consumer_idstringnull使用者的唯一标识符,用于覆盖传入请求中此特定使用者的现有设置。
service_idstringnull服务的唯一标识符,用于覆盖传入请求中此特定服务的现有设置。
route_idstringnull路由的唯一标识符,它覆盖传入请求中此特定路由的现有设置。
config.{property}stringnull插件的配置属性,可以在Kong Hub的插件文档页面找到。
enabledbooltrue是否应用插件。

请求示例:

curl -s -X PATCH --url http://192.168.0.184:8001/plugins/900aeaa3-0a47-49a1-9fea-649e6c90ab7f -d "service_id=da4dce88-4df3-4723-b544-b11b27184e97" | python -m json.tool

返回值:

{
    "config": {
        "anonymous": "",
        "hide_credentials": false
    },
    "created_at": 1540102452000,
    "enabled": true,
    "id": "900aeaa3-0a47-49a1-9fea-649e6c90ab7f",
    "name": "basic-auth",
    "route_id": "d1a10507-ea15-4c61-9d8c-7f10ebc79ecb",
    "service_id": "da4dce88-4df3-4723-b544-b11b27184e97"
}

在上面的请求示例中,我更新了这个插件,原本只针对route启用的,现在又增加了对service生效,由此看出,一个插件是可以对多个实体生效的(前提是插件本身要支持此实体生效),因为插件只会在请求的生命周期中运行一次,所以对多个实体启用同一个插件会受到优先级的限制。

27、更新或添加插件

接口信息:

接口名称更新或添加插件
请求端点/plugins/
请求方法PUT
返回状态HTTP 201 Created or HTTP 200 OK

请求参数:

参数名类型默认值是否必须说明
namestringnull要添加的插件的名称要添加的插件的名称
consumer_idstringnull使用者的唯一标识符,用于覆盖传入请求中此特定使用者的现有设置。
service_idstringnull服务的唯一标识符,用于覆盖传入请求中此特定服务的现有设置。
route_idstringnull路由的唯一标识符,它覆盖传入请求中此特定路由的现有设置。
config.{property}stringnull插件的配置属性,可以在Kong Hub的插件文档页面找到。
enabledbooltrue是否应用插件。

请求示例:

curl -s -X PUT --url http://192.168.0.184:8001/plugins/900aeaa3-0a47-49a1-9fea-649e6c90ab7f -d "service_id=da4dce88-4df3-4723-b544-b11b27184e97" | python -m json.tool

返回值:

{
    "config": {
        "anonymous": "",
        "hide_credentials": false
    },
    "created_at": 1540102452000,
    "enabled": true,
    "id": "900aeaa3-0a47-49a1-9fea-649e6c90ab7f",
    "name": "basic-auth",
    "route_id": "d1a10507-ea15-4c61-9d8c-7f10ebc79ecb",
    "service_id": "da4dce88-4df3-4723-b544-b11b27184e97"
}

这个接口是更新或者创建一个插件。

如果传入的参数name已经存在,那么以传入的参数修改此插件,如果name不存在,则以传入的参数创建此插件。

28、删除插件

接口信息:

接口名称删除插件
请求端点/plugins/{plugin id}
请求方法DELETE
返回状态HTTP 204 No Content

请求参数:

参数名类型默认值是否必须说明
plugin idstring要删除的插件配置的唯一标识符

请求示例:

curl -s -X DELETE --url http://192.168.0.184:8001/plugins/900aeaa3-0a47-49a1-9fea-649e6c90ab7f | python -m json.tool

返回值:

此操作没有返回值

29、查询已启用的插件

接口信息:

接口名称查询已启用的插件
请求端点/plugins/enabled
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s -X GET --url http://192.168.0.184:8001/plugins/enabled | python -m json.tool

返回值:

{
    "enabled_plugins": [
        "response-transformer",
        "oauth2",
        "acl",
        "correlation-id",
        "pre-function",
        "jwt",
        "cors",
        "ip-restriction",
        "basic-auth",
        "key-auth",
        "rate-limiting",
        "request-transformer",
        "http-log",
        "file-log",
        "hmac-auth",
        "ldap-auth",
        "datadog",
        "tcp-log",
        "zipkin",
        "post-function",
        "request-size-limiting",
        "bot-detection",
        "syslog",
        "loggly",
        "azure-functions",
        "udp-log",
        "response-ratelimiting",
        "aws-lambda",
        "statsd",
        "prometheus",
        "request-termination"
    ]
}

返回Kong示例启用了那些插件,只有已经启用的插件才能被应用在实体上,需要说明的是,在kong集群中,插件启用的情况要一致。

30、检索插件架构

接口信息:

接口名称检索插件架构
请求端点/plugins/schema/{plugin name}
请求方法GET
返回状态HTTP 200 OK

请求参数:

请求示例:

curl -s -X GET --url http://192.168.0.184:8001/plugins/schema/basic-auth | python -m json.tool

返回值:

{
    "fields": {
        "anonymous": {
            "default": "",
            "func": "function",
            "type": "string"
        },
        "hide_credentials": {
            "default": false,
            "type": "boolean"
        }
    },
    "no_consumer": true
}

可以通过此接口了解插件接受哪些字段。


七、证书


八、SNI


九、上游信息


十、目标


参考文献

https://docs.konghq.com/
https://www.xiaomastack.com/
https://github.com/PGBI/kong-dashboard
https://www.cnblogs.com/aresxin/p/kong.html

0 条回应