Cloud Service Mesh 用アプリケーションを準備する


Cloud Service Mesh は、分散アプリケーションを管理し、モニタリングするための強力なツールです。Cloud Service Mesh を最大限に活用するには、コンテナと Kubernetes を含む基盤となる抽象概念の理解が役に立ちます。このチュートリアルでは、Cloud Service Mesh をインストールする直前までに、Cloud Service Mesh をインストールするアプリケーション(ソースコードから GKE 上で実行されるコンテナまで)を準備する方法について説明します。

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. Make sure 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

At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

サンプルコードをダウンロードする

  1. helloserver ソースコードをダウンロードします。

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    
  2. サンプルコードのディレクトリに移動します。

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

マルチサービス アプリケーションを確認する

サンプル アプリケーションは Python で記述されており、REST を使用して通信する 2 つのコンポーネントを含んでいます。

  • server: 1 つの GET エンドポイント(/)を持つシンプルなサーバー。Console に「hello world」を出力します。
  • loadgen: server にトラフィックを送信するスクリプト。1 秒あたりのリクエスト数(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 の両方)をコンテナにパッケージ化する必要があります。コンテナは、基盤となる環境から分離されるようにアプリケーションをパッケージ化する方法です。

アプリケーションをコンテナ化するには、Dockerfile が必要です。Dockerfile は、アプリケーション ソースコードとその依存関係を Docker イメージにアセンブルするために必要なコマンドを定義するテキスト ファイルです。イメージをビルドしたら、そのイメージを Docker Hub や Container Registry などのコンテナ レジストリにアップロードします。

このサンプルには、serverloadgen の両方の Dockerfile に、イメージのビルドに必要なすべてのコマンドが用意されています。以下は、server 用の Dockerfile です。

FROM python:3.12-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 コマンドは、最新の Python 3 イメージをベースイメージとして使用するように Docker に指示します。
  • 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"

    ビルドするときに PROJECT_IDGCR_REPO の値を使用して Docker イメージにタグ付けし、プライベートの Container Registry に push します。

  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 に push します。

    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 に push します。

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

イメージのリストを表示する

リポジトリ内のイメージのリストを取得して、イメージが push されたことを確認します。

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

コマンドは、先ほど push したイメージ名を返します。

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

GKE クラスタを作成する

Cloud Shell VM でこれらのコンテナを実行するには、docker run コマンドを使用します。ただし、本番環境では、より統合された方法でコンテナをオーケストレートする必要があります。たとえば、コンテナが常に走行していることを保証するシステムが必要です。また、トラフィックの増加に対応するためにコンテナの追加のインスタンスを起動してスケールアップする必要があります。

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 プラットフォームでは、ノードは物理マシンまたは仮想マシンになります。クラスタとは、1 つのマシンとしてひとまとめに処理できる一連のノードのことで、コンテナ化されたアプリケーションをデプロイする場所になります。

  • Pod: Kubernetes では、コンテナは Pod 内で実行されます。Pod は Kubernetes の最小単位です。Pod は 1 つ以上のコンテナを保持します。server コンテナと loadgen コンテナをそれぞれ独自の Pod にデプロイします。Pod で複数のコンテナ(たとえば、アプリケーション サーバーとプロキシ サーバー)が実行される場合、コンテナは単一のエンティティとして管理され、Pod のリソースを共有します。

  • Deployment: Deployment は、一連の同一の Pod を表す Kubernetes オブジェクトです。Deployment は、クラスタのノード間で分散された Pod の複数のレプリカを実行します。Deployment は、失敗した Pod や応答しなくなった Pod を自動的に置き換えます。

  • Kubernetes Service: GKE でアプリケーション コードを実行すると、loadgenserver の間のネットワーキングが変更されます。Cloud Shell VM でサービスを実行するとき、アドレス localhost:8080 を使用して server にリクエストを送信できます。GKE にデプロイすると、Pod は使用可能なノードで走行するようにスケジュールされます。デフォルトでは、Pod を実行するノードを制御できないため、Pod には安定した IP アドレスがありません。

    server の IP アドレスを取得するには、Kubernetes Service と呼ばれる、Pod に対するネットワーク抽象化を定義する必要があります。Kubernetes Service は、一連の Pod に安定したネットワーク エンドポイントを提供します。Service にはいくつかの種類があります。クラスタの外部から server にアクセスできるように、server は外部 IP アドレスを公開する LoadBalancer を使用します。

    Kubernetes には組み込みの DNS システムもあります。これは DNS 名(たとえば、helloserver.default.cluster.local)を Service に割り当てます。これにより、クラスタ内の Pod が安定したアドレスでクラスタ内の他の Pod にアクセスできます。この DNS 名は、Cloud Shell などクラスタの外部からは使用できません。

