kongのカスタムプラグイン(Go)の作り方

## はじめに

kongのカスタムプラグインについて、色々ハマったので、メモ。
情報自体はそれなりにあるのだが、kongバージョンが古いときの情報が多く、最新(2.4.1)ではちゃんと動作しなかったので、まとめておく。

## TL;DR
* コンパイルするコンテナイメージ(kong/go-plugin-tool)のバージョンは新しいものにしておく。そうしないとコンパイルが通らなかった。
* kong自体はdockerで構築したので、Dockerfile内で`/usr/local/bin`に生成したバイナリをコピーしておく。
* 起動時の環境変数は以下を設定する。今回はプラグインを`key-checker`とした。

|変数名|値|
|---|---|
|KONG_PLUGINSERVER_NAMES|key-checker|
|KONG_PLUGINSERVER_KEY_CHECKER_QUERY_CMD|/usr/local/bin/key-checker -dump
|KONG_PLUGINS|bundled, key-checker|

## 作業の流れ

最初はバージョンとか全く意識せずやっていたので、コンパイルもできるけど動かない状態が続いた。
改めてマニュアルを見ると、作業の流れがちゃんと書いてあった。

1. 構造体を定義する
1. コンスタラクタを定義する
1. 処理を定義する
1. コンパイルする(-buildmode pluginにして.soファイルを作る)
1. ライブラリを`go_plugins_dir`で定義したディレクトリにおく。
1. `go-pdk/server`ライブラリをインクルードする(プラグインをstandaloneにする場合)
1. main関数を定義し、`server.StartSefver`を呼び出す(プラグインをstandaloneにする場合)
1. 実行ファイルを作る

今回はstandaloneとして動かすため4や5は割愛した。

### 構造体を定義

こんな感じで定義した。

```go
type Config struct {
	Apikey string
}
```

### コンストラクタを定義

New()を定義する。

```go
func New() interface{}  {
	return &Config{}
}
```

### 処理するための関数を定義

* Certificate
* Rewrite
* Access
* Response
* Preread
* Log

が実装できるカスタムロジックらしい。シグネチャは全部同じ、とのこと。今回はわかりやすいAccessを使った。
やっていることは、

1. host情報をログに出力
1. ヘッダに`hello`を入れる

だけ。エラー処理とかは全く入れていないので注意。

```go
func (c Config) Access(kong *pdk.PDK)  {
	kong.Log.Notice("plugin called.")
	host, _ := kong.Request.GetHost()
	kong.Log.Notice(host)
	kong.Response.SetHeader("X-hello-from-go", "hello")
}
```

### mainの定義

これだけ。バージョンやpriorityは適当に。

```go
func main() {
	server.StartServer(New, "1.0.0", 1)
}
```

### 実行ファイルのコンパイル

Dockerfileはこんな感じ。

```Dockerfile
FROM kong/go-plugin-tool:latest-alpine-latest AS builder
RUN mkdir -p /tmp/key-checker/
COPY . /tmp/key-checker/
RUN cd /tmp/key-checker/ && \
    go get github.com/Kong/go-pdk && \
    go mod init kong-go-plugin && \
    go build key-checker.go

FROM kong:latest
COPY --from=builder /tmp/key-checker/key-checker /usr/local/bin/key-checker

```

### 起動

docker-composeで以下の様に指定した。

```yaml
  kong:
    image: kong-demo:latest
    user: "${KONG_USER:-kong}"
    depends_on:
      - db
    environment:
      割愛
      KONG_PLUGINSERVER_NAMES: key-checker
      KONG_PLUGINSERVER_KEY_CHECKER_QUERY_CMD: /usr/local/bin/key-checker -dump
      KONG_PLUGINS: bundled, key-checker
```

### 動作確認

key-checkerを各種サービスに登録し、curlを実行すると、こんな感じ。
helloがちゃんとレスポンスヘッダに入った。

```bash
$ curl http://localhost:8000/user-api/users -H "apikey:my-key" --head 
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42
Connection: keep-alive
RateLimit-Reset: 15
X-RateLimit-Remaining-Minute: 4
X-RateLimit-Limit-Minute: 5
RateLimit-Remaining: 4
RateLimit-Limit: 5
X-hello-from-go: hello
Date: Mon, 24 May 2021 04:02:45 GMT
X-Kong-Upstream-Latency: 7
X-Kong-Proxy-Latency: 4
Via: kong/2.4.1
```

ログを見ると、

```bash
docker logs 767
2021/05/24 03:55:44 [notice] 1#0: using the "epoll" event method
2021/05/24 03:55:44 [notice] 1#0: openresty/1.19.3.1
2021/05/24 03:55:44 [notice] 1#0: built by gcc 10.2.1 20201203 (Alpine 10.2.1_pre1) 
2021/05/24 03:55:44 [notice] 1#0: OS: Linux 4.19.76-linuxkit
2021/05/24 03:55:44 [notice] 1#0: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2021/05/24 03:55:44 [notice] 1#0: start worker processes
2021/05/24 03:55:44 [notice] 1#0: start worker process 28
2021/05/24 03:55:44 [notice] 1#0: start worker process 29
2021/05/24 03:55:44 [notice] 28#0: *1 [lua] warmup.lua:92: single_dao(): Preloading 'services' into the core_cache..., context: init_worker_by_lua*
2021/05/24 03:55:44 [notice] 28#0: *1 [lua] warmup.lua:129: single_dao(): finished preloading 'services' into the core_cache (in 87ms), context: init_worker_by_lua*
2021/05/24 03:55:44 [notice] 29#0: *2 [kong] init.lua:290 only worker #0 can manage, context: init_worker_by_lua*
2021/05/24 03:55:44 [notice] 28#0: *10 [kong] process.lua:248 Starting key-checker, context: ngx.timer
2021/05/24 03:55:50 [notice] 28#0: *37 [kong] mp_rpc.lua:286 [key-checker] plugin called., client: 172.22.0.1, server: kong, request: "HEAD /user-api/users HTTP/1.1", host: "localhost:8000"
2021/05/24 03:55:50 [notice] 28#0: *37 [kong] mp_rpc.lua:286 [key-checker] localhost, client: 172.22.0.1, server: kong, request: "HEAD /user-api/users HTTP/1.1", host: "localhost:8000"
172.22.0.1 - - [24/May/2021:03:55:50 +0000] "HEAD /user-api/users HTTP/1.1" 200 0 "-" "curl/7.64.1"
172.22.0.1 - - [24/May/2021:03:56:06 +0000] "HEAD /user-api/users HTTP/1.1" 401 0 "-" "curl/7.64.1"
```

ちゃんとログに出力された。めでたしめでたし。