Envoy Gateway Listener 設計指南:多 Port、多 Hostname、多入口怎麼拆

6 min read

很多人學 Gateway API 時,Listener 只停留在「喔,就是 port: 80 那個欄位」。
但實戰裡,Listener 根本不是小配角,它比較像你整個入口層的交通管制中心。

一個 listener 不只是開一個 port,它其實在定義:

  • 接哪種協定
  • 接哪個 hostname
  • 要不要做 TLS
  • 允許哪類 route 附掛

也就是說,listener 其實是一份入口合約。你合約切得清楚,後面的 route、團隊分工、除錯成本就會跟著一起變乾淨。

先建立正確觀念:一個 Listener = 一個入口插槽

例如這個 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

這不是「一個 Gateway 開三個洞」而已,實際上更像:

  • http listener:給明文 HTTP
  • https listener:給 app.example.com
  • grpc listener:給 gRPC 服務專用入口

當你這樣想,很多設計判斷會瞬間清楚。
你不是在拼 YAML,而是在設計入口邊界。

三種最常見的拆法

拆法 1:按協定拆

這是最直覺的做法:

  • HTTP 一個 listener
  • HTTPS 一個 listener
  • gRPC 一個 listener
  • TCP/TLS passthrough 另外拆

優點是責任清楚。
尤其 gRPC 和一般 Web API 雖然都可能走 HTTP/2,但服務模型不同,拆開後 route 比較不會打架。

拆法 2:按 hostname 拆

如果你有多個網域,常見做法是每個主要 hostname 各自一個 listener:

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

這種方式很適合:

  • 主站和後台要分開治理
  • 不同 hostname 用不同憑證
  • 想把 route 附掛範圍切乾淨

拆法 3:按團隊或環境責任拆

如果你是多人協作,listener 也可以當治理邊界:

  • public-api 給外部產品流量
  • internal-api 給內部系統
  • partner-api 給合作夥伴

這時 listener 的價值就不只是技術設定,而是權限與責任邊界。
這種拆法在中大型團隊超有感,因為它可以減少「一個入口 everyone 都能亂掛」的災難。

sectionNameallowedRoutes:兩個超關鍵欄位

Route 要掛到哪個 listener,通常靠 parentRefs.sectionName

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。
如果你不指定,系統會根據規則去找可附掛的 listener,但多 listener 場景下,明確指定通常比較不容易出事。

另一個很重要的是 allowedRoutes

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

這代表只有跟 Gateway 同 namespace 的 route 才能附掛。
常見值有:

  • Same:只允許同 namespace
  • All:所有 namespace 都能來掛
  • Selector:只允許符合 label selector 的 namespace

如果你的平台邊界想清楚,這個欄位真的超香。
它等於在 listener 層就先決定「誰可以來接這個入口」。

實戰建議:怎麼拆比較不會後悔

先給你一句很實用的原則:

一個 listener 只承擔一種清楚責任。

例如:

  • 一個 listener 只服務一組主要 hostname
  • 一個 listener 只負責一種主要協定
  • 一個 listener 只開放給一種 route 類型或一群固定 namespace

不要把所有流量都塞進一個萬能 listener,再期待後面 route 自己長出秩序。
那種設計一開始看起來省事,三個月後通常會像家裡抽屜裡那包不知道哪來的線材,什麼都在、什麼都亂。

幾個具體建議:

  • listener 名稱要有語意,像 https-publicgrpc-internal
  • 多 listener 場景盡量搭配 sectionName
  • 重要 hostname 不要全混在同一個 listener
  • 預設先收斂 allowedRoutes,不要一開始就 from: All

一句話總結

Listener 當成入口合約,不要當成單純 port 設定。
你把 listener 切得乾淨,route 才會掛得準,團隊分工才會自然,除錯時也比較不會像在迷宮裡追貓。

下一步

Listener 設計完,下一個問題通常就是:
不同 namespace、不同團隊,到底誰能掛 route、誰能引用誰?

下一篇來講跨 namespace 與權限分工: 👉 多 Namespace 與團隊分工