女主宣言

今天小编为大家分享一篇关于Apache APISIX的文章,文章从开发者的角度讲述了 Apache APISIX 网关在 360 基础运维平台的落地实践,希望能对大家有所帮助。

PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!

Api 网关选型

2019 年 10月,我们团队计划改造 360 基础运维平台的网关层,当时我们主要调研了社区几个比较活跃的网关,如 Kong,Orange,Apache APISIX,最终选择了 Apache APISIX 当时主要是考虑到 Apache APISIX 的存储选型 etcd 比较符合我们的使用场景。

线上运行情况

目前我们添加到网关的 API 数量接近 900 个,日均 PV 1000 万左右,从监控系统来看,网关以及我们各个微服务均运行良好。

•日均 PV

•网关 POD 监控

•微服务负载监控图

基础运维平台架构图

下图是我们运维平台项目最终的架构图,网关服务我们部署在公司的容器云上,etcd 服务我们是在 3 台虚机上部署了一套集群。

容器化开发和部署

接下来我具体介绍一下我们是如何使用 Apache APISIX 搭建网关服务的,首先先给大家看下我们网关项目的代码结构

之前我给王院生(Apache APISIX PPMC 之一 )看我们的项目代码结构时,他惊讶的问我,说怎么没有看到 Apache APISIX 的 core 代码。

实际上这是我们在使用容器安装 Apache APISIX 时探索出来的一条道路。它给我们带来最大的好处是,我们业务的代码和 Apache APISIX 的核心代码完全分开,方便 Apache APISIX 升级,也方便我们的业务代码迭代

下面我给大家分步演示一下,我们是怎么搭建一个这样的环境的。(此处假设大家都了解 docker 容器技术)

•启动 openresty 容器

我们团队基于官方的 openresty 镜像,默认安装了一些 Apache APISIX 依赖的软件如 luarocks 等,重新打包了一个新的镜像,详情可见 https://hub.docker.com/r/hulklab/openresty/dockerfile

> docker run -itd -p 9080:9080 -p9443:9443 --name myapisix hulklab/openresty:0.0.1

•进入容器

> docker exec -it myapisix bash

•安装 apisix 0.9 版本

> luarocks install apisix 0.9-0# 安装完你会在最后看到下面这样的一行输出,我们从中可以看出 apisix 的安装目录为 /usr/local/apisix:apisix 0.9-0 is now installed in /usr/local (license: Apache License 2.0)

•进入 Apache APISIX 安装目录

> cd /usr/local/apisix# 进入到 apisix 的安装目录,你会发现里面只有两个目录 `conf` `logs`> ls -ldrwxr-xr-x 3 root   root 4096 Dec 18 11:52 conf
drwxr-xr-x 2 root   root 4096 Dec 18 11:37 logs

•启动 Apache APISIX(运行 Openresty)

> apisix start> ps aux|grep openrestyroot         1  0.0  0.0  11316  4464 pts/0    Ss+  11:24   0:00 nginx: master process /usr/bin/openresty -g daemon off;
root      5040  0.0  0.0  87436  2588 ?        Ss   11:37   0:00 nginx: master process openresty -p /usr/local/apisix -c /usr/local/apisix/conf/nginx.conf

注:如果执行 apisix start 失败,是因为 Apache APISIX 依赖 etcd,你需要启动 etcd,如何启动 etcd 请参考 etcd 官方文档[1],启动 etcd 后需要修改 /usr/local/apisix/conf/config.yaml 中的 etcd.host 配置, 如:

#config.yaml:69
etcd:host: "http://172.17.0.1:2379"   # etcd address

•查看 nginx.conf

apisix 成功运行了,但是安装目录 /usr/local/apisix 里面又没有代码,那么 apisix 核心代码以及依赖代码究竟在哪里呢?

从启动的 openresty 进程看到,apisix/conf 目录下,多出了一个 nginx.conf,这个 nginx.conf 配置文件是 apisix start 命令执行时初始化出来的, 我们查看了一下 nginx.conf 中的 lua 包引用路径

> cat /usr/local/apisix/conf/nginx.conf|grep lua_package_pathlua_package_path  "$prefix/deps/share/lua/5.1/?.lua;/usr/local/apisix/lua/?.lua;;/usr/local/apisix/deps/share/lua/5.1/apisix/lua/?.lua;/usr/local/apisix/deps/share/lua/5.1/?.lua;/usr/share/lua/5.1/apisix/lua/?.lua;/usr/local/share/lua/5.1/apisix/lua/?.lua;/root/.luarocks/share/lua/5.1/?.lua;/root/.luarocks/share/lua/5.1/?/init.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;./?.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;";

