使用 Python 自定义 APISIX 插件
开发 后端
除了 APISIX 官方内置的插件之外,我们也可以根据自己的需求去自定义插件,要自定义插件需要使用到 APISIX 提供的 Runner,目前已经支持 Java、Go 和 Python 语言的 Runner。

除了 APISIX 官方内置的插件之外,我们也可以根据自己的需求去自定义插件,要自定义插件需要使用到 APISIX 提供的 Runner,目前已经支持 Java、Go 和 Python 语言的 Runner,这个 Runner 相当于是 APISIX 和自定义插件之间的桥梁,比如 apache-apisix-python-runner 这个项目通过 Python Runner 可以把 Python 直接应用到 APISIX 的插件开发中,整体架构如下所示:

 

其中的 Plugin Runner 就是各语言的插件运行器,当配置 Plugin Runner 后,APISIX 会启动一个子进程运行 Plugin Runner,该子进程与 APISIX 进程属于同一个用户,当我们重启或重新加载 APISIX 时,Plugin Runner 也将被重启。

如果你为一个给定的路由配置了 ext-plugin-* 插件,请求命中该路由时将触发 APISIX 通过 Unix Socket 向 Plugin Runner 发起 RPC 调用。调用分为两个阶段:

  • ext-plugin-pre-req:在执行 APISIX 内置插件之前
  • ext-plugin-post-req:在执行 APISIX 内置插件之后

接下来我们就以 Python 为例来说明如何自定义插件,首先获取 apache-apisix-python-runner 项目:

  1. ➜ git clone https://github.com/apache/apisix-python-plugin-runner.git 
  2. ➜ cd apisix-python-plugin-runner 
  3. ➜ git checkout 0.1.0  # 切换刀0.1.0版本 

如果是开发模式,则我们可以直接使用下面的命令启动 Python Runner:

  1. ➜ APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock python3 apisix/main.py start 

启动后需要在 APISIX 配置文件中新增外部插件配置,如下所示:

  1. ➜ vim /path/to/apisix/conf/config.yaml 
  2. apisix: 
  3.   admin_key: 
  4.     - name"admin" 
  5.       key: edd1c9f034335f136f87ad84b625c8f1 
  6.       role: admin 
  7.  
  8. ext-plugin: 
  9.   path_for_test: /tmp/runner.sock 

通过 ext-plugin.path_for_test 指定 Python Runner 的 unix socket 文件路径即可,如果是生产环境则可以通过 ext-plugin.cmd 来指定 Runner 的启动命令即可:

  1. ext-plugin: 
  2.   cmd: [ "python3""/path/to/apisix-python-plugin-runner/apisix/main.py""start" ] 

我们这里的 APISIX 是运行 Kubernetes 集群中的,所以要在 APISIX 的 Pod 中去执行 Python Runner 的代码,我们自然需要将我们的 Python 代码放到 APISIX 的容器中去,然后安装自定义插件的相关依赖,直接在 APISIX 配置文件中添加上面的配置即可,所以我们这里基于 APISIX 的镜像来重新定制包含插件的镜像,在 apisix-python-plugin-runner 项目根目录下新增如下所示的 Dockerfile 文件:

  1. FROM apache/apisix:2.10.0-alpine 
  2.  
  3. ADD . /apisix-python-plugin-runner 
  4.  
  5. RUN apk add --update python3 py3-pip && \ 
  6.     cd /apisix-python-plugin-runner && \ 
  7.     python3 -m pip install --upgrade pip && \ 
  8.     python3 -m pip install -r requirements.txt --ignore-installed && \ 
  9.     python3 setup.py install --force 

基于上面 Dockerfile 构建一个新的镜像,推送到 Docker Hub:

  1. ➜ docker build -t cnych/apisix:py3-plugin-2.10.0-alpine . 
  2. # 推送到DockerHub 
  3. ➜ docker push cnych/apisix:py3-plugin-2.10.0-alpine 

接下来我们需要使用上面构建的镜像来安装 APISIX,我们这里使用的是 Helm Chart 进行安装的,所以需要通过 Values 文件进行覆盖,如下所示:

  1. # ci/prod.yaml 
  2. apisix: 
  3.   enabled: true 
  4.  
  5.   image: 
  6.     repository: cnych/apisix 
  7.     tag: py3-plugin-2.10.0-alpine 
  8. ...... 

由于官方的 Helm Chart 没有提供对 ext-plugin 配置的支持,所以需要我们手动修改模板文件 templates/configmap.yaml,在 apisix 属性同级目录下面新增 ext-plugin 相关配置,如下所示:

  1. {{- if .Values.extPlugins.enabled }} 
  2. ext-plugin: 
  3.   {{- if .Values.extPlugins.pathForTest }} 
  4.   path_for_test: {{ .Values.extPlugins.pathForTest }} 
  5.   {{- end }} 
  6.   {{- if .Values.extPlugins.cmds }} 
  7.   cmd: 
  8.   {{- range $cmd := .Values.extPlugins.cmds }} 
  9.   - {{ $cmd }} 
  10.   {{- end }} 
  11.   {{- end }} 
  12. {{- end }} 
  13.  
  14. nginx_config: 
  15.   user: root  # fix 执行 python runner没权限的问题 

