版本说明
本次学习安装kong在2.1.4版本,konga版本0.14.9
此文档地址获取地址: https://gitee.com/PengFei-io/introduction-and-use-of-kong.git
Kong是由Mashape公司开源的可扩展的Api GateWay项目。它运行在调用Api之前,以插件的扩展方式为Api提供了管理。比如,鉴权、限流、监控、健康检查等,Kong是基于lua语言、nginx以及openResty开发的,所有拥有动态路由、负载均衡、高可用、高性能、熔断(基于健康检查)等。Kong提供了许多开箱即用的插件,用户也可以自定义规则使用lua开发插件。
Kong 是在客户端和(微)服务间转发API通信的API网关,通过插件扩展功能。Kong 有两个主要组件:
1、Kong Server :基于nginx的服务器,用来接收 API 请求。
2、Apache Cassandra or Postgresql:用来存储操作数据。
你可以通过增加更多 Kong Server 机器对 Kong 服务进行水平扩展,通过前置的负载均衡器向这些机器分发请求。根据文档描述,两个Cassandra节点就足以支撑绝大多数情况,但如果网络非常拥挤,可以考虑适当增加更多节点。
对于开源社区来说,Kong 中最诱人的一个特性是可以通过插件扩展已有功能,这些插件在 API 请求响应循环的生命周期中被执行。插件使用 Lua 编写,而且Kong还有如下几个基础功能:HTTP 基本认证、密钥认证、CORS( Cross-origin Resource Sharing,跨域资源共享)、TCP、UDP、文件日志、API 请求限流、请求转发以及 nginx 监控。
Kong可运行在某些 Linux 发行版、Mac OS X 和 Docker 中,无论是本地机还是云端服务器皆可运行。
除了免费的开源版本,Mashape 还提供了付费的企业版,其中包括技术支持、使用培训服务以及 API 分析插件。
OpenResty解决的是高并发的痛点。现在服务的后台大部分是java写的,但是用java写出稳定的高并发服务是很复杂的一件事,首先是服务器的选择,web服务器有几个选型,tomcat,apache,weblogic,还有商用webphere. 1、tomcat官方宣称的并发量是1000,厉害点的做点参数调优,也不过3000并发,如果要开发一个并发百万的服务,1000000/3000,需要1000台服务器,想想都不可能。 2、apache的并发比tomcat更不堪,200-300 3、weblogic的并发稍好,平均能达到3000左右,但是也没有达到好一个数量级
但是nginx就不一样了,处理几万的请求很轻松,内存占用也不高,之前我们只是把它用作负载均衡,没想过当做一个web服务器,OpenResty的出现解决了享受nginx高并发优势的拦路虎,因为nginx是使用异步 事件模型,跟传统的编程思想不一样,而lua是用c解释执行的脚本语言(执行效率很高),可以用传统的同步编程思想上,在nginx请求接进来后处理稍复杂的逻辑。
对于高并发的系统来说,都是基于内存的,或者说是基于缓存的,题主说的用mysql支撑高并发是不现实的,mysql的并发量在4000-8000,超过这个量mysql性能就会急剧下降。一次内存读取的时间是几十纳秒,一次缓存读取是几毫秒,大家可能对纳秒比较陌生,一纳秒等于1秒的1000000000分之一,一毫秒等于1秒的1000分之一,请求过来之后直接走内存读取,在需要和数据库交互的时候把数据写入内存,然后再批量入库,快速响应。
web流量也符合二八原则,百分之八十的流量集中在百分之二十的页面,比如电商的首页,产品详情页,使用openResty支撑产品详情页的高并发访问,在处理订购单,购物车等环节用其他的高并发框架处理,比如java的NIO网络框架netty。
java的netty也是处理高并发的利器,不过我做过测试,整体性能可以达到nginx的80%,所以,脏活累活都让nginx做吧,关键业务用netty。
当然,每个人对高并发的理解可能不太一样,有人说1000并发就是高并发了,有人说1万的并发才是高并发,有人说并发百万才是高并发,OpenResty是可以做到百万并发的(当然需要各种调优),现在大部分业务OpenResty都可以胜任,但是像腾讯10亿用户,1亿的并发,OpenResty就搞不定了。
不同的并发量要应对的东西不一样,比如1000并发,用tomcat,springmvc框架加缓存就可以应对,1万的并发在关键节点使用内存处理也很容易,百万并发就需要linux内核调优,socket缓冲区,文件句柄数,内存池,RPS/RFS SMP等优化也可以达到。千万并发就需要考虑用户态协议dpdk了
前置准备
1.k8s环境,Kong完全兼容k8s环境的部署与安装
2.使用相关资源清单进行部署
使用以下三种方式安装Kong
1.资源清单部署,相关yaml文件 可在 https://gitee.com/PengFei-io/introduction-and-use-of-kong.git
中找到
2.Helm方式部署
$ helm repo add kong https://charts.konghq.com
$ helm repo update
# Helm 2
$ helm install kong/kong
# Helm 3
$ helm install kong/kong --generate-name --set ingressController.installCRDs=false
官网链接
: https://charts.konghq.com/
3.Kustomize方式部署
kustomize build github.com/kong/kubernetes-ingress-controller/deploy/manifests/base
Kong for kubernetes官网地址
:
https://docs.konghq.com/2.2.x/kong-for-kubernetes/using-kong-for-kubernetes/
推荐博客,安装的为1.0.2版本kong
https://www.cnblogs.com/langfanyun/p/10298491.html
下载rpm安装包
,这里以Centos7.0为例,rpm下载地址
https://docs.konghq.com/install/centos/ 或者
https://bintray.com/kong/kong-rpm/centos/view/files/centos/7#files/centos/7
安装
如果已经下载了rpm安装包,则执行以下:
$ sudo yum install /path/to/package.rpm --nogpgcheck
如果使用镜像库直接安装则执行:
$ sudo yum update -y
$ sudo yum install -y wget
$ wget https://bintray.com/kong/kong-rpm/rpm -O bintray-kong-kong-rpm.repo
$ export major_version=`grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | cut -d "." -f1`
$ sed -i -e 's/baseurl.*/&\/centos\/'$major_version''/ bintray-kong-kong-rpm.repo
$ sudo mv bintray-kong-kong-rpm.repo /etc/yum.repos.d/
$ sudo yum update -y
$ sudo yum install -y kong
准备安装DB或者声明一个配置文件
kong 可以在有数据库的情况下进行运行,也可以无数据库独立运行,当你使用数据库时,您将使用 kong.conf 配置文件在启动时设置kong的配置属性,并将数据库设置为所有配置实体的存储,例如kong代理到的路由和服务。。如果不使用数据库进行独立运行时,您将使用 kong.conf 其配置属性和 kong.yml 文件将实体指定为声明性配置。
1)使用数据库进行存储kong的信息,如果您使用的是 PostgreSQL,请预配数据库和用户:
配置kong,以便它可以连接到您的数据库。kong支持 PostgreSQL 9.5+ 和 Cassandra 3.x.x 作为数据存储。
create user kong with password '123456';
CREATE DATABASE kong OWNER kong;
2)无数据库下启动kong
如果要在无 DB 模式下运行kong,应首先生成声明性配置文件。以下命令将在当前文件夹中生成一个 kong.yml 文件。它包含有关如何填充它的说明。
$ kong config init
填充 kong.yml 文件后,编辑您的 kong.conf 文件。将数据库选项设置为关闭,declarative_config将选项设置为 kong.yml 文件的路径:
database = off
declarative_config = /path/to/kong.yml
启动kong
启动kong时,Nginx 主进程默认以root进程运行,工作进程作为kong运行。如果这不是所需的行为,您可以将 Nginx 主进程切换到在内置的 kong 用户或启动kong之前运行到自定义非root用户。有关详细信息,请参阅以非root用户运行孔。
$ kong start [-c /path/to/kong.conf]
- 校验是否启动成功
> curl -i http://localhost:8001/
***docker安装kong***
- 创建kong网络
```shell
$ docker network create kong-net
初始化数据库
$ docker run --rm \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=192.168.220.128" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=123456" \
kong:2.1.4-centos kong migrations bootstrap
启动容器
docker run -d --name kong2.1.4 \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=192.168.220.128" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=123456" \
-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.1.4-centos
:8000
kong侦听来自客户端的传入 HTTP 流量,然后转发到您的上游服务。:8443
kong侦听传入的 HTTPS 流量。此端口具有与":8000"端口类似的行为,只不过它仅需要 HTTPS 流量。可以通过配置文件禁用此端口。.:8001
[管理员 API] 用于配置kong侦听。:8444
管理员 API 侦听 HTTPS 流量。$ kong stop
$ kong reload
重要
: kong启动后外部主机无法访问8001管理端口
问题的根本原因就是因为nginx监听的端口,kong默认将admin的管理端口只允许服务器本机访问,不允许给外部机器访问,这样`很安全`,但是一方面就会不方便,解决方案如下:
方案1)添加环境变量,因为Kong启动的时候会读取环境变量进行配置,这点在以下内容会介绍
export KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl
方案2)指定kong的启动文件,在启动文件中配置这个值
添加以下内容
admin_listen = 0.0.0.0:8001, 0.0.0.0:8444 ssl
非推荐,功能简单
kong仪表板是一个 GUI,将允许您管理您的kong网关设置。以下是kong与kong Dashboard的版本匹配
Kong-Dashboard versions | Kong versions | Node versions |
---|---|---|
1.x.x | >= 0.6, < 0.10 | |
2.x.x | 0.10 | |
3.0.x | >= 0.9, <0.12 | >= 6.0.0 |
3.1.x, 3.2.x | >= 0.9, <0.13 | >= 6.0.0 |
3.3.x, 3.4.x | >= 0.9, <0.14 | >= 6.0.0 |
3.5.x | >= 0.9, <0.15 | >= 6.0.0 |
3.6.x | >= 0.9, <2.0.0 | >= 6.0.0 |
安装
- a running Kong gateway. https://getkong.org/install/
- nodejs and npm, or docker
# Install Kong Dashboard
npm install -g kong-dashboard
# Start Kong Dashboard
kong-dashboard start --kong-url http://kong:8001
# Start Kong Dashboard on a custom port
kong-dashboard start \
--kong-url http://kong:8001 \
--port [port]
# Start Kong Dashboard with basic auth
kong-dashboard start \
--kong-url http://kong:8001 \
--basic-auth user1=password1 user2=password2
# See full list of start options
kong-dashboard start --help
# Start Kong Dashboard
docker run --rm -p 8080:8080 pgbi/kong-dashboard start --kong-url http://kong:8001
# Start Kong Dashboard on a custom port
docker run --rm -p [port]:8080 pgbi/kong-dashboard start --kong-url http://kong:8001
# Start Kong Dashboard with basic auth
docker run --rm -p 8080:8080 pgbi/kong-dashboard start \
--kong-url http://kong:8001
--basic-auth user1=password1 user2=password2
# See full list of start options
docker run --rm -p 8080:8080 pgbi/kong-dashboard start --help
官方链接
: https://github.com/PGBI/kong-dashboard
推荐博客
:https://blog.csdn.net/xiaoliu598906167/article/details/85785420
推荐使用,功能相对丰富
由于konga管理工具有自己的用户信息,所以会创建相关的DB进行维护,为了统一kong的数据库,所以统一使用Postgresql进行存储,提前创建相关数据库以及用户信息:
create user konga with password 'konga123456';
CREATE DATABASE konga OWNER konga;
grant all privileges on database konga to konga;
版本不要拉取最新,以免不兼容kong
$ docker pull pantsel/konga:0.14.9
$ docker run -d -p 1337:1337 -e "TOKEN_SECRET=699a85ee59a1" -e "DB_ADAPTER=postgres" -e "DB_HOST=192.168.220.128" -e "DB_PORT=5432" -e "DB_USER=konga" -e "DB_PASSWORD=konga123456" -e "DB_DATABASE=konga" -e "NODE_ENV=development" --name konga pantsel/konga:0.14.9
TOKEN_SECRET
:是创建者随机生成即可
启动后浏览器访问:宿主机IP + 映射端口即可
成熟的流程:
配置upstream -> 配置target -> 配置服务 -> 配置路由 -> 安装插件[可选]
$ curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=nginx-service-demo' \
--data 'url=http://192.168.220.130:30001/'
client调用服务名称nginx-service-demo,访问http://192.168.220.130:30001地址
添加成功后,系统将返回如下信息:
HTTP/1.1 201 Created
Date: Tue, 19 Jan 2021 10:07:45 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/1.0.2
Content-Length: 268
{
"host":"192.168.220.130",
"created_at":1611050865,
"connect_timeout":60000,
"id":"88563cdd-44c1-4c3b-abdd-89073b24f643",
"protocol":"http",
"name":"nginx-service-demo",
"read_timeout":60000,
"port":30001,
"path":"/",
"updated_at":1611050865,
"retries":5,
"write_timeout":60000
}
id:service_id
retries: 访问失败重试5次
添加路由的目的就是定义访问服务Service的条件,如下面这个例子,为nginx-service-demo服务添加路由访问域名nginx.com.
$ curl -i -X POST \
--url http://localhost:8001/services/nginx-service-demo/routes \
--data 'hosts[]=nginx.com'
当客户端client通过域名nginx.com访问Kong将匹配nginx-service-demo服务返回给客户端。通过管理API创建路由成功后会返回如下Json信息:
HTTP/1.1 201 Created
Date: Tue, 19 Jan 2021 10:20:10 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/1.0.2
Content-Length: 346
{
"created_at":1611051610,
"methods":null,
"id":"66a8e379-c0c4-4ae7-891f-3d45e8bbf154",
"service":{
"id":"88563cdd-44c1-4c3b-abdd-89073b24f643"
},
"name":null,
"hosts":[
"nginx.com"
],
"updated_at":1611051610,
"preserve_host":false,
"regex_priority":0,
"paths":null,
"sources":null,
"destinations":null,
"snis":null,
"protocols":[
"http",
"https"
],
"strip_path":true
}
curl -i -X GET \
--url http://localhost:8000/ \
--header 'Host: nginx.com'
HTTP header中定义Host变量值为nginx.com,Kong自动匹配到nginx-service-demo服务,转发到http://192.168.220.130:30001/ 上。
Kong的核心原则之一是其通过插件的可扩展性。插件使您可以轻松地向服务中添加新功能或使其更易于管理。Kong通过插件Plugins实现日志记录、安全检测、性能监控和负载均衡等功能。
在以下步骤中,您将配置key-auth插件以向您的服务添加身份验证。在添加此插件之前,对您服务的所有请求都将在上游被代理。添加并配置此插件后,只会代理具有正确密钥的请求-Kong将拒绝所有其他请求,从而保护上游服务免遭未经授权的使用。
$ curl -i -X POST \
--url http://localhost:8001/services/nginx-service-demo/plugins/ \
--data 'name=key-auth'
注意
:这个插件接收config.key_names定义参数,默认参数名称
['apikey']
。在HTTP请求中 header和params参数中包含apikey参数,参数值必须apikey密钥,Kong网关将坚持密钥,验证通过才可以访问后续服务。注意:仅仅是为example-service服务安装了此插件
。
发出以下curl请求,以验证是否在服务上正确配置了key-auth插件:
$ curl -i -X GET \
--url http://localhost:8000 \
--header 'Host: nginx.com'
如果响应为以下内容,说明key-auth插件安装成功,注意:仅仅是为example-service服务安装了此插件
HTTP/1.1 401 Unauthorized
Date: Tue, 19 Jan 2021 12:22:48 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Content-Length: 41
Server: kong/1.0.2
在本节中,将学习如何将消费者添加到您的kong实例。使用者与使用您的服务的个人关联,可用于跟踪、访问管理等。本例子描述添加Servie服务消费者,定义消费者访问API Key,让他有权限访问nginx-service-demo。
让我们创建一个消费者名字为 zhangsan
。
$ curl -i -X POST \
--url http://localhost:8001/consumers/ \
--data "username=zhangsan"
你会看到以下响应,说明你已经成功创建一个用户zhangsan
:
HTTP/1.1 201 Created
Date: Tue, 19 Jan 2021 12:39:44 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/1.0.2
Content-Length: 108
{
"custom_id":null,"created_at":1611031184,"username":"zhangsan","id":"d3ea51ca-c446-4054-8138-264705ab278d"}
id:消费者ID
username:消费者名称
唯一
注意
: Kong也接受custom_id
参数当创建消费者时 ,这样可以和你自己数据库用户id关联起来。
zhangsan
创建一个api key,输入以下命令:$ curl -i -X POST \
--url http://localhost:8001/consumers/zhangsan/key-auth/ \
--data 'key=ZHANGSAN_KEY'
收到以下响应,说明给消费者zhangsan
创建api key成功:
HTTP/1.1 201 Created
Date: Tue, 19 Jan 2021 13:04:54 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/1.0.2
Content-Length: 147
{
"key":"ZHANGSAN_KEY","created_at":1611032694,"consumer":{
"id":"d3ea51ca-c446-4054-8138-264705ab278d"},"id":"a3a56446-e69b-4732-9f8c-3695d7e785bd"}
$ curl -i -X GET \
--url http://localhost:8000 \
--header "Host: nginx.com" \
--header "apikey: ZHANGSAN_KEY"
可以看到访问成功:
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 16
Connection: keep-alive
Server: nginx/1.17.1
Date: Wed, 13 Jan 2021 21:48:44 GMT
Last-Modified: Wed, 13 Jan 2021 19:29:41 GMT
ETag: "5fff4a25-10"
Accept-Ranges: bytes
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 1
Via: kong/1.0.2
I am is a nginx
如果注释掉配置中的所有值,kong将使用默认设置进行操作。启动时,Kong 会寻找几个可能包含配置文件的默认位置:
/etc/kong/kong.conf
/etc/kong.conf配置格式很简单:只需取消注释任何属性(注释由 # 字符定义),并根据需要对其进行修改。为了方便起见,布尔值可以指定为on/off或true/false
$ cp /etc/kong/kong.conf.default /etc/kong/kong.conf
您可以通过使用 CLI 中的 -c / -conf 参数为配置文件指定自定义路径来覆盖此行为:
$ kong start --conf /path/to/kong.conf
kong支持配置文件的校验,这点类似 nginx -t,此命令将考虑您当前设置的环境变量,并且在设置无效时出错。此外,可以在命令后加入 --vv ,从而输出更多相关信息
$ kong check <path/to/kong.conf> --vv
configuration at <path/to/kong.conf> is valid
从配置文件中加载属性时,kong还将查找同名的环境变量。这允许您通过环境变量完全配置kong,例如,对于基于容器的基础结构来说,这非常方便。若要使用环境变量重写设置,请声明一个环境变量的名称,该变量以KONG_大写。
例如:
log_level = debug # in kong.conf
可以用以下环境变量进行配置:
export KONG_LOG_LEVEL=error
通过调整kong实例的 Nginx 配置,您可以针对您的基础架构优化其性能。
当kong启动时,它会生成一个 Nginx 配置文件。您可以通过kong配置直接向此文件注入自定义 Nginx 指令。
a)
添加到由 nginx_http_、nginx_proxy_ 或 nginx_admin_ 前缀的 kong.conf 文件的任何条目都将通过删除前缀并添加到 Nginx 配置的适当部分转换为等效的 Nginx 指令:
以输入nginx_http_
将注入到整个 http 块指令。
以输入nginx_proxy_
将注入处理kong代理端口的服务器块指令。
将处理nginx_admin_
管理 API 端口的服务器块指令注入具有前缀的条目。
例如:
nginx_proxy_large_client_header_buffers=16 128k
它将向kong的 Nginx 配置的代理服务器块添加以下指令:
large_client_header_buffers 16 128k;
与 kong.conf 中任何其他条目一样,也可以使用环境变量指定这些指令,如上所示。例如,如果声明的环境变量是这样的:
export KONG_NGINX_HTTP_OUTPUT_BUFFERS="4 64k"
这将导致以下 Nginx 指令添加到 http 块中:
output_buffers 4 64k;
有关 Nginx 配置文件结构和块指令的更多详细信息,请参阅 https://nginx.org/en/docs/beginners_guide.html#conf_structure。
有关 Nginx 指令的列表,请参阅https://nginx.org/en/docs/dirindex.html。但是请注意,某些指令依赖于特定的 Nginx 模块,其中一些指令可能不包括在 Kong 的官方版本中。
b)
对于更复杂的配置方案(如添加全新的server块),可以使用上述方法将包含指令注入到 Nginx 配置,并指向包含其他 Nginx 设置的文件。
例如,如果您创建一个名为 my-server.kong.conf 的文件,则包含以下内容:
# custom server
upstream testDemo {
server 192.168.220.130:30001;
}
server {
listen 2112;
location / {
proxy_pass http://testDemo;
}
}
您可以通过向 kong.conf 文件添加以下条目来使kong节点服务于此端口:
nginx_http_include = /path/to/your/my-server.kong.conf
现在,当您启动kong时,该文件中的server部分将添加到该文件中,这意味着在该文件中定义的自定义服务器将响应常规kong端口:
$ curl http://127.0.0.1:2112
I am is a nginx
请注意,如果您使用一个相对路径在nginx_http_include属性中,这个路径将会解析相对于 kong.conf 文件的前缀属性的值。(或者在kong启动时使用 -p 进行属性值的覆盖)。
对于绝大多数用例,使用上述解释的 Nginx 指令注入系统应足以自定义kong的 Nginx 实例的行为。这样,您就可以从单个 kong.conf 文件(以及您自己的包含文件)管理kong节点的配置和调优,而无需处理自定义的 Nginx 配置模板。
在两种情况下,您可能希望直接使用自定义的 Nginx 配置模板:
a)
自定义 Nginx 模板
可以使用 --nginx-conf 参数启动、重新加载和重新启动kong,该参数必须指定 Nginx 配置模板。此类模板使用 `Penlight`[lua工具库] 模板引擎,该引擎使用给定的 Kong 配置编译,然后转储到您的kong前缀目录中,在启动 Nginx 之前。
默认模板可以在以下位置找到: https://github.com/kong/kong/tree/master/kong/templates.
它分为两个 Nginx 配置文件:nginx.lua和nginx_kong.lua,当kong start运行时,就在启动Nginx之前,它将这两个文件复制到前缀目录中,如下所示:
/usr/local/kong
├── nginx-kong.conf
└── nginx.conf
如果必须调整由Kong定义但不能通过kong.conf中的Kong配置进行调整的全局设置,您可以像这样将nginx_kong.lua配置模板的内容内联到自定义模板文件(在此示例中称为custom_nginx.template):
# ---------------------
# custom_nginx.template
# ---------------------
worker_processes ${
{NGINX_WORKER_PROCESSES}}; # can be set by kong.conf
daemon ${
{NGINX_DAEMON}}; # can be set by kong.conf
pid pids/nginx.pid; # this setting is mandatory
error_log logs/error.log ${
{LOG_LEVEL}}; # can be set by kong.conf
events {
use epoll; # a custom setting
multi_accept on;
}
http {
# contents of the nginx_kong.lua template follow:
resolver ${
{DNS_RESOLVER}} ipv6=off;
charset UTF-8;
error_log logs/error.log ${
{LOG_LEVEL}};
access_log logs/access.log;
... # etc
}
然后,您可以使用以下命令启动Kong:
$ kong start -c kong.conf --nginx-conf custom_nginx.template
如果您正在运行自己的OpenResty服务器,则还可以通过使用include指令包含Kong Nginx子配置来轻松嵌入Kong。如果您已有Nginx配置,则只需将Kong输出的配置中特定于Kong的部分包括在单独的nginx-kong.conf文件中:
# my_nginx.conf
# ...your nginx settings...
http {
include 'nginx-kong.conf';
# ...your nginx settings...
}
然后,您可以像这样启动Nginx实例:
$ nginx -p /usr/local/openresty -c my_nginx.conf
并且Kong将在该实例中运行(在nginx-kong.conf中配置)
API提供程序的一个常见用例是使Kong通过代理端口(生产中的80或443)同时提供网站和API本身。例如,https://example.net(网站)和https://example.net/api/v1(API)。为此,我们不能像上一节中那样简单地声明一个新的虚拟服务器块。一个好的解决方案是使用自定义的Nginx配置模板,该模板内联nginx_kong.lua并在Kong Proxy位置块旁边添加一个为网站提供服务的新位置块:
# ---------------------
# custom_nginx.template
# ---------------------
worker_processes ${
{NGINX_WORKER_PROCESSES}}; # can be set by kong.conf
daemon ${
{NGINX_DAEMON}}; # can be set by kong.conf
pid pids/nginx.pid; # this setting is mandatory
error_log logs/error.log ${
{LOG_LEVEL}}; # can be set by kong.conf
events {
}
http {
# here, we inline the contents of nginx_kong.lua
charset UTF-8;
# any contents until Kong's Proxy server block
...
# Kong's Proxy server block
server {
server_name kong;
# any contents until the location / block
...
# here, we declare our custom location serving our website
# (or API portal) which we can optimize for serving static assets
location / {
root /var/www/example.net;
index index.htm index.html;
...
}
# Kong's Proxy location / has been changed to /api/v1
location /api/v1 {
set $upstream_host nil;
set $upstream_scheme nil;
set $upstream_uri nil;
# Any remaining configuration for the Proxy location
...
}
}
# Kong's Admin server block goes below
# ...
}
额外配置参数可参照 kong.conf中的注释进行选择配置,官方参考地址:
https://docs.konghq.com/2.2.x/configuration/
Kong群集允许您通过添加更多计算机来处理更多传入请求来水平扩展系统。由于它们指向相同的数据库,因此它们都将共享相同的配置。
指向同一数据存储的Kong节点将属于同一Kong集群
。您需要在Kong群集前面安装一个负载均衡器,以在可用节点之间分配流量。
拥有一个Kong集群并不意味着您的客户端流量将在您的Kong节点之间实现负载均衡。
您仍然需要在Kong节点前面安装一个负载均衡器来分配流量。相反,Kong集群意味着这些节点将共享相同的配置。出于性能原因,Kong在代理请求时避免数据库连接。kong会将数据库的内容缓存在内存中。缓存的实体包括服务,路由,消费者,插件,凭证
等。由于这些值已存储在内存中,因此需要将通过节点之一的Admin API进行的任何更改传播到其他节点。后续会介绍如何使这些缓存的实体失效以及如何为您的用例配置Kong节点,介于性能和一致性之间。
连接到数据库(Cassandra或PostgreSQL)的单个Kong节点创建一个节点的Kong集群。通过该节点的Admin API应用的任何更改将立即生效。
例如:考虑单个Kong节点A。如果我们删除先前注册的服务:
$ curl -X DELETE http://127.0.0.1:8001/services/test-service
然后,对节点A的任何后续请求都将立即返回404 Not Found,因为该节点从其本地缓存中清除了它:
$ curl -i http://127.0.0.1:8000/test-service
在具有多个Kong节点的集群中,连接到同一数据库的其他节点不会立即收到节点A删除该服务的通知。尽管该服务不再位于数据库中(它已被节点A删除),但它仍位于节点B的内存中。所有节点执行定期后台作业,以与其他节点可能已触发的更改同步。可以通过以下方式配置此作业的频率:
默认5s
使用数据存储库检查更新的实体的频率(以秒为单位)。当节点通过Admin API创建,更新或删除实体时,其他节点需要等待下一个轮询(由该值配置)才能最终清除旧的缓存实体并开始使用新的实体。每隔db_update_frequency秒,所有运行的Kong节点将轮询数据库以获取任何更新,并在必要时从其缓存中清除相关实体。如果我们从节点A删除服务,则此更改不会在节点B中立即生效,直到节点B下一次数据库轮询为止。这也使得kong集群的数据保持最终一致性。
所有核心实体(例如
服务,路由,插件,使用者,凭证
)都由Kong缓存在内存中,并依赖于它们通过要更新的轮询机制的失效。此外,Kong还缓存数据库未命中。这意味着,如果您配置的服务没有插件
,则Kong将缓存此信息。
例如:在节点A上,我们添加服务和路由:
# node A
$ curl -X POST http://127.0.0.1:8001/services \
--data "name=example-service" \
--data "url=http://example.com"
$ curl -X POST http://127.0.0.1:8001/services/example-service/routes \
--data "paths[]=/example"
(请注意,我们使用/ services / example-service / routes作为快捷方式:我们可以改用/ routes端点,但随后我们需要将service_id作为参数传递,并带有新Service的UUID。)对节点A和B的代理端口的请求将缓存此服务,并且事实上并没有在其上配置插件:
# node A
$ curl http://127.0.0.1:8000/example
HTTP 200 OK
...
# node B
$ curl http://127.0.0.2:8000/example
HTTP 200 OK
...
现在,假设我们通过节点A的Admin API向该服务添加了一个插件:
# node A
$ curl -X POST http://127.0.0.1:8001/services/example-service/plugins \
--data "name=example-plugin"
由于此请求是通过节点A的Admin API发出的,因此节点A将在本地缓存将会失效,并且在后续请求中失效,它将检测到此API已配置了插件。但是,节点B尚未进行数据库轮询。并且仍然缓存着没安装插件的API。直到节点B执行了数据库轮训作业,才会同步状态,从而与节点A状态一致。
总结:所有CRUD操作都会触发缓存失效。创建(POST, PUT)将使缓存中的数据库失效,更新/删除(PATCH, DELETE)将使缓存中的数据库失效。
可以在Kong配置文件中配置3个属性,最重要的一个是db_update_frequency,这决定了您的Kong集群在性能和一致性之间的权衡。Kong为一致性调整了默认值,为了让您尝试其集群功能,同时避免“意外”。在准备生产环境时,您应该考虑调整这些值,以确保遵守性能约束。
(默认: 5s)
此值决定了您的Kong节点轮询数据库以获取无效事件的频率。较低的值表示轮询作业将更频繁地执行,但您的Kong节点的数据将保持一致,较高的值表示您的Kong节点将花费较少的时间来运行轮询作业。并将专注于代理您的流量。
注意
:配置的变化将在db_update_frequency 内应用于整个集群。
(默认: 0s)
如果您采用的数据库本身就是最终一致的(即:Cassandra),那您必须配置此值,这是为了确保您的更新有时间在数据库节点之间传播。配置后,从轮询作业接收到无效事件的节点将延迟其缓存清除db_update_propagation秒。
如果连接到最终一致数据库的Kong节点没有延迟事件处理,则它可以清除其缓存,仅再次缓存未更新的值(因为更改尚未在数据库中传播)!
您应该将此值设置为数据库集群传播更改所花费的时间的估计值,即数据库集群达到最终一致性的延迟时间。
注意
:设置此值后,更改将在整个db_update_frequency + db_update_propagation秒内传播。
(默认: 0s)
Kong将缓存数据库实体(命中和未命中)的时间(以秒为单位)。此生存时间值可作为安全措施,以防Kong节点错过失效事件,从而避免其在陈旧数据上运行太长时间。当达到TTL时,将从其缓存中清除该值,并再次缓存下一个数据库结果。
默认情况下,没有数据基于此TTL无效(默认值为0)。通常这很好:Kong节点依赖于无效事件,这些事件在数据库存储级别(Cassandra / PosgreSQL)处理。如果您担心Kong节点可能因任何原因错过失效事件,则应设置TTL。否则,节点可能会在其缓存中以陈旧值运行不确定的时间,直到手动清除缓存或重新启动节点为止。
如果将Cassandra用作Kong数据库,则必须将
db_update_propagation
设置为非零值。由于Cassandra最终在本质上是一致的,因此这将确保Kong节点不会过早地使其缓存失效,而只是再次获取并捕获不最新的实体。如果您在使用Cassandra时未配置该值,则Kong将向您显示警告日志。此外,您可能希望将cassandra_consistency配置为QUORUM或LOCAL_QUORUM之类的值,以确保Kong节点缓存的值是数据库中的最新值。
具体Cassnara数据库一致性配置参考博客:
https://blog.csdn.net/kjh2007abc/article/details/94229419
出于某些原因,您想查看缓存的值,或者手动的删除Kong缓存的实体信息,您可以通过Admin API / cache端点执行此操作。
检查缓存
GET /cache/{cache_key}
如果具有该键的值被缓存:
HTTP 200 OK
...
{
...
}
否则:
HTTP 404 Not Found
注意
:为Kong缓存的每个实体检索cache_key当前是一个未记录的过程。未来版本的Admin API将使此过程更加容易。
清除缓存的值
DELETE /cache/{cache_key}
响应:
HTTP 204 No Content
...
清除节点的缓存
DELETE /cache
响应:
HTTP 204 No Content
...
警告
: 在生产运行节点上清除节点缓存。如果当前节点接收大量流量,同时清除其缓存将触发对数据库的大量请求,并可能导致dog-pile effect
情况发生。
Kong提供了多种对多个后端服务进行请求负载平衡的方式:一种直接的基于DNS的方法,以及一种更加动态的
环形平衡器[ring-balance]
,该平衡器还允许在不需要DNS服务器的情况下进行服务注册。
使用基于DNS的负载平衡时,后端服务的注册在Kong之外完成,并且Kong仅从DNS服务器接收更新。
如果主机名不解析为多个IP地址,且主机名中包含主机名(而不是IP地址)的主机定义的每个服务都将自动使用基于DNS的负载平衡,前提是该主机名不解析为上游名称或您的DNS主机文件。
DNS记录的ttl设置(生存时间)确定刷新信息的频率。使用ttl=0时,将使用其自己的DNS查询来解析每个请求。显然,这将降低性能,但是更新/更改的延迟将非常低。
A records
A record 包含一个或多个IP地址。因此,当主机名解析为 A record 时,每个后端服务必须具有自己的IP地址。
因为没有权重信息,所以所有条目在负载均衡器中都将被平等地加权,并且均衡器将进行直接循环。
SRV records
SRV记录包含其所有IP地址的重量和端口信息。可以通过IP地址和端口号的唯一组合来标识后端服务。因此,单个IP地址可以在不同端口上托管同一服务的多个实例。
因为`权重`信息可用,所以每个条目将在负载均衡器中获得自己的`权重`,并且将执行加权循环。
同样,任何给定的端口信息都将被DNS服务器的端口信息覆盖。如果服务的属性为host = myhost.com和port = 123,并且myhost.com解析为带有127.0.0.1:456的SRV记录,则请求将被代理到http://127.0.0.1:456/somepath,因为端口123将被456覆盖。
DNS 优先级
DNS解析器将按顺序开始解析以下记录类型:
a) The last successful type previously resolved
b) SRV record
c) A record
d) CNAME record
可以通过dns_order
配置属性来配置此顺序。
[Ring-balancer]
使用环形平衡器时,后端服务的添加和删除将由Kong处理,并且不需要DNS更新。 Kong将充当服务注册表。可以使用单个HTTP请求添加/删除节点,这些节点将立即启动/停止接收流量。环形均衡器的配置是通过上游
[upstream]
和目标[target]
实体完成的。
target
后端服务所在的IP地址或主机名以及端口号。 “ 192.168.100.12:80”。每个目标都有一个额外的权重,以指示它获得的相对负载。 IP地址可以采用IPv4和IPv6格式。
upsteam
可以在“路由主机”字段中使用的“虚拟主机名”,例如,上游名为weather.v2.service的主机,将通过host = weather.v2.service接收来自服务的所有请求。
每个上游都有自己的环形平衡器。每个上游都可以附加许多目标条目,并且代理到“虚拟主机名”的请求将在目标之间进行负载平衡。环形平衡器具有预定数量的插槽,并且基于目标权重,插槽将分配给上游目标。
可以通过Admin API上的简单HTTP请求来添加和删除目标。此操作相对便宜。更改上游本身的成本更高,例如,当插槽数量发生变化时,平衡器将需要重建。
自动清除平衡器的唯一情况是清理目标历史记录时;除此之外,它只会根据更改进行重建。
平衡器内有位置(从1到插槽),这些位置随机分布在环上。需要随机性才能使运行时便宜地调用环形平衡器。在轮(位置)上进行简单的轮询将提供在目标上分布均匀的加权轮询,同时在插入/删除目标时也具有廉价的操作。
每个目标要使用的插槽数量(至少)应约为100,以确保正确分配插槽。例如。对于预期的最多8个目标,即使初始设置仅包含2个目标,上游也应至少定义slot = 800。
这里的权衡是,插槽数越多,随机分布越好,但是更改的开销就越大(添加/删除目标)
有关添加和操作
upsteam
的详细信息,请参阅Admin API参考的upsteam
部分。https://docs.konghq.com/1.0.x/admin-api/#upstream-object
Upstream Object
由于
upstream
保留更改历史记录,因此只能添加tartget
,不能修改或删除target
。要更改target
,只需为target
添加一个新条目,然后更改权重值。最后一个条目是将要使用的条目。这样,权重= 0将禁用target
,从而有效地将其从平衡器中删除。有关添加和操作目标的详细信息,请参阅Admin API参考的target
部分。https://docs.konghq.com/1.0.x/admin-api/#upstream-object
Target Object
当非活动条目比活动条目多10倍时,
target
将被自动清除。清洁将涉及重建平衡器,因此比添加target
条目要贵得多。
target
也可以具有主机名而不是IP地址。在这种情况下,名称将被解析,并且找到的所有条目将单独添加到环形均衡器,例如,添加weight.100为api.host.com:123。名称“ api.host.com”解析为具有2个IP地址的A记录。然后,两个IP地址都将作为目标添加,每个IP地址的权重= 100和端口123。注意:权重用于单个条目,而不是整个条目! 它将解析为SRV记录,然后还将提取DNS记录中的端口和权重字段,并且会否决给定的端口123和weight = 100。
例外:当DNS记录的ttl = 0时,主机名将以指定的权重添加为单个目标。在对此目标的每个代理请求后,它将再次查询名称服务器。
默认情况下,环形均衡器将使用加权轮循方案。另一种选择是使用基于哈希的算法。哈希的输入可以是
none
,consumer
,ip
,header
,cookie
。当设置为none时,将使用加权轮循方案,并且将禁用哈希。
不同的哈希选项:
none
:不使用哈希,而应使用weighted-round-robin(默认)。
consumer
: 使用Consumer ID作为哈希输入。如果没有可用的Consumer ID(对于ldap这样的外部身份验证),则此选项将回退到凭证ID。
ip
:远程(原始)IP地址将用作输入。使用此配置时,请查看配置设置以确定实际IP real_ip_header
。
header
: 使用指定的标头(在hash_on_header或hash_fallback_header字段中)作为哈希的输入。
cookie
:使用具有指定路径(在hash_on_cookie_path字段中,默认为“ /”)的指定cookie名称(在hash_on_cookie字段中)作为哈希的输入。如果请求中不存在Cookie,则将由响应进行设置。因此,如果cookie是主要的哈希机制,则hash_fallback设置无效。
哈希算法基于“一致性哈希”(或“ ketama原理”),该算法可确保在通过更改目标(添加,删除,失败或更改权重)修改平衡器时,仅最小数量的哈希损失发生。这将使上游缓存命中最大化。
有关确切设置的更多信息,请参阅Admin API参考的upstream
部分。
环形平衡器旨在与单个节点以及群集中的节点一起使用。对于加权轮循算法,并没有太大的区别,但是使用基于哈希的算法时,重要的一点是所有节点都必须构建完全相同的环形均衡器,以确保它们均能正常工作。为此,必须以确定性方式构建平衡器。
[将服务更新到新的upstream]
使用环形平衡器,可以轻松地为服务编排蓝绿色部署。切换目标基础架构仅需要对服务执行PATCH请求即可更改其主机值。
设置“蓝色”环境,运行地址服务的版本1:
# create an upstream
$ curl -X POST http://kong:8001/upstreams \
--data "name=address.v1.service"
# add two targets to the upstream
# curl -X POST http://kong:8001/upstreams/【upstream名称】/targets
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
--data "target=192.168.34.15:80" \
--data "weight=100"
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
--data "target=192.168.34.16:80" \
--data "weight=50"
# create a Service targeting the Blue upstream
# curl -X POST http://kong:8001/services/ \
# --data "name=address-service" \
# --data "host=【upstream名称】" \
# --data "path=/address"
$ curl -X POST http://kong:8001/services/ \
--data "name=address-service" \
--data "host=address.v1.service" \
--data "path=/address"
# finally, add a Route as an entry-point into the Service
# curl -X POST http://kong:8001/services/【服务名】/routes/
$ curl -X POST http://kong:8001/services/address-service/routes/ \
--data "hosts[]=address.mydomain.com"
name: 服务名称
Host设置为address.mydomain.com的请求现在将由Kong代理到两个已定义的目标; 2/3的请求将发送到http://192.168.34.15:80/address(weight = 100),而1/3的请求将发送到http://192.168.34.16:80/address(weight = 50)。
在部署地址服务的版本2之前,请设置“绿色”环境:
# create a new Green upstream for address service v2
$ curl -X POST http://kong:8001/upstreams \
--data "name=address.v2.service"
# add targets to the upstream
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.17:80"
--data "weight=100"
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.18:80"
--data "weight=100"
要激活蓝色/绿色开关,我们现在只需要更新服务:
# Switch the Service from Blue to Green upstream, v1 -> v2
$ curl -X PATCH http://kong:8001/services/address-service \
--data "host=address.v2.service"
Host设置为address.mydomain.com的传入请求现在将由Kong代理到新目标; 1/2的请求将转到http://192.168.34.17:80/address(weight = 100),其他1/2的请求将转到http://192.168.34.18:80/address(weight = 100) )。
[将请求逐渐发布到其他节点]
使用环形平衡器,可以精确调整目标权重,从而实现平稳,受控的金丝雀释放。
使用一个非常简单的2目标示例:
# first target at 1000
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.17:80"
--data "weight=1000"
# second target at 0
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.18:80"
--data "weight=0"
通过重复请求,但每次更改权重,流量将缓慢
地路由到另一个目标。例如,将其设置为10%:
# first target at 900
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.17:80"
--data "weight=900"
# second target at 100
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.18:80"
--data "weight=100"
通过Kong Admin API进行的更改是动态的,将立即生效。无需重新加载或重启,也不会丢弃任何进行中的请求。
我们将详细介绍Kong的路由功能和内部工作原理,以介绍Kong的代理功能。Kong公开了几个接口,可以通过两个配置属性对其进行调整:
proxy_listen
:它定义了一个地址/端口列表,Kong将在该地址/端口上接受来自客户端的公共流量并将其代理到您的上游服务(默认为8000)。
admin_listen
:它也定义了一个地址和端口列表,但是应该限制这些地址和端口只能由管理员访问,因为它们会显示Kong的配置功能:Admin API(默认为8001)。
client
:指下游客户端向Kong的代理端口发出请求。
upstream service
:指位于Kong后面的您自己的API /服务,客户端请求将转发到该API /服务。
Service
:顾名思义,服务实体是您自己的每个上游服务的抽象。服务的示例将是数据转换微服务,计费API等。
Route
:这是指Kong Routes实体。路由是进入Kong的入口点,它定义了要匹配的请求的规则,并路由到给定的服务。
Plugin
:这是指Kong“插件”,它们是在代理生命周期中运行的业务逻辑。可以通过Admin API配置插件-全局(所有传入流量)或特定的路由和服务上。
从高级角度看,Kong在其配置的代理端口(默认为8000和8443)上监听HTTP通信。 Kong将根据您配置的路由评估任何传入的HTTP请求,并尝试找到匹配的请求。如果给定的请求符合特定路由的规则,则Kong将处理代理该请求。由于每个路由都链接到服务,因此Kong将运行您在路由及其相关服务上配置的插件,然后在上游代理请求。
您可以通过Kong的Admin API管理路线。路由的主机,路径和方法属性定义了用于匹配传入HTTP请求的规则。
如果Kong收到一个请求,它不能与任何已配置的路由匹配(或者如果未配置任何路由),它将以以下方式响应:
HTTP/1.1 404 Not Found
Content-Type: application/json
Server: kong/
{
"message": "no route and no Service found with those values"
}
向Kong添加服务是通过
向Admin API
发送HTTP请求来完成的:
curl -i -X POST http://localhost:8001/services/ \
-d 'name=foo-service' \
-d 'url=http://foo-service.com'
HTTP/1.1 201 Created
...
{
"connect_timeout": 60000,
"created_at": 1515537771,
"host": "foo-service.com",
"id": "d54da06c-d69f-4910-8896-915c63c270cd",
"name": "foo-service",
"path": "/",
"port": 80,
"protocol": "http",
"read_timeout": 60000,
"retries": 5,
"updated_at": 1515537771,
"write_timeout": 60000
}
该请求指示Kong注册一个名为“ foo-service”的服务,该服务指向http://foo-service.com(您的上游)。
注意:url参数是用于一次填充协议,主机,端口和路径属性的简写参数。
现在,为了通过Kong向该服务发送流量,我们需要指定一条路由,该路由充当Kong的入口点:
curl -i -X POST http://localhost:8001/routes/ \
-d 'hosts[]=example.com' \
-d 'paths[]=/foo' \
-d 'service.id=d54da06c-d69f-4910-8896-915c63c270cd'
HTTP/1.1 201 Created
...
{
"created_at": 1515539858,
"hosts": [
"example.com"
],
"id": "ee794195-6783-4056-a5cc-a7e0fde88c81",
"methods": null,
"paths": [
"/foo"
],
"preserve_host": false,
"priority": 0,
"protocols": [
"http",
"https"
],
"service": {
"id": "d54da06c-d69f-4910-8896-915c63c270cd"
},
"strip_path": true,
"updated_at": 1515539858
}
现在,我们已经配置了路由,以匹配与给定主机和路径匹配的传入请求,并将它们转发到我们配置的foo服务,从而将此流量代理到http://foo-service.com。
Kong是透明的代理,默认情况下会将请求原封不动地转发到您的上游服务,但各种头(如Connection,Date和HTTP规范要求的头)除外。
现在,让我们讨论Kong如何将请求与路由的已配置hosts,paths和方法属性(或字段)进行匹配。请注意,这三个字段都是可选的,但必须至少指定其中一个。
对于匹配路由的请求:
a.该请求必须包含所有已配置的字段
b.请求中字段的值必须至少与配置的值之一匹配(虽然字段配置接受一个或多个值,但请求仅需要将其中一个值视为匹配项)
让我们来看几个例子。考虑这样配置的路由:
{
"hosts": ["example.com", "foo-service.com"],
"paths": ["/foo", "/bar"],
"methods": ["GET"]
}
与此路由匹配的一些可能请求如下所示:
GET /foo HTTP/1.1
Host: example.com
GET /bar HTTP/1.1
Host: foo-service.com
GET /foo/hello/world HTTP/1.1
Host: example.com
所有这三个请求都满足“路由定义”中设置的所有条件。
但是,以下请求将不符合配置的条件:
GET / HTTP/1.1
Host: example.com
POST /foo HTTP/1.1
Host: example.com
GET /foo HTTP/1.1
Host: foo.com
所有这三个请求仅满足两个已配置的条件。第一个请求的路径与任何配置的路径都不匹配,第二个请求的HTTP方法和第三个请求的Host标头都不匹配。
现在,我们了解了hosts,paths和method如何协同工作,让我们分别探讨每个属性。
根据请求的Host header 路由请求是通过Kong代理流量的最直接方法,尤其是因为这是HTTP主机头的预期用法。通过Route实体的hosts字段,可以轻松进行Kong的操作。
hosts接受多个值,当通过Admin API指定它们时,这些值必须用逗号分隔:
主机接受多个值,可以直接在JSON有效负载中表示:
curl -i -X POST http://localhost:8001/routes/ \
-H 'Content-Type: application/json' \
-d '{"hosts":["example.com", "foo-service.com"]}'
HTTP/1.1 201 Created
...
但是,由于Admin API还支持表单形式的内容类型,因此您可以通过[]表示法指定一个数组:
curl -i -X POST http://localhost:8001/routes/ \
-d 'hosts[]=example.com' \
-d 'hosts[]=foo-service.com'
HTTP/1.1 201 Created
...
为了满足此路由的Hosts条件,现在来自客户端的任何传入请求都必须将其Host标头设置为以下之一:
Host: example.com 或者 Host: foo-service.com
使用通配符主机名
为了提供灵活性,Kong允许您在主机字段中使用通配符指定主机名。通配符主机名允许任何匹配的Host标头满足条件,从而匹配给定的Route。
通配符主机名在域的最左侧或最右侧标签上只能包含一个
*
号。例子:
一个完整的示例如下所示:
{
"hosts": ["*.example.com", "service.com"]
}
这将允许以下请求匹配此路由:
GET / HTTP/1.1
Host: an.example.com
或者
GET / HTTP/1.1
Host: service.com
preserve_host 属性
代理时,Kong的默认行为是将上游请求的Host头设置为API的upstream_url属性的主机名。这个参数接受一个boolean值,指示Kong不这样做。
例如,当preserve_host
属性未更改且路由配置如下:
{
"hosts": ["service.com"],
"upstream_url": "http://my-service-host.com",
"service": {
"id": "..."
}
}
客户端对Kong的可能请求可能是:
GET / HTTP/1.1
Host: service.com
Kong将从服务的host属性中提取Host标头值,并将发送以下上游请求:
GET / HTTP/1.1
Host:
但是,通过使用preserve_host = true显式配置路由:
{
"hosts": ["service.com"],
"preserve_host": true,
"service": {
"id": "..."
}
}
并假设来自客户端的相同请求:
GET / HTTP/1.1
Host: service.com
Kong将保留客户端请求上的主机,并改为发送以下上游请求:
GET / HTTP/1.1
Host: service.com
路由匹配的另一种方法是通过请求路径。为了满足此路由条件,客户请求的路径必须以paths属性值之一作为前缀。
例如,使用如下配置的路由:
{
"paths": ["/service", "/hello/world"]
}
以下请求将被匹配:
GET /service HTTP/1.1
Host: example.com
GET /service/resource?param=value HTTP/1.1
Host: example.com
GET /hello/world/resource HTTP/1.1
Host: anything.com
对于这些请求中的每一个,Kong都会检测到其URL路径是否以路由的paths值之一为前缀。默认情况下,Kong然后不会更改URL路径的情况下向上游代理请求。
使用路径前缀代理时,最长的路径将首先被进行匹配
。这使您可以定义具有两个路径的两个路由:/ service和/ service / resource,并确保前者不会“覆盖”后者。
在路径中使用正则表达式
Kong通过PCRE(与Perl兼容的正则表达式)支持路由路径字段的正则表达式模式匹配。您可以同时将路径作为前缀和正则表达式分配给路由。
例如,如果我们定义以下路由:
{
"paths": ["/users/\d+/profile", "/following"]
}
此路由将匹配以下请求:
GET /following HTTP/1.1
Host: ...
GET /users/123/profile HTTP/1.1
Host: ...
提供的正则表达式使用PCRE标志(PCRE_ANCHORED)进行评估,这意味着它们将被约束为在路径的第一个匹配点(根/字符)处匹配。
路由评估顺序
Kong按长度评估前缀路径:首先评估最长的前缀路径。但是,Kong将根据Routes的regex_priority属性(从最高优先级到最低优先级)评估正则表达式路径。这意味着要考虑以下路由:
[
{
"paths": ["/status/\d+"],
"regex_priority": 0
},
{
"paths": ["/version/\d+/status/\d+"],
"regex_priority": 6
},
{
"paths": ["/version"],
},
{
"paths": ["/version/any/"],
}
]
在这种情况下,Kong将按照以下定义的URI评估传入的请求:
/version/any/
/version
/version/\d+/status/\d+
/status/\d+
前缀路径始终在正则表达式路径之前进行评估
通常,请求也必须仍然与路由的hosts和method属性匹配,并且Kong将遍历您的路由,直到找到与大多数规则匹配的路由(请参阅[路由优先级][代理路由优先级])。
捕获组
路由支持捕获组,并且将从路径中提取匹配的组,以供插件使用。如果我们考虑以下正则表达式:
/version/(?\d+)/users/(?\S+)
以及以下请求路径:
/version/1/users/john
Kong将认为请求路径是匹配的,并且如果整个Route是匹配的(考虑host和method字段),则可以从ngx.ctx变量中的插件中获取提取的捕获组:
local router_matches = ngx.ctx.router_matches
-- router_matches.uri_captures is:
-- { "1", "john", version = "1", user = "john" }
方法字段允许根据请求的HTTP方法来匹配请求。它接受多个值。其默认值为空(HTTP方法不用于路由)。
以下路由允许通过GET和HEAD进行路由:
{
"methods": ["GET", "HEAD"],
"service": {
"id": "..."
}
}
这样的路由将与以下请求匹配:
GET / HTTP/1.1
Host: ...
HEAD /resource HTTP/1.1
Host: ...
但是它与POST或DELETE请求不匹配。在路由上配置插件时,这可以提供更大的粒度。例如,可以想象有两个路由指向同一服务:一个路由不限速和无需验证的GET请求,第二个路由需要身份验证和并且速率限制的POST请求(通过将身份验证和速率限制插件应用于此类请求)。
匹配优先级
路由可以根据其Host,Path和Method等字段定义匹配规则。为了使Kong将传入请求匹配到Route,必须满足所有现有字段属性。但是,Kong允许通过使用包含相同值的字段配置两个或多个路由来提供相当大的灵活性-发生这种情况时,Kong应用优先级规则。
规则是:在评估请求时,Kong首先会尝试将规则最多的路线
进行匹配。
例如,如果两个路由配置如下:
{
"hosts": ["example.com"],
"service": {
"id": "..."
}
},
{
"hosts": ["example.com"],
"methods": ["POST"],
"service": {
"id": "..."
}
}
第二条路由有一个“hosts”字段和一个“methods”字段,因此它将首先由Kong进行评估。这样,我们避免了针对第二个路线的第一个路线“遮蔽”调用。
因此,此请求将匹配第一个路线
GET / HTTP/1.1
Host: example.com
并且此请求将匹配第二个请求:
POST / HTTP/1.1
Host: example.com
按照此逻辑,如果要为第三条路线配置一个hosts字段,methods字段和uris字段,则Kong将首先对其进行评估。
下面,我们将介绍Kong在将HTTP请求与已注册的Route匹配到请求的实际转发之间的内部变化。
Kong实施了负载平衡功能,可以在上游服务实例池中分配代理请求。
Kong可通过“插件”进行扩展,这些插件将自己挂接到代理请求的请求/响应生命周期中。插件可以在您的环境中执行各种操作和/或根据代理请求进行转换。
可以将插件配置为全局(针对所有代理流量)或在特定的路由和服务上运行。在这两种情况下,您都必须通过Admin API创建插件配置。 匹配路由(及其关联的服务)后,Kong将运行与这些服务中的任何一个关联的插件。在Route上配置的插件先于在Service上配置的插件运行,否则,适用插件关联的通常规则。
这些已配置的插件将运行其访问阶段,可以在插件方面进行了解更多信息
Kong执行完所有必要的逻辑(包括插件)后,就可以将请求转发到上游服务。这是通过Nginx的ngx_http_proxy_module完成的。您可以通过以下服务属性为Kong和给定的上游之间的连接配置所需的超时:
connect_timeout
:以毫秒为单位定义与上游服务建立连接的超时时间。默认为60000write_timeout
:定义以毫秒为单位的两次连续写操作之间的超时,用于将请求传输到上游服务。默认为60000read_timeout
: 定义了两个连续的读取操作之间的超时(以毫秒为单位),该超时用于接收来自上游服务的请求。默认为60000Kong将通过HTTP / 1.1发送请求,并设置以下标头:
Host:
Connection: keep-alive
, 以允许重用上游连接。X-Real-IP:
, 其中$ remote_addr
是带有[ngx_http_core_module]提供的相同名称的变量。请注意,$ remote_addr
可能会被[ngx_http_realip_module]覆盖。X-Forwarded-For:
, 其中,是由[ngx_http_realip_module]提供的$ realip_remote_addr的内容,该名称附加到请求标头后。X-Forwarded-Proto:
, 其中“ ”是客户端使用的协议。如果$ realip_remote_addr是受信任的地址之一,则提供相同名称的请求标头。否则,将使用[ngx_http_core_module]提供的$ scheme变量的值。X-Forwarded-Host:
,其中,是客户端发送的主机名。如果$ realip_remote_addr是受信任的地址之一,则提供相同名称的请求标头。否则,将使用[ngx_http_core_module]提供的$ host变量的值。X-Forwarded-Port:
, 其中,是接受请求的服务器的端口。如果$ realip_remote_addr是受信任的地址之一,则提供相同名称的请求标头。否则,将使用[ngx_http_core_module]提供的$ server_port变量的值。其他所有请求标头均由Kong按原样转发。
使用WebSocket协议时,对此有一个例外。如果是这样,Kong将设置以下标头以允许在客户端和上游服务之间升级协议:
Connection: Upgrade
Upgrade: websocket
每当在代理过程中发生错误时,Kong都会使用底层的Nginx重试机制将请求传递到下一个上游。
这里有两个可配置的元素:
重试次数:可以使用retries属性针对每个服务进行配置。
造成异常的原因:Kong使用Nginx默认值,这意味着在与服务器建立连接,将请求传递给服务器或读取响应标头时发生错误或超时。
第二个选项基于Nginx的proxy_next_upstream指令。此选项不能通过Kong直接配置,但可以使用自定义Nginx配置添加。有关更多详细信息,请参见配置参考。
Kong从上游服务接收响应,然后以流方式将其发送回下游客户端。此时,Kong将执行添加到Route和/或Service的后续插件,这些插件在header_filter阶段实现钩子。一旦所有注册的插件的header_filter阶段执行完毕,Kong将添加以下头,并将完整的头集发送给客户端:
Via: kong/x.x.x
,其中x.x.x
是使用的Kong版本X-Kong-Proxy-Latency:
, 其中“latency”是指Kong从客户端接收请求到将请求发送到上游服务之间的时间(以毫秒为单位)。X-Kong-Upstream-Latency:
, 其中“latency”是指Kong等待上游服务响应的第一个字节的时间(以毫秒为单位)。将标头发送给客户端后,Kong将开始为实现body_filter钩子的Route和/或Service执行已注册的插件。由于Nginx的流式传输特性,该挂钩可能被多次调用。由此类body_filter挂钩成功处理的上游响应的每个块都将发送回客户端。您可以在《插件开发指南》中找到有关body_filter挂钩的更多信息。
作为实际的用例和Kong代理功能提供的灵活性的示例,让我们尝试实现“后备路由”,以便避免Kong用HTTP 404响应,即“未找到路由”,请求并将其代理到特殊的上游服务,或对其应用插件(例如,该插件可以在不代理请求的情况下以不同的状态代码或响应终止请求)。
这是此类后备路由的示例:
{
"paths": ["/"],
"service": {
"id": "..."
}
}
如您所料,由于所有URI都以根字符/为前缀,因此对Kong发出的任何HTTP请求实际上都将与此路由匹配。从[Request path][proxy-request-path]部分中我们知道,最长的URL路径首先由Kong评估,因此/路径最终将由Kong最终评估,并有效地提供了“后备”路线,仅作为最后的选择。然后,您可以将流量发送到特殊服务,或在此路线上应用您希望的任何插件。
Kong提供了一种基于每个连接动态提供SSL证书的方法。 SSL证书直接由核心处理,并且可以通过Admin API进行配置。通过TLS连接到Kong的客户端必须支持服务器名称指示扩展名才能使用此功能。
SSL证书由Kong Admin API中的两个资源处理:
以下是在给定路由上配置SSL证书的方法:首先,通过Admin API上传您的SSL证书和密钥:
curl -i -X POST http://localhost:8001/certificates \
-F "cert=@/path/to/cert.pem" \
-F "key=@/path/to/cert.key" \
-F "snis=ssl-example.com,other-ssl-example.com"
HTTP/1.1 201 Created
...
snis形式参数是一个糖参数,直接插入SNI并将上传的证书与其关联。
您现在必须在Kong中注册以下路由。为了方便起见,我们将仅使用Host标头将请求与此路由匹配:
curl -i -X POST http://localhost:8001/routes \
-d 'hosts=ssl-example.com,other-ssl-example.com' \
-d 'service.id=d54da06c-d69f-4910-8896-915c63c270cd'
HTTP/1.1 201 Created
...
现在,您可以期望Kong可以通过HTTPS来提供路由:
curl -i https://localhost:8443/ \
-H "Host: ssl-example.com"
HTTP/1.1 200 OK
...
建立连接并协商SSL握手时,如果客户端将ssl-example.com作为SNI扩展的一部分发送,则Kong将提供先前配置的cert.pem证书。
限制客户端协议(HTTP / HTTPS / TCP / TLS)
路由具有协议属性,用于限制它们应侦听的客户端协议。此属性接受一组值,可以是“ http”,“ https”,“ tcp”或“ tls”。带有http和https的路由将接受两种协议中的流量。
{
"hosts": ["..."],
"paths": ["..."],
"methods": ["..."],
"protocols": ["http", "https"],
"service": {
"id": "..."
}
}
路由默认为[“ http”,“ https”]。
但是,只有https的路由仅接受HTTPS上的流量。如果SSL终止先前是来自受信任的IP,它也将接受未加密的流量。当请求来自trusted_ips中已配置的IP之一,并且设置了X-Forwarded-Proto:https标头时,认为SSL终止有效。
{
"hosts": ["..."],
"paths": ["..."],
"methods": ["..."],
"protocols": ["https"],
"service": {
"id": "..."
}
}
如果上述路由与请求匹配,但该请求为纯文本且没有有效的SSL终止,Kong会做出以下响应:
HTTP/1.1 426 Upgrade Required
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: Upgrade
Upgrade: TLS/1.2, HTTP/1.1
Server: kong/x.y.z
{"message":"Please use HTTPS protocol"}
从Kong 1.0开始,可以通过在protocols属性中使用“ tcp”为原始TCP(不一定是HTTP)连接创建路由:
{
"hosts": ["..."],
"paths": ["..."],
"methods": ["..."],
"protocols": ["tcp"],
"service": {
"id": "..."
}
}
同样,我们可以创建接受原始TLS流量(不一定是HTTPS)且带有“ tls”值的路由:
{
"hosts": ["..."],
"paths": ["..."],
"methods": ["..."],
"protocols": ["tls"],
"service": {
"id": "..."
}
}
仅具有TLS的路由仅接受基于TLS的流量。也可以同时接受TCP和TLS:
{
"hosts": ["..."],
"paths": ["..."],
"methods": ["..."],
"protocols": ["tcp", "tls"],
"service": {
"id": "..."
}
}
代理WebSocket流量
由于底层的Nginx实现,Kong支持WebSocket流量。当您希望通过Kong在客户端和上游服务之间建立WebSocket连接时,必须建立WebSocket握手。这是通过HTTP升级机制完成的。这是您的客户向Kong发出的请求的样子:
GET / HTTP/1.1
Connection: Upgrade
Host: my-websocket-api.com
Upgrade: WebSocket
这将使Kong将Connection和Upgrade标头转发到您的上游服务,而不是由于标准HTTP代理的逐跳性质而将其关闭。
WebSocket和TLS
Kong将在其各自的http和https端口上接受ws和wss连接。要从客户端强制执行TLS连接,请将“路由”的“协议”属性设置为“仅https”。
在将服务设置为指向上游WebSocket服务时,应仔细选择想要在Kong和上游之间使用的协议。如果要使用TLS(wss),则必须使用“服务协议”属性中的https协议和正确的端口(通常为443)来定义上游WebSocket服务。若要不使用TLS(ws)进行连接,则应在协议中使用http协议和端口(通常为80)。
如果希望Kong终止SSL / TLS,则只能接受来自客户端的wss,但可以通过纯文本或ws代理到上游服务
kong插件主要分五大类,Authentication认证,Security安全,Traffic Control流量控制,Analytics & Monitoring分析&监控,Logging日志,其他还有请求报文处理类
该插件使用指定的状态码和消息终止传入的请求。这允许(临时)停止服务或路线上的流量,甚至阻止使用者。
配置全局的请求熔断插件
再次请求服务
通过用户名和密码保护,向您的API添加基本身份验证。插件将检查Proxy-Authorization和Authorization标头中的有效凭据(按此顺序)。key-auth与basic-auth同时配置,优先进行key-auth认证,认证通过后无需basic-auth认证。
添加一个basic auth插件,功能类似 key-auth:
再次请求kong,密码填写错误则出现以下错误:
使用授权码授予,客户端凭证,隐式授予或资源所有者密码凭证授予流添加OAuth 2.0身份验证层。此插件要求SSL插件(将only_https参数设置为true)已安装在API上,否则将导致安全漏洞。
验证包含HS256或RS256签名的JSON Web令牌的请求(如RFC 7519中所指定)。您的每个使用者都将具有JWT凭证(公用密钥和秘密密钥),这些凭证必须用于对其JWT进行签名。然后可以通过授权标头或请求的URI中传递令牌,并且如果令牌的签名经过验证,Kong会将请求代理到上游服务,否则将请求丢弃。 Kong还可以对RFC 7519的某些已注册声明(exp和nbf)进行验证。
创建一个jwt插件:
添加一个consumer:并配置jwt令牌,注意jwt的加密算法:
在 https://jwt.io/ 网站上生成一个jwt,发送请求时携带即可:
通过将IP地址列入白名单或黑名单来限制对API的访问。可以使用单个IP,多个IP或CIDR表示法中的范围(如10.10.10.0/24)。
给d4a服务加上ip限值的插件:
访问后则反馈:
通过使用任意ACL组名称将使用者添加到允许或拒绝列表中,来限制对服务或路由的访问。此插件需要身份验证插件(例如基本身份验证,密钥身份验证,OAuth 2.0和OpenID Connect)已在服务或路由上启用。请注意,allow和deny模型在用法上是互斥的,因为它们提供了互补的方法。即,不能同时使用允许和拒绝配置来配置ACL。带有allow的ACL提供了肯定的安全模型,其中允许配置的组访问资源,而所有其他组固有地被拒绝。相比之下,拒绝配置提供了否定的安全模型,其中明确拒绝了某些组对资源的访问(而所有其他组固有地被允许)
添加一个ACL插件:
配置的是Consumer组,作用于Consumer Group:
不是Group_02组的Consumer都将无法直接访问:
这插件是用来对发送请求的工具进行筛选的。这个其实主要是针对于机器人或者爬虫的,这些东西无脑的请求我们的服务,尤其那些不遵守规范的低级爬虫会给我们的服务带来很大的麻烦。
添加机器人插件,并配置黑名单,不允许谷歌设备访问:
用谷歌浏览器访问即会遇到以下拦截:
kong默认不支持跨域操作,需要添加Cors跨域插件以支持跨域操作
速率限制开发人员在给定的秒、分钟、小时、天、月或年内可以发出多少 HTTP 请求。如果 API 没有身份验证层,则使用客户端 IP 地址,否则如果配置了身份验证插件,则使用consumer。
给某个服务增加限速插件,这里给某个服务增加2个限速插件,一个插件针对一个consumer:
使用postman测试,这里配置的是每秒最多接收某个consumer的2个请求,每分钟最多3个请求:
配置的policy
模式 | 优点 | 描述 |
---|---|---|
cluster | 准确,不需要依赖其他组件 | 相对来说性能影响最大的,每个请求都会强制对底层的数据源进行读写操作 |
redis | 准确,性能影响比cluster模式小 | 需要额外安装redis,相比local模式性能影响大 |
local | 性能影响最小 | 不太准确,除非在Kong之前使用Hash一致性负载均衡器 |
local
策略,应该在 cluster
或 redis
策略中考量,推荐是先尝试使用 cluster
策略,如果性能急速下降,则切换成 redis
策略,需要注意的是,指标数据无法从原有数据源切换到redis,通常来说,短周期指标(如秒、分)不受影响,长周期指标(月)可能会有影响,所以切换数据源时需要小心local
策略,这需要多些尝试才能找到合适的值,比如用户希望配置限流每秒100个请求,总共有5个Kong节点,设置 local
策略,每秒30个请求,大致可以满足需求,如果觉得返回的失败过于频繁,可以适当增大阈值此插件允许您根据上游服务返回的自定义响应头限制开发人员可以发出的请求数。您可以根据需要随意设置任意数量的限速对象(或配额),并指示Kong按任意数量的单位增加或减少它们。每个自定义速率限制对象可以限制每秒,分钟,小时,天,月或年的入站请求。
如果底层 Service/Route(或已弃用的API实体)没有身份验证层,则将使用客户端IP地址,否则,如果已配置身份验证插件,则将使用Consumer。
这个插件是用来限制请求体的大小。当请求体超过阀值的时候,就会拒绝请求.允许的请求有效负载大小(以M字节为单位),默认值为 128M
添加插件:
访问服务:
这是一个缓存的插件,简单说就是第一次把一些内容(图片,内容)缓存到kong,后续的请求只需在Kong取数据,不用再向后端请求数据。
Kong通过 X-Cache-Status 头表示缓存行为的状态:
状态 | 描述 |
---|---|
Miss | 请求可以缓存,但是缓存中没有数据,数据从代理的服务中获取 |
Hit | 缓存命中,数据从缓存中获取 |
Refresh | 缓存命中,但因为缓存控制行为或者达到了cache_ttl设置的阈值 |
Bypass | 不能使用缓存 |
Kong可以在存储引擎中存储数据,存储时间超过 cache_ttl 或 Cache-Control 阈值所限定的时间,这使得Kong在资源过期后,还能维持一份资源的缓存副本,这样客户端在必要时可以使用 max-age 或 max-stale 头请求过期的数据
模式 | 描述 |
---|---|
memory | 内存模式,是一个lua_shared_dict,默认的kong_cache也被Kong的其他插件用于缓存其他数据,使用这种方式比较简单,但不推荐在大规模场景中使用,因为大量使用会对Kong数据缓存操作造成压力 |
redis | 支持redis和redis哨兵模式 |
kong会发送记录日志到一个http协议的端点中
添加httplog插件
再次请求Kong,端点会收到Json日志:
在到达上游服务器之前,立即转换客户端发送的请求。
remove –> rename –> replace –> add –> append
添加插件并将所有请求方式都转换为Post请求:
客户端发送的是Get请求,在经过kong后,转换成了Post请求:
请求响应到客户端之前可以转换上游服务的响应内容
创建插件,并在响应头中添加额外参数:
用postman测试后会收到以下结果: