In addition to the official built-in plug-ins of APISIX, we can also customize the plug-ins according to our own needs. To customize the plug-ins, we need to use the Runner provided by APISIX. At present, the Runner that supports Java, Go and python languages is equivalent to the bridge between APISIX and custom plug-ins, For example, the project Apache APIs IX Python Runner can directly apply Python to the development of APIs IX plug-ins through Python Runner. The overall architecture is as follows:
Among them, Plugin Runner is the plug-in runner of each language. When Plugin Runner is configured, APISIX will start a sub process to run Plugin Runner, which belongs to the same user as APISIX process. When we restart or reload APISIX, Plugin Runner will also be restarted.
If you configure ext plugin - * plug-in for a given route, when the request hits the route, it will trigger APIs IX to initiate RPC calls to Plugin Runner through Unix Socket. The call is divided into two phases:
- Ext plugin pre req: before executing APIs IX built-in plug-in
- Ext plugin post req: after executing the APIs IX built-in plug-in
Next, let's take Python as an example to illustrate how to customize the plug-in. First, get the Apache APIs IX Python runner project:
➜ git clone https://github.com/apache/apisix-python-plugin-runner.git ➜ cd apisix-python-plugin-runner ➜ git checkout 0.1.0 # Switch tool version 0.1.0
If it is in development mode, we can directly start Python Runner with the following command:
➜ APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock python3 apisix/main.py start
After startup, you need to add an external plug-in configuration in the apisik configuration file, as shown below:
➜ vim /path/to/apisix/conf/config.yaml apisix: admin_key: - name: "admin" key: edd1c9f034335f136f87ad84b625c8f1 role: admin ext-plugin: path_for_test: /tmp/runner.sock
Through ext plugin path_ for_ Test specify the unix socket file path of Python Runner. If it is a production environment, you can use ext plugin CMD to specify the startup command of the Runner:
ext-plugin: cmd: [ "python3", "/path/to/apisix-python-plugin-runner/apisix/main.py", "start" ]
Our APIs IX runs in the Kubernetes cluster, so to execute the Python Runner code in the Pod of APIs IX, we naturally need to put our Python code into the container of APIs IX, and then install the related dependencies of custom plug-ins, and directly add the above configuration in the configuration file of APIs IX, Therefore, we re customize the image containing plug-ins based on the image of APISIX, and add the following Dockerfile file in the root directory of APISIX Python plugin runner project:
FROM apache/apisix:2.10.0-alpine ADD . /apisix-python-plugin-runner RUN apk add --update python3 py3-pip && \ cd /apisix-python-plugin-runner && \ python3 -m pip install --upgrade pip && \ python3 -m pip install -r requirements.txt --ignore-installed && \ python3 setup.py install --force
Build a new image based on the Dockerfile above and push it to the Docker Hub:
➜ docker build -t cnych/apisix:py3-plugin-2.10.0-alpine . # Push to DockerHub ➜ docker push cnych/apisix:py3-plugin-2.10.0-alpine
Next, we need to use the image built above to install apisik. We use Helm Chart to install here, so we need to overwrite it through the Values file, as shown below:
# ci/prod.yaml apisix: enabled: true image: repository: cnych/apisix tag: py3-plugin-2.10.0-alpine ......
Since the official Helm Chart does not provide support for ext plugin configuration, we need to manually modify the template file templates / configmap Yaml, add the configuration of ext plugin under the same level directory of apisik attribute, as shown below:
{{- if .Values.extPlugins.enabled }} ext-plugin: {{- if .Values.extPlugins.pathForTest }} path_for_test: {{ .Values.extPlugins.pathForTest }} {{- end }} {{- if .Values.extPlugins.cmds }} cmd: {{- range $cmd := .Values.extPlugins.cmds }} - {{ $cmd }} {{- end }} {{- end }} {{- end }} nginx_config: user: root # fix does not have permission to execute python runner
Then add the following configuration in the customized Values file:
# ci/prod.yaml extPlugins: enabled: true cmds: ["python3", "/apisix-python-plugin-runner/apisix/main.py", "start"]
Then you can redeploy APIs IX:
➜ helm upgrade --install apisix ./apisix -f ./apisix/ci/prod.yaml -n apisix
After deployment, you can see that a sub process of Python Runner will be started in the Pod of apisik:
In the plug-in directory / apifix Python plugin runner / apifix / plugins py files will be automatically loaded. In the above example, there are two plug-ins stop py and rewrite py, we use stop py as an example, the plug-in code is as follows:
from apisix.runner.plugin.base import Base from apisix.runner.http.request import Request from apisix.runner.http.response import Response class Stop(Base): def __init__(self): super(Stop, self).__init__(self.__class__.__name__) def filter(self, request: Request, response: Response): # You can use ` self Config ` get the configuration information. If the plug-in is configured as JSON, it will be automatically converted to dictionary structure # print(self.config) # Set response Header response.headers["X-Resp-A6-Runner"] = "Python" # Set response body response.body = "Hello, Python Runner of APISIX" # Set response status code response.status_code = 201 # By calling ` self Stop() ` interrupt the request process, and the client will respond to the request immediately # If not shown, call ` self Stop() ` or display call ` self Rewrite() ` will continue the request # The default is ` self rewrite()` self.stop()
To implement the plug-in, you must first inherit the Base class and implement the filter function. The core business logic of the plug-in is in the filter function, which only contains Request and Response class objects as parameters. The Request object parameters can obtain the Request information, and the Response object parameters can set the Response information, self Config can get plug-in configuration information and call self. in filter function. Stop() will immediately interrupt the Request, respond to the data, and call self When rewrite(), the Request will continue.
Then we add a routing rule in the previous Nexus application to test the stop plug-in above, and add a routing rule in the ApisixRoute object, as shown below:
apiVersion: apisix.apache.org/v2beta2 kind: ApisixRoute metadata: name: nexus namespace: default spec: http: - name: ext match: hosts: - ops.qikqiak.com paths: - "/extPlugin" plugins: - name: ext-plugin-pre-req # Enable ext plugin pre req plug-in enable: true config: conf: - name: "stop" # Use stop as a custom plug-in value: "{\"body\":\"hello\"}" backends: - serviceName: nexus servicePort: 8081
Just create the above route directly. The core configuration is to enable the EXT plugin pre req plug-in (provided that the plug-in has been enabled in the configuration file and added to the Values of Helm Chart), and then use the conf attribute under config. Conf is an array format, multiple plug-ins can be set at the same time, and the name in the plug-in configuration object is the plug-in name, The name must be consistent with the plug-in code file and object name. value is the plug-in configuration and can be JSON string.
After creation, you can also see the routing configuration format in apisik in the Dashboard:
Then we can visit http://ops.qikqiak.com/extPlugin Use this path to verify our custom plug-in:
➜ curl -i http://ops.qikqiak.com/extPlugin HTTP/1.1 201 Created Date: Thu, 13 Jan 2022 07:04:50 GMT Content-Type: text/plain; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive accept: */* user-agent: curl/7.64.1 host: ops.qikqiak.com X-Resp-A6-Runner: Python Server: APISIX/2.10.0 Hello, Python Runner of APISIX
There is an x-resp-a6-runner in the access request result: Python header information, and the returned body data is hello, python runner of APIs IX, which is consistent with our definition in the plug-in. Here we have finished using Python to customize the APIs IX plug-in. If we have any business logic to deal with, we can directly define a corresponding plug-in.