Kong Gateway 指南

Kong Gateway 是一个轻量、快速、灵活的云原生API网关,其本质是一个运行在 Nginx中的Lua应用程序。

概述

Kong是Mashape开源的高性能高可用的API网关,可以水平扩展。它通过前置的负载均衡配置把请求分发到各个server,来应对大批量的网络请求。

由于Kong 的成功,Mashape 于 2017 年重组并更名为 Kong Inc.,将业务重点全面转移到 API 网关及相关技术上。如今,已成为 API 网关和服务网格的领先提供商。

主要功能

Kong提供了诸如HTTP基本认证、密钥认证、CORS、TCP、UDP、日志记录、请求限流、请求转发以及NGINX监控等基本功能。除此之外,它还可以通过插件(lua编写)来扩展已有的功能。其示意图如下:

Kong的核心功能如下:

API路由与反向代理

  • Kong Gateway作为API请求的入口,将请求从客户端路由到相应的服务。
  • 支持动态路由,可以根据URL、请求头、参数等条件来决定将请求转发到哪个服务端点。

认证与授权

  • 提供多种认证方式,如OAuth 2.0、JWT、Basic Auth、Key Auth等,确保API只能被经过认证的客户端访问。
  • 支持对用户进行授权控制,确保不同的用户只能访问他们被授权使用的API服务。

安全保护

  • Kong Gateway可以保护API免受常见攻击,如DDos、SQL注入、跨站请求伪造等。
  • 通过API限流,IP白名单/黑明单机制,防止恶意访问。

负载均衡

  • Kong可以在多个服务之间分配流量,确保负载均衡分布,并具备健康检查功能,可以动态监测服务节点的健康状况。
  • 支持协议的转换,比如从HTTP转换为HTTPS、从HTTP2转换为HTTP1.1。

限流与速度限制

  • Kong提供了高级的速率限制功能,你可以为每个API路由配置速率限制策略,限制客户端在一定时间内发送的请求数量,防止资源滥用。

监控与日志记录

  • Kong集成了多种监控工具,支持Prometheus、Datalog、Grafana等监控系统。
  • 提供了详细的日志记录功能,记录API的访问日志,包括响应时间,请求来源等信息。

插件架构

  • 支持通过插件来扩展功能,官方和社区提供了丰富的插件生态系统,覆盖认证、限流、缓存、日志记录等多方面。
  • 开发者还可以自定义插件来满足特定的业务需求。

多协议支持

  • 支持多种协议,如HTTP、HTTPS、gRPC、WebSocket等,允许在API层支持不同类型的服务通信。

服务网格集成

  • Kong Gateway与Kong Mesh无缝集成,支持在更复杂的微服务架构中管理服务间的通信、提供服务发现、负载均衡以及自动化的安全策略。

Kong gateway是与OpenResty一起分发的,而OpenRestry是一个扩展于lua-nginx-module的模块。OpenRestry主要解决Nginx无法动态扩展功能的问题,它将Lua JIT内嵌到了 Nginx 的内部,以支持通过 lua 语言对 Nginx 的能力进行方便地扩展开发。

版本和模式

Kong Gateway的部署方式有两种:使用Kong Konnect进行托管和自主管理。

Konnect提供了实用Kong Gateway的最简单的方法。全局控制平面由Kong托管在云上,你只需要管理各个数据平面节点即可, 但Konnect是收费方案。另一种部署方式就是自管理。

自管理

Kong gateway有两个不同的版本,开源版和企业版。开源版包括基本的API网关功能和开源插件,企业版附加了一些额外功能,包括RBAC和企业插件。企业版是收费的。

开源版和企业版的功能比较:

