Istio マルチクラスタを使用してクラスタ間でワークロードを「バースト」する

1. ようこそ

Google の Istio Multi Cloud Burst Codelab にご参加いただきありがとうございます。この Codelab では、Kubernetes、Node、Go に関する初級レベルの実践経験が必要です。

必要なもの

  • Google Cloud Platform アカウント(既存のアカウントを使用するか、無料のアカウントを取得します)
  • ノートパソコン(「kubectl」、「gcloud」などをインストール)または Google Cloud Shell を使用できます。

学習内容

  • GKE で Kubernetes クラスタを作成する方法
  • Helm を使用して Kubernetes クラスタに Istio をインストールする方法
  • Helm を使用して Istio Multicluster をインストールする方法
  • ソースから Kubernetes にウェブ アプリケーションをデプロイする
  • トラフィック ルーティング ルールの作成と Istio への適用
  • Prometheus 指標
  • Kubernetes クラスタ内でコンテナ イメージをビルドして push する

2. 設定方法

この Codelab は、次のいずれかのデバイスで実施できます。

  • Google Cloud Shell(推奨): ブラウザ内シェル。ツールがインストールされています。
  • ノートパソコン(以下の手順に沿って操作してください)

Google Cloud Platform で始める

  1. GCP アカウントをお持ちでない場合は、インストラクターから無料のユーザー アカウント カードを受け取ってください。
  2. Google Cloud コンソールに移動し、[プロジェクトを選択] アイコン 5c2d9bf74c78f7e4.png をクリックします。
  3. プロジェクトの「ID」をメモしてから、プロジェクトをクリックして選択します。ecc5e8e97bfa6559.png

Cloud Shell は、ブラウザ内にコマンドライン シェルを提供します。必要なツールがインストールされ、Google Cloud Platform アカウントに自動的に認証されます。(この演習を Cloud Shell で実行しない場合は、次のセクションに進んでください)。

Cloud コンソールに移動し、右上のツールバーで [Cloud Shell をアクティブにする] をクリックします。

68a17b036ce24ccb.png

Cloud Shell にツールを追加する

  1. kubectx**** をインストールする: こちらから Bash スクリプトをダウンロードし、$PATH 内の場所に配置します。
  2. こちらの手順に沿って、helm**** をインストールします。

または、次のコマンドを実行して、両方を ~/.bin にインストールし、$PATH に追加します。

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

Cloud Shell をより簡単に使用するための簡単なヒント:

1. シェルを新しいウィンドウにデタッチします。

2. ファイル エディタを使用する: 右上の鉛筆アイコンをクリックして、ブラウザ内ファイル エディタを起動します。コード スニペットをファイルにコピーする際に役立ちます。

3. 新しいタブを開始する: 複数のターミナル プロンプトが必要である場合。

4. テキストを大きくする: Cloud Shell のデフォルトのフォントサイズは小さすぎて読みづらい場合があります。

Linux/Windows の場合は Ctrl+、macOS の場合は ⌘+

Cloud Shell よりも独自のワークステーション環境を使用する方が便利な場合は、次のツールを設定します。

  1. gcloud:インストールします(Cloud Shell にプリインストールされています)。手順に沿って、プラットフォームに gcloud をインストールします。これを使用して Kubernetes クラスタを作成します。
  2. kubectl:インストールします(Cloud Shell にプリインストールされています)。次のコマンドを実行してインストールします。
gcloud components install kubectl

次のコマンドを実行して gcloud を認証します。Google アカウントでログインするよう求められます。次に、事前に作成したプロジェクト(上記を参照)をデフォルト プロジェクトとして選択します。(コンピューティング ゾーンの構成はスキップできます)。

gcloud init
  1. インストール curl: ほとんどの Linux/macOS システムにプリインストールされています。すでにお持ちの可能性があります。インストールされていない場合は、インターネットでインストール方法を検索してください。
  2. kubectx**** をインストールする: こちらから bash スクリプトをダウンロードし、$PATH 内の場所に配置します。
  3. こちらの手順に沿って、helm**** をインストールします。

3. GCP プロジェクトを設定する

プロジェクトで GKE(Google Kubernetes Engine)、GCR(Google Container Registry)、GCB(Google Cloud Build)API を有効にします。

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

環境変数を設定する

設定中に Google Cloud プロジェクトを頻繁に使用するため、環境変数を設定しておきましょう。

export GCLOUD_PROJECT=$(gcloud config get-value project)

このワークショップでは、コードと構成ファイルを作成します。プロジェクト ディレクトリを作成して、そのディレクトリに移動しましょう。

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. 「プライマリ」Kubernetes クラスタを作成します。

Google Kubernetes Engine(GKE)を使用すると、マネージド Kubernetes クラスタを簡単に作成できます。

