在 GKE 上部署 Memcached


在本教學課程中,您會瞭解如何使用 KubernetesHelmMcrouter,在 Google Kubernetes Engine (GKE) 上部署分散式 Memcached 伺服器的叢集。Memcached 是最常使用的開放原始碼多目的快取系統,通常可做為常用資料的暫時儲存庫使用,加快網路應用程式的速度,並減輕資料庫的負擔。

Memcached 的特性

Memcached 具有兩個主要的目標:

  • 簡化:Memcached 的功能就像是大型雜湊表一樣,提供了簡單的 API 來按鍵儲存及擷取任意形狀的物件。
  • 速度:Memcached 可以專門保存隨機存取記憶體 (RAM) 中的快取資料,使資料存取速度達到極快的程度。

Memcached 是一套分散式系統,可在一群伺服器之間水平調度其雜湊表的容量。每個 Memcached 伺服器在操作時都與集區中的其他伺服器完全隔離開,因此伺服器之間的轉送與負載平衡必須在用戶端層級完成。Memcached 用戶端會套用一致性雜湊配置,藉以適當選取目標伺服器。這個配置可以保證下列條件:

  • 一律針對相同的鍵選取相同的伺服器。
  • 伺服器之間的記憶體用量會得到平均分佈。
  • 當伺服器集區縮小或擴充時,會搬遷最少數量的鍵。

下圖說明 Memcached 用戶端與 Memcached 伺服器分散式集區之間的高階互動。

memcached 與 memcached 伺服器集區之間的互動
圖 1:Memcached 用戶端與 Memcached 伺服器分散式集區之間的高階互動。

目標

  • 瞭解 Memcached 分散式架構的一些特性。
  • 使用 Kubernetes 與 Helm 將 Memcached 服務部署至 GKE。
  • 部署 Mcrouter (一種開放原始碼 Memcached Proxy) 以改善系統效能。

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

  • Compute Engine

如要根據預測用量估算費用,請使用 Pricing Calculator

初次使用 Google Cloud 的使用者可能符合免費試用資格。

事前準備

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Compute Engine and GKE APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the Compute Engine and GKE APIs.

    Enable the APIs

  8. 啟動 Cloud Shell 執行個體。
    開啟 Cloud Shell

部署 Memcached 服務

有一種簡單的方法可以將 Memcached 服務部署至 GKE,那就是使用 Helm chart。如要繼續部署,請在 Cloud Shell 中按照下列步驟操作:

  1. 建立三個節點的新 GKE 叢集:

    gcloud container clusters create demo-cluster --num-nodes 3 --zone us-central1-f
    
  2. 下載 helm 二進位檔封存:

    HELM_VERSION=3.7.1
    cd ~
    wget https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz
    
  3. 將封存檔案解壓縮至本機系統:

    mkdir helm-v${HELM_VERSION}
    tar zxfv helm-v${HELM_VERSION}-linux-amd64.tar.gz -C helm-v${HELM_VERSION}
    
  4. helm 二進位檔的目錄加到您的 PATH 環境變數中:

    export PATH="$(echo ~)/helm-v${HELM_VERSION}/linux-amd64:$PATH"
    

    這個指令讓您在目前的 Cloud Shell 工作階段期間,從任何目錄探索到 helm 二進位檔。如要在多個工作階段中持續維持這個設定,請將指令加到您的 Cloud Shell 使用者的 ~/.bashrc 檔案。

  5. 安裝具有高可用性架構的新 Memcached Helm chart 版本:

    helm repo add bitnami https://charts.bitnami.com/bitnami
    helm install mycache bitnami/memcached --set architecture="high-availability" --set autoscaling.enabled="true"
    

    Memcached Helm chart 使用 StatefulSet 控制器。使用 StatefulSet 控制器有一個好處,就是 Pod 的名稱經過排序,且可以預測。在本例中,名稱為 mycache-memcached-{0..2}。進行排序可讓 Memcached 更容易參照伺服器。

  6. 如要查看執行中的 Pod,請執行下列指令:

    kubectl get pods
    

    Google Cloud 控制台輸出內容如下所示:

    NAME                  READY     STATUS    RESTARTS   AGE
    mycache-memcached-0   1/1       Running   0          45s
    mycache-memcached-1   1/1       Running   0          35s
    mycache-memcached-2   1/1       Running   0          25s