功能描述 开源版 企业版
API基础设施现代化
API网关
端对端自动化
Kong Ingress Controller
网关Mocking
admin界面管理器
流量管理和转换
基础流量管控插件
简单数据转换
gRPC转换
GraphQL
请求验证
jq转换
高级缓存
高级qps限制
安全管控
认证和基本授权(Bot检测,CORS,ACLs)
高级认证
基于角色的访问控制(RBAC)
高级授权(OPA)
证书管理
FIPS 140-2 支持
Signed Kong镜像
AI网关
多种大语言模型支持
AI流量控制
AI prompt安全
AI 可观测性
企业支持与服务

Kong Admin API 和 Kong Manager

Kong 网关的所有实体包括服务,路由,插件,消费者等等,都可以通过admin api来访问和配置。

Kong Manager是Kong 网关的图形化管理界面,实际上还是通过Admin API来调用接口进行管理。

Kubernetes

前文说过Kong Gateway是云原生网关,主要体现为它可以通过其ingress controller运行在k8s中。

工作原理

本节将从路由,负载均衡方面来介绍Kong Gateway的工作原理。

路由规则

Kong Gateway对外暴露多个接口,体现在如下的配置属性中:

  • proxy_listen:这个属性定义了一系列地址/端口,通过这些地址和端口来接受公开的HTTP, gRPC, WebSocket等请求,随后将这些请求代理到服务(默认8000端口)。
  • admin_listen: 这个属性也定义了一系列地址和端口,但这个属性指定的是Admin API 的地址,提供给管理员来对Kong进行配置(默认8001端口)。
  • stream_listen : 这个属性与proxy_listen作用类似,只不过它是适用于4层的代理(如TCP、TLS)。默认情况下是关闭的。

从顶层来看,Kong Gateway既可以监听7层的HTTP请求,也可以监听4层的请求(分别通过proxy_listenstream_listen指定)。然后根据配置的路由,将匹配的请求代理到对应的服务。路由条件是根据子系统(HTTP/HTTPS, gRPC/gRPCS, TCP/TLS)来区分的,主要有下列条件:

  • http: methods, hosts, headers, paths (对于https请求还有snis)
  • tcp: sources, destinations(对于tls还有snis)
  • grpc: hosts, headers, paths(对于grpcs还有snis)

假设现在我们创建了一个路由如下:

curl -i -X POST http://localhost:8001/routes/ \
 -d 'hosts[]=test.com' \
 -d 'hosts[]=foo-service.com' \
 -d 'headers.region=north'

该路由规定了hosts和header参数,只有满足条件的请求才能被路由接受。

域名通配符

设置路由属性时既可以指定完整的域名,也可以使用域名通配符。

路径正则表达式

设置路由属性时既可以指定完整的路径,也可以使用正则表达式来表示路径。当路径是正则表达式时,需要用~作为前缀,如

paths: ["~/foo/bar$"]

没有~前缀会被当成纯文本,如

"paths": ["/users/\d+/profile", "/following"]

如果一个请求的路径同时满足两个路径条件,那应该如何路由呢?这时候就需要考虑路径优先级了。优先级是根据regex_priority 来判断的。该值越大,则优先级越高。如果没有设置regex_priority , 则要根据其他规则来判断优先级:具体路径 > 前缀匹配 > 正则匹配。

捕获组

在Kong中,捕获组是正则表达式的一种功能,用于在路由中提取特定部分的请求路径。假设有一个正则表达式路由配置如下:

routes:
 - name: user-route
   paths:
     - ~^/users/(\d+)/details$
   service:
     id: user-service

这里的(\d+)就是一个捕获组。请求/users/123/details可以匹配到该路径,其中的123就是捕获组参数。后端服务可以通过动态路径来接受该参数,如Springboot中的@GetMapping("/users/{id}/details")

除此之外,还可以通过插件来对捕获组参数进行其他处理。例如request-transformer插件可以将参数作为请求头放进去:

plugins:
  - name: request-transformer
    service: user-service
    config:
      add:
        headers:
          - "X-User-ID: $1"

preserve_host

Kong Gateway在代理请求时,会默认将Kong中配置的服务域名作为Host发送给服务。如果将preserve_host 设为true,则会将原始客户端请求的host发送给服务。