次のコマンドを実行すると、Kubernetes クラスタが作成されます。

  • という名前のプライマリ ネットワーク インターフェースを作成します。
  • us-west1-a ゾーンに、
  • 使用可能な Kubernetes の最新バージョン、
  • 4 つの初期ノード
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(この処理には 5 分ほどかかります。クラスタの作成状況は Cloud コンソールで確認できます)。

Kubernetes クラスタが作成されると、gcloud はクラスタを指す認証情報を使用して kubectl を構成します。

gcloud container clusters get-credentials $cluster --zone=$zone

これで、新しいクラスタで kubectl を使用できるようになります。

次のコマンドを実行して、クラスタの Kubernetes ノードを一覧表示します(ステータスは「Ready」になります)。

kubectl get nodes

使いやすくするために Kubeconfig の名前を変更する

コンテキストを頻繁に切り替えるため、クラスタに短いエイリアスを付けておくと便利です。

このコマンドを実行すると、作成した kubeconfig エントリの名前が primary に変更されます。

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

権限を設定する:

Istio をデプロイするには、クラスタ管理者である必要があります。このコマンドにより、Google Cloud アカウントに関連付けられているメールアドレスがクラスタ管理者として設定されます。

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. 「バースト」クラスタを作成する

次のコマンドを実行すると、Kubernetes クラスタが作成されます。

  • という名前の
  • us-west1-a ゾーンに、
  • 使用可能な Kubernetes の最新バージョン、
  • 初期ノードが 1 つの場合
  • 最大 5 ノードまでの自動スケーリングが有効
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(この処理には 5 分ほどかかります。クラスタの作成状況は Cloud コンソールで確認できます)。

Kubernetes クラスタが作成されると、gcloud はクラスタを指す認証情報を使用して kubectl を構成します。

gcloud container clusters get-credentials $cluster --zone=$zone

これで、新しいクラスタで kubectl を使用できるようになります。

次のコマンドを実行して、クラスタの Kubernetes ノードを一覧表示します(ステータスは「Ready」になります)。

kubectl get nodes

使いやすくするために Kubeconfig の名前を変更する

このコマンドを実行すると、作成した kubeconfig エントリが burst に変更されます。

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

権限を設定する:

Istio Remote をデプロイするには、クラスタ管理者である必要があります。このコマンドにより、Google Cloud アカウントに関連付けられているメールアドレスがクラスタ管理者として設定されます。

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

6. ファイアウォール ルールを適用する

2 つのクラスタが相互に通信できるようにするには、ファイアウォール ルールを作成する必要があります。

次のコマンドを実行して、クラスタ間の通信を許可するファイアウォール ルールを Google Cloud Platform に作成します。

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

これで、両方のクラスタが設定され、アプリケーションと Istio をデプロイする準備が整いました。

7. Istio の概要

Istio とは

Istio は、「サービスの接続、保護、制御、監視」を目的としたサービス メッシュ コントロール プレーンです。これはさまざまな方法で行われますが、主に、デプロイされた Kubernetes Pod ごとにプロキシ コンテナ(Envoy)をサイドカーとして配置します。プロキシ コンテナは、汎用ポリシーとテレメトリー ハブ(Mixer)とともに、マイクロサービス間のすべてのネットワーク通信を制御します。

a25613cd581825da.png

これらのポリシーは Kubernetes Deployment と Service から独立して適用できます。つまり、ネットワーク事業者は、関連するアプリケーションを再デプロイすることなく、ネットワーク アクティビティをモニタリングし、ネットワーク ポリシーを制限、リダイレクト、書き換えることができます。

Istio がサポートするトラフィック管理機能には次のものがあります。

  • サーキット ブレーカー
  • 割合ベースのトラフィック分割
  • URL の書き換え
  • TLS 終端
  • ヘルスチェック
  • 負荷分散

このワークショップでは、割合ベースのトラフィック分割に焦点を当てます。

使用する Istio の用語

VirtualService

VirtualService は、ホストがアドレス指定されたときに適用される一連のトラフィック ルーティング ルールを定義します。

Gateway

ゲートウェイは、メッシュのエッジで動作し、受信または送信 HTTP/TCP 接続を処理するロードバランサです。ゲートウェイでは、ポートや SNI 構成などを指定できます。

DestinationRule

DestinationRule は、ルーティングの発生後に Service を対象とするトラフィックに適用するポリシーを定義します。ロード バランシング、サイドカーからの接続プールのサイズ、外れ値検出の設定を指定します。

Istio マルチクラスタ

2 つのクラスタを作成したときに、primary クラスタは自動スケーリングなしで 4 ノード、burst クラスタは最大 5 ノードまで自動スケーリングで 1 ノードであることに気づいたかもしれません。

