原文:https://cloud.tencent.com/developer/article/1477672

1. 什么是Kong?

目前互联网后台架构一般是采用微服务,或者类似微服务的形式,应用的请求通常需要访问多个后台系统。如果让每一个后台系统都实现鉴权、限流、负载均衡、审计等基础功能是不合适的,通用的做法是把这些功能抽离出来放到网关层。Kong是目前最流行的网关平台。

Kong - The World’s Most Popular Open Source Microservice API Gateway and Platform.

  • Nginx = Http Server + Reversed Proxy + Load Balancer
  • Openresty = Nginx + Lua-nginx-module
  • Kong = Openresty + Customized Framework

2. 概念介绍

kong的API使用Restful风格,每个对象都是一个Object,其中最重要的两个对象是:

  • Service 代表一个后台服务
  • Route 是一条规则,告诉kong怎么把网关收到的请求发送到某个特定的后台服务,一个Service可以有多个Routes。

例如:主流http server都能根据不同的主机名,端口号,路径等信息把请求转发给不同的后台服务

kong 默认绑定4个端口:

  • :8000 用来接受用户的HTTP请求,并转发到后台系统
  • :8443 用来接受用户的HTTPS请求,并转发到后台系统
  • :8001 通过HTTP协议提供管理功能的API (Admin API)
  • :8444 通过HTTPS协议提供管理功能的API

这些端口可以在**/etc/kong/kong.conf**中修改,:8000 和 :8443 默认绑定0.0.0.0;:8001 和 :8444 默认绑定 127.0.0.1