举个例子,假设原始客户端请求的域名为service.com, 该请求将被路由到域名为api.internal.service的服务。如果不设置preserve_host , 则服务收到的请求的Host将是api.internal.service。如果将preserve_host 设为true,则服务收到的请求的Host为原始的service.com

负载均衡

Kong gateway提供了多种负载均衡方式,默认的是基于DNS的方式。

默认情况下,DNS负载均衡器是开启的,并且仅限于轮询的负载均衡的算法中。换句话说,默认使用DNS负载均衡器时,Kong不提供其他负载均衡算法,如最少连接,随机等。因此,尽管支持多种负载均衡策略,但是DNS负载均衡的上下文中,轮询是唯一可用的选择。

开启Kong Gateway之旅

安装方式有多种,既可以下载安装包,也可以通过docker来安装。本文以docker安装为例进行介绍。

安装kong gateway

首先创建一个docker网络:

docker network create kong-net

然后启动一个postgresql容器:

 docker run -d --name kong-database \
  --network=kong-net \
  -p 5432:5432 \
  -e "POSTGRES_USER=kong" \
  -e "POSTGRES_DB=kong" \
  -e "POSTGRES_PASSWORD=kongpass" \
  postgres:12

用户名和数据库名设置为kong是Kong gateway的默认值。密码则无要求。

接下来准备初始化数据

docker run --rm --network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_PASSWORD=kongpass" \
kong:3.8.0 kong miigrations bootstrap

如果docker的镜像仓库访问不了,可以用docker pull public.ecr.aws/docker/library/kong:3.8.0从aws的免费仓库拉取, 然后重新打标签: docker tag public.ecr.aws/docker/library/kong:3.8.0 kong:3.8.0

这里KONG_DATABASE指定数据库类型,KONG_PG_HOST就是上一步创建的postgres容器,密码也是上一步的密码。kong migrations bootstrap 是数据初始化命令。--rm参数表示这是一个一次性任务,完成之后即删除容器。

启动容器:

docker run -d --name kong-gateway \
 --network=kong-net \
 -e "KONG_DATABASE=postgres" \
 -e "KONG_PG_HOST=kong-database" \
 -e "KONG_PG_USER=kong" \
 -e "KONG_PG_PASSWORD=kongpass" \
 -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" \
 -e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
 -p 8000:8000 \
 -p 8443:8443 \
 -p 127.0.0.1:8001:8001 \
 -p 127.0.0.1:8002:8002 \
 -p 127.0.0.1:8444:8444 \
 kong:3.8.0

KONG_ADMIN_LISTEN指定admin api监听的端口

验证是否安装成功:

 curl -i -X GET --url http://localhost:8001/services

如果成功安装,你可以看到返回200状态码。通过http://localhost:8002查看管理界面。

Kong gateway还可以以无数据库模式启动,此时通过配置文件来加载配置。这种方式虽然更简单,但只适合独立的网关实例或小规模环境。这里就不介绍了。

服务与路由

服务和路由是Kong的流量管理策略中的两个重要组件。

服务管理

创建服务

先通过Admin API创建一个服务,该服务将映射到https://httpbin.konghq.com

curl -i -s -X POST http://localhost:8001/services \
  --data name=example_service \
  --data url='https://httpbin.konghq.com'

查看服务

查看服务配置:

curl -X GET http://localhost:8001/services/example_service

输出如下:

{
  "host": "httpbin.konghq.com",
  "name": "example_service",
  "enabled": true,
  ...
}

更新服务

将retries参数从5改成6:

curl --request PATCH --url localhost:8001/services/example_service --data retries=6

列出服务

curl -X GET http://localhost:8001/services

路由管理

创建路由

创建关联服务的路由,请求该路由地址的流量将被转发到关联的服务:

curl -i -X POST http://localhost:8001/services/example_service/routes --data 'paths[]=/mock'  --data name=example_route