この構成には 2 つの理由があります。

まず、オンプレミスから Cloud へのシナリオをシミュレートします。オンプレミス環境では、インフラストラクチャが固定されているため、自動スケーリング クラスタにアクセスできません。

2 つ目に、Istio を実行するための最小要件は、上記の 4 ノード構成です。Istio に 4 つ以上のノードが必要である場合、burst クラスタで 1 つのノードで Istio を実行できるのはなぜでしょうか。Istio Multicluster は、Istio サービスのセットを大幅に少なくインストールし、プライマリ クラスタの Istio インストールと通信してポリシー ルールを取得し、テレメトリー情報を公開します。

8. アプリケーション アーキテクチャの概要

コンポーネントの概要

NodeJSRedis を使用して、3 層アプリケーションをデプロイします。

ワーカー

ワーカー アプリケーションは NodeJS で記述されており、受信した POST HTTP リクエストをリッスンし、ハッシュ化オペレーションを実行します。PREFIX という名前の環境変数が定義されている場合は、その値をハッシュの前に追加します。ハッシュが計算されると、アプリケーションは指定された Redis サーバーのチャンネル「calculation」に結果を送信します。

後で、PREFIX 環境変数を使用してマルチクラスタ機能を説明します。

なお、これらはアプリで使用されるパッケージです。

  • body-parser: HTTP リクエストを解析できるようにします。
  • cors: クロスオリジン リソース シェアリングの使用を許可します。
  • dotenv: 環境変数の簡単な解析
  • express: 簡単なウェブサイト ホスティング
  • ioredis: Redis データベースと通信するクライアント ライブラリ
  • morgan: わかりやすい構造化ログを提供

フロントエンド

フロントエンドは、express を使用してウェブページをホストする NodeJS アプリケーションでもあります。ユーザーが入力した頻度に基づいて、そのレートで worker アプリケーションにリクエストを送信します。このアプリケーションは、「calculation」という名前の Redis チャネルのメッセージをサブスクライブし、結果をウェブページに表示します。

このアプリケーションは、次の依存関係を使用します。

  • body-parser: HTTP リクエストを解析できるようにします。
  • dotenv: 環境変数の簡単な解析
  • express: 簡単なウェブサイト ホスティング
  • ioredis: Redis データベースと通信するクライアント ライブラリ
  • morgan: 構造化されたログを提供します。
  • request: HTTP リクエストを実行できるようにします。
  • socket.io: ウェブページからサーバーへの双方向通信を可能にします。

このウェブページでは、スタイル設定に Bootstrap を使用しています。実行すると、次のように表示されます。

e5e3b9cbede4cac4.png

アーキテクチャ図

7ae4bc22a58f80a6.png

デプロイ図

作成した 2 つのクラスタに最終的なアプリケーションをデプロイします。primary クラスタにはすべてのコンポーネント(frontendworker、Redis)がデプロイされますが、burst クラスタには worker アプリケーションのみがデプロイされます。

次の図は、2 つのクラスタを示しています。赤い枠線のボックスは Kubernetes Service、青い枠線のボックスは Kubernetes Deployment です。黄色のボックスは、Istio がインストールされていることを示します。

561db37c510944bd.png

クラスタ内に Redis の Deployment が存在しなくても、burst クラスタに Redis のサービスがデプロイされていることに注目してください。Kubernetes DNS がリクエストを解決できるように、このサービスをクラスタ内に配置する必要があります。ただし、リクエストが実際に行われた場合、Istio Proxy はリクエストを primary クラスタ内の Redis デプロイメントに再ルーティングします。

最終的なアプリケーションでは、primary クラスタで istiowatcher. という名前の追加の Deployment が実行されます。これにより、トラフィックが特定のしきい値を超えると、トラフィックを burst に動的に再ルーティングできます。

8f6183bdfc3f813c.png

9. アプリケーションのデプロイ ファイルを作成する

アプリケーションをデプロイするには、一連の Kubernetes マニフェストを作成する必要があります。

プロジェクトのルート ディレクトリに移動し、kubernetes という名前の新しいフォルダを作成します。

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

frontend.yaml を作成する

これにより、フロントエンド イメージにアクセスする Kubernetes Deployment と Service の両方が作成されます。

次の内容を frontend.yaml に挿入します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Deployment で注意すべき点

  • アプリケーションを実行するポートを 8080 に指定しています。
  • ワーカーのアドレスを「http://worker-service」に設定し、Kubernetes の組み込み DNS 機能を使用して、結果のサービスを解決します。
  • REDIS_URL のアドレスを「redis-cache-service:6379」に設定し、Kubernetes の組み込み DNS 機能を使用して、結果の IP アドレスを解決します。
  • また、コンテナが稼働状態になったことを Kubernetes に通知するために、liveness プローブと readiness プローブをコンテナに設定しています。

