準備 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. Verify that billing is enabled for your Google Cloud project.

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

準備 Cloud Shell

使用 Cloud Shell 的優點如下:

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

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

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

In the Google Cloud console, activate Cloud Shell.

Activate Cloud Shell

下載程式碼範例

  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. 如要停止 loadgenserver,請在每個終端機視窗中輸入 Ctrl-c

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

    deactivate
    

裝載應用程式

如要在 GKE 上執行應用程式,您需要將範例應用程式 (serverloadgen) 封裝為容器容器可封裝應用程式,使其與基礎環境隔離。

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

這個範例隨附 server,適用於 serverloadgen,並包含建構映像檔所需的所有指令。Dockerfile以下是 serverDockerfile

FROM python:3.13-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 替換為專案 ID。Google Cloud

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

    建構 Docker 映像檔並推送至私人 Container Registry 時,您會使用 PROJECT_IDGCR_REPO 的值標記映像檔。

  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 包含一或多個容器。您會將 serverloadgen 容器分別部署到各自的 Pod。如果 Pod 執行多個容器 (例如應用程式伺服器和Proxy 伺服器),系統會將這些容器視為單個實體進行管理,這些容器也共用 Pod 的資源。

  • 部署作業:部署作業是 Kubernetes 物件,代表一組相同的 Pod。Deployment 會執行多個 Pod 副本,並將這些副本分散到叢集的節點中。Deployment 會自動取代失敗或無法回應的 Pod。

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

    如要取得 server 的 IP 位址,您必須在 Pod 上方定義稱為 Kubernetes Service 的網路抽象化。Kubernetes Service 可為一組 Pod 提供穩定的網路端點。 Service 分為幾種類型server 使用 LoadBalancer,公開外部 IP 位址,讓您從叢集外部連線至 server

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

Kubernetes 資訊清單

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

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

相較之下,Kubernetes 採用宣告式模型。 也就是說,您不必明確指示 Kubernetes 執行作業,只需提供所需狀態。舉例來說,Kubernetes 會視需要啟動及終止 Pod,確保實際系統狀態符合所需狀態。

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

範例包含 serverloadgen 的 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 改成您的專案 ID。 Google Cloud

  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 改成您的專案 ID。 Google Cloud

  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) 為多少?是否有任何錯誤回應?
  • 安全性資訊loadgenserver 之間的流量是純文字 HTTP 還是 mTLS?

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

服務網格

由於 Envoy 代理程式會在自己的容器中執行,因此您可以在 GKE 叢集上安裝 Cloud Service Mesh,不必大幅變更應用程式程式碼。不過,您準備應用程式以透過 Cloud Service Mesh 進行檢測時,有幾項重要做法:

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

安裝 Cloud Service Mesh

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

清除所用資源

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

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

gcloud container clusters delete asm-ready

後續步驟