IstioをGKEに導入した際にやったこと

* 2020年1月時点での事例のため情報が古い箇所がありますが随時更新していきます。

## マイクロサービスアーキテクチャとは

モノリシックシックアーキテクチャ（一枚岩、巨大なコードベース）のように一つのコードが複数の環境に影響を及ばさないように適切なサイズにアプリケーションを分割すること。

### メリット

- 適切なサイズのコードベースで開発できる
- チームを適切なサイズに保つことができる
- 特定のコードベースに所属しない（フレームワーク）

### デメリット

- パフォーマンスの低下

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

#### 依存関係の解決

適切にロードバランシングする必要がある

#### 通信の可観測性

あるサービスで起きた障害がどの程度の影響を及ぼすのか考える必要がある
サービス間通信が正常に行われているか、サービス間の通信がどの程度行われたかを観測する必要がある

#### 認証・認可

複数を協調してサービスを実装する際に特定のサービスが接続するサービスを制限したり特定の API を呼び出す権限を制限したりすることが必要になるケースがあるので各マイクロサービス間も外部サービスの連携と同じく、暗号鍵や権限管理などを正しく行う必要がある

#### 障害の分離

マイクロサービス間の通信はプロセスによる通信よりも失敗する可能性が高くなるためにリトライ回数やタイムアウトの設定、リトライ上限を正しく設定する必要がある

## サービスメッシュとは

マイクロサービスアーキテクチャによって生じる課題に対応するためのデザインパターン
各マイクロサービスのサイドカーとしてサービスプロキシをデプロイする
サイドカーとしてデプロイする理由は各開発言語に左右されないこととアプリケーション側でリトライ処理やサーキットブレーカーのコードを記述する必要やメンテナンスコストがないため
サービス間の全ての通信はデータプレーンであるサービスプロキシを経由しておこなう

## できること

- リトライ、 タイムアウトなどの通信の障害への対策
- 分散トレーシングによるネットワークレベルの情報収集、サービス監視、ログ収集

## できないこと

- ネットワーク以外の対応
- サービスのロールバック

## データプレーン

実際に通信の制御を行うコンポーネント
サービスメッシュでは Envoy などのサービスプロキシに相当

## コントロールプレーン

データプレーンで用いる設定を出力しデータプレーンを制御するコンポーネント
データプレーンが定期的、リアルタイムでコントロールプレーンに問い合わせを行い変更があれば設定を変更する
複数のデータプレーンの設定を一度に更新することができる

# Istio

サービスメッシュのデファクトスタンダードなコントロールプレーン

## 主な機能

- ネットワークトラフィックの管理
- セキュリティ
- 監視とロギング
- OpenTracing

## アーキテクチャ

![スクリーンショット 2020-01-29 11.21.07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238769/aef2e0ef-733f-6bd9-a163-fd4624141394.jpeg)


## コントロールプレーン

- Pilot
- Mixer
- Galley
- Citadel

#### Pilot

- トラフィックの送信先のサービスディスカバリ
- トラフィックに関する機能
- Blue/Green, Canary
- タイムアウト、リトライの制御
- istio-proxy に設定情報を提供

#### Mixer

- データプレーンにあるメトリクスやログを収集し Stackdriver, Prometheus に送信
- サービスが別のサービスに対して通信する際のアクセスコントロール(認証、認可、レート制限)
- トラフィック後に Envoy から送られてきたテレメトリを受信

#### Citadel

- 認証局
- セキュリティ
- rbac 設定
- kubernetes のアクセス制御と連携

#### Galley

- istio に対する管理操作を受け付けを行いバリデーション

#### Polocy

#### Sidecar Injector

- Deployment に istio-proxy を追加

### データプレーン

トラフィックの管理 Envoy を拡張したサービスプロキシ istio-proxy

## GKE における istio 関連の Workload