worker-service.yaml を作成する

この Service は複数のクラスタで再利用しますが、クラスタごとに異なる Deployment を作成するため、Kubernetes Service の定義は Deployment の定義とは別のファイルに記述します。

worker-service.yaml に次の内容を挿入します。

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

worker-primary.yaml を作成する

これは、プライマリ クラスタに push する worker のデプロイです。

worker-primary.yaml に次の内容を挿入します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

ここでは、liveness プローブと readiness プローブを指定するパターンと、アプリケーションで使用する PORT 環境変数と REDIS_URL 環境変数を指定するパターンが同じであることに注意してください。

このデプロイで注意すべきもう 1 つの点は、PREFIX 環境変数がないことです。つまり、計算結果は未加工のハッシュ(接頭辞なし)になります。

このデプロイの最後の重要なポイントは、cluster-type: primary-cluster ラベルです。これは、後で Istio マルチクラスタでトラフィック ルーティングを行うときに使用します。

redis.yaml を作成する

ワーカーからフロントエンドへの通信は Redis チャネルを介して行われるため、Redis アプリケーションをクラスタにデプロイする必要があります。

redis.yaml に以下を挿入します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

これは、Redis アプリケーションのセミスタンダード デプロイです。redis:alpine イメージに基づいてコンテナを起動し、適切なポートを公開して、適切なリソース上限を設定します。

redis-service.yaml を作成する

Redis アプリケーションと通信するために Kubernetes Service が必要です。

redis-service.yaml に以下を挿入します。

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

これにより、Redis Deployment にアクセスする redis-cache-service という名前のサービスが提供されます。

10. アプリケーションをデプロイする

イメージが GCR に push され、Kubernetes マニフェストが記述されたので、アプリケーションをデプロイして動作を確認しましょう。

次のコマンドを実行してアプリケーションをデプロイします。

  1. 正しいクラスタにいることを確認する
kubectx primary
  1. Redis Cache をデプロイする
kubectl apply -f redis.yaml
  1. Redis Service をデプロイする
kubectl apply -f redis-service.yaml
  1. フロントエンドをデプロイする
kubectl apply -f frontend.yaml
  1. ワーカーをデプロイする
kubectl apply -f worker-primary.yaml
  1. Worker Service をデプロイする
kubectl apply -f worker-service.yaml

アプリケーションを GKE にデプロイしました。おめでとうございます!

テスト

Pod がオンラインになるのを待つ

kubectl get pods -w

すべての Pod が「実行中」になったら、Ctrl+C キーを押します。

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

LoadBalancer を介してフロントエンドを公開していないことがわかります。これは、後で Istio 経由でアプリケーションにアクセスするためです。すべてが稼働していることを確認するには、kubectl port-forward. を使用します。次のコマンドを実行して、ローカルマシン(または Cloud Shell)のポート 8080 を、frontend デプロイメントを実行しているポート 8080 に転送します。

kubectl port-forward \
$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

ローカルで実行している場合: ウェブブラウザを開き、http://localhost:8080 に移動します。

Cloud Shell で実行している場合: [ウェブでプレビュー] ボタンをクリックし、[ポート 8080 でプレビュー] を選択します。

bdb5dc75f415be11.png

フロントエンドが表示されます。[頻度] ボックスに数字を入力すると、ハッシュが表示されます。

1caafaffab26897a.png

これで設定は完了です。

Ctrl+C を押してポート転送を停止します。

11. デプロイされたアプリケーションのクリーンアップ

Istio をクラスタに適用してからアプリケーションを再デプロイするため、まず現在のアプリケーションをクリーンアップします。

次のコマンドを実行して、作成した Deployment とサービスをすべて削除します。

  1. redis-cache-service を削除
kubectl delete -f redis-service.yaml
  1. redis を削除
kubectl delete -f redis.yaml
  1. frontend を削除
kubectl delete -f frontend.yaml
  1. worker を削除
kubectl delete -f worker-primary.yaml
  1. worker-service を削除
kubectl delete -f worker-service.yaml

12. プライマリ クラスタに Istio をインストールする

Istio を入手する

Istio のリリースは GitHub でホストされています。次のコマンドを実行すると、istio の 1.0.0 バージョンがダウンロードされ、解凍されます。

  1. プロジェクトのルートに移動します。
cd ${proj}
  1. アーカイブをダウンロードする
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. アーカイブを解凍して削除する
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Istio テンプレートを作成する

次の Helm コマンドを実行すると、Istio をクラスタにインストールするテンプレートが作成されます。

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