Kubernetes マニフェスト

ソースコードからアプリケーションを実行したとき、命令型コマンド python3 server.py を使用しました。

命令型とは、「これをしろ」のような動詞駆動という意味です。

対照的に、Kubernetes は宣言型モデルで動作します。つまり、Kubernetes には、実際の処理を指示するのではなく、望ましい状態を提示します。たとえば、実際のシステム状態が望ましい状態になるように、Kubernetes は必要に応じて Pod を起動または停止します。

望ましい状態は、一連のマニフェスト、YAML ファイルで指定します。YAML ファイルには、1 つ以上の Kubernetes オブジェクトの仕様が含まれています。

このサンプルには、serverloadgen のための YAML ファイルが含まれています。各 YAML ファイルは、Kubernetes Deployment オブジェクトと Service に対して望ましい状態を指定します。

サーバー

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 の仕様には、Container Registry から pull するイメージの名前である image フィールドが含まれています。

Service は、次のように定義されます。

apiVersion: v1
kind: Service
metadata:
  name: hellosvc
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: helloserver
  type: LoadBalancer
  • LoadBalancer: クライアントは、安定した IP アドレスを持ち、クラスタの外部からアクセス可能なネットワーク ロードバランサの IP アドレスにリクエストを送信します。
  • targetPort: DockerfileEXPOSE 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 アドレスを提供しますが、その 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_IPhellosvc の外部 IP アドレスに置き換えます。

    curl http://EXTERNAL_IP
    

Cloud Service Mesh の準備

これで、GKE にアプリケーションがデプロイされました。Kubernetes DNS(hellosvc:80)を使用した loadgen による server へのリクエスト送信や、外部 IP アドレスを使用したユーザーによる server へのリクエスト送信が可能です。Kubernetes には多くの機能が用意されていますが、サービスに関する一部の情報が不足しています。

  • サービス間のやり取りはどのように行われるのか。サービス間の関係とは何か。トラフィックはサービス間でどのように流れるか。loadgenserver にリクエストを送信するという知識はあっても、アプリケーションには精通していないかもしれません。GKE で実行中の Pod のリストを確認しても、こうした疑問に対する答えは得られません。
  • 指標: server は受信したリクエストに応答するまでにどれくらいかかるか。server が受信するリクエストの 1 秒あたりの件数(RPS)はいくつか。エラー レスポンスはあるか。
  • セキュリティ情報: loadgenserver プレーン間のトラフィックは HTTP か、それとも mTLS か。

Cloud Service Mesh は、こうした疑問を解決できます。Cloud Service Mesh は、オープンソース Istio プロジェクトの Google Cloud 管理バージョンです。Cloud Service Mesh は、各 Pod に Envoy サイドカー プロキシを配置することで機能します。Envoy プロキシは、アプリケーション コンテナへのすべての受信トラフィックと送信トラフィックを傍受します。つまり、serverloadgen はそれぞれ Envoy サイドカー プロキシを取得し、loadgen から server へのすべてのトラフィックは Envoy プロキシによって仲介されます。これらの Envoy プロキシ間の接続がサービス メッシュを形成します。このサービス メッシュ アーキテクチャは、Kubernetes の上に制御レイヤを提供します。

サービス メッシュ

Envoy プロキシは、独自のコンテナ内で実行されるため、アプリケーション コードに大きな変更を加えることなく、Cloud Service Mesh を GKE クラスタ上にインストールできます。ただし、Cloud Service Mesh でインストゥルメント化するためにアプリケーションを準備するには、いくつかの重要な手順があります。

  • すべてのコンテナのサービス: serverloadgen の両方の Deployment に Kubernetes サービスが接続されています。受信リクエストを受信しない loadgen にも、サービスがあります。
  • サービスのポートには名前を付ける必要がある: GKE では名前のないサービスポートを定義できますが、Cloud Service Mesh ではポートのプロトコルと一致するポートの名前を指定する必要があります。YAML ファイルでは、serverHTTP 通信プロトコルを使用するため、server 用のポートは http という名前になります。servicegRPC を使用した場合は、ポートに grpc と名付けます。
  • Deployment がラベル付けされる: これにより、同じサービスの複数のバージョン間でトラフィックを分割するなど、Cloud Service Mesh のトラフィック管理機能を使用できます。

Cloud Service Mesh をインストールする

Cloud Service Mesh のインストール ガイドの手順で、クラスタに Cloud Service Mesh をインストールします。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

クリーンアップするには、GKE クラスタを削除します。クラスタの削除では、コンテナ クラスタを構成するすべてのリソース(コンピューティング インスタンス、ディスク、ネットワーク リソースなど)が削除されます。

gcloud container clusters delete asm-ready

次のステップ