Envoyとは
Lyft社が開発しているHTTP/2に対応しているモダンなL7プロキシです。CNCF Graduatedなプロジェクトで、既に多くの企業での採用事例もあります。マイクロサービス基盤におけるService MeshでSideCarとして使われる事が多く、SDやCircuit Breakerといった機能も備わっています。あくまでプロキシなので、静的ファイルの配信といった目的には使われません。
HTTP Filters
Envoyに備わっている機能の一つで、HTTPなIngress/Egressに付加されている情報を動的に書き換えたりする機能があります。この機能を用いることで、Headerに記述されているメタな情報を動的に書き換えて色々なことが出来ます。例えばJWTの検証とか、Fault Injectionとか。また、Luaを用いてプログラマブルな制御も可能です。Worker Thread単位でLuaJITを起動しています。
やりたいこと
HTTP Filterを使って動的にヘッダを書き換え、Kubernetes上の同一Pod内での経路制御を行うことです。HTTP/2では、コロンから始まるヘッダフィールドが実装されていて、このヘッダフィールドの中に:path
ヘッダが含まれるようになっています。このフィールドの値をLuaで動的に書き換えたいという話ですね。
この:path
ヘッダの値を、Header内に定義されているユーザーに対して一意に割り振られている情報を参照しつつ書き換えます。これは、ユーザーの値に応じて疎通先を変えたいA/Bテストやカナリアリリースのような事にも応用可能です。この方法が正しいかどうかはよくわかりませんが。
実装

構成図は上のような感じで、Goで書かれたGreen, Blueのアプリケーションに対して、Envoyでヘッダに書かれたユーザーIDを参照しつつ、Luaを用いて経路制御を行うといったものになります。Redisはセッションストアとしてしか用いていません。
全体の実装はここに置いてます。
http_filters:
- name: envoy.lua
typed_config:
"@type": type.googleapis.com/envoy.config.filter.http.lua.v2.Lua
inline_code: |
function envoy_on_request(request_handle)
local mod = request_handle:headers():get("x-user-id-mod")
local current_post = request_handle:headers():get(":path")
if mod == 0 then
local replaced_post = "/green" .. current_post
request_handle:headers():replace(":path", replaced_post)
else
local replaced_post = "/blue" .. current_post
request_handle:headers():replace(":path", replaced_post)
end
end
実装は上みたいな感じで、x-user-id-mod
というカスタムヘッダに、ユーザーIDの剰余が設定されていて、その値に応じてアクセスを振り分けるといったものになっています。剰余の取り方や、経路制御の条件をより細かく設定すれば、より細かい制御も可能だと思います。
総括
工夫すればこのような事もEnvoyでできますし、アプリケーション側から煩わしいネットワーク周りの処理も削減することができそうな機能です。ただ、この実装はあまりエレガントではない気がするので、もっとキレイに書けたら良さそうです。1
-
一応Issueだけ立てた https://github.com/envoyproxy/envoy/issues/8316 ↩