Kong Gatewayでは3.0以降、Routeの表現方法にExpressionsというものが追加されている。
Routeを設定する時、従来はPathsやHeadersなど各項目ごとに値を設定して、条件に一致するものがRouteのパスとして評価されていたが、Expressionsの場合は複数の項目ではなく1つの評価式だけでRouteのパスを設定するような硬派な設定方法である。
Expressionsで設定する場合、以下のメリット・デメリットがある。
- メリット:
- プラグインなしで許可しないヘッダなどの指定がブラックリスト的な書き方などが出来たり、表現方法が広がる
- 評価式の優先順位をコントロールしたり、正規表現を避けながら複雑な式が書けるため、性能チューニングが可能
- デメリット:
- 評価式の書き方の学習コストが発生する
- 従来のRouteをコンバートする方法がなく、既存のRouteでExpressionに切り替える場合は書き直しが必要(
deck file openapi2kong
でも不可)
ドキュメントにも以下のように書いてあり、
It is recommended that new deployments use the expressions router as it is more powerful and expressive.
(意訳:より強力で表現力が豊かなため、新規導入の場合はExpressionsのRouteを使用することを推奨する。)
使い方は覚えておいて損はないだろう。
ということで、今回はExpressionsの使い方をざっくり紹介する。
Expressionsを利用するための設定
kong.confにrouter_flavor
というパラメータがある。
デフォルトではtraditional_compatible
となっていて、内部的にはExpressionsは利用するのだが、外部から見えず指定できない設定となっている。
Expressionsを利用する場合はこれをexpressions
に変更してKong Gatewayを起動する必要がある。
Dockerで起動する場合はdocker run
の引数に-e "KONG_ROUTER_FLAVOR=expressions"
を追加する。
起動に成功すると、Routeの作成画面にExpressions
のタブが表示されるようになる。
Expressionsの前提知識
Expressionsで指定する評価式のフォーマットはLanguage Referencesに記載がある。
最低限これだけ知っておけば良さそうな演算子(Operator)だけここで紹介する。
演算子 | 意味 |
---|---|
== |
完全一致 |
!= |
一致しない |
~ |
正規表現と一致 |
^= |
前方一致(value*) |
=^ |
後方一致(*value) |
&& |
And条件 |
|| |
Or条件 |
(評価式) |
所謂算数のカッコ。評価する対象をグループ化 |
! |
カッコの前でのみ利用でき、結果を否定する |
シェルスクリプトや一般的な開発言語の演算子とかとそれほど差はないので、コードを書いたことがある人からするとそれほど敷居は高くないと思われる。
更に細かい使い方はLanguage Referencesの方を参考にして欲しい。
なお、正規表現についてはRustの正規表現に従う。
なので、例えば「barのあとに1桁の数字が続く」というパターンの場合はr#"bar\d"#
となる。
あと、ホストやパス、ヘッダなどの指定はMaching fieldsを参考にして欲しい。
1つ注意点として、ヘッダ名は小文字とアンダースコアだけで定義する必要がある。
例えばFoo-BaRというヘッダ名の場合はfoo_barと指定する。
動作検証
検証にあたり、Local PCにDockerでKong Gatewayを起動した。
また以下の手順で事前にServiceを作成している。
SVC_NAME="httpbin-service"
curl -s -X POST localhost:8001/services \
-H "Content-Type: application/json" \
-d '{
"name":"'"${SVC_NAME}"'",
"url":"https://httpbin.konghq.com"
}'
基本ケース:パスのみの指定
最初にパスのみを設定したRouteを作成して動作確認を行う。
ここではcurlで設定する。
curlで設定する際、Expressionの指定はContent-Type: multipart/form-data
で渡す必要がある。
curlで指定する場合は--form-string
で評価式を渡せばMIME typeをmulitpart/form-data
にしてくれるので、パスを/httpbin
としてRouteを作成する場合は以下のような式で作成できる。
curl -X POST \
http://localhost:8001/services/${SVC_NAME}/routes \
--form-string name=httpbin-route \
--form-string \
'expression=(http.path == "/httpbin")'
作成するとKong Managerでは以下のように評価式が確認できる。
deck gateway dump
だと以下のようになる。
routes:
- expression: (http.path == "/httpbin")
https_redirect_status_code: 426
name: httpbin-route
path_handling: v0
preserve_host: false
priority: 0
protocols:
- http
- https
regex_priority: 0
request_buffering: true
response_buffering: true
strip_path: true
実際にアクセスするとHTMLが取得できる。
$ curl -s localhost:8000/httpbin | head
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
:(省略)
ただし、先程はパスの指定を==
で指定したため、パスが完全一致しないとルーティングしてくれない。
なので、/httpbin/ip
などにアクセスしても「no Route」とエラーになる。
$ curl -s localhost:8000/httpbin/ip
{
"message":"no Route matched with those values",
"request_id":"cec3864c95a8cb452a0f99285f32b759"
}
パスの指定を前方一致の^=
に変えてみる。
curl -X PATCH \
http://localhost:8001/routes/httpbin-route \
--form-string \
'expression=(http.path ^= "/httpbin")'
前方一致に変えると問題なくアクセスできた。
$ curl -s localhost:8000/httpbin/ip
{
"origin": "192.168.127.1"
}
複雑なケース:ヘッダによる指定
先程作成したRouteにヘッダによる条件も足してみる。
ここでは少し複雑な条件として、以下の時にルーティングするよう設定する。
-
my-header: foo
またはmy-header: bar
が指定されてたら許可 -
my-bad-header
が指定されていたら不許可
curl -X PATCH \
http://localhost:8001/routes/httpbin-route \
--form-string \
'expression=(http.path ^= "/httpbin")
&& ((http.headers.my_header == "foo" || http.headers.my_header == "bar" )
&& !(http.headers.my_bad_header ~ r#".*"#))'
ヘッダ名は前述の通りハイフンはアンダースコアに変更する必要があるので変換している。
またmy-bad-header
を弾く部分は~
で正規表現を指定し、r#".*"#
で何か文字列が指定していたら条件が合致する、とし、この評価式の先頭に!
をつけることで否定することでヘッダが指定されていたら弾くようにしている。
実際に実行した結果は以下となる。
ヘッダなしの場合は以下。
$ curl -s localhost:8000/httpbin/ip
{
"message":"no Route matched with those values",
"request_id":"850e8d007d405e2ad6e8e63b4502e72f"
}
許可されたヘッダを指定した場合は以下。
$ curl -s localhost:8000/httpbin/ip -H "my-header: bar"
{
"origin": "192.168.127.1"
}
許可されたヘッダ+禁止ヘッダを指定した場合は以下。
$curl -s localhost:8000/httpbin/ip -H "my-header: bar" -H "my-bad-header: true"
{
"message":"no Route matched with those values",
"request_id":"94f9cd786de3f270c7d763580090615d"
}
ということで、従来のRoute設定だと難しい複雑な設定を簡単に設定することが出来た。
Router Playground
最後にRouter Playgroundという機能についても触れておきたい。
Kong ManagerでRoute設定する際、ExpressionsのタブをクリックしてExpressionを記入する時にTest with Router Playground
という項目が確認できる。
これをクリックすると以下のような画面に飛び、作成した評価式に問題がないかを確認する事が出来る。
例えば、ヘッダにアンダースコアではなくハイフンを指定すると、以下のように枠全体が赤くなり、またエラー箇所にアンダーラインが走ってエラーを教えてくれる。
マウスカーソルをアンダーラインに合わせるとエラーの理由も教えてくれる。(ただ、このケースだとparsing error: expected binary_operator
と少し分かりにくいエラーメッセージになるが。。)
Add
から接続先を書けば、http.host
などの評価の時にハイライトしてくれたりもする。
評価式を作成する際にはこれで事前にチェックするとよさそうだ。
まとめ
Expressionを使った場合、少しプログラミングっぽい書き方が必要で利用者の敷居を高くする反面、自由度が高くプラグインなしでも様々な条件を設定できることが確認できた。
また今回は検証しなかったが、評価式が評価される順番や正規表現で引っかかる回数などを意識して評価式を書けば性能チューニングも出来、細かく厳しい要件があるようなシステムでは使い勝手がよさそうだ。
なお、Routeの書き方を一度Expressionを選ぶと従来のTraditionalな設定は選べなくなり、またその逆もしかりなので、利用する際はその点は注意した方がよい。
あとAPIOps的なところでAPI Specから変換する場合もExpressionが利用できないようなので、パイプラインでExpressionのRouteを自動で作る場合は工夫が必要な点も気をつけておきたい。