查看路由配置

路由查看地址:

/services/{service name or id}/routes/{route name or id}
/routes/{route name or id}

以前者为例:

curl -X GET http://localhost:8001/services/example_service/routes/example_route

更新路由

curl --request PATCH --url localhost:8001/services/example_service/routes/example_route --data tags="tutorial"

列出路由

curl http://localhost:8001/routes

除了Admin API之外,也可以通过Manager查看和管理。

请求代理

根据上面创建的路由可知,当向/mock发送请求时,将会被路由到example_service

发送请求如下:

curl -X GET http://localhost:8000/mock/anything

可以看到服务返回的响应结果。

请求速率限制

Rps限制可以针对全局,可以针对服务和路由,还可以针对消费者。速率限制插件是默认安装的。

全局限制

curl -i -X POST http://localhost:8001/plugins --data name=rate-limiting --data config.minute=5 --data config.policy=local

local表示当个kong节点计数,cluster表示多节点部署时,计数共享。

我们执行一个命令来发送6条请求,看看是否会触发rate limit:

for _ in {1..6}; do curl -s -i localhost:8000/mock/anything; echo; sleep 1; done

前面5次请求正常输出,最后一条请求输出:

{
  "message":"API rate limit exceeded",
  "request_id":"c9e6a941e6217c3011b8b86146081a72"
}

说明触发了速率限制。

服务限制

服务级别的rate limiting需要请求具体服务的plugins

curl -X POST http://localhost:8001/services/example_service/plugins --data "name=rate-limiting" --data config.minute=5 --data config.policy=local

路由限制

curl -X POST http://localhost:8001/routes/example_route/plugins --data "name=rate-limiting" --data config.minute=5 --data config.policy=local

消费者限制

所谓消费者即服务的调用方。

先创建一个消费者:

curl -X POST http://localhost:8001/consumers/ --data username=jsmith

再开启消费速率限制

curl -X POST http://localhost:8001/plugins --data "name=rate-limiting" --data "consumer.username=jsmith" --data "config.second=5"

在生产环境中,消费者通常是基于API密钥或者JWT等方式来识别的。

速率限制只是Kong Gateway的其中一个功能,其他的,如缓存、认证、负载均衡等,都是以类似的方式来进行配置的,并且这些基础功能,都可以通过Manger来操作。这里就不一一列举了,具体可以参见网上资料。

生产实践

部署方式

Kong Gateway一共有四种部署方式,分别是:

  • Konnect(托管控制平面)
  • Hybrid
  • Traditional(database)
  • DB-less and declarative

不通的模式都有各自的特点,需要在生产环境中需要根据具体情况来考虑。

Konnect

Konnect是一种混合模式的部署,它提供了使用Kong Gateway的最简单方法。全局控制平面由Kong托管在云上,而你可以在自己的网络环境中管理各个数据平面节点。

Hybrid

在混合模式中,Kong Gateway节点分成两种角色,一种是控制平面(CP),一种是数据平面(DP)。控制平面负责管理配置信息并提供Admin API接口。数据平面负责请求路由。控制平面访问数据库,数据平面连接到控制平面节点,获取最新的配置信息。如下图所示:


Traditional (database)

传统模式中,Kong Gateway在数据库中存储配置信息。所有的kong gateway节点均连接到数据库,每个节点自己管理配置信息。示意图如下:

与混合模式相比,传统模式没有控制平面。因此每个节点既有控制平面的作用,也有数据平面的作用。这意味着如果您的任何节点被泄露,则整个运行中的网关配置也会被泄露。相比之下,混合模式具有不同的CP和DP节点,减小了被攻击的暴露面。

DB-less and declarative mode

无数据库模式通过配置文件来读取配置信息,并将配置信息保存在节点的内存中。

此种模式下,Kong Gateway不能完全发挥所有功能。

Admin API 安全保护

通过Admin API 可以对Kong Gateway进行完全的控制,所以对Admin API 的访问控制就尤为显得重要。