```
$ kubectl get deploy -n istio-system

NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE
grafana                  1         1         1            1
istio-citadel            1         1         1            1
istio-galley             1         1         1            1
istio-pilot              1         1         1            1
istio-policy             1         1         1            1
istio-sidecar-injector   1         1         1            1
istio-telemetry          1         1         1            1
kiali                    1         1         1            1
prometheus               1         1         1            1
```

# Envoy

サービスプロキシ、Istio などに採用される代表的なデータプレーン
data plane api によってコントロールプレーンから設定を読み込む grpc でやりとりする xds api

- 様々なプロトコルのサポート
  - http/2
  - gRPC
- 高度なロードバランシング
  - パッシブヘルスチェック
  - リクエストのリトライ、タイムアウト、レートリミット
  - サーキットブレーカー
- 可観測性
  - リクエストの流量や回数
  - タイムアウトやリトライ、サーキットブレーカが発動した回数
- 動的な設定の変更
  - オンラインで設定の更新

## 静的な Envoy の設定

```
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address:
          - address: 0.0.0.0
          - port_value: 80
      filter_chains:
        - filters:
            - name: envoy.http_connection_manager
              config:
                route_config:
                  name: route
                  virtual_hosts:
                    - name: backend
                      domains:
                        - "*"
                      routes:
                        - match:
                            prefix: "/"
                          route:
                            cluster: service1
  clusters:
    - name: service1
      load_assignment:
        cluster_name: service1
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      - address: sevice1.example.com
                      - port_value: 80
```

## コントロールプレーンを使用した Envoy の設定

### EDS

```
  clusters:
    - name: service1
      type: EDS
      eds_cluster_config:
        eds_config:
          api_config_source:
            api_type: GRPC
            grpc_services:
              envoy_grpc:
                cluster_name: xds_cluster
    - name: xds_cluster
      cluster_name: xds_cluster
      endpoints:
        - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    - address: 127.0.0.1
                    - port_value: 20000
```

### xDS API

Envoy がコントロールプレーンから設定を取得するための API

API スキーマに Protobuf, エンドポイントに gRPC(bidi) or REST

##### envoy の設定を動的に取得するためのサービス

- Endpoint Discovery Service (EDS)
- Cluster Discovery Service (CDS)
- Route Discovery Service (RDS)
- Listener Discovery Service (LDS)
- Health Discovery Service (HDS)
- Aggregated Discovery Service (ADS)
- Secret Discovery Service (SDS)

## GKE に導入(コマンドラインベース)

### 構成例

```
# Gateways Configuration, refer to the charts/gateways/values.yaml
# for detailed configuration
#
gateways:
  enabled: false

# sidecar-injector webhook configuration, refer to the
# charts/sidecarInjectorWebhook/values.yaml for detailed configuration
#
sidecarInjectorWebhook:
  enabled: true

# galley configuration, refer to charts/galley/values.yaml
# for detailed configuration
#
galley:
  enabled: true

# mixer configuration
#
# @see charts/mixer/values.yaml for all values
mixer:
  policy:
    # if policy is enabled the global.disablePolicyChecks has affect.
    enabled: true

  telemetry:
    enabled: true
#
# pilot configuration
#
# @see charts/pilot/values.yaml
pilot:
  enabled: true

# security configuration
#
security:
  enabled: true

# nodeagent configuration
#
nodeagent:
  enabled: false

# addon grafana configuration
#
grafana:
  enabled: true
  security:
    enabled: true

# addon prometheus configuration
#
prometheus:
  enabled: true

# addon jaeger tracing configuration
#
tracing:
  enabled: false

# addon kiali tracing configuration
#
kiali:
  enabled: true

# addon certmanager configuration
#
certmanager:
  enabled: false

```

### 手順

