Envoy Gateway 複数 Namespace とチーム分担: allowedRoutes、ReferenceGrant、Cross-Team ガバナンス

8 min read

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: 8080

shared-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 が持つもの

  • GatewayClass
  • Gateway
  • Listener
  • TLS 証明書管理
  • allowedRoutes policy

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-aallow-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 の読み方を扱います。 👉 ステータスとデバッグ手引き