探索 Memcached 服務端點

Memcached Helm chart 使用無介面服務。無介面服務會公開其所有 Pod 的 IP 位址,以便能夠單獨探索到這些 Pod。

  1. 確認部署的服務是無介面服務:

    kubectl get service mycache-memcached -o jsonpath="{.spec.clusterIP}"
    

    輸出 None 會確認服務沒有 clusterIP,而且因此是無介面的。

    服務會為下列格式的主機名稱建立 DNS 記錄:

    [SERVICE_NAME].[NAMESPACE].svc.cluster.local
    

    在本教學課程中,服務名稱為 mycache-memcached。由於命名空間並未明確定義,所以會使用預設命名空間,因此完整的主機名稱為 mycache-memcached.default.svc.cluster.local。這個主機名稱會解析為服務公開的全部三個 Pod 的一組 IP 位址及網域。如果未來某些 Pod 新增至集區,或者舊 Pod 被移除,kube-dns 將會自動更新 DNS 記錄。

    客戶必須負責探索 Memcached 服務端點,如後續步驟所述。

  2. 擷取端點的 IP 位址:

    kubectl get endpoints mycache-memcached
    

    輸出結果會與下列內容相似:

    NAME                ENDPOINTS                                            AGE
    mycache-memcached   10.36.0.32:11211,10.36.0.33:11211,10.36.1.25:11211   3m
    

    請注意,每個 Memcached Pod 都有個別的 IP 位址,分別為 10.36.0.3210.36.0.3310.36.1.25。這些 IP 位址可能與您自己的伺服器執行個體不同。每個 Pod 都會接聽通訊埠 11211,這是 Memcached 的預設通訊埠。

  3. 步驟 2 還有另一個替代做法,就是使用 Python 之類的程式設計語言執行 DNS 檢查:

    1. 在您的叢集中啟動 Python 互動式主控台:

      kubectl run -it --rm python --image=python:3.10-alpine --restart=Never python
      
    2. 在 Python 主控台中,執行下列指令:

      import socket
      print(socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local'))
      exit()
      

      輸出結果會與下列內容相似:

      ('mycache-memcached.default.svc.cluster.local', ['mycache-memcached.default.svc.cluster.local'], ['10.36.0.32', '10.36.0.33', '10.36.1.25'])
  4. 在通訊埠 11211 上執行其中一個 Memcached 伺服器,開啟 telnet 工作階段以測試部署:

    kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet mycache-memcached-0.mycache-memcached.default.svc.cluster.local 11211
    

    telnet 提示之下,使用 Memcached ASCII 通訊協定執行下列指令:

    set mykey 0 0 5
    hello
    get mykey
    quit

    產生的輸出會在此以粗體顯示:

    set mykey 0 0 5
    hello
    STORED
    get mykey
    VALUE mykey 0 5
    hello
    END
    quit

實作服務探索邏輯

您現在已經準備好實作如下圖所示的基本服務探索邏輯。

<img <="" alt="service discovery logic" img="" src="/static/architecture/images/memcached-fig-2.svg" />
圖 2:服務探索邏輯。

整體來說,服務探索邏輯包含下列步驟:

  1. 應用程式會查詢 kube-dns 的 DNS 記錄。mycache-memcached.default.svc.cluster.local
  2. 應用程式會擷取與該記錄相關聯的 IP 位址。
  3. 應用程式會實例化新的 Memcached 用戶端,並為該用戶端提供擷取的 IP 位址。
  4. Memcached 用戶端的整合式負載平衡器會連線至位於特定 IP 位址的 Memcached 伺服器。

現在您可以使用 Python 實作這個服務探索邏輯:

  1. 在您的叢集中部署新的具有 Python 功能的 Pod,並在該 Pod 中啟動殼層工作階段:

    kubectl run -it --rm python --image=python:3.10-alpine --restart=Never sh
    
  2. 安裝 pymemcache 程式庫:

    pip install pymemcache
    
  3. 執行 python 指令以啟動 Python 互動式主控台。

  4. 在 Python 主控台中,執行下列指令:

    import socket
    from pymemcache.client.hash import HashClient
    _, _, ips = socket.gethostbyname_ex('mycache-memcached.default.svc.cluster.local')
    servers = [(ip, 11211) for ip in ips]
    client = HashClient(servers, use_pooling=True)
    client.set('mykey', 'hello')
    client.get('mykey')
    

    輸出看起來像這樣:

    b'hello'

    b 前置字串表示位元組常值,這是 Memcached 儲存資料的格式。

  5. 結束 Python 主控台:

    exit()
    
  6. 按下 Control+D 鍵結束 Pod 的殼層工作階段。

啟用連線集區

隨著您的快取需求的增加,以及集區規模擴充至數十、數百或數千個 Memcached 伺服器,您可能會遇到一些限制。特別的是,大量來自 Memcached 用戶端的公開連線可能會為伺服器增加龐大負載,如下圖所示。

<img <="" alt="當所有 Memcached 用戶端都直接存取所有 Memcached 伺服器時,所產生的大量公開連線。" img="" src="/static/architecture/images/memcached-fig-3.svg" />
圖 3:當所有 Memcached 用戶端都直接存取所有 Memcached 伺服器時,所產生的大量公開連線。

為了減少公開連線數,您必須導入 Proxy 以啟用連線集區,如下圖所示。

<img <="" alt="啟用連線集區的 Proxy。" img="" src="/static/architecture/images/memcached-fig-4.svg" />
圖 4:使用 Proxy 減少公開連線數。

Mcrouter (發音為「mick router」) 是一款功能強大的開放原始碼 Memcached Proxy,可以用來啟用連線集區。您可以完美整合 Mcrouter,因為這個 Proxy 使用標準的 Memcached ASCII 通訊協定。對 Memcached 用戶端而言,Mcrouter 的作用與正常 Memcached 伺服器類似。對 Memcached 伺服器而言,Mcrouter 的作用與正常 Memcached 用戶端類似。

如要部署 Mcrouter,請在 Cloud Shell 中執行下列指令。

  1. 刪除先前安裝的 mycache Helm chart 版本:

    helm delete mycache
    
  2. 安裝新的 Mcrouter Helm chart 版本以部署新的 Memcached Pod 與 Mcrouter Pod:

    helm repo add stable https://charts.helm.sh/stable
    helm install mycache stable/mcrouter --set memcached.replicaCount=3
    

    Proxy Pod 現在已經準備好接受來自用戶端應用程式的要求。

  3. 連線至其中一個 Proxy Pod 以測試此設定。請在通訊埠 5000 上使用 telnet 指令,該通訊埠是 Mcrouter 的預設通訊埠。

    MCROUTER_POD_IP=$(kubectl get pods -l app=mycache-mcrouter -o jsonpath="{.items[0].status.podIP}")
    
    kubectl run -it --rm busybox --image=busybox:1.33 --restart=Never telnet $MCROUTER_POD_IP 5000
    

    telnet 提示下,執行下列指令:

    set anotherkey 0 0 15
    Mcrouter is fun
    get anotherkey
    quit

    這些指令會設定及回應您的鍵值。

您現在已經部署了啟用連線集區的 Proxy。

減少延遲時間

為了增加彈性,使用內含多個節點的叢集是常見的做法。本教學課程使用內含三個節點的叢集。但是,使用多個節點也會因節點之間較繁重的網路流量而帶來增加延遲時間的風險。

並置 Proxy Pod

您只能將用戶端應用程式 Pod 連線至位於同一節點上的 Memcached Proxy Pod 來降低這個風險。下圖說明這個設定。

<img <="" alt="topology for interactions between pods" img="" src="/static/architecture/images/memcached-fig-5.svg" />
圖 5:跨三節點叢集的應用程式 Pod、Mcrouter Pod 與 Memcached Pod 之間的互動拓撲。

執行這個設定的方式如下:

  1. 確保每個節點都包含一個執行中的 Proxy Pod。常用的方法是使用 DaemonSet 控制器部署 Proxy Pod。將節點加到叢集時,新的 Proxy Pod 也會自動加入其中。從叢集移除節點時,這些 Pod 會被當成垃圾回收處理。在本教學課程中,您稍早時部署的 Mcrouter Helm chart 根據預設使用 DaemonSet 控制器。因此,這個步驟已經完成。
  2. 在 Proxy 容器的 Kubernetes 參數中設定 hostPort 值,使節點接聽該通訊埠,並將流量重新導向至 Proxy。在本教學課程中,Mcrouter Helm chart 根據預設針對通訊埠 5000 使用這個參數,因此這個步驟已經完成。
  3. 使用 spec.env 項目並選取 spec.nodeName fieldRef 值,將節點名稱公開為應用程式 Pod 中的環境變數。進一步閱讀 Kubernetes 說明文件中的這個方法。

    1. 部署範例應用程式 Pod。下列指令會套用 Kubernetes Deployment。Deployment 是 Kubernetes API 物件,可讓您執行多個 Pod 副本,並將這些副本分散到叢集中的節點:

      cat <<EOF | kubectl create -f -
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: sample-application
      spec:
        selector:
          matchLabels:
            app: sample-application
        replicas: 9
        template:
          metadata:
            labels:
              app: sample-application
          spec:
            containers:
              - name: busybox
                image: busybox:1.33
                command: [ "sh", "-c"]
                args:
                - while true; do sleep 10; done;
                env:
                  - name: NODE_NAME
                    valueFrom:
                      fieldRef:
                        fieldPath: spec.nodeName
      EOF
      
  4. 進入其中一個應用程式範例 Pod 中查看,確認節點名稱是否已公開:

    POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}")
    
    kubectl exec -it $POD -- sh -c 'echo $NODE_NAME'
    

    這個指令輸出的節點名稱格式如下:

    gke-demo-cluster-default-pool-XXXXXXXX-XXXX

