この記事では、マイクロサービスやクラウドコンピューティング、DevOps の文脈で登場する サービスメッシュ について解説します。登場した背景や概念を見た後、サービスメッシュを実現するための OSS である Istio についても解説し、実際にサンプルアプリケーションを動かしてみます。
サービスメッシュとは
マイクロサービスの課題
- サービスが増えて、通信が複雑になってくると、マイクロサービスにおいては以下のようなことが課題となってきます。
Service Discovery(サービスの発見)
- 依存関係にあるサービスの接続情報の管理が煩雑になってくる
- 従来では DNS やロードバランサによって実現していたが、デプロイやリリースの度にそれらの設定変更を行うことは、ソフトウェアの迅速な提供を妨げる
Fault Isolation(障害の分離)
- あるサービスで障害が起こった時に、他のサービスに連鎖して大規模な障害につながることを防ぐ
- 障害があったサービスへのアクセスを遮断する サーキットブレーカー(CircuitBreaker)パターン を実現するためには各サービスの外部呼び出し側に実装せねばならないため、それぞれのサービスに実装するのは大きなコストになる
Observability(分散トレーシング)
- マイクロサービスでは、1 つのリクエストが複数のサービスにまたがるため、全体の処理の流れを把握するのが困難になる
- あるサービスでレスポンスの悪化やエラーが発生した場合、どのサービスで問題が発生したかを確認することが難しい
Security(認証・認可)
- アクセス元のサービスの権限に応じて、提供するコンテンツを制御する仕組みを各サービスで個別実装することは大きなコストとなる
サービスメッシュの概念
- 上記のような問題点を解決すべく登場したのが サービスメッシュ です。
- すべてのサービスに対して サイドカープロキシ を用意し、サービス間の通信をすべてサイドカープロキシ経由とします。
- 下図のように、全てのサービスがメッシュ状の接続されることから「サービスメッシュ」と呼ばれます。
Istio : サービスメッシュを実現するソフトウェア
- Istio 公式サイト
- Istio は、Google, IBM, Lyft の共同開発によって OSS 化されたサービスメッシュフレームワークです。
- 環境非依存で、複数のプラットフォームで利用可能です。
- Kubernetes はもちろん、Nomad&Consul、Cloud Foundry、仮想マシン等
- Istio が提供する機能は以下の3つです。
1. トラフィック管理
2. セキュリティ
- サービス間の通信の認証・認可、暗号化を提供
- 様々なプロトコルやランタイムに渡って一貫したポリシーを適用できる
- K8s のネットワークポリシーとともに使用することで、ネットワーク層やアプリケーション層での pod 間の通信、service 間の通信を安全に行える
3. 可観測性
- トレース機能、監視機能、ログ機能を提供
- カスタムダッシュボードを利用することで、すべてのサービスのパフォーマンスを可視化し、そのパフォーマンスが他のプロセスにどのような影響を与えているかを確認できる。
Istio のアーキテクチャ
- Istio のサービスメッシュは大きく以下の 2 つに分割されます。
Envoy
Istiod
- サービス検出・構成・証明書管理を行います
- 以下の 3 つの構成要素を含みます:
実際に Istio を利用してみる
- 実際に Istio を利用したアプリケーションをデプロイしてみます。
- Istio 公式で提供されている Bookinfo Application をデプロイしてみます。
アプリケーションの構成
- 以下の 4 つのマイクロサービスから構成されています
reviews
は以下の 3 つのバージョンがあります- v1:
ratings
を呼ばない - v2:
ratings
を呼び、黒い星での 5 段階評価を表示する - v3:
ratings
を呼び、赤い星での 5 段階評価を表示する
- v1:
Istio の動作環境を整える
- 今回は、ローカル環境の Minikube (バージョン 0.28.1 以上)を利用します。
- Minikube のインストールについては前回同様、 Minikubeのインストール - Kubernetes を参考に行います。
- 通常のアプリケーションと一緒に Istio を動かす場合、メモリ 8192 MB、CPU 4 つを割り当てることを推奨されているため、 Minikube 起動時に下記のようなオプションを指定して起動します。
$ minikube start --memory=8192 --cpus=4 --vm-driver=hyperkit --kubernetes-version=v1.18.3
- Istio をインストールするのに必要なファイル群をダウンロードします
$ curl -L https://istio.io/downloadIstio | sh -
- ディレクトリ移動し、パスの設定を行います。
$ cd istio-1.6.1 $ export PATH=$PWD/bin:$PATH
- Istio をインストールします。
$ istioctl install --set profile=demo Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details. ✔ Istio core installed ✔ Istiod installed ✔ Egress gateways installed ✔ Ingress gateways installed ✔ Addons installed ✔ Installation complete
- アプリケーションをデプロイする前に、
default
名前空間にistio-injection=enabled
というラベルをつけておきます。 - これにより、
default
名前空間 にデプロイされるサービスに対し、Istio が自動的に各サービスのコンテナへサイドカーをデプロイしてくれるようになります。
$ kubectl label namespace default istio-injection=enabled
namespace/default labeled
- 以上で、 Istio を起動する準備は整いました。
サンプルアプリケーションを動かす
- 次のコマンドで必要なマニフェストを適用し、アプリケーションが起動するのを待ちます。無事適用されると 3 つのマイクロサービスがデプロイされます。
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml service/details created serviceaccount/bookinfo-details created deployment.apps/details-v1 created service/ratings created serviceaccount/bookinfo-ratings created deployment.apps/ratings-v1 created service/reviews created serviceaccount/bookinfo-reviews created deployment.apps/reviews-v1 created deployment.apps/reviews-v2 created deployment.apps/reviews-v3 created service/productpage created serviceaccount/bookinfo-productpage created deployment.apps/productpage-v1 created $ kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE details ClusterIP 10.99.129.73 <none> 9080/TCP 18m kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 55d productpage ClusterIP 10.101.23.113 <none> 9080/TCP 18m ratings ClusterIP 10.107.80.196 <none> 9080/TCP 18m reviews ClusterIP 10.107.79.61 <none> 9080/TCP 18m $ kubectl get pods NAME READY STATUS RESTARTS AGE details-v1-6c9f8bcbcb-qfzjj 2/2 Running 0 19m productpage-v1-7df7cb7f86-7fscw 2/2 Running 0 19m ratings-v1-65cff55fb8-h2dk7 2/2 Running 0 19m reviews-v1-7bccdbbf96-zbspt 2/2 Running 0 19m reviews-v2-7c9685df46-bq774 2/2 Running 0 19m reviews-v3-58fc46b64-f2rtt 2/2 Running 0 19m
- マニフェスト全文は以下のとおりです:
アプリケーションのマニフェストファイルの内容(クリックして展開)
################################################################################################## # Details サービスの内容 ################################################################################################## apiVersion: v1 kind: Service metadata: name: details labels: app: details service: details spec: ports: - port: 9080 name: http selector: app: details --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-details labels: account: details --- apiVersion: apps/v1 kind: Deployment metadata: name: details-v1 labels: app: details version: v1 spec: replicas: 1 selector: matchLabels: app: details version: v1 template: metadata: labels: app: details version: v1 spec: serviceAccountName: bookinfo-details containers: - name: details image: docker.io/istio/examples-bookinfo-details-v1:1.15.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- ################################################################################################## # Ratings サービスの内容 ################################################################################################## apiVersion: v1 kind: Service metadata: name: ratings labels: app: ratings service: ratings spec: ports: - port: 9080 name: http selector: app: ratings --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-ratings labels: account: ratings --- apiVersion: apps/v1 kind: Deployment metadata: name: ratings-v1 labels: app: ratings version: v1 spec: replicas: 1 selector: matchLabels: app: ratings version: v1 template: metadata: labels: app: ratings version: v1 spec: serviceAccountName: bookinfo-ratings containers: - name: ratings image: docker.io/istio/examples-bookinfo-ratings-v1:1.15.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 --- ################################################################################################## # Reviews サービスの内容 ################################################################################################## apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-reviews labels: account: reviews --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v1 labels: app: reviews version: v1 spec: replicas: 1 selector: matchLabels: app: reviews version: v1 template: metadata: labels: app: reviews version: v1 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v1:1.15.1 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v2 labels: app: reviews version: v2 spec: replicas: 1 selector: matchLabels: app: reviews version: v2 template: metadata: labels: app: reviews version: v2 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.1 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} --- apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v3 labels: app: reviews version: v3 spec: replicas: 1 selector: matchLabels: app: reviews version: v3 template: metadata: labels: app: reviews version: v3 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v3:1.15.1 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} --- ################################################################################################## # Productpage サービスの内容 ################################################################################################## apiVersion: v1 kind: Service metadata: name: productpage labels: app: productpage service: productpage spec: ports: - port: 9080 name: http selector: app: productpage --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-productpage labels: account: productpage --- apiVersion: apps/v1 kind: Deployment metadata: name: productpage-v1 labels: app: productpage version: v1 spec: replicas: 1 selector: matchLabels: app: productpage version: v1 template: metadata: labels: app: productpage version: v1 spec: serviceAccountName: bookinfo-productpage containers: - name: productpage image: docker.io/istio/examples-bookinfo-productpage-v1:1.15.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp volumes: - name: tmp emptyDir: {} ---
- 稼働したら、確認のため、下記のコマンドを実行して、ページタイトルが表示されることを確認します
$ kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}') -c ratings -- curl productpage:9080/productpage | grep -o "<title>.*</title>" <title>Simple Bookstore App</title>
- ここまでの状態では、外部からアクセスができないので、
Gateway
と呼ばれるリソースを作成します。
$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created
Gateway
は、サービスメッシュのゲートウェイ(一番外側に位置するロードバランサとなる部分)を設定するためのリソースです。以下のようなことを設定できます。Gateway
とともに、VirtualService
リソースも作成しています。適用したマニフェストの中身はこちら:
Gateway
と VirtualService
のマニフェストファイルの内容(クリックして展開)
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: bookinfo-gateway spec: selector: istio: ingressgateway # ingressgateway のラベルを持つ Pod を設定対象のロードバランサーとして指定 servers: - port: # 外部からの通信を受け付けるための設定:ポート番号とプロトコル number: 80 name: http protocol: HTTP hosts: # この Gateway リソースが受け付ける対象のホスト名を指定 - "*" --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "*" gateways: - bookinfo-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080
- 構成に問題がないことを確認します。
$ istioctl analyze ✔ No validation issues found when analyzing namespace: default.
$ minikube tunnel
- アクセスのためのホスト名とポート番号を取得します。Minikube を利用している場合は、下記のコマンドで確認します。
$ kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}' 31345 $ minikube ip 192.168.64.3 # INGRESS_HOST
以上の結果から、アクセス先のURLは
http://192.168.64.3:31345
となります。ブラウザで
http://192.168.64.3:31345/productpage
アクセスし、以下のような画面が表示されることを確認します。
ダッシュボードを見てみる
- Istio のデモインストールに含まれる Kiali のダッシュボードを見てみます。
- このダッシュボードは、トポロジを表示してメッシュの状態を示すことで、サービスメッシュの構造を理解するのに役立ります。
- 以下のコマンドで、Kiali のダッシュボードにアクセスします。
$ istioctl dashboard kiali
- ログイン画面が表示されたら、ユーザー名とパスワードを入力します。
- デフォルトのユーザー名は
admin
、パスワードはadmin
- デフォルトのユーザー名は
- 上記のように、デプロイしたアプリケーションのメッシュが可視化されます。
まとめ
- 分散システムアーキテクチャパターンである「サービスメッシュ」の基本的な概念を確認しました。
- サービスメッシュを実現する方法として、代表的なソフトウェアである Istio を試用してみました。
- 今回は、ローカル環境の Minikube 上で Istio を使ったアプリケーションを稼働させてみましたが、次の機会に、 Amazon EKS 上で稼働させてみたいと思います。
参考資料
- Pattern: Service Mesh
- サービスメッシュの基本的な概念について述べられています。
- サービスメッシュ必読ガイド - マイクロサービス時代のサービス間通信管理 - InfoQ
- サービスメッシュの基本概念について参考にしました。
- サービスメッシュとは - RedHat
- サービスメッシュの基本概念・背景について参考にしました。
- サービスメッシュについて調査してみた件 - Qiita
- サービスメッシュの基本概念について参考にしました。
- Istioサービスメッシュ入門
- Istio の基本について参考にしました。
- サービスメッシュを実現するIstioをEKS上で動かす - その1 まずはMinikubeでサンプルアプリケーションを動かしてみる
- Istio を利用したアプリケーションのデプロイについて参考にしました。