これにより、現在のディレクトリに istio-primary.yaml という名前のファイルが作成されます。このファイルには、Istio のデプロイと実行に必要なすべての定義と仕様が含まれています。

2 つの --set パラメータに注意してください。これにより、Istio システムに PrometheusServiceGraph のサポートが追加されます。Prometheus サービスは、ラボの後半で使用します。

Istio をデプロイする

istio をデプロイするには、まず、Istio Deployment と Service を実行できる istio-system という Namespace を作成する必要があります。

kubectl create namespace istio-system

最後に、Helm で作成した istio-primary.yaml ファイルを適用します。

kubectl apply -f istio-primary.yaml

デフォルト Namespace にラベルを付ける

Istio は、各 Deployment にサイドカー プロキシ サービスを挿入することで機能します。これはオプトインベースで行われるため、Istio がサイドカーを自動的に挿入できるように、default Namespace に istio-injection=enabled というラベルを付ける必要があります。

kubectl label namespace default istio-injection=enabled

おめでとうございます。Istio を搭載したクラスタが稼働し、アプリケーションをデプロイする準備が整いました。

13. Istio のトラフィック管理を使用してアプリケーションをデプロイする

Istio のトラフィック管理構成ファイルを作成する

Istio は、構成に yaml ファイルを使用するため、Kubernetes と同様の方法で動作します。同様に、トラフィックを公開してルーティングする方法を Istio に指示する一連のファイルを作成する必要があります。

istio-manifests という名前のディレクトリを作成し、そのディレクトリに移動します。

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

frontend-gateway.yaml を作成する

このファイルは、GKE LoadBalancer と同様に Kubernetes クラスタを公開し、受信したすべてのトラフィックをフロントエンド サービスに転送します。

frontend-gateway.yaml という名前のファイルを作成し、次の内容を挿入します。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

redis-virtualservice.yaml を作成します

redis-virtualservice.yaml という名前のファイルを作成し、次の内容を挿入します。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

worker-virtualservice.yaml を作成する

worker-virtualservice.yaml という名前のファイルを作成し、次の内容を挿入します。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       port:
         number: 80

Istio トラフィック管理ポリシーをデプロイする

Istio ポリシーのデプロイは、他の Kubernetes リソースと同様に kubectl apply を使用して行います。

  1. Gateway を適用する
kubectl apply -f frontend-gateway.yaml
  1. Redis VirtualService を適用する
kubectl apply -f redis-virtualservice.yaml
  1. Worker VirtualService を適用する
kubectl apply -f worker-virtualservice.yaml

アプリケーションをデプロイする

  1. kubernetes ディレクトリに戻ります。
cd ${proj}/kubernetes
  1. Redis Cache をデプロイする
kubectl apply -f redis.yaml
  1. Redis Service をデプロイする
kubectl apply -f redis-service.yaml
  1. フロントエンドをデプロイする
kubectl apply -f frontend.yaml
  1. ワーカーをデプロイする
kubectl apply -f worker-primary.yaml
  1. Worker Service をデプロイする
kubectl apply -f worker-service.yaml

確認

この時点で、Istio とトラフィック管理ポリシーを使用してクラスタにアプリケーションを再デプロイしました。

すべてのワークロードがオンラインになるまで待ちます。

すべてオンラインになったら、frontend-ingressgateway.yaml で構成した IngressGateway を取得します。

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

<EXTERNAL-IP> アドレスにブラウジングするか、curl を実行すると、フロントエンドが表示されます。

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. Istio を「バースト」クラスタにインストールする

primary クラスタの設定とデプロイに多くの時間を費やしましたが、デプロイするクラスタがもう 1 つあります。

このセクションでは、両方のクラスタの構成変数を取得する必要があります。そのため、各コマンドでどのクラスタが参照されているかに注意してください。

Istio リモート マニフェストを作成する

Istio を primary クラスタにデプロイしたときと同様に、Helm を使用して、burst クラスタへの Istio リモートのデプロイをテンプレート化します。ただし、その前に、primary クラスタに関する情報を取得する必要があります。

プライマリ クラスタ情報を収集する

primary クラスタに変更

kubectx primary

次のコマンドは、プライマリ クラスタ内のさまざまな Pod の IP アドレスを取得します。これらは、Istio Remote がプライマリ クラスタとの通信に使用します。

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

リモート テンプレートを作成する

次に、helm を使用して istio-remote-burst.yaml という名前のファイルを作成し、burst クラスタにデプロイします。

プロジェクトのルートに変更する

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

バースト クラスタに Istio Remote をインストールする

burst クラスタに Istio をインストールするには、primary クラスタにインストールする場合と同じ手順に沿って、代わりに istio-remote-burst.yaml ファイルを使用する必要があります。

