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

相关推荐
cyt涛3 小时前
SpringCloudGateway — 网关路由
java·开发语言·网关·gateway·路由·断言·转发
风霜不见闲沉月12 小时前
kong网关的使用
junit·kong
代码魔法师Sunny2 天前
502 Bad Gateway 错误详解:从表现推测原因,逐步排查直至解决
gateway
RedCong3 天前
通过route访问Openshift上的HTTP request报错504 Gateway Time-out【已解决】
http·gateway·openshift
飞升不如收破烂~4 天前
包括 Nginx、Gateway、Nacos、Dubbo、Sentinel、RocketMQ 和 Seata 的调用链路描述:
nginx·gateway·dubbo
攒了一袋星辰9 天前
微服务网关gateway过滤器工厂与自定义过滤器
微服务·架构·gateway
猫猫不是喵喵.10 天前
Gateway 统一网关
java·网络·微服务·gateway
10 天前
504 Gateway Time-outopenresty
gateway
普通人zzz~10 天前
SpringCloud Gateway路由核心原理解析
java·spring cloud·gateway