Istioは何ができるか

# マイクロサービスの課題

マイクロサービスの課題の中で以下のものがあります

- 障害の連鎖
    - 1つのサービスが障害でレスポンスが悪くなった場合、他のサービスのレスポンスも悪くなり、さらにはシステム全体に影響が出てしまう場合がある
- 障害が起きているもしくはレスポンスタイムが悪いサービスの特定
    - 1つのレスポンスの中で複数のマイクロサービスを呼び出すと、どのマイクロサービスが原因でエラーになっているのか、またはレスポンスタイムが悪くなっているのか、特定が容易ではなくなる
- カナリーリリース
    - 複数のマイクロサービスが絡み合っている場合、カナリーリリースをしようとすると複雑になりがち。
    - サービスAとBとCがあって、リクエストの10%だけAとCの新バージョンにリクエストを流す場合とか
- マイクロサービス間の通信の安全性
    - とあるマイクロサービスからのアクセスは本当に私達がデプロイした正規のマイクロサービスからのリクエストか？

これらの課題をサービスメッシュを使うことで解決できます。

# サービスメッシュとは
サービスメッシュはサービス間通信を処理するソフトウェアレイヤーで、アプリケーションに代わってネットワーク要求を処理するヘルパーサービスです。

以下がサービスメッシュが提供する代表的な機能

- トラフィック制御
- 通信の回復性、耐障害性
- 通信の可視化、分散トレーシング
- 通信の認証、セキュリティ機能

# Istioとは？
- Google, IBM, Lyft社 共同開発によりOSS化されたサービスメッシュ
- 環境非依存で複数のプラットフォームで利用可能
  - Kubernetes, Nomad & Consul, etc
- Kubernetes Pod内にプロキシ(Envoy)をSidecarとしてデプロイするため **コードを変更することなくサービスメッシュ機能の追加が可能**
    - EnvoyはCNCFホストのプロジェクトで、ハイパフォーマンスなL4/L7プロキシーサーバ(ロードバランサー)
        - Envoyには以下のような機能がある
            - Dynamic service discovery
            - Load balancing
            - TLS termination
            - HTTP/2 and gRPC proxies
            - Circuit breakers
            - Health checks
            - Staged rollouts with %-based traffic split
            - Fault injection
            - Rich metrics

# KubernetesにIstioを入れた場合の構成図


<img width="646" alt="スクリーンショット 2019-08-10 17.03.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14124/c8b259b2-5c8a-256c-0a1f-5c3ab71a5b66.png">

リクエストはすべてプロキシサーバーであるEnvoyが担当します。これによりアプリケーションを修正することなく、ネットワークの制御が可能になっています。
またEnvoyはPodのサイドカーとしてデプロイされるためアプリケーションのコードを修正する必要がありません。
コントロールプレーンは、トラフィックルートのプロキシの管理や設定を行います。またポリシーの実施やテレメトリー(メトリックスやログ)の収集を行います。

# Istioの機能

## Load Balancing

サポートされているロードバランシングアルゴリズム
https://www.envoyproxy.io/docs/envoy/v1.5.0/intro/arch_overview/load_balancing#supported-load-balancers

- Random
- Round robin
- Original destination
  - リクエスト情報をもとに、常に同じサーバーへリクエストを送る (sticky sessionのようなもの)
- Weighted least request
  - 接続数が少ないサーバーへリクエストを送る
- Ring hash
  - Consistent Hashingの一種

## Traffic Management

### Request Routing
- URLやRequest Headerの中身をみてルーティングを設定することができる

例:

リクエストヘッダーに`end-user: jason`が含まれていれば、reviewsサービスのv2にリクエストがいき、
その他はreviewsサービスのv1にリクエストがいく。

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v1
```

### Traffic Shifting
- リクエストを流す割合を指定できる。例えば既存バージョンと新しいバージョンのアプリケーションに対して、新しいバージョンは1%しかリクエストを流さないということができる(カナリーリリース)

例:

90%のリクエストはreviewsサービスのv1にいき、残りの10%はv2にいく。

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 90
    - destination:
        host: reviews
        subset: v2
      weight: 10
```


### Request Timeouts
- リクエストタイムアウト値を設定できる
    - これにより、レスポンスタイムが異常に悪いマイクロサービスが存在することによって他のマイクロサービスにまで影響がでることを防げる

例: 

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
    timeout: 9s