```
$ curl -L https://git.io/getLatestIstio | sh -
$ cd istio-LATEST-VERSION
$ kubectl create namespace istio-system
$ helm template install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | kubectl apply -f -

$ PASSWORD=$(head -c 12 /dev/urandom | shasum| cut -d' ' -f1)
$ kubectl -n istio-system create secret generic grafana --from-literal=username=admin --from-literal=passphrase="$PASSWORD"

パスワードの確認
$ kubectl get secret -n istio-system grafana -o jsonpath="{.data.passphrase}" | base64 --decode ; echo

$ helm template --namespace istio-system install/kubernetes/helm/istio > istio.yaml

$ KIALI_USERNAME=$(read '?Kiali Username: ' uval && echo -n $uval | base64)
$ KIALI_PASSPHRASE=$(read -s "?Kiali Passphrase: " pval && echo -n $pval | base64)

$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: kiali
  namespace: $NAMESPACE
  labels:
    app: kiali
type: Opaque
data:
  username: $KIALI_USERNAME
  passphrase: $KIALI_PASSPHRASE
EOF

$ kubectl patch svc kiali -n istio-system -p '{"spec": {"type": "LoadBalancer"}}'
$ kubectl patch svc grafana -n istio-system -p '{"spec": {"type": "LoadBalancer"}}'
```

## Istio によるトラフィック制御

- VirtualService
- ServiceEntry
- DestinationRule
- Sidecar

![スクリーンショット 2020-01-21 14.49.43.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238769/5303fd8b-e2dd-e5c9-cb14-f1a10f699458.jpeg)


### VirtualService

リトライやタイムアウトの設定

### ServiceEntry

クラスタ外部にある API に対してリクエストする際の設定

### DestinationRule

トラフィックが Pod に届くまでの振る舞いを設定
サーキットブレーカー, ロードバランシング, コネクションプーリング

### Sidecar

特定の Envoy に対する設定

## Sidecar Injection の有効化

Pod 起動時に自動的に Sidecar を注入

```
有効化
$ kubectl label namespace {YOUR_NAMESPACE} istio-injection=enabled

無効化
$ kubectl label namespace {YOUR_NAMESPACE} istio-injection-

設定の確認
$ kubectl get namespaces -L istio-injection
```

## マニフェスト例

### Retry, Timeout

```
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: virtual-svc
spec:
 hosts:
   - service-a
 http:
   - route:
       - destination:
           host: service-a
     retries:
       attempts: 5
       perTryTimeout: 2s
       retryOn: 5xx
     timeout: 120s
```

### Fault Injection

```
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: virtual-svc
spec:
 hosts:
   - service-a
 http:
   - route:
       - destination:
           host: service-a
     fault:
       delay:
         percent: 50
         fixedDelay: 5s     
```

### Circuit Breaker

```
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: circuit-breaker
spec:
  host: service-c
  subsets:
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http2MaxRequests: 1024
        maxRequestsPerConnection: 10
    outlierDetection:
      consecutiveErrors: 7
      interval: 5m
      baseEjectionTime: 3m
      maxEjectionPercent: 100     
```

Destination Rule | 役割
--- | ---
spec.trafficPolicy.connectionPool.tcp | TCPのコネクションプール
maxConnections | HTTP/TCPの最大コネクション数
connectTimeout | TCPのタイムアウト
tcpKeepalive | ソケットにkeepaliveを適用するか
spec.trafficPolicy.connectionPool.http | HTTPのコネクションプール
http1MaxRequests | 保留するリクエスト最大数
http2MaxRequests | バックエンドへの要求最大数
maxRequestsPerConnection | バックエンドに対するコネクションごとのリクエストの最大数
maxRetries | リトライの最大数
spec.trafficPolicy.outlinerDetection | 異常値の閾値とその時の挙動を定義
consecutiveErrors | 継続した障害だと判定される閾値
Interval | 解析を行う間隔
baseEjectionTime | consecutiveErrorsの上限を越したときに、ホストが即時エラーを返す最小期間

https://istio.io/docs/reference/config/networking/destination-rule/