Plugins in Other Languages

    Each plugin server hosts one or more plugins and communicates with the main Kong Gateway process through Unix sockets. If so configured, Kong Gateway can manage those processes, starting, restarting and stopping as necessary.

    Kong Gateway currently maintains a Go language plugin server, go-pluginserver, the corresponding PDK library package , the JavaScript language support kong-js-pdk and Python language support .

    Kong Gateway plugin server configuration

    The property is a comma-separated list of names, one for each plugin server process. These names are used to group each process’ properties and to annotate log entries.

    For each name, other properties can be defined:

    For example, you could set Go and Python plugins like this (assuming an hypothetical Python plugin server called pypluginserver.py):

    To enable those plugins, add the each plugin name to the plugins config. Assume we have those hello plugins in each language:

    1. plugins = bundled, go-hello, js-hello, py-hello

    Kong Gateway versions 2.0.x to 2.2.x supported only Go external plugins and a single plugin server using a different configuration style. Starting with Kong Gateway version 2.3, the old style is recognized and internally transformed to the new style.

    If property pluginserver_names isn’t defined, the legacy properties go_plugins_dir and go_pluginserver_exe are tried:

    PropertyDescriptionDefault
    go_plugins_dirDirectory with Go pluginsoff, meaning to disable Go plugins
    go_pluginserver_exePath to the go-pluginserver executable/usr/local/bin/go-pluginserver

    Notes:

    • The old style doesn’t allow multiple plugin servers.
    • Version 0.5.0 of requires the old style configuration.
    • The new style configuration requires v0.6.0 of go-pluginserver

    Kong Gateway support for the Go language consist of two parts:

    • as a library, provides Go functions to access Kong Gateway features of the PDK.
    • an executable to dynamically load plugins written in Go.

    Notes:

    The Kong Gateway version 2.3 allows multiple plugin servers; in particular it’s now possible to write single-plugin servers, in effect plugins as microservices. To help with this, version v0.6.0 of the go-pdk package includes an optional plugin server. See for more information.

    The go-pluginserver process is still supported. Its main advantage is that it’s a single process for any number of plugins, but the dynamic loading of plugins has proven challenging under the Go language (unlike the microservice architecture, which is well supported by the language and tools).

    Development

    To write a Kong Gateway plugin in Go, you need to:

    1. Define a structure type to hold configuration.
    2. Write a New() function to create instances of your structure.
    3. Add methods on that structure to handle phases.

      If you want a dynamically-loaded plugin to be used with go-pluginserver:

    4. Compile your Go plugin with go build -buildmode plugin.

    5. Put the resulting library (the .so file) into the go_plugins_dir directory.

      If you want a standalone plugin microservice:

    6. Include the go-pdk/server sub-library.

    7. Add a main() function that calls server.StartServer(New, Version, Priority).
    8. Compile as an executable with go build.

    Note: Check out for example Go plugins.

    1. Configuration Structure

    Plugins written in Lua define a schema to specify how to read and validate configuration data coming from the datastore or the Admin API. Since Go is a statically-typed language, all that specification is handled by defining a configuration structure:

    1. type MyConfig struct {
    2. Path string
    3. Reopen bool
    4. }

    Public fields (that is, those starting with a capital letter) will be filled with configuration data. If you want them to have a different name in the datastore, add field tags as defined in the encoding/json package:

    1. type MyConfig struct {
    2. Path string `json:my_file_path`
    3. Reopen bool `json:reopen`
    4. }

    2. New() Constructor

    Your plugin must define a function called New that creates an instance of this type and returns as an interface{}. In most cases, it’s just this:

    1. func New() interface{} {
    2. return &MyConfig{}
    3. }

    You can add more fields to the structure and they’ll be passed around, but there are no guarantees about the lifetime or quantity of configuration instances.

    3. Phase Handlers

    Similarly to Kong Gateway Lua plugins, you can implement custom logic to be executed at various points of the request processing lifecycle. For example, to execute custom Go code in the access phase, define a function named Access:

    1. func (conf *MyConfig) Access (kong *pdk.PDK) {
    2. ...
    3. }
    • Certificate
    • Rewrite
    • Access
    • Response
    • Preread
    • Log

    Similar to Lua plugins, the presence of the Response handler automatically enables the buffered proxy mode.

    4. Version and Priority

    Similarly to Kong Gateway Lua plugins, you can define the version number and priority of execution by having following lines in plugin code:

    1. const Version = "1.0.0"
    2. const Priority = 1

    Kong Gateway executes plugins from highest priority to lowest ones.

    Embedded server

    Each plugin can be a microservice, compiled as a standalone executable.

    To use the embedded server, include github.com/Kong/go-pdk/server in the imports list, and add a main() function:

    1. func main () {
    2. server.StartServer(New, Version, Priority)
    3. }

    Note that the main() function must have a package main line at the top of the file.

    Then, a standard Go build creates an executable. There are no extra go-pluginserver, no plugin loading, and no compiler/library/environment compatibility issues.

    The resulting executable can be placed somewhere in your path (for example, /usr/local/bin). The common -h flag shows a usage help message:

    1. $ my-plugin -h
    2. Usage of my-plugin:
    3. -dump
    4. Dump info about plugins
    5. -help
    6. Show usage info
    7. -kong-prefix string
    8. Kong prefix path (specified by the -p argument commonly used in the Kong CLI) (default "/usr/local/kong")

    When run without arguments, it creates a socket file with the kong-prefix and the executable name, appending .socket. For example, if the executable is my-plugin, it would be /usr/local/kong/my-plugin.socket by default.

    Example configuration

    Two standalone plugins, called my-plugin and other-one:

    1. pluginserver_names = my-plugin,other-one
    2. pluginserver_my_plugin_socket = /usr/local/kong/my-plugin.socket
    3. pluginserver_my_plugin_start_cmd = /usr/local/bin/my-plugin
    4. pluginserver_my_plugin_query_cmd = /usr/local/bin/my-plugin -dump
    5. pluginserver_other_one_socket = /usr/local/kong/other-one.socket
    6. pluginserver_other_one_start_cmd = /usr/local/bin/other-one
    7. pluginserver_other_one_query_cmd = /usr/local/bin/other-one -dump

    Note that the socket and start command settings coincide with their defaults, so they can be omitted:

    Developing JavaScript plugins

    Kong Gateway support for the JavaScript language is provided by . The library provides a plugin server to provide runtime for JavaScript plugins, and functions to access Kong Gateway features of the PDK.

    TypeScript is also supported in the following ways:

    • includes type definitions for PDK functions that allow type checking when developing plugins in TypeScript.
    • Plugin written in TypeScript can be loaded directly and transpiled on the fly.

    kong-js-pdk can be installed using npm. To install the plugin server binary globally:

    1. npm install kong-pdk -g

    Assume the plugins are stored in /usr/local/kong/js-plugins:

    1. pluginserver_names = js
    2. pluginserver_js_socket = /usr/local/kong/js_pluginserver.sock
    3. pluginserver_js_start_cmd = /usr/local/bin/kong-js-pluginserver --plugins-directory /usr/local/kong/js-plugins
    4. pluginserver_js_query_cmd = /usr/local/bin/kong-js-pluginserver --plugins-directory /usr/local/kong/js-plugins --dump-all-plugins

    Development

    Install kong-js-pdk in your local development directory:

    1. npm install kong-pdk --save

    A valid JavaScript plugin implementation should export the following object:

    1. module.exports = {
    2. Plugin: KongPlugin,
    3. Schema: [
    4. { message: { type: "string" } },
    5. ],
    6. Version: '0.1.0',
    7. Priority: 0,
    8. }

    Plugin attribute defines the class that implements this plugin. Schema defines the config schema of plugin, it shares the same syntax as it’s a Lua plugin. Version and Priority defines the version number and priority of execution respectively.

    Note: Check out for example JavaScript and TypeScript plugins.

    1. Phase Handlers

    Similarly to Kong Gateway Lua plugins, you can implement custom logic to be executed at various points of the request processing lifecycle. For example, to execute custom JavaScript code in the access phase, define a function named access:

    1. class KongPlugin {
    2. constructor(config) {
    3. this.config = config
    4. }
    5. async access(kong) {
    6. // ...
    7. }
    8. }

    The phases you can implement custom logic for are as follows, and the expected function signature is the same for all of them:

    • certificate
    • rewrite
    • access
    • response
    • preread
    • log

    Similar to Lua plugins, the presence of the response handler automatically enables the buffered proxy mode.

    2. PDK functions

    kong-js-pdk invokes PDK functions in Kong through network-based IPC (inter-process communication). So each function returns a Promise instance; it’s convenient to use async/await keywords in phase handlers for better readability.

    1. class KongPlugin {
    2. constructor(config) {
    3. this.config = config
    4. }
    5. async access(kong) {
    6. let host = await kong.request.getHeader("host")
    7. // do something to host
    8. }
    9. }

    Or consume Promise in a traditional way:

    1. class KongPlugin {
    2. constructor(config) {
    3. this.config = config
    4. }
    5. async access(kong) {
    6. kong.request.getHeader("host")
    7. .then((host) => {
    8. // do something to host
    9. })
    10. }
    11. }

    3. Plugin dependencies

    When using the plugin server, plugins are allowed to have extra dependencies, as long as the directory that holds plugin source code also includes a node_modules directory.

    Assuming plugins are stored under /usr/local/kong/js-plugins, the extra dependencies are then defined in /usr/local/kong/js-plugins/package.json. Developers also need to run npm install under /usr/local/kong/js-plugins to install those dependencies locally into /usr/local/kong/js-plugins/node_modules.

    Note in this case, the node version and architecture that runs the plugin server and the one that runs npm install under plugins directory must match. For example, it may break when you run npm install under macOS and mount the working directory into a Linux container.

    Testing

    Install jest as a development dependency, and add the test script in package.json:

    1. npm install jest --save-dev

    The package.json has content similar to the following:

    1. {
    2. "test": "jest"
    3. },
    4. "devDependencies": {
    5. "jest": "^26.6.3",
    6. "kong-pdk": "^0.3.2"
    7. }

    Run the test through npm with:

    Note: Check out for examples on how to write test using jest.

    Kong Gateway support for the Python language is provided by kong-python-pdk. The library provides a plugin server to provide runtime for Python plugins, and functions to access Kong Gateway features of the .

    kong-python-pdk can be installed using pip. To install the plugin server binary and PDK globally, use:

    1. pip3 install kong-pdk

    Assume the plugins are stored in /usr/local/kong/python-plugins:

    1. pluginserver_names = python
    2. pluginserver_python_socket = /usr/local/kong/python_pluginserver.sock
    3. pluginserver_python_start_cmd = /usr/local/bin/kong-python-pluginserver --plugins-directory /usr/local/kong/python-plugins
    4. pluginserver_python_query_cmd = /usr/local/bin/kong-python-pluginserver --plugins-directory /usr/local/kong/python-plugins --dump-all-plugins

    Development

    A valid Python plugin implementation has following attributes:

    1. Schema = (
    2. { "message": { "type": "string" } },
    3. )
    4. version = '0.1.0'
    5. priority = 0
    6. class Plugin(object):
    7. pass

    A class named Plugin defines the class that implements this plugin. Schema defines the config schema of plugin, it shares the same syntax as it’s a Lua plugin. version and priority defines the version number and priority of execution respectively.

    Note: Check out this repository for example Python plugins and .

    1. Phase Handlers

    Similarly to Kong Gateway Lua plugins, you can implement custom logic to be executed at various points of the request processing lifecycle. For example, to execute custom Go code in the access phase, define a function named access:

    1. class Plugin(object):
    2. def __init__(self, config):
    3. self.config = config
    4. def access(self, kong):
    5. pass

    The phases you can implement custom logic for are as follows, and the expected function signature is the same for all of them:

    • certificate
    • rewrite
    • access
    • response
    • preread
    • log

    Similar to Lua plugins, the presence of the response handler automatically enables the buffered proxy mode.

    2. Type hints

    kong-python-pdk supports in Python 3.x. To use type lint and autocomplete in IDE, user can annotate the kong parameter in phase handler:

    1. import kong_pdk.pdk.kong as kong
    2. class Plugin(object):
    3. def __init__(self, config):
    4. self.config = config
    5. def access(self, kong: kong.kong):
    6. host, err = kong.request.get_header("host")

    Embedded server

    Each plugin can be a microservice. To use the embedded server, use the following code:

    1. if __name__ == "__main__":
    2. from kong_pdk.cli import start_dedicated_server
    3. start_dedicated_server("py-hello", Plugin, version, priority)

    Note the first argument to start_dedicated_server defines the plugin name and must be unique across all languages.

    Example configuration

    Two standalone plugins, called my-plugin and other-one:

    1. pluginserver_names = my-plugin,other-one
    2. pluginserver_my_plugin_socket = /usr/local/kong/my-plugin.socket
    3. pluginserver_my_plugin_start_cmd = /path/to/my-plugin.py
    4. pluginserver_my_plugin_query_cmd = /path/to/my-plugin.py --dump
    5. pluginserver_other_one_socket = /usr/local/kong/other-one.socket
    6. pluginserver_other_one_start_cmd = /path/to/other-one.py
    7. pluginserver_other_one_query_cmd = /path/to/other-one.py -dump

    Note that the socket and start command settings coincide with their defaults, so they can be omitted:

    1. pluginserver_names = my-plugin,other-one
    2. pluginserver_my_plugin_query_cmd = /path/to/my-plugin --dump
    3. pluginserver_other_one_query_cmd = /path/to/other-one --dump

    Python plugin server and embedded server supports multiple concurrency model. By default, the server starts in multi-threading mode.

    If your workload is IO intensive, consider the gevent model by adding -g to pluginserver’s start_cmd. If your workload is CPU intensive, consider the multi-processing model by adding -m to pluginserver’s start_cmd.

    Performance for external plugins

    Depending on implementation details, Go plugins are able to use multiple CPU cores and so perform best on a multi-core system. JavaScript plugins are currently single-core only and there’s no dedicated plugin server support. Python plugins can use a dedicated plugin server to span workload to multiple CPU cores as well.

    Unlike Lua plugins where invoking PDK functions are handled in local processes, calling PDK functions in external plugins implies inter-process communications and so is a relatively expensive operation. Because of the expense of calling PDK functions in external plugins, the performance of Kong using external plugins is highly related to the number of IPC (inter-process communication) calls in each request.

    The following graph demonstrates the correlation between performance and count of IPC calls per request. Numbers of RPS and latency are removed as they are dependent on hardware and to avoid confusion.

    Plugins in Other Languages - 图2

    To use plugins requiring external plugin servers, both the plugin servers and the plugins themselves need to be installed inside the Kong Gateway container.

    For plugins written in Golang, build external plugins in embedded server mode in a builder container and copy or mount the binary artifacts into the Kong Gateway container. For plugins written in JavaScript, first install Node and npm, then use npm to install kong-pdk, and finally copy or mount the plugins source code into the Kong Gateway container. For plugins written in Python, install Python and pip. Then use pip to install kong-pdk. Finally, copy or mount the plugin’s source code into the Kong Gateway container.

    Note: Official Kong Gateway images are configured to run as the nobody user. When building a custom image, to copy files into the Kong Gateway image, you must temporarily set the user to root.

    1. FROM kong
    2. USER root
    3. # Example for GO:
    4. COPY your-go-plugin /usr/local/bin/your-go-plugin
    5. # Example for JavaScript:
    6. RUN apk update && apk add nodejs npm && npm install -g kong-pdk
    7. COPY you-js-plugin /path/to/your/js-plugins/you-js-plugin
    8. # Example for Python
    9. # PYTHONWARNINGS=ignore is needed to build gevent on Python 3.9
    10. RUN apk update && \
    11. apk add python3 py3-pip python3-dev musl-dev libffi-dev gcc g++ file make && \
    12. PYTHONWARNINGS=ignore pip3 install kong-pdk
    13. COPY you-py-plugin /path/to/your/py-plugins/you-py-plugin
    14. # reset back the defaults
    15. USER kong
    16. ENTRYPOINT ["/docker-entrypoint.sh"]
    17. EXPOSE 8000 8443 8001 8444
    18. STOPSIGNAL SIGQUIT