kubecontext を burst に変更する

kubectx burst

istio-system Namespace を作成する

kubectl create ns istio-system

istio-burst.yaml を適用する

kubectl apply -f istio-remote-burst.yaml

ラベルのデフォルト Namespace

プロキシを自動的に挿入できるように、default Namespace にラベルを付ける必要があります。

kubectl label namespace default istio-injection=enabled

おめでとうございます。この時点で、burst クラスタに Istio Remote が設定されています。この時点では、クラスタはまだ通信できません。burst クラスタの kubeconfig ファイルを生成して、primary クラスタにデプロイし、クラスタをリンクする必要があります。

「バースト」クラスタの kubeconfig を作成する

バースト クラスタに変更する

kubectx burst

環境を設定する

クラスタの kubeconfig ファイルを作成するには、クラスタに関する情報を収集する必要があります。

  1. クラスタの名前を取得する
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. クラスタ サーバー名を取得する
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. istio-multi サービス アカウントの認証局の Secret の名前を取得する
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. 前のシークレットに保存されている認証局データを取得する
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. 前のシークレットに保存されているトークンを取得する
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

kubeconfig ファイルを作成する

これらの環境変数をすべて設定したら、kubeconfig ファイルを作成する必要があります。

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

これにより、現在のディレクトリに burst-kubeconfig という新しいファイルが作成されます。このファイルは、primary クラスタが burst クラスタの認証と管理に使用できます。

プライマリ クラスタに戻す

kubectx primary

シークレットを作成してラベルを付け、「バースト」の kubeconfig を適用する

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

Istio がマルチクラスタ認証にシークレットを使用するようにラベルを付ける

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

おめでとうございます。両方のクラスタが認証され、Istio マルチクラスタを介して相互に通信しています。アプリケーションをクラスタ間でデプロイする

15. クラスタ間アプリケーションをデプロイする

Deployment を作成する

kubernetes ディレクトリに移動します。

cd ${proj}/kubernetes

「バースト」クラスタのワーカー デプロイメントを作成する: worker-burst.yaml

worker-burst.yaml という名前のファイルを作成し、次の内容を挿入します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

これは、前に作成した worker-primary.yaml とほぼ同じです。主な違いは次の 2 つです。

最初の主な違いは、値が「bursty-」の PREFIX 環境変数を追加したことです。

env:
- name: PORT
  value: "8081"
- name: REDIS_URL
  value: "redis-cache-service:6379"
- name: PREFIX
  value: "bursty-"

つまり、burst クラスタ内のワーカーは、送信するすべてのハッシュに「bursty-」という接頭辞を付けます。これにより、アプリケーションが本当にクラスタ間であることを確認できます。

2 つ目の主な違いは、この Deployment の cluster-type ラベルを primary-cluster から burst-cluster に変更したことです。

labels:
  app: worker
  cluster-type: burst-cluster

このラベルは、後で VirtualService を更新するときに使用します。

Istio サービスを変更する

現在、Istio サービスは両方のデプロイメントを活用していません。トラフィックの 100% が「プライマリ」クラスタにルーティングされています。これをわかりやすく変更しましょう。

istio-manifests ディレクトリに移動します。

cd ${proj}/istio-manifests

worker-virtualservice.yaml を編集して DestinationRules を含める

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

VirtualService に 2 つ目のデスティネーションを追加したことがわかります。同じホスト(worker-service.default.svc.cluster.local))を参照しますが、トラフィックの 50% は primary サブセットにルーティングされ、残りの 50% は burst サブセットにルーティングされます。

primary サブセットはラベル cluster-type: primary-cluster を持つ Deployment に、burst サブセットはラベル cluster-type: burst-cluster を持つ Deployment に定義されています。

これにより、トラフィックが 2 つのクラスタ間で 50/50 に分割されます。

クラスタにデプロイする

redis-service.yaml をバースト クラスタにデプロイする

burst kubeconfig に変更する

kubectx burst

プロジェクトのルートに移動します。

cd ${proj}

次に、デプロイします。

redis-service.yaml をバースト クラスタにデプロイする

kubectl apply -f kubernetes/redis-service.yaml

worker-burst.yaml をバースト クラスタにデプロイする

kubectl apply -f kubernetes/worker-burst.yaml

worker-service.yaml をバースト クラスタにデプロイする

kubectl apply -f kubernetes/worker-service.yaml

Istio VirtualService を適用する

primary kubeconfig に変更する

kubectx primary

次にデプロイ

kubectl apply -f istio-manifests/worker-virtualservice.yaml

動作を確認する

動作を確認するには、Istio Ingress ポイントに移動し、ハッシュの約 50% に「burst-」という接頭辞が付いていることを確認します。