```

reviews v1へのリクエストは9秒でタイムアウトする


### Retry
- リトライ回数を設定することができる。これにより、とあるマイクロサービスへのリクエストがたまたまリクエスト処理に時間がかかっている場合に、リクエストをリトライさせることができる
- n秒間以上リクエストに処理がかかった場合、n回リトライさせるといった設定ができる

例:

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
    timeout: 9s          # 1
    retries:
      attempts: 3        # 2
      perTryTimeout: 3s  # 3
```

- 1: 1つのリクエストのタイムアウトは9秒
- 2: 3回リトライする
- 3: 1回のリトライのタイムアウト値は3秒

つまり、1リクエストの中で最大3回リトライする


### Circuit Breaking
- 障害が発生したマイクロサービスによって、システム全体に影響が発生することがあります。これを防ぐための仕組みとしてサーキット・ブレーカーというものがあります。サーキット・ブレーカーは、とあるマイクロサービスに継続的に障害が発生している場合にはそのマイクロサービスからのリクエストタイムアウトを待たずにエラーを返します。
  - n秒間の間に500系エラーがn回返った場合、n秒間そのマイクロサービスからのリクエストタイムアウトを待たずにエラーを返す といった設定ができる

例:

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  trafficPolicy:
    ...
    outlierDetection:
      consecutiveErrors: 7  # 継続した障害だと判定されるエラー(500系)数
      interval: 10s         # 解析を行う間隔
      baseEjectionTime: 30s # consecutiveErrorsの上限を越したときに、ホストが即時エラーを返す最小期間
```

とあるPodから10秒間の間に7回以上500エラーが返った返った場合、30秒間そのPodへリクエストは送られないようにする


### Fault Injection
- アプリケーションの信頼性をテストするために、ネットワーク層の障害を注入することができる(ネットワーク層のカオスエンジニアリング)
- 例えばn%のリクエストをn秒間遅らせたり、n%のリクエストを特定のhttp status(例えば500エラー)を返すようにできたりする。
- これによりアプリケーションの変更なしに簡単マイクロサービスの耐障害性をテストすることができる

delayを注入する例:

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings
  ...
spec:
  hosts:
  - ratings
  http:
  - fault:
      delay:
        fixedDelay: 7s
        percent: 90
    match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: ratings
        subset: v1
  - route:
    - destination:
        host: ratings
        subset: v1
```

リクエストヘッダーに`end-user: jason`が含まれている場合、そのリクエストの90%を7秒遅延させる

エラーを注入する例:

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings
  ...
spec:
  hosts:
  - ratings
  http:
  - fault:
      abort:
        httpStatus: 500
        percent: 90
    match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: ratings
        subset: v1
  - route:
    - destination:
        host: ratings
        subset: v1
```

リクエストヘッダーに`end-user: jason`が含まれている場合、そのリクエストの90%はHttpStatus: 500を返すようにさせる


### Mirroring
  - 別のサービスにリクエストをコピーして送る
  - これにより、本番と同等のステージング環境が作れたり、新しい機能や新しいクラスタが問題なく動くかをユーザに影響なしにテストすることが可能になる。

例:

```yaml
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 100
    mirror:
      host: reviews
      subset: v2
