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 Console に移動し、[プロジェクトを選択](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. 「primary」Kubernetes クラスタを作成します

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

次のコマンドは、Kubernetes クラスタを作成します。

  • 「primary」という名前の
  • 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 Console で確認できます)。

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 クラスタを作成します。

  • 「burst」という名前の
  • 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 Console で確認できます)。

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

a25613cd581825da.png

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

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

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

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

Istio で使用する用語

VirtualService

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

Gateway

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

DestinationRule

DestinationRule は、ルーティングのにサービス宛てのトラフィックに適用されるポリシーを定義します。ロード バランシングの構成、サイドカーからの接続プール サイズ、外れ値検出の設定を指定します。

Istio Multicluster

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

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

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

第 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 の Service がデプロイされていることに注目してください。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. ワーカー サービスをデプロイする
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 経由でアプリケーションにアクセスするためです。すべてが稼働していることをテストするには、次のコマンドを実行して、ローカルマシン(または Cloud Shell)のポート 8080 を frontend デプロイを実行しているポート 8080 に転送します。kubectl port-forward.

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 と Service をすべて削除します。

  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 のデプロイと実行に必要なすべての定義と仕様を含む istio-primary.yaml という名前のファイルが現在のディレクトリに作成されます。

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. ワーカー サービスをデプロイする
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 リモート マニフェストを作成する

primary クラスタに Istio をデプロイしたときと同様に、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 をバーストに変更する

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 がマルチクラスタ認証に使用するように、Secret にラベルを付けます。

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

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

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 つ目の大きな違いは、このデプロイの cluster-type ラベルを primary-cluster から burst-cluster に変更したことです。

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

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

Istio サービスの変更

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

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

cd ${proj}/istio-manifests

DestinationRule を含めるように 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: 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 を持つデプロイ、burst サブセットはラベル cluster-type: burst-cluster を持つデプロイとして定義されています。

これにより、トラフィックが 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

burst クラスタに 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

[公開] を選択します。

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

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

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 のすべてのトラフィックをプライマリ クラスタに設定する

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 を使用して記述します。アプリケーションの全体的なフローは、起動して 1 秒ごとに Prometheus にクエリを実行することです。

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 を作成する

frontend から worker への 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 名前空間に存在しない)。

トラフィックが自動的に切り替わるのを確認します。

いよいよマジック モーメントです。フロントエンドに移動して、リクエスト/秒を 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

今後の展望