从上面的 lua_package_path 中我们挨个查看,从中发现了两个有用的信息

1.Apache APISIX 核心代码的路径:/usr/local/share/lua/5.1/apisix/lua/2.Apache APISIX 安装路径 /usr/local/apisix/lua 下的 lua 文件的加载优先级最高

于是我们做了一个尝试,仿照 Apache APISIX 的 plugins 路径,在 /usr/local/apisix 目录下创建 lua/apisix/plugins/my-plugin.lua,并在配置文件 config.yaml 中添加该插件,发现果然生效了。

•Dockerfile

贴出我们项目的 Dockerfile 文件,供大家参考。最终我们项目只有 conf 和 lua 两个目录,conf 中存放我们自己定义 config.yaml 和 nginx.conf 配置文件,lua 中存放我们自定义的插件和类库。

FROM hulklab/openresty:0.0.1RUN luarocks install apisix 0.9-0; \luarocks install lua-resty-cookie; \luarocks install lua-resty-kafka; \luarocks install lua-resty-urlWORKDIR /usr/local/apisixRUN rm -rf conf/*; \mkdir -p lua; \mkdir -p logs/archive; \install -d -m 777 /tmp/apisix_cores/COPY conf conf
COPY lua lua
COPY logrotate /etc/logrotate.dEXPOSE 9080 9443ENTRYPOINT ["openresty", "-p", "/usr/local/apisix", "-c", "/usr/local/apisix/conf/nginx.conf", "-g", "daemon off;"]

插件化开发

1

项目插件介绍

正如在上面代码结构图中所看到的,我们项目的 apisix 目录里面有两个目录,libs 和 plugins,libs 里面我们放一些常用的类库,plugins 里面存放我们自定义的业务插件,我们所有的业务都采用插件机制来开发。下图是我们项目中目前使用到的插件。

稍微解释一下,我们项目的入口域名有两个,一个是提供给 openApi 访问的,认证插件使用的是 Basic Auth,一个是提供给 web 浏览器访问的,认证插件使用的是 web auth(cookie 认证)。

对应 OpenResty 的请求处理流程,我们的插件主要集中在 access 和 log 阶段

插件 阶段 描述
ip-restriction access_by_lua ip 限流,使用 apisix 原生插件
basic-auth access_by_lua 对 openApi 请求用户鉴权,自研插件
web-auth access_by_lua 对 webApi 请求用户鉴权,自研插件
limit-rate access_by_lua 对请求实现用户级别和用户+请求参数级别的限流,自研插件
proxy-rewrite access_by_lua,balancer_by_lua 对请求进行转发,设置接口级别的超时时间,自研插件
log log_by_lua 将请求日志记录到 kafka,再通过 logstash 读到 es 中,自研插件
alarm log_by_lua 根据响应的 statusCode 来报警,自研插件

2

插件开发样例

接下来以 basic-auth 插件 为例,介绍一下 Apache APISIX 插件是怎么开发的

•定义插件对象

local plugin_name = "odin-basic-auth"local schema = {type = "object",properties = {enable = { type = "boolean", default = true, enum = { true, false } },},
}local _M = {version = 0.1,priority = 2802,name = plugin_name,schema = schema,
}

odin-basic-auth 插件只有一个参数 enable,enable 参数表示是否使用本插件,这是由于 Apache APISIX 的插件可以绑定到 service,也可以绑定到 route,如果插件绑定到 service 之后,route 是没有办法关闭插件的,所以需要一个参数用来精细的控制某个 route 不使用 service 绑定的插件,建议官方插件都配上此参数。

•实现检测插件参数的方法

function _M.check_schema(conf)local ok, err = core.schema.check(schema, conf)if not ok thenreturn false, errendreturn true
end

check_schema 方法基本每个插件都一样

•实现插件对应阶段的方法

function _M.access(conf, ctx)-- 0. 检测配置文件,看看是否开启了 enableif not conf.enable thenreturnend-- 1. 获取 basic_auth 里面的 username 和 passwordlocal headers = ngx.req.get_headers()if not headers.Authorization thenreturn 401, { message = "authorization is required" }endlocal username, password, err = extract_auth_header(headers.Authorization)if err thenreturn 401, { message = err }end-- 2. 查看 etcd 获取 username 对应的记录local res = authorizations_etcd:get(username)if res == nil thenreturn 401, { message = "failed to find authorization from etcd" }end-- 3. 如果没有,报认证失败if not res.value or not res.value.id thenreturn 401, { message = "user is not found" }endlocal value = res.value-- 4. 如果有,判断是否用户密码是否正确if value.password ~= password thenreturn 401, { message = "password is error" }end
end

3

Etcd 缓存对象

上面样例中的第二步,我们获取当前请求用户的实际密码以及授权路由列表时使用了 authorizations_etcd:get(username), 这里使用到了 Apache APISIX 的 etcd 缓存对象。

etcd 缓存对象的原理是利用 etcd 的 watch 功能,将数据从 etcd 缓存到内存对象中,业务使用的时候直接从内存中读取,避免网络 io 消耗,etcd 的 watch 功能又保障了数据的实时性,Apache APISIX 的这一特性,简直是让人拍案叫绝。

下面介绍一下如何使用:

•定义一个 etcd 环境对象变量

local authorizations_etcd-- 定义 etcd 对象储存的值 scheme
local appkey_scheme = {type = "object",properties = {username = {description = "username",type = "string",},password = {type = "string",}},
}

•在插件的 init 阶段实例化

function _M.init()authorizations_etcd, err = core.config.new("/authorizations", {automatic = true,item_schema = appkey_scheme})if not authorizations_etcd thenerror("failed to create etcd instance for fetching authorizations: " .. err)returnendend

插件的 init 方法发生在 OpenResty 的 init_worker_by_lua 阶段,换句话说,每个 worker 只初始化一次。automatic 参数设置为 true,Apache APISIX 会开启 watch 功能。业务层只需要实例化 etcd 缓存对象,剩余的事情就都交给 Apache APISIX 做了。

•插件中使用 etcd 缓存对象

 local res = authorizations_etcd:get(username)

4

插件 Api 的使用

上文中 etcd 缓存对象的本质还是需要从 etcd 中取数据,那么这个插件中使用到的用户相关数据是怎么添加到 etcd 呢?这不得不提到插件另一个让人尖叫的特性:Api 特性。

•定义 api

function _M.api()return {{methods = { "POST", "PUT" },uri = "/apisix/plugin/basic-auth/set",handler = set_auth,}}
end

•实现 api 的 handler

local function set_auth()local username = req.get_str("username")local password = req.get_str("password")local key = "/authorizations/" .. username-- 此处存入到 etcdlocal res, err = core.etcd.set(key, { username = username, password = password})if not res thencore.response.exit(500, err)endcore.response.exit(res.status, res.body)
end

•调用接口

> curl -i -X PUT 'http://127.0.0.1:9080/apisix/plugin/basic-auth/set' -d username=zhangsan -d password=hao123 -d user_id=3 -d action_ids=,1,2,3,

上线后遇到的问题

crontab 清理日结

由于我们网关部署在容器,运行一段时间之后,日志文件超过了默认的配额 50G,后来我们在镜像里面默认安装了 cron 和 logrotate,然后在容器 entrypoint 里面开启了 cron 用来解决这个问题。

感谢

最后特别感谢一下 Apache APISIX 的贡献者们,贴出 Apache APISIX 官网[2] 和 Apache APISIX Github 地址[3]

References

[1] etcd 官方文档: https://doczhcn.gitbook.io/etcd/index
[2] Apache APISIX 官网: https://www.iresty.com/
[3] Apache APISIX Github 地址: https://github.com/apache/incubator-apisix

以上就是本次分享的内容~

如果有什么建议,也可以在我们评论区留言,供大家参考学习。

360云计算

由360云平台团队打造的技术分享公众号,内容涉及数据库、大数据、微服务、容器、AIOps、IoT等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享

网关 Apache APISIX 在 360 基础运维平台项目中的实践相关推荐

  1. 广州白云国际机场IT运维平台项目建设方案

     项目背景: 经过多年建设,白云信息科技公司的IT服务管理水平较好地支撑了白云机场弱电系统的运作,但同时还不能满足日益发展的信息化业务对IT服务的要求. 近年来,国内DevOps的出现.发展.不断迭代 ...

  2. 阿里智能运维平台如何助力研发应对双11挑战

    摘要: 12月13-14日,由云栖社区与阿里巴巴技术协会共同主办的<2017阿里巴巴双11技术十二讲>顺利结束,集中为大家分享了2017双11背后的黑科技.本文是<阿里智能运维平台如 ...

  3. 运维的本质是什么?阿里“无人化”智能运维平台的演进

    开发者盛宴来袭!7月28日51CTO首届开发者大赛决赛带来技术创新分享 差不多在两年前,阿里内部出现了很多运维中台.研发中台等等,那有没有后台呢?不好意思,我们只有中台,没有后台,会在中台上构建与业务 ...

  4. 企业级IT运维平台的发展趋势与规划要点

    在IT运维平台建设中常见四个阶段,发现IT运维平台与我们社会生产发展阶段居然有着极其相似的地方,大致分别对应为IT运维的"农耕时代"."工业时代"." ...

  5. 低代码、高敏捷的智和网管平台,运维平台二次开发极简化

    随着信息化进程加快,企业户业务规模的不断扩大,整个IT运维环境变得日益复杂,传统的被动.孤立.半自动式的运维手段已经无法满足运维人员实际运维需求,自动.智能化运维势在必行.但是,任何一款运维软件都无法 ...

  6. SSM医院移动收费运维平台毕业设计源码161045

    目  录 摘要 1 绪论 1.1 开发意义 1.2研究现状 1.3论文结构与章节安排 2医院移动收费运维平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流 ...

  7. 一体化运维平台规划介绍

    运维平台规划体系全介绍 识别运维平台的边界在哪儿,才能更好地构建平台,从而协助运维的日常工作. 在之前的文章中,谈到过"运维的本质--可视化",在可视化的篇幅中,着重介绍自动化的可 ...

  8. 智能化IT运维平台建设方案,基于智和信通运维体系的高敏捷二次开发

    随着企业信息进程不断加速,运维人员需要面对越来越复杂的业务和越来越多样化的用户需求,不断扩展的应用需要越来越合理的模式.越来越智能的工具来保障运维能灵活便捷.安全稳定地开展.企业网络规模的不断扩大,从 ...

  9. SSM+医院移动收费运维平台 毕业设计-附源码161045

    目  录 摘要 1 绪论 1.1 开发意义 1.2研究现状 1.3论文结构与章节安排 2医院移动收费运维平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流 ...

最新文章

  1. 【注意事项】论文/申报书格式
  2. Vue js 的生命周期(看了就懂)
  3. cmd中如何切换指定目录
  4. Linux (redhat 6.5)服务器上安装Webmin
  5. Go的sync.WaitGroup(二):WaitGroup让主程序与协程全部都执行 并且全部执行完成
  6. 9/100. Reverse Linked List
  7. 蓝桥杯第六届国赛JAVA真题----奇怪的数列
  8. 十大WordPress安全设置技巧
  9. 码农和程序员之间的5个关键差异
  10. 移动硬盘打不开(不用格式化)修复方法
  11. 百度百科之中秋节——百度又在中国一个新领域处于领先了,Google……
  12. 【Hadoop】HDFS文件写入与文件读取过程
  13. lamport面包店算法详细讲解及代码实现
  14. 保安值班安排系统C语言课程设计
  15. 基于3D等变图转换的条件抗体设计
  16. 有限元刚度矩阵的一维变带宽存储用C++实现(一)
  17. Boston_House_Price经典例子分析
  18. git与svn的区别及优缺点
  19. PCB生产文件输出(Gerber、钻孔、网表、贴片坐标)
  20. Oracle 保留两位小数详解

热门文章

  1. SpringBoot脚手架工程快速搭建
  2. Hibernate之Criteria查询
  3. 回文串判断(string类:反转reverse)
  4. SpringBoot实战:整合Redis、mybatis,封装RedisUtils工具类等(附源码)
  5. vue点击input框出现弹窗_vue组件实现弹出框点击显示隐藏效果
  6. springmvc进不到controller_Spring、SpringMVC、MyBatis的整合
  7. 云栖社区,诚邀技术同仁一同入驻
  8. web服务器集群(多台web服务器)后session如何同步和共享
  9. MySQL常用函数系列之九:日期和时间函数 函数(4)
  10. HTML5 Canvas平移,放缩,旋转演示