「大使」模式是一種特化的邊車,負責隱藏複雜度、並為「存取 Pod 之外的服務」提供統一介面。它扮演代理(proxy),把主 Pod 與「直接存取外部依賴」這件事解耦。

問題#

容器化服務不會孤立存在,經常必須存取其他服務,而這些服務未必容易可靠地連上。困難可能來自:

  • 位址是動態且會變的
  • 需要對叢集化的服務實例做負載平衡
  • 協定不可靠
  • 資料格式棘手

理想上,容器應該是單一目的、且能在不同脈絡中重用的。

但如果一個容器既提供商業功能、又以某種特殊方式消費外部服務,它就背負了不只一項職責

消費外部服務可能需要一套特殊的服務發現函式庫,而我們不想把它塞進自己的容器;或者我們想用不同的服務發現函式庫與方法來抽換不同種類的服務。把「存取外部世界其他服務」的邏輯抽象並隔離出來,正是大使模式的目標。

解法#

以應用的快取為例:在開發環境存取本地快取可能只是簡單的組態,但在正式環境,我們可能需要一套能連上快取各個分片(shard)的客戶端組態。其他例子還有:

  • 透過在註冊表中查詢來消費服務,執行客戶端服務發現。
  • 透過 HTTP 這類不可靠協定消費服務,為了保護應用必須使用斷路器(circuit-breaker)邏輯、設定逾時、執行重試等。

在所有這些情況下,我們都可以用一個大使容器來隱藏存取外部服務的複雜度,並透過 localhost 為主應用容器提供簡化的視圖與存取方式。

圖 17-1:用大使容器存取遠端分散式快取——資料存取可委派給 Etcd 這類完全分散式的遠端儲存

圖 17-2:用大使容器存取本地快取——開發用途下,這個大使容器可以輕易換成本地執行的記憶體內鍵值儲存(如 memcached)

這個模式的好處與邊車相似:讓容器保持單一目的且可重用。應用容器可以專注於商業邏輯,把消費外部服務的職責與細節委派給另一個專門容器;同時也讓我們能打造專門且可重用的大使容器,與其他應用容器自由組合。

以下範例中,大使與一個 REST 服務並行執行。REST 服務在回傳回應之前,會把產生的資料送到固定 URL http://localhost:9009 記錄下來;大使行程監聽這個埠並處理資料。範例中它只是把資料印到主控台,但也可以做更精緻的事,例如把資料轉發到完整的日誌基礎設施。

apiVersion: v1
kind: Pod
metadata:
  name: random-generator
  labels:
    app: random-generator
spec:
  containers:
    # 主應用容器:提供產生亂數的 REST 服務
    - image: k8spatterns/random-generator:1.0
      name: main
      env:
        - name: LOG_URL
          value: http://localhost:9009 # 透過 localhost 與大使溝通的連線 URL
      ports:
        - containerPort: 8080
          protocol: TCP
    # 大使容器:平行執行,監聽 9009 埠(該埠不對 Pod 外部曝露)
    - image: k8spatterns/random-generator-log-ambassador
      name: ambassador

討論#

從更高層次看,大使就是一種邊車模式。

兩者的主要差異在於:大使不為主應用增添額外能力,它只是扮演通往外部世界的智慧代理——名稱由此而來(這個模式有時也被稱為 Proxy 模式)。

更多資訊#

  • Ambassador Example
  • How to Use the Ambassador Pattern to Dynamically Configure Services
  • Dynamic Docker Links with an Ambassador Powered
  • Link via an Ambassador Container
  • Modifications to the CoreOS Ambassador Pattern