57

NGINX的定制化 | API Management学习第四篇

 5 years ago
source link: http://www.10tiao.com/html/360/201807/2663488717/1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言


本文仅代表作者的个人观点;

本文的内容仅限于技术探讨,不能作为指导生产环境的素材;

本文素材是红帽公司产品技术和手册;

本文分为系列文章,将会有多篇,初步预计将会有10篇。



一、3 Scale中的NGINX



3 SCALE中API gateway,是基于NGINX(OpenResty Web Platform =  Nginx + Lua )。


NGINX的特点和作用如下:

  • HTTP和反向代理服务器

  • 邮件和TCP / UDP代理服务器

  • 为世界上最繁忙的网站提供30%的能力

  • 非阻塞,基于事件的反应堆

  • 单线程,主人+工人

  • 模块化架构

  • SSL和TLS SNI支持



二、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模块

  • 缓存DNS解析器

  • 负载均衡器

  • HTTP客户端(带缓存)


内部使用

  • 微服务(API mashup)

  • 路由器/代理服务器

  • 上行IP白名单/黑名单


APIcast策略(模块)

  • 一个入口点模块,可以调用其他模块

  • 有每个阶段的方法(init,init_worker,重写,访问等)

  • 默认模块称为APIcast:更换它重新定义了整个网关;与继承一样,自定义模块应该调用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阶段的政策:


  1. init - 加载配置(引导模式)

  2. init_worker - 启动计时器以刷新配置(启动模式)

  3. rewrite - 为给定主机加载配置(延迟模式)


4. 访问:

如果需要,匹配请求并同步授权

如果需要,解析上游DNS记录

设置NGINX变量以正确代理上游


5.balancer - 根据所选算法选择peer

6.post_action - 如果缓存的呼叫通过,则向3scale后端报告


政策链 - 结合政策来提供服务



自定义模块

要自定义APIcast:

  • 覆盖功能

  • 扩展配置

  • 与HTTP请求/响应相关。示例:跨源资源共享(CORS)

  • 自定义验证

  • 限时访问

  • 限制打开的连接


标准配置

  • 主机名,服务配置等的配置

  • 网关的JSON配置

  • 从文件或API加载

  • https://ACCOUNT-admin.3scale.net/admin/api/nginx/spec.json

  • 在init和init_worker阶段加载

  • THREESCALE_CONFIG_FILE定位配置文件


自定义NGNIX配置

  • 将自定义NGINX配置注入网关

  • 不覆盖现有配置

  • 装载到conf.d或sites.d

  • 挂钩到NGINX阶段



Lua

  • 脚本语言

  • 支持程序,面向对象,功能,数据驱动的编程

  • 基于的C

  • 独立或嵌入式

  • 动态键入的语言

  • 八种基本类型:nil,boolean,number,string,function,userdata,thread,table

  • 错误处理 - 控制返回主机

  • 使用自动内存管理进行垃圾收集

  • 变量 - 全局,本地,表

  • 与JavaScript非常相似,Lua表就像JavaScript对象



四、代码集成方式引入3Scale(侵入式)


  • 社区扩展

  • 允许API流量管理

  • 服务管理API的包装器:

  • API访问控制和安全性

  • 流量报告

  • API货币化


支持的语言:

插件API调用后端:


authrep - 访问API和报告流量的单一呼叫

授权 - 访问API请求的调用

报告 - 报告API的流量

插件验证:

  • App ID

  • 用户密钥

  • OAuth的



接下来的实验,一共有三个,分别是:

  • 在NGINX网关中创建自定义模块以进行日志记录

  • 在NGINX网关中创建自定义配置以回显请求标头

  • 为CORS定制NGINX网关(跨源资源共享)


四、实验展现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客户化配置


  • 自定义NGNIX配置注入网关 - 例如,添加另一个服务器块以处理某些路由。此配置不会覆盖现有配置。

  • 与自定义模块类似,可以使用自定义配置继承标准配置并对其进行扩展。

  • 在本节中,我们将创建一个自定义配置,通过回显响应中的所有请求标头以及API响应,为客户端提供更详细的响应。


拷贝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 起作用了。



魏新宇

  • "大魏分享"运营者、红帽资深解决方案架构师

  • 专注开源云计算、容器及自动化运维在金融行业的推广

  • 拥有MBA、ITIL V3、Cobit5、C-STAR、TOGAF9.1(鉴定级)等管理认证。

  • 拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、AIX、HPUX等技术认证。



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK