前言
本文仅代表作者的个人观点;
本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,将会有多篇,初步预计将会有10篇。
一、3 Scale中的NGINX
3 SCALE中API gateway,是基于NGINX(OpenResty Web Platform = Nginx + Lua )。
NGINX的特点和作用如下:
二、Openshift中的OpenResty Web Platform = Nginx + Lua
OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
三、APIcast的库和配置
APIcast库是扩展的NGINX模块
内部使用
APIcast策略(模块)
九个APIcast模块阶段
init | Executed in the master process, loaded code shared between workers |
---|---|
init_worker | Executed for each worker, can perform background work |
rewrite | First request phase to change the request |
access | Allow/deny the request |
balancer | Used for load balancing |
header_filter | Process response headers |
body_filter | Process response body |
post_action | Not official NGINX phase, update cache information out of band |
log | Can extend logging (for example, to external service) |
默认模块(默认的apicast策略)
六个NGINX阶段的政策:
4. 访问:
如果需要,匹配请求并同步授权
如果需要,解析上游DNS记录
设置NGINX变量以正确代理上游
5.balancer - 根据所选算法选择peer
6.post_action - 如果缓存的呼叫通过,则向3scale后端报告
政策链 - 结合政策来提供服务
自定义模块
要自定义APIcast:
标准配置
自定义NGNIX配置
Lua
四、代码集成方式引入3Scale(侵入式)
支持的语言:
插件API调用后端:
authrep - 访问API和报告流量的单一呼叫
授权 - 访问API请求的调用
报告 - 报告API的流量
插件验证:
接下来的实验,一共有三个,分别是:
四、实验展现1:为NGINX增加日志模块
NGINX模块插入网关的生命周期阶段。 有各种APIcast阶段,例如init,init_worker,rewrite,access,content,log,post_action,balancer,header_filter,body_filter等。模块处理每个请求的处理。 只能执行一个模块。
执行的模块的名称由环境变量APICAST_MODULE定义,默认为apicast。
要编写自定义模块,我们需要使用继承来使用自定义代码覆盖默认的APICAST_MODULE代码。 自定义模块提供了编写自定义代码的推荐框架。
在本节中,我们将创建一个自定义模块,以在APIcast中提供更详细的日志记录信息。 自定义Lua文件可在此处获得。
检查日志功能的代码:
日志中添加了两个新字段:upstream_response_time和upstream_connect_time。
在成功的API调用中,新日志信息将打印在日志中。
拷贝代码:
[root@master david]# cat verbose.lua
local apicast = require('apicast').new()
local _M = { _VERSION = '0.0' }
local mt = { __index = setmetatable(_M, { __index = apicast }) }
function _M.new()
return setmetatable({}, mt)
end
function _M.log()
ngx.log(ngx.WARN,
'upstream response time: ', ngx.var.upstream_response_time, ' ',
'upstream connect time: ', ngx.var.upstream_connect_time)
return apicast:log()
end
return _M
在OpenShift项目中创建config map,确保提供verbose.lua文件的正确路径:
为容器创建卷,并将其挂载到适当的路径:
oc set volume dc/apicast-staging --add --name=apicast-verbose --mount-path /root/david/verbose.lua --source='{"configMap":{"name":"apicast-verbose","items":[{"key":"verbose.lua","path":"verbose.lua"}]}}'
oc volume命令不支持添加子路径,因此我们需要应用补丁:
oc patch dc/apicast-staging --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/0/subPath", "value":"verbose.lua"}]'
设置环境变量APICAST_MODULE:
oc env dc/apicast-staging APICAST_MODULE=verbose
测试效果
使用curl命令将测试请求发送到任何业务服务的暂存路由。
日志中出现:
30#30: *114 [lua] verbose.lua:11: log(): upstream response time: 0.085 upstream connect time: 0.000 while logging request, c
四、实验展现2:NGINX客户化配置
拷贝echo.conf配置
curl -o echo.conf https://raw.githubusercontent.com/3scale/apicast/3.1-stable/examples/custom-config/echo.conf
我们查看这个配置的内容:
接下来,进行如下操作(完整步骤):
[root@master src]# oc project $OCP_PROJECT_PREFIX-3scale-amp
Already on project "david-3scale-amp" on server "https://master.example.com:8443".
[root@master src]#
[root@master src]# oc create configmap echo-conf --from-file=./echo.conf
configmap "echo-conf" created
[root@master src]# oc set volume dc/apicast-staging --add --name=echo-conf --mount-path /opt/app-root/app/sites.d/echo.conf --source='{"configMap":{"name":"echo-conf","items":[{"key":"echo.conf","path":"echo.conf"}]}}'
\deploymentconfig "apicast-staging" updated
[root@master src]# oc patch dc/apicast-staging --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/1/subPath", "value":"echo.conf"}]'
deploymentconfig "apicast-staging" patched
[root@master src]#
接下来,apicast-staging将会重新部署:
测试效果
登录到apicast-staging pod
发送请求到localhost的端口8080:
五、实验展现3:为NGINX增加模块:CORS
跨源资源共享(CORS)是一种机制,它使用其他HTTP标头让用户代理获得从当前正在使用的站点的不同源(域)上的服务器访问所选资源的权限。 用户代理在请求来自与当前文档所源自的域,协议或端口不同的域,协议或端口的资源时,会发出跨源HTTP请求。
两个文件-cors.lua和cors.conf-为NGINX配置CORS。
将CORS自定义模块和配置部署到OpenShift
两个配置文件的内容:
[root@master src]# cors.conf
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Headers: $http_access_control_request_headers';
more_set_headers 'Access-Control-Allow-Methods: $http_access_control_request_method';
[root@master src]# vi cors.lua
local apicast = require('apicast').new()
local _M = { _VERSION = apicast._VERSION, _NAME = 'APIcast with CORS' }
local mt = { __index = setmetatable(_M, { __index = apicast }) }
function _M.new()
return setmetatable({}, mt)
end
local function set_cors_headers()
local origin = ngx.var.http_origin
if not origin then return end
ngx.header['Access-Control-Allow-Headers'] = ngx.var.http_access_control_request_headers
ngx.header['Access-Control-Allow-Methods'] = ngx.var.http_access_control_request_method
ngx.header['Access-Control-Allow-Origin'] = origin
ngx.header['Access-Control-Allow-Credentials'] = 'true'
end
local function cors_preflight_response()
set_cors_headers()
ngx.status = 204
ngx.exit(ngx.status)
end
local function cors_preflight()
return (
ngx.req.get_method() == 'OPTIONS' and
ngx.var.http_origin and
ngx.var.http_access_control_request_method
)
end
-- header_filter is used to manipulate response headers
function _M.header_filter()
set_cors_headers()
return apicast:header_filter()
end
-- rewrite is the first phase executed and can hijack the whole request handling
function _M.rewrite()
-- for CORS preflight sent by the browser, return a 204 status code
if cors_preflight() then
[root@master src]# cat cors.lua
local apicast = require('apicast').new()
local _M = { _VERSION = apicast._VERSION, _NAME = 'APIcast with CORS' }
local mt = { __index = setmetatable(_M, { __index = apicast }) }
function _M.new()
return setmetatable({}, mt)
end
local function set_cors_headers()
local origin = ngx.var.http_origin
if not origin then return end
ngx.header['Access-Control-Allow-Headers'] = ngx.var.http_access_control_request_headers
ngx.header['Access-Control-Allow-Methods'] = ngx.var.http_access_control_request_method
ngx.header['Access-Control-Allow-Origin'] = origin
ngx.header['Access-Control-Allow-Credentials'] = 'true'
end
local function cors_preflight_response()
set_cors_headers()
ngx.status = 204
ngx.exit(ngx.status)
end
local function cors_preflight()
return (
ngx.req.get_method() == 'OPTIONS' and
ngx.var.http_origin and
ngx.var.http_access_control_request_method
)
end
-- header_filter is used to manipulate response headers
function _M.header_filter()
set_cors_headers()
return apicast:header_filter()
end
-- rewrite is the first phase executed and can hijack the whole request handling
function _M.rewrite()
-- for CORS preflight sent by the browser, return a 204 status code
if cors_preflight() then
return cors_preflight_response()
else
-- if the request is not CORS preflight jut continue with APIcast flow
return apicast:rewrite()
end
end
return _M
操作步骤:
curl -o cors.lua https://raw.githubusercontent.com/3scale/apicast/3.1-stable/examples/cors/cors.lua
curl -o cors.conf https://raw.githubusercontent.com/3scale/apicast/3.1-stable/examples/cors/cors.conf
oc project $OCP_PROJECT_PREFIX-3scale-amp
oc create configmap apicast-cors --from-file=./cors.lua
oc create configmap cors-conf --from-file=./cors.conf
oc set volume dc/apicast-staging --add --name=apicast-cors --mount-path /opt/app-root/src/src/cors.lua --source='{"configMap":{"name":"apicast-cors","items":[{"key":"cors.lua","path":"cors.lua"}]}}'
oc set volume dc/apicast-staging --add --name=cors-conf --mount-path /opt/app-root/src/apicast.d/cors.conf --source='{"configMap":{"name":"cors-conf","items":[{"key":"cors.conf","path":"cors.conf"}]}}'
oc patch dc/apicast-staging --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/2/subPath", "value":"cors.lua"},{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/3/subPath", "value":"cors.conf"}]'
oc env dc/apicast-staging APICAST_MODULE=cors
接下来pod会重新部署:
测试效果:
对API enpoint发起curl请求:
我们观察到,输出结果中有如下信息,说明CORS 起作用了。
魏新宇