準備 Cloud Service Mesh 所需的應用程式

Cloud Service Mesh 是管理及監控分散式應用程式的強大工具。如要充分運用 Cloud Service Mesh,瞭解其基礎抽象化機制 (包括容器和 Kubernetes) 會有所助益。本教學課程說明如何準備應用程式,以便從原始碼到在 GKE 上執行的容器,都能使用 Cloud Service Mesh,直到安裝 Cloud Service Mesh 之前為止。

如果您已熟悉 Kubernetes 和服務網格概念,可以略過本教學課程,直接參閱 Cloud Service Mesh 安裝指南。

目標

  1. 探索簡單的多服務「Hello World」應用程式。
  2. 從來源執行應用程式
  3. 將應用程式容器化。
  4. 建立 Kubernetes 叢集。
  5. 將容器部署至叢集。

事前準備

請按照下列步驟啟用 Cloud Service Mesh API:
  1. 前往 Google Cloud 控制台的 Kubernetes Engine 頁面。
  2. 建立或選取專案。
  3. 等待 API 和相關服務完成啟用。 這可能需要幾分鐘的時間。
  4. 確認 Google Cloud 專案已啟用計費功能。

本教學課程使用 Cloud Shell,其會佈建 g1-small Compute Engine 虛擬機器 (VM),並執行以 Debian 為基礎的 Linux 作業系統。

準備 Cloud Shell

使用 Cloud Shell 的優點如下:

  • Python 2 和 Python 3 開發環境 (包括 virtualenv) 都已設定完成。
  • 本教學課程使用的 gcloud、docker、git 和 kubectl 指令列工具皆已安裝。
  • 您可以選擇文字編輯器:

    • 程式碼編輯器:按一下 Cloud Shell 視窗頂端的 即可存取。

    • Emacs、Vim 或 Nano,可從 Cloud Shell 的指令列存取。

在 Google Cloud 控制台啟用 Cloud Shell。

啟用 Cloud Shell

Google Cloud 控制台底部會開啟 Cloud Shell 工作階段,並顯示指令列提示。Cloud Shell 是已安裝 Google Cloud CLI 的殼層環境,並已設定適用於您目前專案的值。工作階段可能要幾秒鐘的時間才能初始化。

下載程式碼範例

  1. 下載 helloserver 原始碼:

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    
  2. 切換至程式碼範例目錄:

    cd anthos-service-mesh-samples/docs/helloserver
    

探索多服務應用程式

範例應用程式是以 Python 編寫,並有兩個使用 REST 通訊的元件:

  • server:簡單的伺服器,具有一個 GET 端點 /,可將「hello world」列印到控制台。
  • loadgen:將流量傳送至 server 的指令碼,每秒要求數 (RPS) 可設定。

範例應用程式

從來源執行應用程式

