Envoy Gateway 複数 Namespace とチーム分担: allowedRoutes、ReferenceGrant、Cross-Team ガバナンス
Gateway API の大きな価値は、YAML がきれいになることだけではありません。長年ぼんやりしていた問いに、ようやく明確な答えを出してくれることです。
入口層を誰が持つのか? Route を誰が変えられるのか? Namespace をまたいで相互参照できるのか?
Ingress で痛みが出やすかった理由の一つは、全部が一緒くたに絡んでいたことです。 Platform team は無断の公開変更を怖がり、app team は path を変えるだけでも ticket を切る感覚になり、最終的にみんなで YAML を拷問し合う構図になりがちでした。
Gateway API の考え方は明快です。
- Platform の ingress は独立してガバナンスできる
- Route は app team に委譲できる
- Cross-namespace の挙動には明示的な許可が必要
この記事では、その ownership model を上から下まで整理します。
最初にここ: Attachment と Reference は別物
ここが最も混同されやすいポイントです。
Route の Gateway / Listener への Attachment
これは次で決まります。
- route の
parentRefs - listener の
allowedRoutes
つまり、route が listener に attach できるかどうかは ReferenceGrant では決まりません。listener 側が受け入れるかどうかで決まります。
Cross-Namespace の Resource Reference
ここで登場するのが通常 ReferenceGrant です。
たとえば:
Gatewayが別 namespace のSecretを参照したいHTTPRouteが別 namespace のServiceを参照したい
これは attachment の話ではなく、「対象 namespace が、自分の資源を使ってよいと正式に認可したか」です。
この 2 つを一つに混ぜると、だいたい正気を疑い始めます。役割を分ければ、ルールはそこまで神秘的ではありません。
allowedRoutes: まず誰が attach できるかを決める
最も厳しく絞った listener はこうです。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gateway
namespace: infra
spec:
gatewayClassName: eg
listeners:
- name: https
protocol: HTTPS
port: 443
allowedRoutes:
namespaces:
from: Sameこれは infra namespace にある route だけがこの listener に attach できることを意味します。
特定 team の namespace を許可したい場合は Selector を使います。
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"このパターンは platform team に向いています。
すべてを完全封鎖しなくてもいいし、かといって from: All で誰でも入れてしまう必要もありません。特定 label を持つ namespace だけが attach できるようにできます。
💡
allowedRoutesは Gateway 単位ではなく listener 単位です。listener ごとに異なる access policy を持たせられます。
ReferenceGrant: 決めるのは対象 Namespace 側
route や gateway が他 namespace の resource を参照したい場合、対象 namespace 側が許可を出す必要があります。
たとえば app team の HTTPRoute が共有 namespace の backend へトラフィックを送りたい場合:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: team-a
spec:
parentRefs:
- name: shared-gateway
namespace: infra
sectionName: https
rules:
- backendRefs:
- name: shared-api
namespace: shared-services
port: 8080shared-services namespace 側には ReferenceGrant が必要です。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-team-a-route
namespace: shared-services
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: team-a
to:
- group: ""
kind: Service平たく言うと:
team-aにあるHTTPRouteがshared-servicesにあるServiceを参照してよい
ここで重要なのは、認可を作るのは source 側ではなく target 側 だということです。 source が「使いたい」と宣言するだけでは足りず、resource owner が決めます。この設計はかなり筋が良くて、所有者が誰にアクセスを許すかを制御できるべきだからです。
よくある Platform / App Team の分担
最も一般的な ownership model はこうです。
Platform Team が持つもの
GatewayClassGatewayListener- TLS 証明書管理
allowedRoutespolicy
App Team が持つもの
- 自分たちの namespace にある
HTTPRoute/GRPCRoute - Backend service
- Routing rule の調整
この分担の利点は明確です。
- Platform team は入口の公開面と security boundary を守れる
- App team は自分たちの traffic rule を素早く変えられる
- path を変えるためだけに共有 Gateway を触る必要がない
もちろん、すべての権限を 1 グループに集めても間違いではありません。 ただし team が大きくなると、そのモデルは「誰でも変えられるので、壊れたとき誰が何をしたかわからない」に育ちがちです。
実践アドバイス: Multi-Tenant 事故を避けるには
本当に役立つ習慣を挙げると:
- 共有 Gateway は
allowedRoutes.namespaces.from: Selectorをデフォルトにする - Namespace label の命名規約は安定させる。来月になって
team-aをallow-gwに変えたりしない - Cross-namespace の backend/Secret reference はすべて
ReferenceGrant経由にする - Route 名には team または service がわかる情報を入れる。例:
team-a-api-route - 共有 listener では必ず
sectionNameを使い、誤 attach を防ぐ
一つ持っておきたい原則があります。
入口層で境界を強制できるなら、文書だけ置いて「みんなが自律的に守ってくれるはず」に期待しないこと。
エンジニアリングでは、口約束より仕組みのほうが強いです。ちょっと年上の人の説教みたいですが、実際かなり使えます。
一文まとめ
Gateway API では、allowedRoutes が「誰が入れるか」を制御し、ReferenceGrant が「入ったあと何にアクセスできるか」を制御します。
この 2 つをきちんと分けて理解すると、複数 namespace・複数 team の構成がかなり扱いやすくなります。
次の一歩
ownership と権限が整理できたら、最後のピースはこれです。 設定が効かないとき、最初にどの status、どの condition、どの command を見るべきか?
次の記事では debugging と status の読み方を扱います。 👉 ステータスとデバッグ手引き