```

reviews v1へのリクエストをv2にコピーして送る


## Security

### Istioのセキュリティゴール
https://istio.io/docs/concepts/security/

- Security by default
  - アプリケーションコードとインフラの変更を必要としない
- Defense in depth
  - 複数の防御レイヤーを提供するために、既存のセキュリティシステムと連携
- Zero-trust network
  - 信頼できないネットワーク上にセキュリティソリューションを構築

### 認証機能(Authentication)

- 相互 TLS 認証
- エンドユーザ認証
  - JWTトークンによるリクエストレベルの認証
  - Auth0、Firebase Auth、Google Auth、カスタム認証
- 鍵管理
  - Istio の鍵管理システムは、鍵と証明書の生成、配布、ローテーション、失効を自動化します。
- 役割ベースのアクセス制御（RBAC）

## Policies

### Rate Limits

ある一定時間のアクセス量を設定できる。
たとえば /timeline へのアクセスは1秒間100リクエストまで受け付け、100を超えたら429エラー(Too Many Requests)にできる。

https://istio.io/docs/tasks/policy-enforcement/rate-limiting/


### Denials
特定の条件を満たすリクエストを遮断する

### White/Black Listing
特定の条件を満たすリクエストのホワイトリスト/ブラックリストを定義できる

https://istio.io/docs/tasks/policy-enforcement/denial-and-list/

## Observability

### Logging

トラフィックに関するログ情報を標準出力したり、FluentdやStackdriverに送ることができる
https://istio.io/docs/reference/config/policy-and-telemetry/templates/

### Metrics
トラフィックの各種統計情報
一般的には送信元/送信先、送信時刻といった情報が含まれる
Istioではこれ以外にも任意の情報をメトリックとして扱うことが可能
プラグイン機構によって以下のようなサービスにメトリックスを送信することができる

- Amazon CloudWatch
- Prometheus
- Stackdriver
- Datadog
- Fluentd
- etc

https://istio.io/docs/reference/config/policy-and-telemetry/adapters

#### Grafana

Grafanaで可視化した様子
<img width="1378" alt="スクリーンショット 2019-08-12 13.12.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14124/c1d32f14-13d5-aea8-da49-f4e9e4417174.png">


#### Kiali

Istioはすべてのネットワークを把握しているので、どのコンポーネントにリクエストが送られていて、そのネットワークはどのくらい健康なのかを可視化できる。

以下は[Kiali](https://www.kiali.io)を使ってネットワークグラフを表示したもの。グラフは自動で生成してくれる。

<img width="1383" alt="スクリーンショット 2019-08-12 13.19.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14124/8c6208ff-40fb-c4d4-4b1d-e322c4999503.png">

##### アニメーションON

アニメーションONにするとリクエストの様子が見れてちょっと楽しい

![Aug-12-2019 13-38-09.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14124/b45632f2-2fff-445f-0541-687725075580.gif)



### Tracing

#### 分散トレーシングとは

分散トレーシングとはマイクロサービスのような分散されたサービスで処理されるリクエストを追跡(トレーシング)するためのもの。
マイクロサービスのような分散アーキテクチャでは、1つのリクエストが複数のサービスで処理されることになり、リクエスト全体の処理の流れの把握やボトルネックの特定が難しくなります。
このような問題を、サービスの依存関係やサービス単位のレイテンシーの可視化をすることによって、解決の手助けするためのシステムが分散トレーシングシステムです。
具体的には1リクエストの中でどのサービスが呼ばれ、どのくらい時間がかかったのかを、ユニークなidをリクエストヘッダーに付与することでトレーシングできるようにします。またそれらを可視化もできる。


#### サポートしているトレーシングツール


##### Jaeger 
- Uber社で開発OSS化。CNCFプロジェクトの一つ
- Istioのデフォルトトレースはこちら。Zipkin互換、OpenTracing仕様準拠
- https://istio.io/docs/tasks/telemetry/distributed-tracing/jaeger/istio-tracing-details.png

https://www.jaegertracing.io

どのコンポーネントが遅いか可視化してくれる
<img width="1383" alt="スクリーンショット 2019-08-12 13.14.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14124/45cc8217-e95c-427a-b693-2d88be619216.png">

##### Zipkin
- Twitter社開発でOSS化。GoogleのDapperを参考にして作られた。OpenTracing仕様準拠
- https://istio.io/docs/tasks/telemetry/distributed-tracing/zipkin/istio-tracing-details-zipkin.png

##### LightStep
- Saas。GUIを提供してくれる。 
- OpenTracing仕様準拠
- GoogleのDapperを作ったGoogleのメインエンジニアが作ったツール
- https://lightstep.com/products/tracing/
- https://istio.io/docs/tasks/telemetry/distributed-tracing/lightstep/istio-tracing-details-lightstep.png

https://istio.io/docs/tasks/telemetry/distributed-tracing/

アプリケーション側で上記のような分散トレーシングツールを使う場合、Envoyが生成したトレースヘッダーを独自に伝播させる実装を行う必要がある。



# 参考

- Istioサービスメッシュ入門 https://www.slideshare.net/yokawasa/istio-114360124
- マイクロサービスアーキテクチャ向けにサービスメッシュを提供する「Istio」の概要と環境構築、トラフィックルーティング設定 https://knowledge.sakura.ad.jp/20489/
- Istioが備えるテレメトリ/ポリシー管理機能を使った統計情報取得と接続管理 https://knowledge.sakura.ad.jp/20681/ 
- 分散トレーシングシステムのZipkinを使ってみた話 https://qiita.com/miya10kei/items/2532b80fcd8d19eb2e75