フューチャー技術ブログ

GCP連載#2 Istio on GKEではじめるサービスメッシュ

はじめに

こんにちは、TIGの村田です。GCP連載企画2日目の記事です!

皆さんは普段どのようなアプリケーションを開発されてますか? 私は最近Kubernetes上にデプロイされるマイクロサービスなアプリケーションに携わっています。

今回はIstio on GKEに焦点をあててGCPにおけるサービスメッシュの活用方法をご紹介したいと思います。

GKEクラスタを作成する

まずはクラスタを作成します。

基本的にはデフォルト値のままクラスタ作成してしまおうと思います。
変えるのは一箇所だけ、今回の目玉の部分です。

Additional featuresEnable Istio にチェック!
チェックを入れると Enable mTLS 欄が登場します。

選べるのは PermissiveStrict の2種類ですが、今回はデフォルト値である Permissive のままとします。詳しくは以下のページを見てみてくださいmm
The service mesh era: Securing your environment with Istio

少し待つとクラスタができました!

クラスタ内に作成されたServiceを確認してみましょう。

$ gcloud container clusters list --project <your-project>
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
standard-cluster-1 us-central1-a 1.13.11-gke.23 130.211.127.151 n1-standard-1 1.13.11-gke.23 3 RUNNING

Istio関連のService(Podも)は istio-system のNamespaceに作成されます。

$ kubectl get service -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-citadel ClusterIP 10.12.6.189 <none> 8060/TCP,15014/TCP 49m
istio-galley ClusterIP 10.12.10.108 <none> 443/TCP,15014/TCP,9901/TCP 49m
istio-ingressgateway LoadBalancer 10.12.11.17 34.69.104.178 15020:30222/TCP,80:30345/TCP,443:31293/TCP,31400:31428/TCP,15029:30807/TCP,15030:30916/TCP,15031:32448/TCP,15032:31430/TCP,15443:30612/TCP 49m
istio-pilot ClusterIP 10.12.15.0 <none> 15010/TCP,15011/TCP,8080/TCP,15014/TCP 49m
istio-policy ClusterIP 10.12.3.166 <none> 9091/TCP,15004/TCP,15014/TCP 49m
istio-sidecar-injector ClusterIP 10.12.5.178 <none> 443/TCP 49m
istio-telemetry ClusterIP 10.12.14.103 <none> 9091/TCP,15004/TCP,15014/TCP,42422/TCP 49m
promsd ClusterIP 10.12.3.103 <none> 9090/TCP 49m

また、デフォルトでは sidecar injection が有効になっていません。
Istioのメリットを最大限享受するためにはサイドカーとして動くEnvoy proxyが必要です。
以下のコマンドで有効化します。

$ kubectl label namespace default istio-injection=enabled
namespace/default labeled

クラスタの準備はひとまず完了です。

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

マイクロサービスのデモアプリケーションといえばこれ、ということで Hipster Shop: Cloud-Native Microservices Demo Application を早速デプロイします。

まずは microservices-demo をclone。

git clone https://github.com/GoogleCloudPlatform/microservices-demo.git

Docker, kubectl, skaffold が必要とのことでインストール。筆者環境では skaffold のみが無かったのでHomebrew経由で入れました。

brew install skaffold

skaffold はバージョン制約(≥v0.20)があるので念の為チェック。

$ skaffold version
v1.3.1

問題なさそうですね。次に進みます。
READMEに従う形でMANIFESTをapply。ここの紐解きは後述(★)します。

$ kubectl apply -f ./istio-manifests
gateway.networking.istio.io/frontend-gateway created
virtualservice.networking.istio.io/frontend-ingress created
virtualservice.networking.istio.io/frontend created
serviceentry.networking.istio.io/whitelist-egress-googleapis created
serviceentry.networking.istio.io/whitelist-egress-google-metadata created

skaffoldを使って一気にイメージ登録からデプロイまでを行います。Docでは30分未満との記述がありましたが、筆者環境では1時間半以上かかりました。。。マシンスペックとネットワークスピードですかね。。

skaffold run --default-repo=gcr.io/<your-project>