网络层的访问限制

默认情况下,Kong只能通过本地接口访问,其admin_listen的设置如下:

admin_listen = 127.0.0.1:8001

这个值是可以修改的,但不要扩大暴露面,比如0.0.0.0:8081是不行的。

L3/4 的访问控制

如果必须将Admin API 的接口暴露给非本地接口,那也要从网络层面上做尽可能多的访问限制。假设现在有这样一个场景:Admin API 暴露在某个私有网络中,但只能给某个IP范围段的部分地址访问。在这种情况下,就必须配置基于主机的防火墙(例如 iptables)。

grep admin_listen /etc/kong/kong.conf
admin_listen 10.10.10.3:8001

# 显示设置只允许来自自身8001端口的TCP包
iptables -A INPUT -s 10.10.10.3 -m tcp -p tcp --dport 8001 -j ACCEPT

# 显示设置只允许来自以下地址的8001端口的TCP包
iptables -A INPUT -s 10.10.10.4 -m tcp -p tcp --dport 8001 -j ACCEPT
iptables -A INPUT -s 10.10.10.5 -m tcp -p tcp --dport 8001 -j ACCEPT

# 其他地址发送过来的包全部丢弃
iptables -A INPUT -m tcp -p tcp --dport 8001 -j DROP

灰度部署

蓝绿部署

"Blue" Environment

先创建upstream:

curl -X POST http://localhost:8001/upstreams --data "name=address.v1.service"

添加两个目标地址:

curl -X POST http://localhost:8001/upstreams/address.v1.service/targets --data "target=192.168.34.15:80" --data "weight=100"
curl -X POST http://localhost:8001/upstreams/address.v1.service/targets --data "target=192.168.34.16:80" --data "weight=50"

创建一个服务,将其关联到上面的upstream:

curl -X POST http://localhost:8001/services/ --data "name=address-service" --data "host=address.v1.service" --data "path=/address"

添加服务路由:

curl -X POST http://localhost:8001/services/address-service/routes/ --data "hosts[]=address.mydomain.com"

当请求的域名符合路由规则时,该请求将被转发到service对应的upstream上,并根据服务器实例的权重值进行负载均衡。三分之二的请求被转发到http://192.168.34.15:80/address,三分之一的请求转发到另一台机器。

"Green" Environment

同样地再创建一个"Green"环境。

# 创建upstream
curl -X POST http://localhost:8001/upstreams --data "name=address.v2.service"

# 添加targets
curl -X POST http://localhost:8001/upstreams/address.v2.service/targets --data "target=192.168.34.17:80" --data "weight=100"
curl -X POST http://localhost:8001/upstreams/address.v2.service/targets --data "target=192.168.34.18:80" --data "weight=100"

服务和路由已经有了,接下来只需要修改一下服务的域名:

curl -X PATCH http://localhost:8001/services/address-service --data "host=address.v2.service"

此时再次发送请求curl http://address.mydomain.com/address时,请求将被转发到服务对应的域名address.v2.service关联的upstream上,并根据每台服务器的权重来做负载均衡。

通过Admin API 更改的Kong Gateway 配置实时生效,无需重载或者重启,当前正在处理中的请求也不会受到影响。

金丝雀部署

在上文的基础上,我们更新域名address.v2.service关联的targets的权重值。假设192.168.34.17:80为原机器,192.168.34.18:80为新机器。

# 获取upstreams
curl http://localhost:8001/upstreams/

# 获取指定upstream关联的targets
curl http://localhost:8001/upstreams/{upstream_id}/targets

# 修改target(原机器ip)的权重
curl -X PATCH http://localhost:8001/upstreams/{upstream_id}/targets/{target_id} --data "weight=1000"
# 修改target(新机器ip)权重
curl -X PATCH http://localhost:8001/upstreams/{upstream_id}/targets/{target_id} --data "weight=0"

所有admin api的操作均可在manager console上操作