如要熟悉範例應用程式,請在 Cloud Shell 中執行。

  1. 從 sample-apps/helloserver 目錄執行 server:

    python3 server/server.py
    

    server 啟動時會顯示以下內容:

    INFO:root:Starting server...
    
  2. 開啟另一個終端機視窗,以便將要求傳送至 server。按一下 即可開啟另一個工作階段。

  3. 向 server 傳送要求:

    curl http://localhost:8080
    

    server 回覆:

    Hello World!
    
  4. 從下載程式碼範例的目錄,變更為包含 loadgen 的目錄:

    cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/loadgen
  5. 建立下列的環境變數:

    export SERVER_ADDR=http://localhost:8080
    export REQUESTS_PER_SECOND=5
    
  6. 開始時間 virtualenv:

    virtualenv --python python3 env
    
  7. 啟用虛擬環境:

    source env/bin/activate
    
  8. 安裝 loadgen 的必要條件:

    pip3 install -r requirements.txt
    
  9. 執行 loadgen:

    python3 loadgen.py
    

    loadgen 啟動時,會輸出類似以下的訊息:

    Starting loadgen: 2019-05-20 10:44:12.448415
    5 request(s) complete to http://localhost:8080
    

    在另一個終端機視窗中,server 會將訊息寫入控制台,類似於下列內容:

    127.0.0.1 - - [21/Jun/2019 14:22:01] "GET / HTTP/1.1" 200 -
    INFO:root:GET request,
    Path: /
    Headers:
    Host: localhost:8080
    User-Agent: python-requests/2.22.0
    Accept-Encoding: gzip, deflate
    Accept: */*
    

    從網路的角度來看,整個應用程式現在都在同一個主機上執行。因此,您可以使用 localhost 將要求傳送至 server。

  10. 如要停止 loadgen 和 server,請在每個終端機視窗中輸入 Ctrl-c。

  11. 在 loadgen 終端機視窗中停用虛擬環境:

    deactivate
    

將應用程式容器化

如要在 GKE 上執行應用程式,您必須將範例應用程式 (server 和 loadgen) 封裝至容器。容器是一種封裝應用程式的方式,可將應用程式與基礎環境隔離。

如要將應用程式容器化,您需要 Dockerfile。Dockerfile 是文字檔案,定義了將應用程式原始碼及其依附元件組合成 Docker 映像檔所需的指令。建構映像檔後,請將其上傳至容器登錄檔,例如 Docker Hub 或 Container Registry。

這個範例包含 Dockerfile 和 loadgen 的 server,以及 建構映像檔所需的所有指令。以下是 server 的 Dockerfile:

FROM python:3.14-slim as base
FROM base as builder
RUN apt-get -qq update \
    && apt-get install -y --no-install-recommends \
        g++ \
    && rm -rf /var/lib/apt/lists/*

# Enable unbuffered logging
FROM base as final
ENV PYTHONUNBUFFERED=1

RUN apt-get -qq update \
    && apt-get install -y --no-install-recommends \
        wget

WORKDIR /helloserver

# Grab packages from builder
COPY --from=builder /usr/local/lib/python3.* /usr/local/lib/

# Add the application
COPY . .

EXPOSE 8080
ENTRYPOINT [ "python", "server.py" ]
  • FROM python:3-slim as base 指令會告知 Docker 使用最新的 Python 3 映像檔做為基本映像檔。
  • COPY . . 指令會將目前工作目錄中的來源檔案 (在本例中只有 server.py) 複製到容器的檔案系統。
  • ENTRYPOINT 會定義用於執行容器的指令。在本例中,這個指令與您用來從原始碼執行 server.py 的指令幾乎相同。
  • EXPOSE 指令會指定 server 監聽通訊埠 8080。這個指令不會公開任何通訊埠,但會做為說明文件,指出您在執行容器時需要開啟通訊埠 8080。

準備將應用程式容器化

  1. 設定下列環境變數。將 PROJECT_ID 替換為Google Cloud 專案 ID。

    export PROJECT_ID="PROJECT_ID"
    export GCR_REPO="asm-ready"

    建構 Docker 映像檔時,您會使用 PROJECT_ID 和 GCR_REPO 的值標記映像檔,然後將映像檔推送至私人 Container Registry。

  2. 設定 Google Cloud CLI 的預設 Google Cloud 專案。

    gcloud config set project $PROJECT_ID
  3. 設定 Google Cloud CLI 的預設可用區。

    gcloud config set compute/zone us-central1-b
    
  4. 確認Google Cloud 專案已啟用 Container Registry 服務。

    gcloud services enable containerregistry.googleapis.com
    

將 server 容器化

  1. 切換至範例 server 所在的目錄:

    cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/server/
  2. 使用 Dockerfile 和先前定義的環境變數建構映像檔:

    docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1 .
    

    -t 旗標代表 Docker 標記。這是部署容器時使用的映像檔名稱。

  3. 將映像檔推送至 Container Registry:

    docker push gcr.io/$PROJECT_ID/$GCR_REPO/helloserver:v0.0.1
    

將 loadgen 容器化

  1. 切換至範例 loadgen 所在的目錄:

    cd ../loadgen
    
  2. 建構映像檔:

    docker build -t gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1 .
    
  3. 將映像檔推送至 Container Registry:

    docker push gcr.io/$PROJECT_ID/$GCR_REPO/loadgen:v0.0.1
    

列出映像檔

取得存放區中的映像檔清單,確認映像檔已推送:

gcloud container images list --repository gcr.io/$PROJECT_ID/asm-ready

系統會傳回您剛才推送的映像檔名稱:

NAME
gcr.io/PROJECT_ID/asm-ready/helloserver
gcr.io/PROJECT_ID/asm-ready/loadgen

建立 GKE 叢集

您可以使用 docker run 指令,在 Cloud Shell VM 上執行這些容器。但在正式環境中,您需要以更統一的方式自動調度管理容器。舉例來說,您需要一個系統來確保容器持續運作,並需要一種方法來擴充及啟動容器的其他執行個體,以處理增加的流量。

您可以使用 GKE 執行容器化應用程式。GKE 是容器協調平台,可將 VM 連線至叢集。每個 VM 都稱為「節點」。GKE 叢集是由 Kubernetes 開放原始碼叢集管理系統提供技術支援。您可以透過 Kubernetes 提供的機制與叢集互動。

如要建立 GKE 叢集,請按照下列步驟操作:

  1. 建立叢集:

    gcloud container clusters create asm-ready \
      --cluster-version latest \
      --machine-type=n1-standard-4 \
      --num-nodes 4
    

    gcloud 指令會在您先前設定的Google Cloud 專案和可用區中建立叢集。如要執行 Cloud Service Mesh,建議至少使用 4 個節點和 n1-standard-4 機型。

    建立叢集的指令需要幾分鐘才能完成。叢集準備就緒時,指令會輸出類似以下的訊息:

    NAME        LOCATION       MASTER_VERSION  MASTER_IP      MACHINE_TYPE   NODE_VERSION   NUM_NODES  STATUS
    asm-ready  us-central1-b  1.13.5-gke.10   203.0.113.1    n1-standard-2  1.13.5-gke.10  4          RUNNING
    
  2. 向 kubectl 指令列工具提供憑證,以便使用該工具管理叢集:

    gcloud container clusters get-credentials asm-ready
    
  3. 現在您可以使用 kubectl 與 Kubernetes 通訊。舉例來說,您可以執行下列指令來取得節點狀態:

    kubectl get nodes
    

    指令會傳回節點清單,類似於下列內容:

    NAME                                       STATUS   ROLES    AGE    VERSION
    gke-asm-ready-default-pool-dbeb23dc-1vg0   Ready    <none>   99s    v1.13.6-gke.13
    gke-asm-ready-default-pool-dbeb23dc-36z5   Ready    <none>   100s   v1.13.6-gke.13
    gke-asm-ready-default-pool-dbeb23dc-fj7s   Ready    <none>   99s    v1.13.6-gke.13
    gke-asm-ready-default-pool-dbeb23dc-wbjw   Ready    <none>   99s    v1.13.6-gke.13
    

瞭解 Kubernetes 的重要概念

下圖顯示在 GKE 上執行的應用程式:

容器化應用程式

將容器部署至 GKE 之前,建議您先瞭解一些重要的 Kubernetes 概念。本教學課程的結尾提供相關連結,方便您進一步瞭解各項概念。

  • 節點和叢集:在 GKE 中,節點是 VM。在其他 Kubernetes 平台上,節點可以是實體或虛擬機器。叢集是一組節點,可視為單一機器,您可以在其中部署容器化應用程式。

  • Pod:在 Kubernetes 中,容器是在 Pod 內執行。Pod 是 Kubernetes 的基本單位。Pod 包含一或多個容器。您將 server 和 loadgen 容器分別部署在各自的 Pod 中。當 Pod 執行多個容器時 (例如應用程式伺服器和Proxy 伺服器),這些容器會以單一實體的形式管理,並共用 Pod 的資源。

  • Deployment:Deployment 是代表一組相同 Pod 的 Kubernetes 物件。Deployment 會執行多個 Pod 副本,並分散在叢集的節點中。Deployment 會自動替換任何失敗或沒有回應的 Pod。

  • Kubernetes Service:在 GKE 中執行應用程式程式碼會變更 loadgen 和 server 之間的網路。在 Cloud Shell VM 中執行服務時,您可以使用 localhost:8080 位址將要求傳送至 server。部署至 GKE 後,Pod 會排定在可用的節點上執行。根據預設,您無法控制 Pod 在哪個節點上執行,因此 Pod 沒有穩定的 IP 位址。

    如要取得 server 的 IP 位址,您必須在 Pod 之上定義稱為 Kubernetes Service 的網路抽象層。Kubernetes Service 可為一組 Pod 提供穩定的網路端點。服務分為多種類型。server 使用 LoadBalancer,公開外部 IP 位址,因此您可以從叢集外部連上 server。

    Kubernetes 也內建 DNS 系統,可為 Service 指派 DNS 名稱 (例如 helloserver.default.cluster.local)。這樣一來,叢集內的 Pod 就能透過穩定的位址,連線至叢集內的其他 Pod。您無法在叢集外 (例如從 Cloud Shell) 使用這個 DNS 名稱。

Kubernetes 資訊清單

從原始碼執行應用程式時,您使用了命令式指令:python3 server.py

命令式是指以動詞為主的「執行這項操作」。

相較之下,Kubernetes 則採用宣告式模型。也就是說,您不必明確指示 Kubernetes 該怎麼做,而是提供 Kubernetes 預期狀態。舉例來說,Kubernetes 會視需要啟動及終止 Pod,確保實際系統狀態符合所需狀態。

您可以在一組資訊清單或 YAML 檔案中指定所需狀態。YAML 檔案包含一或多個 Kubernetes 物件的規格。

範例包含 server 和 loadgen 的 YAML 檔案。每個 YAML 檔案都會指定 Kubernetes Deployment 物件和服務的所需狀態。

伺服器

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloserver
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloserver
  template:
    metadata:
      labels:
        app: helloserver
    spec:
      containers:
      - image: gcr.io/google-samples/istio/helloserver:v0.0.1
        imagePullPolicy: Always
        name: main
      restartPolicy: Always
      terminationGracePeriodSeconds: 5
  • kind 表示物件類型。
  • metadata.name 會指定 Deployment 的名稱。
  • 第一個 spec 欄位包含所需狀態的說明。
  • spec.replicas 會指定所需的 Pod 數量。
  • spec.template 區段定義了 Pod 範本。Pod 的規格包含 image 欄位,也就是要從 Container Registry 提取的映像檔名稱。

「服務」定義如下:

apiVersion: v1
kind: Service
metadata:
  name: hellosvc
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: helloserver
  type: LoadBalancer
  • LoadBalancer:用戶端會將要求傳送至網路負載平衡器的 IP 位址,該位址穩定且可從叢集外部連線。
  • targetPort:請注意,Dockerfile 中的 EXPOSE 8080 指令實際上不會公開任何連接埠。公開通訊埠 8080,以便從叢集外部連線至 server 容器。在本例中,hellosvc.default.cluster.local:80 (簡短名稱:hellosvc) 會對應至 helloserver Pod IP 的通訊埠 8080。
  • port:叢集中的其他服務傳送要求時,會使用這個通訊埠編號。

負載產生器

loadgen.yaml 中的 Deployment 物件與 server.yaml 類似。其中一項顯著差異是 Deployment 物件包含名為 env 的區段。本節定義 loadgen 所需的環境變數,這些變數是您先前從來源執行應用程式時設定的。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: loadgenerator
spec:
  replicas: 1
  selector:
    matchLabels:
      app: loadgenerator
  template:
    metadata:
      labels:
        app: loadgenerator
    spec:
      containers:
      - env:
        - name: SERVER_ADDR
          value: http://hellosvc:80/
        - name: REQUESTS_PER_SECOND
          value: '10'
        image: gcr.io/google-samples/istio/loadgen:v0.0.1
        imagePullPolicy: Always
        name: main
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 300m
            memory: 256Mi
      restartPolicy: Always
      terminationGracePeriodSeconds: 5

由於 loadgen 不接受傳入要求,因此 type 欄位會設為 ClusterIP。這種類型會提供穩定的 IP 位址,可供叢集中的服務使用,但不會向外部用戶端公開。

apiVersion: v1
kind: Service
metadata:
  name: loadgensvc
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: loadgenerator
  type: ClusterIP

將容器部署至 GKE

  1. 切換至範例 server 所在的目錄:

    cd YOUR_WORKING_DIRECTORY/anthos-service-mesh-samples/docs/helloserver/server/
  2. 在文字編輯器中開啟 server.yaml。

  3. 將 image 欄位中的名稱替換為 Docker 映像檔名稱。

    image: gcr.io/PROJECT_ID/asm-ready/helloserver:v0.0.1
    

    將 PROJECT_ID 替換為 Google Cloud 專案 ID。

  4. 儲存並關閉 server.yaml。

  5. 將 YAML 檔案部署至 Kubernetes:

    kubectl apply -f server.yaml
    

    如果成功,指令會傳回下列內容:

    deployment.apps/helloserver created
    service/hellosvc created
    

  6. 切換至 loadgen 所在的目錄。

    cd ../loadgen
    
  7. 在文字編輯器中開啟 loadgen.yaml。

  8. 將 image 欄位中的名稱替換為 Docker 映像檔名稱。

    image: gcr.io/PROJECT_ID/asm-ready/loadgen:v0.0.1
    

    將 PROJECT_ID 替換為 Google Cloud 專案 ID。

  9. 儲存並關閉 loadgen.yaml,然後關閉文字編輯器。

  10. 將 YAML 檔案部署至 Kubernetes:

    kubectl apply -f loadgen.yaml
    

    如果成功,指令會傳回下列內容:

    deployment.apps/loadgenerator created
    service/loadgensvc created
    

  11. 檢查 Pod 的狀態:

    kubectl get pods
    

    指令會傳回類似下列內容的狀態:

    NAME                             READY   STATUS    RESTARTS   AGE
    helloserver-69b9576d96-mwtcj     1/1     Running   0          58s
    loadgenerator-774dbc46fb-gpbrz   1/1     Running   0          57s
    
  12. 從 loadgen Pod 取得應用程式記錄。將 POD_ID 替換為先前輸出內容中的 ID。

    kubectl logs loadgenerator-POD_ID
    
  13. 取得 hellosvc 的外部 IP 位址:

    kubectl get service
    

    該指令會輸出類似以下的結果:

    NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
    hellosvc     LoadBalancer   10.81.15.158   192.0.2.1       80:31127/TCP   33m
    kubernetes   ClusterIP      10.81.0.1      <none>          443/TCP        93m
    loadgensvc   ClusterIP      10.81.15.155   <none>          80/TCP         4m52s
    
  14. 向 hellosvc 傳送要求。將 EXTERNAL_IP 替換為 hellosvc 的外部 IP 位址。

    curl http://EXTERNAL_IP
    

可使用 Cloud Service Mesh

您現在已將應用程式部署至 GKE。loadgen可以使用 Kubernetes DNS (hellosvc:80) 將要求傳送至 server,您也可以使用外部 IP 位址將要求傳送至 server。雖然 Kubernetes 提供許多功能,但缺少一些服務相關資訊:

  • 服務如何互動?這些服務之間的關係為何?流量如何在服務之間流動?您知道 loadgen 會向 server 傳送要求,但假設您不熟悉這個應用程式。您無法透過查看 GKE 上執行中的 Pod 清單來回答這些問題。
  • 指標:server 需要多久時間才能回應傳入的要求?server 每秒收到的要求數 (RPS) 為何?是否有任何錯誤回應?
  • 安全性資訊:loadgen 和 server 之間的流量是純文字HTTP還是 mTLS?

Cloud Service Mesh 可協助您解答這些問題。Cloud Service Mesh 是 Google Cloud代管的開放原始碼 Istio 專案版本。Cloud Service Mesh 會在每個 Pod 中放置 Envoy Sidecar Proxy,Envoy Proxy 會攔截所有傳入和傳出應用程式容器的流量。這表示 server 和 loadgen 各自取得 Envoy Sidecar Proxy,且從 loadgen 到 server 的所有流量都會由 Envoy Proxy 進行中介。這些 Envoy Proxy 之間的連線會形成服務網格。這項服務網格架構會在 Kubernetes 上方提供控制層。

服務網格

由於 Envoy Proxy 是在自己的容器中執行,因此您可以在 GKE 叢集上安裝 Cloud Service Mesh,而不必大幅變更應用程式程式碼。不過,您在準備應用程式,以便使用 Cloud Service Mesh 檢測時,有幾個關鍵做法:

  • 所有容器的服務:server 和 loadgen Deployment 都已附加 Kubernetes 服務。即使是 loadgen,雖然不會收到任何傳入要求,但也有服務。
  • 服務中的通訊埠必須命名:雖然 GKE 允許您定義未命名的服務通訊埠,但 Cloud Service Mesh 要求您提供與通訊埠通訊協定相符的通訊埠名稱。在 YAML 檔案中,server 的通訊埠名為 http,因為 server 使用 HTTP 通訊協定。如果 service 使用 gRPC,請將通訊埠命名為 grpc。
  • Deployment 加上標籤:這樣您就能使用 Cloud Service Mesh 流量管理功能,例如在相同服務的不同版本之間分配流量。

安裝 Cloud Service Mesh

請參閱 Cloud Service Mesh 安裝指南,按照說明在叢集上安裝 Cloud Service Mesh。

清除所用資源

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

如要清除資源,請刪除 GKE 叢集。刪除叢集會刪除組成容器叢集的所有資源,例如運算執行個體、磁碟和網路資源。

gcloud container clusters delete asm-ready

後續步驟