78fb6e235e9f4a07.png

これは、クラスタ間で正常に通信されていることを意味します。さまざまなサービスの重みを変更して、worker-virtualservice.yaml ファイルを適用してみてください。これはクラスタ間のトラフィックを分散するのに適した方法ですが、自動的に行うことはできないでしょうか。

16. Prometheus 指標を活用する

Prometheus の概要

Prometheus は、もともと SoundCloud で構築されたオープンソースのシステム モニタリングとアラート用のツールキットです。指標名と Key-Value ペアで識別される時系列データを含む多次元データモデルを維持します。

以下に、Prometheus のアーキテクチャ図を示します。

601e1155a825e0c2.png

Istio を Prometheus とともにデプロイすると、さまざまな指標が Prometheus サーバーに自動的に報告されます。これらの指標を使用して、クラスタをオンザフライで管理できます。

Prometheus 指標の確認

まず、Prometheus Deployment を公開する必要があります。

GKE の [ワークロード] タブに移動し、[prometheus] ワークロードにドリルダウンします。

b4a7a3cd67db05b3.png

デプロイの詳細が表示されたら、[アクション] > [公開] に移動します。

c04a482e55bdfd41.png

ポート 9090 に転送することを選択し、「ロードバランサ」と入力します。

d5af3ba22a7a6ebb.png

[公開] を選択します。

これにより、一般公開されている IP アドレスに Service が作成され、Prometheus 指標の探索に使用できるようになります。

エンドポイントが動作可能になるまで待ち、[外部エンドポイント] b1e40ad90851da29.png の横にある IP アドレスをクリックします。

Prometheus UI が表示されます。

ed273552270337ec.png

Prometheus には、独自のワークショップとして使用できる十分な指標が用意されています。では、まず istio_requests_total 指標について説明します。

このクエリを実行すると、大量のデータが返されます。これは、Istio サービス メッシュを通過するすべてのリクエストに関する指標です。式を変更して、本当に興味のあるものに絞り込みを行います。

宛先サービスが worker-service.default.svc.cluster.local で、送信元が frontend-deployment のリクエスト(過去 15 秒間に限定)

クエリは次のようになります。

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

扱いやすいデータセットが得られます。

19d551fd5eac3785.png

ただし、まだ少し密集しています。すべてのリクエストではなく、1 秒あたりのリクエスト数を把握する必要があります。

そのためには、組み込みの rate 関数を使用します。

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

これで目的に近づきましたが、これらの指標を論理的なグループにさらに絞り込む必要があります。

これを行うには、sum キーワードと by キーワードを使用して結果をグループ化して合計します。

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

なら、Prometheus から必要な指標を正確に取得できます。

最後の Prometheus クエリ

ここまで学んだことを踏まえて、Prometheus に実行する最後のクエリは次のとおりです。

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

これで、HTTP API を使用して指標を取得できます。

次のように HTTP GET リクエストを送信することで、クエリで API をクエリできます。<prometheus-ip-here> は置き換えてください。

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

以下に示すのはレスポンスの例です。

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

これで、JSON から指標値を抽出できます。

クリーンアップ

Prometheus の公開に使用した Service を削除する必要があります。Google Cloud コンソールで、作成したサービスの上部に移動し、[削除] をクリックします。

d58cb51b4c922751.png

次のステップ:

クラスタ内をトラフィックがどのように移動し、どのくらいのレートで移動するかを検出する方法が見つかりました。次のステップは、Prometheus を定期的にクエリする小さなバイナリを作成することです。worker への 1 秒あたりのリクエスト数が特定のしきい値を超えると、ワーカー仮想サービスに異なる宛先重みを適用して、すべてのトラフィックを burst クラスタに送信します。1 秒あたりのリクエスト数が下限しきい値を下回ったら、すべてのトラフィックを primary に戻します。

17. クラスタ間バーストを作成する

設定

ワーカーサービスのすべてのトラフィックをプライマリ クラスタに設定する

worker-service 宛てのすべてのトラフィックが primary クラスタにルーティングされることを、アプリケーションの「デフォルト」の状態と見なします。

$proj/istio-manifests/worker-virtualservice.yaml を次のように編集します。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

primary クラスタに接続していることを確認する

kubectx primary

istio-manifests/worker-virtualservice.yaml を適用する

kubectl apply -f istio-manifests/worker-virtualservice.yaml

istiowatcher デーモンを作成する

このサービスは、速度とポータビリティを重視して Go で記述します。アプリケーションの全体的なフローでは、起動時に Prometheus にクエリを実行し、1 秒ごとにクエリを実行します。

src に istiowatcher という名前の新しいディレクトリを作成します。

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