何はともあれ無事にデモアプリのデプロイが完了しました。
Podの稼働状況を見てみます。

$ kubectl get po
NAME READY STATUS RESTARTS AGE
adservice-7b456d8f85-29s59 2/2 Running 0 9h
cartservice-7874b9c947-7vgmz 2/2 Running 3 9h
checkoutservice-68c84b95cb-kx7z5 2/2 Running 0 9h
currencyservice-58dc87958b-knkzr 2/2 Running 0 9h
emailservice-5f4d4cc69-4mp75 2/2 Running 0 9h
frontend-75b6b5d576-pxbcv 2/2 Running 6 9h
loadgenerator-554f8b4bc8-9rzzx 2/2 Running 6 9h
paymentservice-58867f6b85-nfcwn 2/2 Running 0 9h
productcatalogservice-5c567d44cf-jsgjx 2/2 Running 0 9h
recommendationservice-6fb9fd5b8f-5p449 2/2 Running 0 9h
redis-cart-698f46b844-f4qr8 2/2 Running 0 9h
shippingservice-6fd9764fc5-b44lc 2/2 Running 0 142m

デフォルトのクラスタ設定ではリソースが足らず、半数以上のPodがPending状態となってしまいました。
Autoscalingの設定を入れることで解消しましたが、それに伴いCrashLoopBackoffが発生してしまったPodがいくつか存在しています。

Autoscale後は n1-standard-1 が6台並ぶ構成となりました(初期3台構成)

sidecar injection を有効にしたので、Podの中も少し覗いてみます。

$ kubectl describe pod adservice-7b456d8f85-29s59
Name: adservice-7b456d8f85-29s59
Namespace: default
Priority: 0
PriorityClassName: <none>
・・・
・・・
Containers:
server:
・・・
・・・
istio-proxy:
・・・
・・・

istio-proxy がコンテナとして稼働していることが確認できました。

画面へのアクセスも試みてみましょう。
まずはIPを取得。

$ kubectl get service frontend-external
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend-external LoadBalancer 10.12.0.203 35.232.239.135 80:30731/TCP 9h

35.232.239.135 がIPなのでアクセスすると…

無事にデモ画面が表示されました!!

サービスモニタリングを行う

さて、Istioを使う意義の1つであるモニタリングに踏み込んでいきましょう。
細かい設定手順は公式のMonitoring解説パートがとても丁寧なので参照すると良いです。

以下はAdd ChartよりServer LatencyのChartを作成している画面です。
各項目設定時は選択候補がとてもたくさんありましたが、インクリメンタルサーチでどんどん絞れるのでストレスなく作成を進められました。

いくつかのメトリクスを追加してみると…

簡単にダッシュボードが作成できました。
既に様々なデータがStackdriver Monitoringへ送られているので、それをダッシュボード化すれば良いだけで特段難しいことは無かったです。

サービストレースを行う

簡単ですがトレースも行ってみました。
Stackdriver Traceの画面にて時系列順にLatencyがplotされます。

ちょうど点線のあたりでポチポチ画面を触っていたのですが、気になるほど高Latencyになってるわけではありませんでした。

処理ごとのLatencyも確認できます。

もちろん実装次第ではもっとブレークダウンした形でLatencyを参照できます。
こんなに簡単に見られることが分かってしまうと、OpenCensusを使った可視化にもっともっとチャレンジしたいという気持ちに包まれます!

GKEクラスタに適用したIstio用MANIFESTについて

READMEに従う形でMANIFESTをapply。ここの紐解きは後述(★)します。

ここの布石を回収します。
applyしたのはここにある3つのyamlでした。

1つ目は 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
spec:
hosts:
- "*"
gateways:
- フロントエンド-gateway
http:
- route:
- destination:
host: frontend
port:
number: 80

Istioを有効化すると自動的に Ingress Gateway が有効化されるのですが、具体的にGatewayがどのようなリクエストを受け付けるか定義しているのが kind: Gateway の箇所です。全てのHTTPリクエストを受け付ける形で定義されています。

Ingress Gateway 自体の詳細はこちらも参照してみてください。

