Kong 的安装(部署)方式较为多样,支持多种操作环境,如Docker,Ubuntu,CentOS,MacOS系统等等,详情可见Kong安装 ,但其开发环境却是有些受限,在windows系统中表现尤为明显。 在Windos系统中,本人做了如下尝试:
关于Kong插件开发,实际上也可以理解为Lua程序的编写,本文采用的是vscode+lua扩展(如EmmyLua)作为IDE进行开发的,其中值得注意的是Lua的编译器采用的是LuaJIT,详情可参考OpenResty最佳实践一书(开源书籍,包括了Lua & Nginx& Lua Module & OpenResty开发测试等内容)
从github上获取最新源码
smj@smj-ubuntu:~$ mkdir workspace
smj@smj-ubuntu:~$ cd workspace/
smj@smj-ubuntu:~/workspace$ git clone https://github.com/Kong/kong.git
正克隆到 'kong'...
1 在vscode extends 搜索emmyLua插件直接安装,或者通过离线安装方式加载
2 配置EmmyLua插件,文件>>首选项>>设置,搜索emmylua设置JAVA_HOME选项,(settings.json)设置如下:
{
"terminal.integrated.shell.linux": "/bin/bash",
"editor.mouseWheelZoom": true,
"[lua]": {
"editor.defaultFormatter": "trixnz.vscode-lua"
},
"terminal.explorerKind": "external",
"terminal.integrated.fontFamily": "monospace",
"emmylua.java.home": "/opt/java/jdk1.8.0_181",
}
如果Linux系统无java环境,请先安装java环境,可参考Ubuntu安装java8
3 加载Kong源码文件,即可看到Lua文件的高亮的显示(编写Lua也会有相应的代码提示)
上述前置环境准备就绪,接下来就就正式进入我们的Lua插件开发啦~
If you are planning on developing on Kong, you’ll need a development installation. The next branch holds the latest unreleased source code. --from github-kong
本文主要以源码编译方式进行插件开发及验证,可参考官方文档源码编译指南的文档
拥有Linux开发环境(虚拟或裸机)后,还需要一些初始化工作,包括有(1)必要的软件包,包括编译器,工具和编译其他内容所需的库等:(2) OpenResty系统,包括Nginx,LuaJIT,PCRE等;(3)数据库,Kong使用Posgres,Cassandra和Redis。
以Ubuntu系统为例,使用root 或者sudo角色安装下列依赖(基本涵盖了所需的软件包):
$: apt-get update
$: apt-get install \
automake \
build-essential \
curl \
docker \
docker-compose \
git \
libpcre3 \
libyaml-dev \
m4 \
openssl \
perl \
procps \
unzip \
zlib1g-dev
获取OpenResty(暂时可忽略该步骤,本文不涉及)
$: git clone https://github.com/kong/openresty-build-tools
$: cd openresty-build-tools
$: ./kong-ngx-build -p build \
--openresty 1.15.8.2 \
--openssl 1.1.1d \
--luarocks 3.2.1 \
--pcre 8.43
注意:此处配置并非唯一确定的,
根据不同的开发方式进行不同设置,本文是在源码基础上进行插件扩展开发,需要进行如下设置:
打开源码目录,需要修改2个文件:
#!/usr/bin/env resty
=> #!/usr/local/openresty/bin/resty
plugins = bundled,hello-world
,其中bundled 表示kong自带的开源插件上述配置完成即可写自己的插件了,在 kong/plugins/
目录下新建自定义插件,如hello-world
开发自定义插件之前,需要了解下kong插件开发指南,详情点击官方指导文档
所有的Kong插件都位于kong/plugins
目录下, 如在plugins目录我们创建一个名为hello-world
插件, 至少需要包含2个文件,如下所示:
plugins
├── hello-world
│ ├── handler.lua
│ └── schema.lua # (必需)插件配置参数定义, 可加入自定义校验函数
└── other-plugin
完全版目录结构
hello-world
├── api.lua #
├── daos.lua
├── handler.lua
├── migrations
│ ├── init.lua
│ └── 000_base_complete_plugin.lua
└── schema.lua
模块名称 | 必需 | 描述 |
---|---|---|
api.lua | 否 | 扩展Admin API,在需要对外暴露接口时候 |
daos.lua | 否 | 数据访问层,对插件自定义实体的抽象,并存储在数据存储区中 |
handler.lua | 是 | 对外暴露出在请求/连接生命周期的接口,由Kong实现逻辑并运行 |
migrations/*.lua | 否 | 数据库迁移(例如,创建表),仅当插件应用daos.lua进行存储时才需要进行迁移 |
schema.lua | 是 | 保留插件配置结构,以便用户只能输入有效的配置值 |
Kong插件允许您在Kong代理的 request/response 或tcp stream 连接的生命周期中的几个入口点注入自定义逻辑(通过 Lua)。为此必须实现一个或多个base_plugin.lua 的方法接口,在kong.plugins.
中实现。 Kong执行生命周期的各个入口点:
函数名 | 阶段 | 描述 |
---|---|---|
:init_worker() |
init_worker | 在每个 Nginx 工作进程启动时执行 |
:certificate() |
ssl_certificate | 在SSL握手阶段的SSL证书服务阶段执行 |
:rewrite() |
rewrite | 从客户端收到的每个请求作为重写阶段处理程序执行。在这个阶段,无论是Service 还是Customer都没有被识别,因此这个处理器只在插件被配置为全局插件时执行 |
:access() |
access | 针对客户端的每个请求并在将其代理到上游服务之前执行 |
:header_filter() |
header_filter | 当已从上游服务接收到所有响应头字节时执行 |
:body_filter() |
body_filter | 对从上游服务接收到的响应主体的每个块执行。由于响应流回客户端,因此它可能超出缓冲区大小,并且逐块流式传输。因此,如果响应较大,则可以多次调用此方法 |
:log() |
log | 当最后一个响应字节已经发送到客户端时执行 |
函数名 | 阶段 | 描述 |
---|---|---|
:init_worker() |
init_worker | 在每个 Nginx 工作进程启动时执行 |
:preread() |
preread | 每个连接执行一次 |
:log() |
log | 关闭每个连接后,对每个连接执行一次 |
该handler.lua文件必须返回一个实现您希望执行的功能的表。为了简洁起见,这是一个注释示例模块,实现了两个模块的所有可用方法(请注意,其中一些在模块之间共享,例如log):
-- 扩展Base Plugin handler是可选的,因为Lua中没有真正的接口概念,
-- 但是Base Plugin handler的方法可以从子实现中调用,
-- 并将在“error.log”中打印日志(其中打印所有日志)。
local BasePlugin = require "kong.plugins.base_plugin"
local CustomHandler = BasePlugin:extend()
CustomHandler.VERSION = "1.0.0"
--设置执行的优先级,Kong 将按照插件的优先级来确定其执行顺序(越大越优先)
CustomHandler.PRIORITY = 10
-- 你插件handler的构造函数。
-- 如果要扩展Base Plugin handler,它的唯一作用就是用名称实例化自己。
-- 该名称是您的插件名称,同时它将打印在日志中
function CustomHandler:new()
CustomHandler.super.new(self, "my-custom-plugin")
end
function CustomHandler:init_worker()
-- 最终执行父实现 (并将记录您的插件正在进入此上下文)
CustomHandler.super.init_worker(self)
-- 实现任何自定义逻辑
end
function CustomHandler:preread(config)
CustomHandler.super.preread(self)
-- Implement any custom logic here
end
function CustomHandler:certificate(config)
CustomHandler.super.certificate(self)
-- Implement any custom logic here
end
function CustomHandler:rewrite(config)
CustomHandler.super.rewrite(self)
-- Implement any custom logic here
end
function CustomHandler:access(config)
CustomHandler.super.access(self)
-- Implement any custom logic here
end
function CustomHandler:header_filter(config)
CustomHandler.super.header_filter(self)
-- Implement any custom logic here
end
function CustomHandler:body_filter(config)
CustomHandler.super.body_filter(self)
-- Implement any custom logic here
end
function CustomHandler:log(config)
CustomHandler.super.log(self)
-- Implement any custom logic here
end
-- 该模块需要返回创建的表以便让Kong 可以执行这些功能。
return CustomHandler
当然,插件本身的逻辑可以抽象到另一个模块中,并从处理程序模块调用。许多现有的插件在逻辑冗长时已经选择了这种模式。
Kong为您提供了一种验证用户插件配置的方法,它包含用户通过Admin API启用插件时将设置的键/值属性。该模块将返回具有属性的Lua表,该属性将定义用户以后如何配置插件。
此处不做过多说明,详情请参考schema.lua规范
为了了解kong插件开发,我们也需要了解kong的开发套件pdk,基于此我们实现一个函数,用来打印pdk中部分函数的输出,通过运行kong的插件拦截我们web http请求,打印文本内容输出到web浏览器,即:
-- hello-world.handlar.lua
local BasePlugin = require "kong.plugins.base_plugin"
local CustomHandler = BasePlugin:extend()
local resultAns = ">>插件开始运行了\n"
CustomHandler.VERSION = "1.0.0"
CustomHandler.PRIORITY = 10
function CustomHandler:new()
CustomHandler.super.new(self, "hello-world")
end
function CustomHandler:access(config)
CustomHandler.super.access(self)
resultAns = resultAns .. ">>>>>>>执行:access阶段开始\n输出嵌入的内容(请求在还未到达上游服务器):\n"
resultAns = resultAns .. "kong.version:\t" .. kong.version .. "\n"
resultAns = resultAns .. "kong.client.get_ip():\t" .. kong.client.get_ip() .. "\n"
resultAns = resultAns .. "kong.request.get_scheme():\t" .. kong.request.get_scheme() .. "\n"
resultAns = resultAns .. "kong.request.get_host():\t" .. kong.request.get_host() .. "\n"
resultAns = resultAns .. "kong.request.get_port()\t:" .. kong.request.get_port() .. "\n"
resultAns = resultAns .. "kong.request.get_http_version():\t" .. kong.request.get_http_version() .. "\n"
resultAns = resultAns .. "kong.request.get_method():\t" .. kong.request.get_method() .. "\n"
resultAns = resultAns .. "kong.request.get_path():\t" .. kong.request.get_path() .. "\n"
resultAns = resultAns .. "<<<<<<<执行access阶段结束 \n"
return kong.response.exit(
200,
resultAns,
{
["Content-Type"] = "application/json",
["WWW-Authenticate"] = "Basic"
}
)
end
return CustomHandler
-- hello-world.schema.lua
local typedefs = require "kong.db.schema.typedefs"
return {
name = "hello-world",
fields = {
{
consumer = typedefs.no_consumer
},
{
config = {
type = "record",
fields = {
-- 这里的username, 会显示在插件配置页
{
username = {
type = "array",
elements = {type = "string"},
default = {}
}
}
}
}
}
}
}
上述插件编写完成后,需要安装并启动Kong软件,此处目前笔者已知有2种方式:
bin\kong start
命令安装并启动Kongroot@smj-ubuntu:/home/smj/workspace/kong# bin/kong start
2019/11/12 18:51:01 [warn] ulimit is currently set to "1024". For better performance set it to at least "4096" using "ulimit -n"
Kong started
luarocks make
命令安装Kongroot@smj-ubuntu:/home/smj/workspace/kong# luarocks make
kong 1.4.0-0 is now installed in /usr/local (license: Apache 2.0)
root@smj-ubuntu:/home/smj/workspace/kong# kong start
2019/11/12 17:54:39 [warn] ulimit is currently set to "1024". For better performance set it to at least "4096" using "ulimit -n"
Kong started
root@smj-ubuntu:/home/smj/workspace/kong# kong stop
Kong stopped
sudo netstat -tunlp | grep nginx
查看下kong的端口信息smj@smj-ubuntu:~$ sudo netstat -tunlp | grep nginx
tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 6289/nginx: master
tcp 0 0 127.0.0.1:8444 0.0.0.0:* LISTEN 6289/nginx: master
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 6289/nginx: master
tcp 0 0 127.0.0.1:8001 0.0.0.0:* LISTEN 6289/nginx: master
kong admin api 对外暴露的端口为8001,可通过curl命令进行设置,
除此之外,还有可选择的管理界面konga,通过该工程可对kong进行可视化配置管理。
konga的安装较为简单,使用前需要具备一些条件:
此处使用源码进行安装,在准备的工作目录执行:
$ git clone https://github.com/pantsel/konga.git
$ cd konga
$ npm i
mysql安装
过程(略)……
检验是否安装:
smj@smj-ubuntu:~$ mysqladmin --version
mysqladmin Ver 8.42 Distrib 5.7.27, for Linux on x86_64
登录并创建konga数据库:
假设您已经设置了mysql的用户名(root)和密码 (123456)
# 1 登录mysql
smj@smj-ubuntu:~$ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
(省略)……
# 2 创建konga
mysql> create database if not exist konga charset utf8 column utf8_general_ci ;
# 3 检查是否创建成功,若有konga ,表示创建成功
mysql> show databases;
完成依赖包和数据库的安装后, 需要配置一下.env 中关于数据库连接的配置
在源码目录执行如下命令:
## 进入源码目录
cd ~/workspace/konga
cp .env_example .env
vim .env
## 修改为如下设置
PORT=1337
NODE_ENV=production
KONGA_HOOK_TIMEOUT=120000
DB_ADAPTER=mysql
DB_URI=mysql://localhost:3306/konga
DB_USER=root
DB_PASSWORD=123456
KONGA_LOG_LEVEL=warn
TOKEN_SECRET=some_secret_token
## 执行下面的语句 创建数据库的表
node ./bin/konga.js prepare --adapter mysql --uri mysql://localhost:3306/konga
## 启动服务
npm start
接下来我们就可以通过konga进行kong的配置咧,并且可以据此验证我们自定义插件的效果~
SERVICES (服务)
/ROUTES(路由)
/ PLUGINS(插件)
>>插件开始运行了
>>>>>>>执行:access阶段开始
输出嵌入的内容(请求在还未到达上游服务器):
kong.version: 1.4.0
kong.client.get_ip(): 127.0.0.1
kong.request.get_scheme(): http
kong.request.get_host(): localhost
kong.request.get_port() :8000
kong.request.get_http_version(): 1.1
kong.request.get_method(): GET
kong.request.get_path(): /index
<<<<<<<执行access阶段结束
curl -i http://localhost:8000/index
可通过该流程进行插件的开发测试
kong开发环境搭建方式多种,文中只介绍了自己使用的方式。
如有疏漏,烦请指正~