Envoy Gateway Listener 設計ガイド: 複数 Port、複数 Hostname、複数 Ingress パターン

7 min read

Gateway API を学び始めた人の多くは、Listener を「まあ port: 80 の field だよね」くらいに見がちです。 でも実運用では Listener は端役どころか、入口層全体の traffic control center に近い存在です。

listener は単に port を開けるだけではありません。次を定義します。

  • どの protocol を受けるか
  • どの hostname を受けるか
  • TLS を行うか
  • どの route type を attach 可能にするか

つまり listener は ingress 契約 です。契約をきれいに書けば、下流の route、チーム分担、デバッグ難易度まで連鎖的にきれいになります。

正しいメンタルモデルを作る: 1 Listener = 1 Ingress Slot

次の Gateway を見てください。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
    - name: https
      protocol: HTTPS
      port: 443
      hostname: app.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: app-cert
    - name: grpc
      protocol: HTTPS
      port: 8443
      hostname: grpc.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: grpc-cert

これは「穴が 3 つある Gateway」ではありません。むしろ次のように考えます。

  • http listener: 平文 HTTP 用
  • https listener: app.example.com
  • grpc listener: gRPC service 専用の入口スロット

こう捉えると、多くの設計判断がすぐ明確になります。 YAML を組み立てているのではなく、入口の境界を設計しているのです。

よくある 3 つの分割パターン

Pattern 1: Protocol で分ける

最も直感的なやり方です。

  • HTTP は 1 listener
  • HTTPS は 1 listener
  • gRPC は 1 listener
  • TCP/TLS passthrough は別で分ける

責務がはっきりします。 gRPC と通常の web API はどちらも HTTP/2 上に乗ることがありますが、service モデルは異なります。分けておくことで route の干渉を防ぎやすくなります。

Pattern 2: Hostname で分ける

複数 domain があるなら、主要 hostname ごとに 1 listener というパターンがよく使われます。

listeners:
  - name: app
    hostname: app.example.com
    protocol: HTTPS
    port: 443
  - name: admin
    hostname: admin.example.com
    protocol: HTTPS
    port: 443

これは次に向いています。

  • 公開系と管理系のガバナンス分離
  • hostname ごとに別証明書が必要
  • route の attach 境界を明確に保つ

Pattern 3: Team または環境責務で分ける

複数 team 構成では、listener 自体を governance 境界として使えます。

  • public-api は外部向け product traffic
  • internal-api は内部システム向け
  • partner-api は partner 連携向け

ここで listener の価値は技術設定を超え、権限と責任の境界になります。 中規模以上の team では特に効きます。誰でも何でも attach できる「1 つの巨大 ingress」の事故を減らせます。

sectionNameallowedRoutes: 重要な 2 field

route は parentRefs.sectionName で特定 listener に attach します。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
    - name: eg
      sectionName: https
  hostnames:
    - "app.example.com"
  rules:
    - backendRefs:
        - name: app-service
          port: 8080

この route は https listener だけを探します。 sectionName を指定しない場合は attach 可能な listener を探すためのルールが働きますが、複数 listener がある構成では明示したほうがほぼ安全です。

もう一つの重要 field が allowedRoutes です。

listeners:
  - name: public
    protocol: HTTPS
    port: 443
    allowedRoutes:
      namespaces:
        from: Same

これは、Gateway と同じ namespace にある route だけが attach できることを意味します。 代表的な値:

  • Same: 同じ namespace のみ
  • All: どの namespace でも可
  • Selector: label selector に合う namespace のみ

platform 境界を意図的に作りたいなら、この field はかなり強力です。 「誰がこの ingress に attach できるか」を listener レベルで決められます。

実践アドバイス: 後悔しない Listener 構造

かなり役立つ原則が一つあります。

各 listener には、1 つの明確な責務だけを持たせる。

たとえば:

  • 1 listener は 1 つの主要 hostname 群に対応する
  • 1 listener は 1 つの主要 protocol type を扱う
  • 1 listener は 1 つの route type、または固定された namespace 群に開く

すべての traffic を万能 listener 1 個に詰め込んで、下流で route が自動的に整理されることを期待しないでください。 最初は効率的に見えても、3 か月後にはどの家にもある謎ケーブル引き出しみたいになります。全部入っているのに、何も見つかりません。

具体的な提案:

  • Listener 名は意味を持たせる: https-public, grpc-internal
  • 複数 listener 構成では必ず sectionName とセットで使う
  • 重要な hostname を 1 listener に混ぜ込まない
  • allowedRoutes はまず制限寄りに始める。最初から from: All にしない

一文まとめ

Listener は単なる port 設定ではなく ingress 契約として扱ってください。 listener 境界がきれいだと、route は正しく attach し、team は自然に担当範囲を持ち、デバッグも迷路で猫を追いかける感じがかなり減ります。

次の一歩

listener 設計が整理できたら、次によく出る疑問はこれです。 namespace が分かれた複数 team 環境で、誰が route を attach できて、誰が何を参照できるのか?

次の記事では cross-namespace パターンと ownership を扱います。 👉 複数 namespace とチーム分担