kind: VirtualService で定義されているのが、Gatewayを通過したリクエストの転送先です。 destination にて具体的な記載があり frontend と記載されているため今回のデモアプリではWeb画面を返却するWebサーバへルーティングされます。

Ingress GatewayのIPアドレスは以下のコマンドで取得可能です。

$ INGRESS_HOST="$(kubectl -n istio-system get service istio-ingressgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
$ echo "$INGRESS_HOST"
34.69.104.178

2つ目は frontend.yaml です。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: frontend
spec:
hosts:
- "frontend.default.svc.cluster.local"
http:
- route:
- destination:
host: frontend
port:
number: 80

これの存在意義はしっかり腹落ちしきっていないのですが、(1)IstioのIngress gatewayを使ったアクセスと(2)LoadBalancerを使ったアクセスではHTTPレスポンスヘッダに差異がありました。

まずは(1)の場合。

$ curl -v http://34.69.104.178
* Rebuilt URL to: http://34.69.104.178/
* Trying 34.69.104.178...
* TCP_NODELAY set
* Connected to 34.69.104.178 (34.69.104.178) port 80 (#0)
> GET / HTTP/1.1
> Host: 34.69.104.178
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< set-cookie: shop_session-id=e654493b-ee35-4c30-9cbd-c11b72f89a4d; Max-Age=172800
< date: Wed, 05 Feb 2020 07:23:26 GMT
< content-type: text/html; charset=utf-8
< x-envoy-upstream-service-time: 70
< server: istio-envoy
< transfer-encoding: chunked

次は(2)の場合。

$ curl -v http://35.232.239.135
* Rebuilt URL to: http://35.232.239.135/
* Trying 35.232.239.135...
* TCP_NODELAY set
* Connected to 35.232.239.135 (35.232.239.135) port 80 (#0)
> GET / HTTP/1.1
> Host: 35.232.239.135
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< set-cookie: shop_session-id=828d0405-1c54-4605-8156-6178c480540f; Max-Age=172800
< date: Wed, 05 Feb 2020 07:24:24 GMT
< content-type: text/html; charset=utf-8
< x-envoy-upstream-service-time: 32
< server: istio-envoy
< x-envoy-decorator-operation: frontend.default.svc.cluster.local:80/*
< transfer-encoding: chunked

(2)のLoadBalancerを使ったアクセスの場合のみ x-envoy-decorator-operation というヘッダが含まれました。Envoyの公式ドキュメントにてこのヘッダについて言及されているのですが、おそらく内部ルーティングにおける名前解決のために本yamlをapplyしているものと思います。

3つ目は whitelist-egress-googleapis.yaml
外部への通信を許可する設定です。

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: whitelist-egress-googleapis
spec:
hosts:
- "accounts.google.com" # Used to get token
- "*.googleapis.com"
ports:
- number: 80
protocol: HTTP
name: http
- number: 443
protocol: HTTPS
name: https
---
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: whitelist-egress-google-metadata
spec:
hosts:
- metadata.google.internal
addresses:
- 169.254.169.254 # GCE metadata server
ports:
- number: 80
name: http
protocol: HTTP
- number: 443
name: https
protocol: HTTPS

※外部への通信に対する挙動はIstioのバージョンによって異なるので利用バージョンの公式ドキュメントを確認してください。

all outbound traffic from an Istio-enabled pod is redirected to its sidecar proxy by default

執筆時点(2020.02.05)での最新バージョンは 1.4.3 で、デフォルトの挙動は「クラスタ外部への通信を許可する」となります。

ちなみに今回検証で利用したGKEクラスタにインストールしたIstioのバージョンは 1.1.7 で、正確には Istio 1.1.17-gke.2 です。

以下のページにて、GKEクラスタのバージョンごとのIstioバージョンのマッピングが確認できます。
https://cloud.google.com/istio/docs/istio-on-gke/versions

さいごに

今回は触りの部分でしたがGKEにおけるIstio活用について皆さんにお伝えできていれば幸いです。Traffic RoutingなどIstioの真髄とも言える部分への踏み込みはまた別に機会でやれたらと思います。

以上、GCP連載企画2日目でした。明日は澁川さんのGoでサーバーレスな管理画面アプリを作るです。お楽しみに!!