当然我们可以把Admin API作为一个服务通过kong的网关暴露出去,请参考[Kong API Loopback] (https://docs.konghq.com/1.1.x/secure-admin-api/#kong-api-loopback)不过要注意安全。

3. Cheat Sheet

3.1 文件路径

  • 配置文件:/etc/kong/kong.conf
  • 日志文件:/usr/local/kong/logs

3.2 基本启动停止

kong start [ -c /etc/kong/kong.conf ]
kong reload
kong stop

3.3 命令行参考

前面说过,Kong采用了Restful风格的API,所有对象都通过HTTP的不同方法去操作。比如 curl -X GET http://localhost:8111/{object}

其中/{object}是某个对象的在Admin API中的路径,下面以services为例:

方法

作用

url 路径

POST

添加一个service

/services

GET

查询service列表

/services

GET

查询某个特定service

/services/{name or id}

PATCH

更新service配置

/services/{name or id}

PUT

添加或更新serivce配置

/services/{name or id}

DELETE

删除service

/services/{name or id}

4. 实战

下面的内容以一个nodejs的server [node-demo] (https://github.com/4179e1/node-demo)为例,介绍kong的使用。

下文的配置中禁用了HTTPS,:8081 用来接受HTTP请求,:8111提供Admin API

4.1 Node Demo

这个服务默认绑定8080端口,提供若干API

$ curl http://127.0.0.1:8080/api?pretty
{
"api": [
{
"method": "GET, POST",
"desc": "GET demo",
"url": "http://127.0.0.1:8080/api/demo/headers"
},
{
"method": "GET",
"desc": "GET demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
},
{
"method": "PUT",
"desc": "PUT demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
},
{
"method": "DELETE",
"desc": "DELETE demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
},
{
"method": "POST",
"desc": "POST demo",
"url": "http://127.0.0.1:8080/api/demo/demo"
}
]
}

其中/api/api/demo/headers会详细的打印请求参数

$ curl http://127.0.0.1:8080/api/demo/headers?pretty
{
"result": true,
"hostname": "localhost",
"headers": {
"host": "127.0.0.1:8080",
"user-agent": "curl/7.58.0",
"accept": "*/*"
},
"query": {
"pretty": ""
},
"body": {}
}

在下面的例子中,我们尝试把网关的/api转发到 nodedemo 的 /api,也就是访问网关地址http://127.0.0.1/api的时候,把对应的请求转发到nodedemo 的http://127.0.0.1:8080/api

4.2 添加service 和 route

下面这条命令添加了nodedemo.service这个service,凡是访问这个服务的请求都会被反向代理到http://127.0.0.1:8080这个地址

curl -i -X POST -H 'Content-Type: application/json' --url http://localhost:8111/services/ --data '{"name": "nodedemo.service", "url" : "http://127.0.0.1:8080"}'
  • 我习惯给object的名字带上类型(.service),这样交叉引用的时候不容易混淆
  • kong的Admin API支持urlencode参数,但我更习惯使用json

定义service之后,我们还得声明路由,把哪些特定的请求发送到这个service,这里我们配置的规则是“所有以/api开始的路径都转发给nodedemo.servce”:

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service/routes -d '{"name": "nodedemo.route", "paths" : ["/api"], "strip_path": false }'

其中strip_path: false表示请求后端服务时,同时还会把/api 这个前缀带上,更多细节见后文[Tips]部分。

4.3 测试

可以看到这个请求已经转发给nodedemo,并且网关自动添加了几个headres

# curl http://127.0.0.1/api/demo/headers?pretty
{
"result": true,
"hostname": "localhost",
"headers": {
"host": "127.0.0.1:8080",
"connection": "keep-alive",
"x-forwarded-for": "127.0.0.1",
"x-forwarded-proto": "http",
"x-forwarded-host": "127.0.0.1",
"x-forwarded-port": "80",
"x-real-ip": "127.0.0.1",
"user-agent": "curl/7.58.0",
"accept": "*/*",
"proxy-connection": "Keep-Alive"
},
"query": {
"pretty": ""
},
"body": {}
}

4.4 负载均衡

我们知道,nginx是能通过upstream支持多个后端server的负载均衡的,kong中要怎么做呢?

4.4.1 创建一个upstream对象

下面创建一个叫nodedemo.upstream的对象

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams -d '{"name":"nodedemo.upstream"}'

4.4.2 为upstream添加target

刚才创建的upstream对象是空的,还需要给他添加实际的后端server地址,kong中称为target,实际就是host:port的组合,其中weight用于round robin的加权平均。

为了看到负载均衡的效果,我们可以再起一个nodedmo运行在8088端口:PORT=8088 node server.js

下面把两个nodedemo添加到upstream中

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams/nodedemo.upstream/targets -d '{"target": "127.0.0.1:8080", "weight": 100}'
curl -X POST -H 'Content-Type: application/json' http://localhost:8111/upstreams/nodedemo.upstream/targets -d '{"target": "127.0.0.1:8088", "weight": 100}'

4.4.3 把service的url指向upstream

我们创建nodedemo.service的时候,url是直接指向http://127.0.0.1:8080的后端地址,此时把它改为upstream即可

curl -X PATCH -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service --data '{ "url": "http://nodedemo.upstream" }'

访问http://localhost/api/demo/headers 数次可以发现轮询了不同的后端。

4.5 插件

kong 自带了很多插件,我们让我们给nodedemo启用一个试试,request-transformer可以修改请求内容,这里我们给请求添加一个header Hello: world

curl -X POST -H 'Content-Type: application/json' http://localhost:8111/services/nodedemo.service/plugins -d '{"name":"request-transformer","config":{"add":{"headers":["Hello: world"]}}}'

再次请求api,可以发现这个header已经有了

# curl http://127.0.0.1/api/demo/headers?pretty
{
"result": true,
"hostname": "localhost",
"headers": {
"host": "127.0.0.1:8080",
"connection": "keep-alive",
"x-forwarded-for": "127.0.0.1",
"x-forwarded-proto": "http",
"x-forwarded-host": "127.0.0.1",
"x-forwarded-port": "80",
"x-real-ip": "127.0.0.1",
"user-agent": "curl/7.58.0",
"accept": "*/*",
"hello": "world" # <==== here
},
"query": {
"pretty": ""
},
"body": {}
}

plugins也可以是全局的,Admin API调用的时候地址改为 /plugins 即可,如果同一个插件在全局和某个单独的service都配置了,以service的为准。

5. Tips

5.1 静态文件要放哪里?

Q:呃……我想用kong来搭建一个前后端分离的网站,我的静态文件要放哪?

A: 尽管kong是基于nginx的,但是作为一个API 网关,它对静态文件的支持不是特别友好,有两种方式:

  1. 修改kong的配置模版,详情请参考[Serving both a website and your APIs from Kong] (https://docs.konghq.com/1.2.x/configuration/)
  2. 后端再起一个http server,把静态文件都放到这个http server中,并配置对应的路由。

5.2 好多对象都有跟path相关的参数,它们都是干什么的?

我用到的path有几个

  1. route中的paths参数,表示符合这些请求路径要发到route对应的service中
  2. route中的strip_path 参数,决定kong转发给后端的时候是否保留源请求用于路由匹配的路径
  3. service中的path参数,上面的例子没有配置,默认为null,kong转发请求时会把这个作为前缀加上

假设网关以/api为路由把请求转发给nodedemo(即route.paths = ['/api']),它们的组合关系如下:

strip_path

service.path

请求地址

网关实际访问后端地址

true

null 或者 /

http://127.0.0.1/api/demo

http://127.0.0.1:8080/demo

true

/test

http://127.0.0.1/api/demo

http://127.0.0.1:8080/test/demo

false

null 或者 /

http://127.0.0.1/api/demo

http://127.0.0.1:8080/api/demo

false

/test

http://127.0.0.1/api/demo

http://127.0.0.1:8080/test/api/demo

以最后一行为例,相当于访问 http://127.0.0.1/api/demo 时,实际访问的是/test/api/demo,也就是把 service.path (/test)跟实际请求的路径(/api/demo)拼接起来发给后端。

5.3 wildcard域名匹配

kong同样可以基于域名把请求转发到不同的服务,比如a.example.com转发到服务A,b.example.com转发到服务B;同时kong还支持通配域名,比如*.example.com转发到服务C。

我遇到一个坑是这样的:在已经配置a.example.comb.example.com这两个路由转发的前提下,我遇到的一个坑是这样的:我还有一个服务C,需要让*.example.com/c/ (包括a.example.com/cb.example.com/c)都转发到服务C。略一思索,我给服务C的Hosts添加了一个通配地址*.example.com,然而这并不work,a.example.com/c/b.example.com/c/并不能匹配到这个路由,但是c.example.com/c/等没有配置过路由的域名可以匹配。

最终解决方案是,需要同时添加a.example.comb.example.com*.example.com三个域名,类似这样:

{
"hosts": [
"a.example.com",
"b.example.com",
"*.example.com"
],
}

我估计这不是kong的问题,大概nginx本来就是这样的,未验证

5.4 Log rotate

网关访问量太大,日志要把硬盘写满了怎么办?上logroate,添加一个配置文件/etc/logroate.d/kong,内容如下:

/usr/local/kong/logs/*.log {
daily
missingok
rotate 3
compress
delaycompress
notifempty
create 640 root root
sharedscripts
postrotate
[ -f /usr/local/kong/pids/nginx.pid ] && kill -USR1 `cat /usr/local/kong/pids/nginx.pid`
endscript
}

这个文件是从CentOS自带的/etc/logrotate.d/nginx 修改而来的,保留最近3天的日志(rotate 3)

5.5 kong有GUI吗?

kong 社区版没有GUI组件,但是有一个第三方的[konga] (https://github.com/pantsel/konga)。

6. Reference

  • [微服务与API网关(上): 为什么需要API网关?] (http://blog.didispace.com/hzf-ms-apigateway-1/)
  • [ 微服务与API 网关(下)- Kong能为我们做什么?] (http://blog.didispace.com/hzf-ms-apigateway-2/)
  • [Documentation for Kong] (https://docs.konghq.com/)

5-API 网关 kong 实战的更多相关文章

  1. API 网关 Kong

    什么是 API 网关? 所谓网关,主要作用就是连接两个不同网络的设备,而今天所讲的 API 网关是指承接和分发客户端所有请求的网关层. 为什么需要网关层?最初是单体服务时,客户端发起的所有请求都可以直 ...

  2. Api网关Kong集成Consul做服务发现及在Asp.Net Core中的使用

    写在前面   Api网关我们之前是用 .netcore写的 Ocelot的,使用后并没有完全达到我们的预期,花了些时间了解后觉得kong可能是个更合适的选择. 简单说下kong对比ocelot打动我的 ...

  3. API网关Kong系列(一)初识

    最近工作需要,加上国内Kong的文章相对缺乏(搜来搜去就那么两篇文章),而且官方文档在某些demo上也有一些过时的地方,遂提笔记录下这些,希望能有帮助. 先随大流介绍下KONG(主要参考官网): 官方 ...

  4. API网关--Kong的实践

    1. 什么是Kong 目前互联网后台架构一般是采用微服务,或者类似微服务的形式,应用的请求通常需要访问多个后台系统.如果让每一个后台系统都实现鉴权.限流.负载均衡.审计等基础功能是不合适的,通用的做法 ...

  5. API网关Kong系列(三)添加服务

    进入之前部署好的kong-ui,默认第一次登陆需要配置kong服务的地址 进入API菜单,点击+号 按照要求填入相关信息 至此完成,可以使用诸如 https://your.domain.com:208 ...

  6. API网关Kong系列(二)部署

    部署环境: [OS] centos 6.8(如果是centos6.5,请自行先升级到6.8,否则不支持docker) [Docker] Client version: 1.7.1 Client API ...

  7. API网关Kong部署和使用文档

    KONG安装使用说明 系统版本:ubuntu14 1.下载安装包 $ wget https://github.com/Mashape/kong/releases/download/0.8.3/kong ...

  8. API网关——Kong实践分享

    概述 01 什么是Kong Kong是一个在Nginx中运行的Lua应用程序,可以通过lua-nginx模块实现,Kong不是用这个模块编译Nginx,而是与OpenRestry一起发布,OpenRe ...

  9. API网关Kong系列(四)认证配置

    目前根据业务需要先介绍2种认证插件:Key Authentication 及 HMAC-SHA1 认证  Key Authentication 向API添加密钥身份验证(也称为API密钥). 然后,消 ...

随机推荐

  1. C++入门到理解阶段二基础篇(8)——C++指针

    1.什么是指针? 为了更加清楚的了解什么是指针?我们首先看下变量和内存的关系,当我们定义了int a=10之后.相当于在内存之中找了块4个字节大小的空间,并且存储10,要想操作这块空间,就通过a这个变 ...

  2. redis 事务(悲观锁和乐观锁)

    MULTI 开启事务,后续的命令会被加入到同一个事务中 事务中的操作会发送给客服端,但是不会立即执行,而是将操作放到了该事务对应的一个队列中,服务端返回QUEQUD EXEC 执行EXEC后,事务中的 ...

  3. Difference between JDK, JRE and JVM

    With Java programming language, the three terms i.e. JDK, JRE and JVM will always be there to unders ...

  4. [算法]PriorityQueue的应用

    1. 数据流中的第K大元素 题目 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组 ...

  5. Spring Boot 2.2.2.RELEASE 版本中文参考文档

    写在前面 在我初次接触MongoDB的时候,是为了做一个监控系统和日志分析系统.当时在用Java操作MongoDB数据里的数据的时候,都是在网上查找Demo示例然后完成的功能,相信大家也同样的体会,网 ...

  6. 首个企业架构TOGAF角色扮演案例培训的诞生

    BangEA企业架构系列在不同机构做了不少TOGAF认证课,自己都觉得有点枯燥了,我在想我们IT帮2020年第一期的认证课是不是该换种授课方式呢?想到就做......TOGAF认证培训,最早主要就是讲 ...

  7. 应用Tomcat进行多端口域名访问,并配置开启gzip压缩方法

    1.除了默认的8080端口以外,我们尝试应用9090端口进行域名访问,打开server.xml 如图: 2.在代码里面进行添加如下9090下面的代码: 如图: 3.用9090端口进行访问 如图: 4. ...

  8. 038.[转] JVM启动过程与类加载

    From: https://blog.csdn.net/luanlouis/article/details/40043991 Step 1.根据JVM内存配置要求,为JVM申请特定大小的内存空间 ? ...

  9. Playbook剧本小结

    1.Playbook剧本小结 1.什么是playbook,playbook翻译过来就是"剧本",那playbook组成如下 play: 定义的是主机的角色task: 定义的是具体执 ...

  10. Scrum会议(十周)

    1.任务分配 2.会议内容探讨了本次取得的重大突破和后续要继续开展的工作.分析了自己在前端开发遇到的问题,以及如何优化自己的前端界面.然后分工,每人都去优化一部分界面,比如段祥负责个人中心的优化,程吉 ...