背景
Orange是一个基于OpenResty的API Gateway,提供API及自定义规则的监控和管理,如访问统计、流量切分、API重定向、API鉴权、WEB防火墙等功能。Orange可用来替代前置机中广泛使用的Nginx/OpenResty, 在应用服务上无痛前置一个功能丰富的网关系统。
流量网关 业务网关;
API
内置插件以HTTP Restful形式开放全部API,详细可查看API文档
注意
- 现实中由于用户的业务系统多种多样,对于复杂应用,Orange并不是一个开箱即用的组件,需要调整一些配置才能集成到现有系统中。
- Orange提供的的配置文件和示例都是最简配置,用户使用时请根据具体项目或业务需要自行调整,这些调整可能包括但不限于:
- 使用的各个shared dict的大小, 如ngx.shared.status
- nginx.conf配置文件中各个server、location的配置及其权限控制,比如orange dashboard/API的server应该只对内部有权限的机器开放访问
- 根据不同业务而设置的不同nginx配置,如timeout、keepalive、gzip、log、connections等等
Licence
Orange采用MIT协议开源
其它
- Orange的插件模式参考了Kong,Kong是一个功能比较全面的API Gateway实现,推荐关注。
- Orange与Kong的不同(刨除基础设计,如存储、API)主要体现在针对”插件”和”API”的组织方式, Orange在流量筛选和变量提取方面相对来说更灵活一些。
第一部分 Nginx
知识准备
1.1 Nginx配置文件
nginx 的配置文件结构中 HTTP 配置主要包括三个区块,结构如下:
1 | Global: nginx 运行相关 |
从上面展示的 nginx 结构中可以看出 location 属于请求级别配置,这也是我们最常用的配置。
1.2 location介绍
1.2.1 location 语法
Location 块通过指定模式来与客户端请求的URI相匹配。Location基本语法:
- 匹配 URI 类型,有四种参数可选,当然也可以不带参数。
- 命名location,用@来标识,类似于定义goto语句块。
1 | location [ = | ~ | ~* | ^~ | !~ | !~* ] /uri/{ … } |
各类参数含义:
=
表示请求字符串与其精准匹配,成功则立即处理,nginx停止搜索其他匹配;- ~ 表示区分大小写正则匹配;
- ~* 表示不区分大小写正则匹配;
- ^~ 表示URI以某个常规字符串开头,并要求一旦匹配到就会立即处理,不再去匹配其他的正则 URI,一般用来匹配目录;
- !~ 表示区分大小写正则不匹配;
- !~* 表示不区分大小写正则不匹配;
- / 通用匹配,任何请求都会匹配到;
@
定义一个命名的 location,@ 定义的locaiton名字一般用在内部定向,例如error_page, try_files命令中。它的功能类似于编程中的goto。
1.2.2 location匹配顺序
nginx有两层指令来匹配请求 URI 。第一个层次是 server 指令,它通过域名、ip 和端口来做第一层级匹配,当找到匹配的 server 后就进入此 server 的 location 匹配。
location 的匹配并不完全按照其在配置文件中出现的顺序来匹配,请求URI 会按如下规则进行匹配:
- 先精准匹配
=
,精准匹配成功则会立即停止其他类型匹配; - 没有精准匹配成功时,进行前缀匹配。先查找带有
^~
的前缀匹配,带有^~
的前缀匹配成功则立即停止其他类型匹配,普通前缀匹配(不带参数^~
)成功则会暂存,继续查找正则匹配; =
和^~
均未匹配成功前提下,查找正则匹配~
和~\*
。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;- 所有正则匹配均未成功时,返回步骤 2 中暂存的普通前缀匹配(不带参数
^~
)结果
以上规则简单总结就是优先级从高到低依次为(序号越小优先级越高):
1 | 1. location = # 精准匹配 |
1.2 网关中流量筛选
1.2.1 orange中流量选择器
orange本质是使用web的方式动态配置nginx,就需要能过滤流量。实现方式是:流量选择器,如下图:
名称,定义流量选择器名称;
类型,可选参数有:全流量、自定义流量;
- 全流量匹配就是对原始流量不过滤。
- 自定义流量,需要设置匹配方式与条件,符合条件的请求才会被进行流量管理。
规则,自定义流量开启参数。可选参数有:单一条件匹配、and匹配、or匹配、复杂匹配。
单一条件匹配,单个条件,只能配置一个条件;
and匹配,多个条件以且的逻辑过滤;
or匹配,多个条件以或的逻辑过滤;
复杂匹配,即自定义条件之间逻辑关系;
按照表达式对所有条件求值,表达式不能为空。表达式中每个值的格式为
v[index]
, 比如v[1]对应的就是第一个条件的值。例如我们编写了3个条件,表达式为:(v[1] or v[2]) and v[3]。即前两个条件至少一个为真并且第三个条件为真时,规则为真。3个条件按照顺序分别对应:v[1] 、v[2]、v[3]。
条件编写,一条完整的调优有三个要素:条件类型、匹配类型、正则表达式。
条件类型有:
Random,
URI
根据你请求路径中的 uri 来进行匹配,在接入网关的时候,前端几乎不用做任何更改。
在选择器中,推荐使用 uri 中的前缀来进行匹配,而在规则中,则使用具体路径来进行匹配。
Header,K/V类型
根据
http
请求头中的字段值来匹配。这个类型的name非空。Query,K/V类型
根据
uri
中的查询参数来进行匹配,比如/test?a=1&b=2
,那么可以选择该匹配方式。Cookie
Cookie是用于维持服务端会话状态的,通常由服务端写入,在后续请求中,供服务端读取。
Postparams,K/V类型
IP
根据 http 调用方的 ip 来进行匹配。尤其是在 waf 插件里面,如果发现一个 ip 地址有攻击,可以新增一条匹配条件,填上该 ip ,拒绝该 ip 的访问。
UserAgent
User-Agent会告诉网站服务器,访问者是通过什么工具来请求的。包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。例如火狐浏览器发起的请求,User-Agent字段为:
1
Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
Host
根据 http 调用方的 host 来进行匹配。尤其是在 waf 插件里面,如果发现一个 host 地址有攻击,可以新增一条匹配条件,填上该 host ,拒绝该 host 的访问。
Referer
HTTP 协议在请求(request)的头信息里面,设计了一个
Referer
字段,给出”引荐网页”的 URL。这个字段是可选的。客户端发送请求的时候,自主决定是否加上该字段。HttpMethod
HTTP 请求可以使用多种请求方法。HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。一共有8种请求方法。
匹配的类型有:
- Match,正则匹配
- Not Match,正则不匹配
=
,精确相等!=
,精确不相等>
>=
<
<=
%
表达式,这里填写正则表达式或者匹配值。
处理,两个参数:继续后续选择器、略过后续选择器;
- 继续后续选择器,流量继续被其他选择器过滤。
- 略过后续选择器,流量终止后面的选择器过滤。
是否开启,是否生效选择器。
时候记录日志,过滤结果是否记录日志。
在多个插件里(比如URL重定向插件、WAF插件、自定义监控插件)都使用了选择器来将流量进行第一步划分, 在流量被一个选择器命中后才会进入它内部的规则过滤。
使用选择器的目的是减少不必要的规则判断,进一步提高性能。
1.2.2 orange中的流量规则
流量规则必须归属一个流量选择器,用来进一步过滤被流量选择器过滤后的流量。
一个流量选择器,可以有多个所属规则器。
下图是一个规则器,不同的插件的规则器不相同,我们后续在插件中分别介绍。
1.3 变量提取
变量提取模块是很多orange插件都使用到的一个概念, 它主要用来从请求中提取出各种信息, 比如query string, form表单里的字段, 某个header头等等。
它支持两种提取方式:
- 索引式提取
- 模板式提取
1.3.1 索引式提取
顾名思义, 索引式提取的含义是将提取出的所有变量按照顺序保存在一个数组中, 后续以下标的方式来使用。
比如我们在”变量提取模块”提取了三个值:
那么之后就可以通过\${1}、\${2}、\${3}来使用, 其中
- ${1}指的是header头里的app_version字段, 如果header没有此字段, 则赋一个默认值
v0.1
- ${2}指的是query中的uid字段
- ${3}指的是query中age字段, 若无则给予默认值18
1.3.2 模板式提取
模板时提取主要为了解决索引式提取必须要按序使用的问题, 并且当需要从uri中提取多个值时索引式提取方式并不友好。
如以下示例, 我们提取了四个值:
则之后我们可以通过以下方式来使用:
指的是从query中提取出的uid字段
指的是从query中提取出的age字段, 若无则给予默认值18
- 指的是从格式为
1
^/start/(.*)/(.*)/end
的URI中提取出的第1个分段值
- 比如, 如果URI为/start/abc/123/end, 则此时值为abc
- 如果URI为/start/momo/sharp/end, 则此时值为momo
- 指的是从格式为
1
^/start/(.*)/(.*)/end
的URI中提取出的第2个分段值
- 比如, 若URI为/start/abc/123/end, 则此时值为123
- 如果URI为/start/momo/sharp/end, 则此时值为sharp
注意, 若从URI中提取, 仍然要根据顺序来使用, 如、、.
设计原理:https://github.com/orlabs/orange/issues/15
第二部分 核心组件介绍
最新稳定版本0.8.1
,该版本对第三方组件进行去除。这里核心组件主要是0.6.4
版本中组件。
2.1 全局统计
可统计API访问情况、Nginx连接情况、流量统计、QPS、应用版本、服务器信息等。如下图:
2.2 自定义监控
可根据配置的规则筛选出流量并监控,统计其各个指标。当前的监控指标有:
- 请求总次数: 分别统计200/300/400/500区间的请求数
- QPS
- 请求总耗时/平均请求耗时
- 总流量/平均请求流量
案例:
例如筛选出指定流量,进行监控。下面是监控视图:
2.3 URL重定向(redirect)
网关实现的重定向主要是:当客户端向网关请求URL资源的时候,网关通知客户端实际的资源地址,然后客户端向实际的URL请求资源。重定向是指当浏览器请求一个URL时,服务器返回一个重定向指令,告诉浏览器地址已经变了,麻烦使用新的URL再重新发送新请求。
网关实现了通过UI配置各种rewrite策略,省去手写nginx rewrite和重启。redirect是浏览器和服务器发生两次请求,也就是服务器命令客户端“去访问某个页面”。
2.3.1 案例
我们使用重定向来代理网关的官网(http://orange.sumory.com/)。
首先,添加选择器:
然后在选择器中创建新的规则:
配置完成后,当我们访问192.168.52.137:8888/to_orange
时候,会自动跳转为:http://orange.sumory.com/
。
2.3.2 参数说明
重定向有两种:一种是302响应,称为临时重定向,一种是301响应,称为永久重定向。两者的区别是,如果服务器发送301永久重定向响应,浏览器会缓存/hi
到/hello
这个重定向的关联,下次请求/hi
的时候,浏览器就直接发送/hello
请求了。
2.4 URI 重写(Rewrite)
Url重写主要用于站内请求的重写。rewrite则是服务器内部的一个接管,在服务器内部告诉“某个页面请帮我处理这个用户的请求”,浏览器和服务器只发生一次交互,浏览器不知道是该页面做的响应,浏览器只是向服务器发出一个请求。
URL重写用于将页面映射到本站另一页面,若重写到另一网络主机(域名),则按重定向处理。
rewrite是把一个地址重写成另一个地址。地址栏不跳转。相当于给另一个地址加了一个别名一样。
2.4.1 案例
我们使用重写(rewrite)来重写上面案例中地址。
首先添加选择器:
然后在选择器中创建新的规则:
配置完成后,当我们访问http://192.168.52.137:8888/to_orange_test
时候,流量被映射到本站另一个地址http://192.168.52.137:8888/to_orange
。而后面地址被重定向到http://orange.sumory.com/
。
2.4.2 参数说明
2.5 HTTP Basic Authorization
basic auth
是最简单权限认证方式,密钥被base64
加密,但是网络传输是非加密的,一旦被截取,解密是容易的。所以通常用于安全的内部网络。
案例
参数说明
2.6 HTTP Key Auth
案例
参数说明
2.7 Signature Auth
案例
参数说明
https://www.cnblogs.com/Sinte-Beuve/p/12093307.html
2.8 Rate Limiting 访问限速
案例
参数说明
2.9 Rate Limiting 防刷
案例
参数说明
2.10 WAF 防火墙
案例
参数说明
2.11 代理 & 分流 & ABTesting
当前divide分流插件是静态的,需要提前在nginx.conf里配置upstream,但是这样不利于灵活管理,能否实现动态配置upstream。
分流插件,可分为三个使用场景:
- 作为proxy,如代理后端的多个HTTP应用
- 用于AB测试
- 用于动态分流,API版本控制等
https://book.aikaiyuan.com/openresty/orange-divide.html#%E8%AF%95%E9%AA%8C%E7%8E%AF%E5%A2%83
http://bbs.orchina.org/topic/160/view
案例
我们使用该插件代理elasticsearch集群节点。
我们对es进行负载配置
http://www.ttlsa.com/nginx/nginx-elasticsearch/
1 | upstream es_upstream { |
https://github.com/orlabs/orange/issues/136
2.12 KV Store
第三部分 第三方插件
https://zhjwpku.com/2017/11/14/orange-balancer-plugin-tutorial.html
- The
balancer
plugin migrated tov0.9.0-dev
due to conflicts with existing features. - The
dynamic_upstream
plugin migrated tov0.9.0-dev
due to conflicts with existing features. - The
consul_balancer
plugin migrated tov0.9.0-dev
due to conflict with existing functions. - The
persist
plugin migrated tov0.9.0-dev
due to conflicts with existing features.
3.1 node插件
该插件主要用户网关集群管理。
node plugin(容器集群节点管理插件)
- 新增集群节点注册命令
orange register
- 通过 dashboard 面板同步节点配置信息
- 配合 persist 插件,可以查看历史统计信息
1 | influxdb:/usr/local/orange/conf # opm install ledgetech/lua-resty-http |
https://github.com/orlabs/orange/issues/353
3.2 headers 插件
用于修改请求头。
3.3 balancer插件
用户负载多个upstream
https://zhjwpku.com/2017/11/14/orange-balancer-plugin-tutorial.html
3.4 Consul Upstream
3.4 Dynamic Upstream
案例
参数说明
3.6 HTTP Jwt Auth
3.7 HTTP Hmac Auth
3.8 持久日志
第四部分 插件的优先级
orange中所有插件都是继承基本组件的,文件plugins\base_handler.lua
中定义如下:
1 | local Object = require("orange.lib.classic") |
第五部分 Dashboard 用户管理
从v0.1.1版本开始,Orange Dashboard支持授权验证(默认未开启)。当配置开启后,只有通过成功登录的账户才能登陆展现Dashboard。
配置
这部分功能在配置文件conf/orange.conf
中:
1 | "dashboard": { |
Dashboard 用户的用户信息存储在Mysql的dashboard_user
表中。
默认系统管理员用户名和密钥如下,首次登陆后可以修改和添加其他用户:
1 | 用户名:admin |
参考文献及资料
[1] Orange官网,链接:http://orange.sumory.com/
[2] Orange网关官网docker,链接:https://hub.docker.com/r/syhily/orange