連線 Pod

應用程式範例 Pod 現在已經準備好連線至 Mcrouter Pod,該 Mcrouter Pod 在通訊埠 5000 的各自雙向節點上執行,這是 Mcrouter 的預設通訊埠。

  1. 開啟 telnet 工作階段,啟動其中一個 Pod 的連線:

    POD=$(kubectl get pods -l app=sample-application -o jsonpath="{.items[0].metadata.name}")
    
    kubectl exec -it $POD -- sh -c 'telnet $NODE_NAME 5000'
    
  2. telnet 提示下,執行下列指令:

    get anotherkey
    quit
    

    產生的輸出:

    Mcrouter is fun

最後,為了提供說明,下列 Python 程式碼提供了執行此連線的程式範例,此範例從環境中擷取了 NODE_NAME 變數,並使用 pymemcache 程式庫:

import os
from pymemcache.client.base import Client

NODE_NAME = os.environ['NODE_NAME']
client = Client((NODE_NAME, 5000))
client.set('some_key', 'some_value')
result = client.get('some_key')

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程中所用資源的相關費用,請刪除含有該項資源的專案,或者保留專案但刪除個別資源。

  1. 執行以下指令來刪除 GKE 叢集:

    gcloud container clusters delete demo-cluster --zone us-central1-f
    
  2. 或者刪除 Helm 二進位檔:

    cd ~
    rm -rf helm-v3.7.1
    rm helm-v3.7.1-linux-amd64.tar.gz
    

後續步驟

  • 除了簡單的連線集區以外,您也可以探索 Mcrouter 提供的其他許多功能,例如容錯移轉備用資源、可靠的刪除串流、冷快取暖機、多叢集廣播。
  • 探索 Memcached chartMcrouter chart 的原始碼檔案,進一步瞭解各個 Kubernetes 設定。
  • 進一步瞭解在 App Engine 上使用 Memcached 的有效技巧。其中一些技巧適用於其他平台,例如 GKE。