然后在定制的 Values 文件中添加如下所示的配置:

  1. # ci/prod.yaml 
  2. extPlugins: 
  3.   enabled: true 
  4.   cmds: ["python3""/apisix-python-plugin-runner/apisix/main.py""start"

接着就可以重新部署 APISIX 了:

  1. ➜ helm upgrade --install apisix ./apisix -f ./apisix/ci/prod.yaml -n apisix 

部署完成后在 APISIX 的 Pod 中可以看到会启动一个 Python Runner 的子进程:

在插件目录 /apisix-python-plugin-runner/apisix/plugins 中的 .py 文件都会被自动加载,上面示例中有两个插件 stop.py 和 rewrite.py,我们以 stop.py 为例进行说明,该插件代码如下所示:

  1. from apisix.runner.plugin.base import Base 
  2. from apisix.runner.http.request import Request 
  3. from apisix.runner.http.response import Response 
  4.  
  5.  
  6. class Stop(Base): 
  7.     def __init__(self): 
  8.         super(Stop, self).__init__(self.__class__.__name__) 
  9.  
  10.     def filter(self, request: Request, response: Response): 
  11.         # 可以通过 `self.config` 获取配置信息,如果插件配置为JSON将自动转换为字典结构 
  12.         # print(self.config) 
  13.  
  14.         # 设置响应 Header 头 
  15.         response.headers["X-Resp-A6-Runner"] = "Python" 
  16.         # 设置响应body 
  17.         response.body = "Hello, Python Runner of APISIX" 
  18.         # 设置响应状态码 
  19.         response.status_code = 201 
  20.  
  21.         # 通过调用 `self.stop()` 中断请求流程,此时将立即响应请求给客户端 
  22.         # 如果未显示调用 `self.stop()` 或 显示调用 `self.rewrite()`将继续将请求 
  23.         # 默认为 `self.rewrite()` 
  24.         self.stop() 

实现插件首先必须要继承 Base 类,必须实现 filter 函数,插件执行核心业务逻辑就是在 filter 函数中,该函数只包含 Request 和 Response 类对象作为参数,Request 对象参数可以获取请求信息,Response 对象参数可以设置响应信息 ,self.config 可以获取插件配置信息,在 filter 函数中调用 self.stop() 时将马上中断请求,响应数据,调用 self.rewrite() 时,将会继续请求。

然后我们在前面的 Nexus 应用中新增一个路由来测试我们上面的 stop 插件,在 ApisixRoute 对象中新增一个路由规则,如下所示:

  1. apiVersion: apisix.apache.org/v2beta2 
  2. kind: ApisixRoute 
  3. metadata: 
  4.   name: nexus 
  5.   namespace: default 
  6. spec: 
  7.   http: 
  8.     - name: ext 
  9.       match: 
  10.         hosts: 
  11.           - ops.qikqiak.com 
  12.         paths: 
  13.           - "/extPlugin" 
  14.       plugins: 
  15.       - name: ext-plugin-pre-req  # 启用ext-plugin-pre-req插件 
  16.         enable: true 
  17.         config: 
  18.           conf: 
  19.           - name"stop"  # 使用 stop 这个自定义插件 
  20.             value: "{\"body\":\"hello\"}" 
  21.       backends: 
  22.       - serviceName: nexus 
  23.         servicePort: 8081 

直接创建上面的路由即可,核心配置是启用 ext-plugin-pre-req 插件(前提是在配置文件中已经启用该插件,在 Helm Chart 的 Values 中添加上),然后在 config 下面使用 conf 属性进行配置,conf 为数组格式可以同时设置多个插件,插件配置对象中 name 为插件名称,该名称需要与插件代码文件和对象名称一致,value 为插件配置,可以为 JSON 字符串。

创建后同样在 Dashboard 中也可以看到 APISIX 中的路由配置格式:

接着我们可以来访问 http://ops.qikqiak.com/extPlugin 这个路径来验证我们的自定义插件:

  1. ➜ curl -i http://ops.qikqiak.com/extPlugin 
  2. HTTP/1.1 201 Created 
  3. Date: Thu, 13 Jan 2022 07:04:50 GMT 
  4. Content-Type: text/plain; charset=utf-8 
  5. Transfer-Encoding: chunked 
  6. Connection: keep-alive 
  7. accept: */* 
  8. user-agent: curl/7.64.1 
  9. host: ops.qikqiak.com 
  10. X-Resp-A6-Runner: Python 
  11. Server: APISIX/2.10.0 
  12.  
  13. Hello, Python Runner of APISIX 

访问请求结果中有一个 X-Resp-A6-Runner: Python 头信息,返回的 body 数据为 Hello, Python Runner of APISIX,和我们在插件中的定义是符合的。到这里就完成了使用 Python 进行 APISIX 自定义插件,我们有任何的业务逻辑需要处理直接去定义一个对应的插件即可。

 

责任编辑:姜华 来源: k8s技术圈

同话题下的热门内容

Python教程:删除列表中某个元素的三种方法用 Python 机器学习预测黄金价格Rust VS Python:为什么越来越流行,取代榜一Python?Python 为什么不设计 Do-while 循环结构?为什么要开发 Go 这门新语言?六个实例,八段代码,详解Python中的for循环你可能不知道但却很有用的 Java 特性抽丝剥茧,深入剖析 Python 如何实现变量交换!

编辑推荐

使用Kotlin做开发一个月后的感想面试官问你什么是消息队列?把这篇甩给他!五大自动化测试的Python框架图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)【教程】终于有人把Java内存模型说清楚了!
我收藏的内容
点赞
收藏

51CTO技术栈视频号