修改完权重后,因为新服务实例的权重为0,因此所有请求将被全部转发原服务实例上。接下来修改两个服务实例的权重值,其中一个降低为900,另一个增长为100。修改完成后10%的请求将被转发到新服务实例上。以此类推,直到所有请求全部被转发到新机器上。

区别

蓝绿部署和金丝雀部署都是用于发布和部署更新的策略,但两者的方式和目标有所不同。

蓝绿部署是一种发布策略,将应用的两个完整版本(蓝色和绿色)分别部署在独立的环境中。通常,"蓝色"表示旧版本,"绿色"表示新版本。其流程是:蓝色环境承载生产流量,新版本在绿色环境中完全准备好后,流量从蓝色环境切换到绿色环境,使所有用户瞬间访问到新版本,如果需要回滚,可以快速切换回蓝色环境。

金丝雀部署是一种渐进式发布策略,将新版本逐步推送给一小部分用户,进行验证后再逐步增加流量直到全部用户使用新版本。其流程是:新版本部署到少量节点,并分配一部分流量,根据监控的指标和反馈验证新版本的稳定性,逐渐增加流量。如果发生问题,可以快速将流量恢复到旧版本节点。

可以看到,蓝绿部署需要两套完整的生产环境,适用于发布较大更新的场景。而金丝雀部署需要复杂的流量控制和监控机制,适合频繁的小版本更新。

K8S中金丝雀部署

在k8s环境中,我们无法直接指定具体的服务实例,因此不能将流量控制到服务器级别,但可以控制到service级别。

假设现在有两个服务如下:

apiVersion: v1
kind: Service
metadata:
  name: app-v1
spec:
  selector:
    app: your-app
    version: v1
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: app-v2
spec:
  selector:
    app: your-app
    version: v2
  ports:
  - port: 80
    targetPort: 80

创建一个ingress用来进行流量路由:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    konghq.com/ingress.class: "kong"
    konghq.com/plugins: "traffic-split"
spec:
  rules:
  - host: your-host
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-v1
            port:
              number: 80

再创建一个KongIngress来分配流量:

apiVersion: configuration.konghq.com/v1
kind: KongIngress
metadata:
  name: app-ingress-config
  namespace: your-namespace
proxy:
  protocols:
  - http
  - https
  route:
    splits:
    - weight: 90
      service:
        name: app-v1
        port: 80
    - weight: 10
      service:
        name: app-v2
        port: 80

此时10%的流量将被转发到app-v2服务,继续更新KongIngress配置,逐步将流量导入到v2版本的服务,直至全部切到v2为止。这个过程即k8s下的canary部署。

参考资料

[1]. https://docs.konghq.com/gateway/latest/

[2]. https://zhuanlan.zhihu.com/p/698162981

相关推荐
wclass-zhengge1 天前
SpringCloud篇(服务网关 - GateWay)
spring boot·spring cloud·gateway
H愚公移山H1 天前
Spring Cloud Alibaba [Gateway]网关。
java·gateway·springcloud
醇氧1 天前
【spring 】Spring Cloud Gateway 的Filter学习
学习·spring·gateway
蚰蜒螟1 天前
Spring gateway 路由 配置在数据库
数据库·spring·gateway
因我你好久不见2 天前
解决绿盟漏洞扫描 gateway、nacos、springboot tomcat检测到目标主机可能存在缓慢的HTTP拒绝服务攻击问题
spring boot·http·gateway
moxiaoran57535 天前
搭建Spring gateway网关微服务
spring·微服务·gateway
飞天大拖把5 天前
Zuul和GateWay
gateway
.生产的驴7 天前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
菜菜-plus8 天前
分布式,微服务,SpringCloudAlibaba,nacos,gateway,openFeign
java·分布式·微服务·nacos·gateway·springcloud·openfeign
七月在野,八月在宇,九月在户8 天前
前端--> nginx-->gateway产生的跨域问题分析
前端·nginx·gateway