クラスタ内から Istio コントロール プレーンを操作するため、コンテナ内から istioctl を呼び出します。

istiowatcher.go を作成する

そのディレクトリに istiowatcher.go という名前のファイルを作成し、次の内容を挿入します。

package main

import (
        "github.com/tidwall/gjson"
        "io/ioutil"
        "log"
        "net/http"
        "os/exec"
        "time"
)

func main() {
        //These are in requests per second
        var targetLow float64 = 10
        var targetHigh float64 = 15
        // This is for the ticker in milliseconds
        ticker := time.NewTicker(1000 * time.Millisecond)

        isBurst := false

        // Our prometheus query
        reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

        for t := range ticker.C {
                log.Printf("Checking Prometheus at %v", t)

                // Check prometheus
                // Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
                // If we wanted this to be a little "snappier" we can scale it down to say 30s
                resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
                if err != nil {
                        log.Printf("Error: %v", err)
                        continue
                }
                defer resp.Body.Close()
                body, _ := ioutil.ReadAll(resp.Body)

                val := gjson.Get(string(body), "data.result.0.value.1")
                log.Printf("Value: %v", val)

                currentReqPerSecond := val.Float()
                log.Printf("Reqs per second %f", currentReqPerSecond)

                if currentReqPerSecond > targetHigh && !isBurst {
                        applyIstio("burst.yaml")
                        log.Println("Entering burst mode")
                        isBurst = true
                } else if currentReqPerSecond < targetLow && isBurst {
                        applyIstio("natural.yaml")
                        log.Println("Returning to natural state.")
                        isBurst = false
                }
        }
}

func applyIstio(filename string) {
        cmd := exec.Command("istioctl", "replace", "-f", filename)
        if err := cmd.Run(); err != nil {
                log.Printf("Error hit applying istio manifests: %v", err)
        }
}

Dockerfile を作成する

Dockerfile という名前の新しいファイルを作成し、次の内容を挿入します。

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base 

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

このマルチステージ Dockerfile は、最初のステージで Istio の 1.0.0 リリースをダウンロードして解凍します。2 番目のステージでは、ディレクトリ内のすべてをイメージにコピーし、istioctl をビルドステージから /usr/local/bin にコピーして(アプリケーションから呼び出せるように)、依存関係を取得し、コードをコンパイルして、CMD を「istiowatcher」に設定します。

burst.yaml を作成する

これは、frontend から worker へのリクエスト数/秒が 15 を超えたときに istiowatcher が適用されるファイルです。

burst.yaml という名前の新しいファイルを作成し、次の内容を挿入します。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight:  100

natural.yaml を作成する

これは、frontendworker の 1 秒あたりのリクエスト数が 10 を下回ったときに復元される「自然な」状態と見なされます。この状態では、トラフィックの 100% が primary クラスタにルーティングされます。

natural.yaml という名前の新しいファイルを作成し、次の内容を挿入します。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight: 0

istiowatcher をビルドして push する

次のコマンドを実行して、現在のディレクトリを Google Cloud Build(GCB)に送信します。これにより、GCR でイメージがビルドされ、タグ付けされます。

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

istiowatcher をデプロイする

kubernetes ディレクトリに移動します。

cd ${proj}/kubernetes/

デプロイ ファイル istiowatcher.yaml を作成します

istiowatcher.yaml というファイルを作成し、次の内容を挿入します(<your-project-id> を置き換えます)。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

導入

プライマリ クラスタで実行されていることを確認する

kubectx primary

istio-system Namespace に istiowatcher.yaml をデプロイする

kubectl apply -n istio-system -f istiowatcher.yaml

yaml の serviceAccountName ディレクティブと automountServiceAccountToken ディレクティブに注意してください。これにより、クラスタ内から istioctl を実行するために必要な認証情報が得られます。

また、istio-pilot-service-account の認証情報を確実に取得できるように、これを istio-system 名前空間内にデプロイする必要があります。(default 名前空間には存在しません)。

トラフィックが自動的に切り替わる様子を確認する

次に、マジック モーメントです。フロントエンドに移動して、1 秒あたりのリクエスト数を 20 に引き上げましょう。

数秒かかることに注意してください。しかし、ハッシュはすべて「bursty-」という接頭辞が付いて、徐々に増加します。

これは、15s 範囲で Prometheus をサンプリングしているため、レスポンス時間が少し遅れるためです。より狭いバンドが必要な場合は、prometheus のクエリを 5s. に変更できます。

18. 次のステップ

クリーンアップ

このワークショップ用に提供された一時アカウントを使用している場合は、クリーンアップする必要はありません。

Kubernetes クラスタ、ファイアウォール ルール、GCR 内のイメージを削除できます。

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

今後の対応