背景

Kong Gatewayを使ってAPIにアクセスする時には、もしリクエストが失敗したら通知して欲しいですね。通常のやり方はLog系のプラグインでLogを保存してから、3rd partyの製品(ELK)でAlertを設定して通知することです。でも他の製品を使うとデプロイも面倒だし、ライセンスや設定内容も必要だし、できればKong内部で実現したいな。。という要望があると思います。 今回は、Exit Transformer プラグインを使って、リクエストがエラーだったらLuaスクリプトでSlack Webhookを叩くことをメモします。このプラグインは、Lua 関数を使用して、Kong 応答終了メッセージを変換およびカスタマイズすることができます。メッセージ、ステータスコード、ヘッダーの変更から、Kong 応答の構造の完全な変換まで、さまざまな機能があります。

プラグインを試す

まずはページ上にある例を試してみよう。

サービスとルートを作成

1
2
$ http :8001/services name=example.com host=mockbin.org
$ http -f :8001/services/example.com/routes hosts=example.com

失敗させるため key auth プラグインを実装

1
$ http :8001/services/example.com/plugins name=key-auth

Luaスクリプトを作成

以下のコードでは、x-some-headerのヘッダーを追加し、メッセージの最後に, arrを追加した。 この内容をtransform.luaとして保存する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    -- transform.lua
    return function(status, body, headers)
      if not body or not body.message then
        return status, body, headers
      end

      headers = { ["x-some-header"] = "some value" }
      local new_body = {
        error = true,
        status = status,
        message = body.message .. ", arr!",
      }

      return status, new_body, headers
    end

スクリプトを使ってプラグインをデプロイ

1
2
3
   http -f :8001/services/example.com/plugins \
     name=exit-transformer \
     [email protected]

動作確認

ここまで設定したらアクセスしてみましょう。ヘッダーと最後のメッセージarrがちゃんとレスポンスに追加されました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
❯ http :8000 Host:example.com
HTTP/1.1 401 Unauthorized
Connection: keep-alive
Content-Length: 73
Content-Type: application/json; charset=utf-8
Date: Fri, 10 Feb 2023 07:36:15 GMT
Server: kong/3.1.1.3-enterprise-edition
WWW-Authenticate: Key realm="kong"
X-Kong-Response-Latency: 2
x-some-header: some value

{
    "error": true,
    "message": "No API key found in request, arr!",
    "status": 401
}

Webhookをスクリプトに実装

今回はSlackのWebhookを利用します。メッセージに内容を追加した後にWebhookをキックする実装にします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
...
      local httpc = require("resty.http").new()

      -- Single-shot requests use the `request_uri` interface.
      local res, err = httpc:request_uri("https://hooks.slack.com/services/xxxxxx/xxxxxx/xxxxxxxx", {
          method = "POST",
          body = '{"text": "Hello, world."}',
          headers = {
              ["Content-Type"] = "application/json",
          },
      })
      if not res then
          ngx.log(ngx.ERR, "request failed: ", err)
          return
      end

      return status, new_body, headers
...

リクエストを送るために、lua-resty-httpを利用しています。一般的にはrequireを利用してリクエストを出しますが、nginx イベント ループでうまく機能するとは思わない心配があります。つまり、hooks.slack.com からの応答を待機している間、nginx が他のリクエストを処理できなくなる可能性があります。

もう一つ注意すべきところは、外部のライブラリを呼び出しているため、untrusted_luaパラメータをOnにする必要があります。これをしないと以下のようなエラーが出ます。

1
2023/02/10 02:19:13 [error] 2175#0: *15812 lua entry thread aborted: runtime error: /usr/local/share/lua/5.1/kong/tools/sandbox.lua:88: require 'ssl.https' not allowed within sandbox

もう一度動作確認

さてさっきのAPIにもう一度アクセスしてSlackのメッセージを確認しましょう!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
❯ http :8000 Host:example.com
HTTP/1.1 401 Unauthorized
Connection: keep-alive
Content-Length: 73
Content-Type: application/json; charset=utf-8
Date: Mon, 13 Feb 2023 13:28:42 GMT
Server: kong/3.1.1.3-enterprise-edition
WWW-Authenticate: Key realm="kong"
X-Kong-Response-Latency: 0
x-some-header: some value

{
    "error": true,
    "message": "No API key found in request, arr!",
    "status": 401
}

iShot_2023-02-13_22.29.31.png

きたーーーーーーーーーーーーー!