Istioのコンポーネントをひとつずつインストールしながら解説する

この投稿は[Fujitsu Advent Calendar 2019](https://qiita.com/advent-calendar/2019/fujitsu)の10日目の記事です。
なお、記事は全て個人の見解です。会社・組織を代表するものではありません。

## 概要

現在サービスメッシュの認知の広まりとともに、その代表的なOSSであるIstioが注目されています。
Istioを使ってみたいという声を聴く一方で、Istioはコンポーネントが多すぎてわからないといった声も聴きます。
確かにIstio全体としてはとても巨大です。
しかし、各コンポーネントは疎結合なため、必要なもののみインストールすることも可能なのです。
この記事ではひとつずつインストールしながら、コンポーネントについて解説していきます。

## 環境

- MacBook Air
- CPU: 3, Memory: 8GiB
- Docker Version: 19.03.5
- Kubernetes Version: v1.15.5
- Istio Version: 1.4.1
- Helm Version: v2.12.0

※KubernetesはDocker for Kubernetesを使用しています。

## Istioの全体像

まずはIstioの全体像について解説します。

![istio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152318/cd51c48c-b8c9-3916-7efe-6a94bf73d452.png)

Istioにはざっと上記のようなコンポーネントが存在します。
大まかな動作としてはサービス通信の間にEnvoyが存在し、それがプロキシとしてすべての通信(Inbound, Outbound両方)の仲介をします。
そのEnvoyがサービス間通信の際に考えなければならないめんどくさいこと(timeoutや流動制御など)を肩代わりしてくれます。
その他のコンポーネントはこのあと解説しますが、主にEnvoyの制御や情報収集などEnvoyをControlするコンポーネントたちです。
それでは、実際にインストールしながら解説していきます。

参考: https://istio.io/docs/ops/deployment/architecture/

## Istioインストールの前準備

今回はhelmコマンドを使ってコンポーネントをインストールしていこうと思います。
helmのインストールとistio用のtemplateの準備方法は以下の通りです。

```bash
$ brew install helm
$ helm version

$ curl -L https://istio.io/downloadIstio | sh -
$ sudo cp istio-1.4.1/bin/istioctl /usr/local/bin
$ istioctl version
```

その後、コンポーネントをインストールするための前段階のNamespsceやCRD(Custom Resource Definition)をインストールします

```bash
$ kubectl create namespace istio-system
$ helm template istio-1.4.1/install/kubernetes/helm/istio-init --name istio-init --namespace istio-system | k apply -f -
```

#### 参考:
- helmでのセットアップ: https://istio.io/docs/setup/install/helm/

## Core Components

### Pilot

PilotはユーザーがKubernetesのCRD(Custom Resource Definition)として設定した値をEnvoyに伝えるためのコンポーネントです。
このコンポーネントによってDestinationRuleやVirtualServiceなどのリソースを解釈してEnvoyの設定として反映してやることができます。

#### Pilotのインストールによってできるようになる操作

Load Balancingや特定バージョンのみリクエストを送るなどTraffic Managementが行えるようになります。

- Traffic Management一覧
  - 特定のバージョンのみへのルーティング
  - Fault Injection(強制的にInternal Server Errorを返すなど)
  - リクエストにtimeoutを設定
  - Circuit Breaking

#### デプロイコマンド

今回はhelmのvalueファイルを用意し、そこからkubernetesのyamlファイルをtemplateコマンドで作成していきます。

```bash
$ vi values-pilot.yaml
  # ※下記参照
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-pilot.yaml > pilot.yaml
$ kubectl apply -f pilot.yaml
```

```values.yaml
pilot:
  enabled: true
  sidecar: false

gateways:
  enabled: false

security:
  enabled: false

sidecarInjectorWebhook:
  enabled: false

galley:
  enabled: false

mixer:
  policy:
    enabled: false
  telemetry:
    enabled: false

prometheus:
  enabled: false

# Common settings.
global:
  proxy:
    envoyStatsd:
      enabled: false
  useMCP: false
```


#### テスト

テストアプリは[BookInfo](https://istio.io/docs/examples/bookinfo/)を使用します。
今回は全てのリクエストをv1のみに送るようにしたいと思います。

```bash
$ vi istio-1.4.1/samples/bookinfo/platform/kube/bookinfo.yaml
  # とりあえずアクセスするためにProductpageのServiceをNodePortにします(下記参照)
$ istioctl kube-inject -f istio-1.4.1/samples/bookinfo/platform/kube/bookinfo.yaml | kubectl apply -f -
$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-5c76c87c6-5w2pr        2/2     Running   0          4m53s
productpage-v1-7bc5cb86bd-9srfr   2/2     Running   0          4m50s
ratings-v1-5597f5d56d-zr225       2/2     Running   0          4m51s
reviews-v1-67b6db665d-48968       2/2     Running   0          4m52s
reviews-v2-5f78d5fdf9-zvcff       2/2     Running   0          4m52s
reviews-v3-69f68ff8bd-fxsfc       2/2     Running   0          4m52s

# 動作しているかブラウザから確認(http://localhost:30000/productpage)
# 何度か更新するとレビューの星が黒や赤や無くなったりするはずです

$ kubectl apply -f istio-1.4.1/samples/bookinfo/networking/destination-rule-all.yaml
$ kubectl apply -f istio-1.4.1/samples/bookinfo/networking/virtual-service-all-v1.yaml

# ブラウザからアクセスするとreviewの星がない状態しか表示されなくなる
```

```bookinfo.yaml
# ・・・(省略)
# 240行目付近
apiVersion: v1
kind: Service
metadata:
  name: productpage
  labels:
    app: productpage
    service: productpage
spec:
  type: NodePort
  ports:
  - port: 9080
    name: http
    nodePort: 30000
  selector:
    app: productpage
---
# ・・・(省略)
```

PodがEnvoy付きで起動したのでREADYが2/2となっているのが確認できると思います。
また、特定バージョンのみへのルーティングしかできないようになっているのが確認できると思います。

#### 参考:
- Traffic Management: https://istio.io/docs/tasks/traffic-management/
- BookInfoアプリ: https://istio.io/docs/examples/bookinfo/


### Policy

PolicyはMixerの一部で、流動制御やブラックリスト(ホワイトリスト)でのアクセス制御などEnvoyに対してアクセスポリシーを設定するためのコンポーネントです。

#### デプロイコマンド

先ほどのvalues-pilot.yamlからpolicyの項目をenableにします。
また、disablePolicyChecksをfalseにします。

```values-policy.yaml
# ・・・(省略)
mixer:
  policy:
    enabled: true
  telemetry:
    enabled: false
# ・・・(省略)
global:
  disablePolicyChecks: false
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-policy.yaml > policy.yaml
$ kubectl apply -f policy.yaml
```

#### テスト

今回はブラックリストによってhelloworldサービスのv3(reviewsの星が赤色)にアクセスできないようにしてみます。

```bash
# all-v1のルールは削除しておきます
$ kubectl delete -f istio-1.4.1/samples/bookinfo/networking/virtual-service-all-v1.yaml
$ kubectl apply -f istio-1.4.1/samples/bookinfo/policy/mixer-rule-deny-label.yaml

# ブラウザを更新し続けると以下のようにアクセスできなくなっているのが確認できると思います
# Ratings service is currently unavailable
```

### Telemetry

Telemetryも同じくMixerの一部で、Envoyからメトリクスやログ情報を収集するコンポーネントです。
収拾したデータはバックエンドにつながっているPrometheusやDatadogなどにMixer Adapterを通して送信されます。
Adapterとはistioの持つデータ形式を各バックエンドのデータ形式に変換するもので、例えばPrometheus Adapterの場合、Envoyから収集したメトリクスをPrometheusの形式に変更して送信してくれます。
このAdapterは自作することも可能なので、任意のバックエンドに送信することができます。

#### デプロイコマンド

今回も同様にvaluesファイルを作成していきます。
※TelemetryはデフォルトのResource Requestが多いので減らしています。

```values-telemetry.yaml
# ・・・(省略)
mixer:
  policy:
    enabled: true
  telemetry:
    enabled: true
    resources:
      requests:
        cpu: 200m
        memory: 500Mi
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-telemetry.yaml > telemetry.yaml
$ kubectl apply -f telemetry.yaml
```

#### テスト

最もわかりやすい使い方はPrometheusに情報を送ることだと思うので、ついでにPrometheusもインストールしてみます。
values-telemetry.yamlを次のように修正してPrometheusをONにします。

```values-temeletry.yaml
# ・・・(省略)
prometheus:
  enabled: true
  service:
    nodePort:
      enabled: true
  security:
    enabled: false
# ・・・(省略)
```

[http://localhost:32090](http://localhost:32090)にアクセスするとPrometheusのUI画面が見えて、メトリクス情報が取得できるのが確認できると思います。

### Ingress/Egress Gateway

まず、Ingress Gatewayは外部からCluster内への通信を制御するためのコンポーネントです。
KubernetesのIngress Controllerと似たようなもの(どちらもL7の通信を制御)ですが、Istio Ingress Gatewayはより細かい設定ができます。
Egress Gatewayはその逆でCluster内から外部への通信を制御するためのコンポーネントです。
今回はIngress Gatewayについてのみ紹介したいと思います。

#### デプロイコマンド

先ほどのvalues-telemetry.yamlからgatewaysの項目を設定していきます。

```values-gateways.yaml
# ・・・(省略)
gateways:
  istio-ingressgateway:
    autoscaleEnabled: false
    resources:
      requests:
        cpu: 10m
        memory: 40Mi

  istio-egressgateway:
    enabled: false
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-gateways.yaml > gateways.yaml
$ kubectl apply -f gateways.yaml
```

#### テスト

まず、先ほど設定したbookinfoのNodePortの設定はもう用がないので外します。
その後、Istio IngressGateway用のリソースをデプロイします。

```bash
$ vi istio-1.4.1/samples/bookinfo/platform/kube/bookinfo.yaml
# ※下記参照
$ istioctl kube-inject -f istio-1.4.1/samples/bookinfo/platform/kube/bookinfo.yaml | kubectl apply -f -
$ kubectl apply -f istio-1.4.1/samples/bookinfo/networking/bookinfo-gateway.yaml
```

```bookinfo.yaml
# ・・・(省略)
# 240行目付近
apiVersion: v1
kind: Service
metadata:
  name: productpage
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage
---
# ・・・(省略)
```

最後にブラウザから[http://localhost/productpage](http://localhost/productpage)(istio-ingressgatewayのServiceのアドレス)にアクセスします。


### Citadel

CitadelはIstioのセキュリティを担う部分です。
Istioにはセキュリティ機能として以下の2つがあります。

- Mutual TLS(相互認証)
- Istio RBAC(Role Based Access Control)

#### 相互認証

通常のhttpsのサービスなどの認証はクライアントが`サーバがだれなのか`というのを認証します。
相互認証ではクライアントがサーバを認証すると同時にサーバが`クライアントがだれなのか`ということを認証します。
これによりサービス間で正しいサービス同士が安全に通信できるようになります。
認証には通常SSL証明書が必要になります。
証明書は発行や管理など扱いは大変なのですが、IstioではCitadelがこの証明書をすべて管理してくれます。

#### Istio RBAC

Istio RBACはIstioのアクセスコントロール機能の一つです。
KubernetesのRBACと同じ様な使い方でService間の通信のアクセス制御できます。(k8sはリソースのアクセス制御)
例としてserviceAのGET /pathにはuserAだけアクセスを許可するというような設定が可能になります。

#### Citadelがやってくれること

- サービス間(Envoy間)通信の暗号化
- 通信先の認証(※k8s Cluster外サービスを除く)
- Roleによる通信の認可

※より詳しくk8s, istioのセキュリティについて知りたい方はぜひ私のGitHub(https://github.com/sh-miyoshi/sectest )を参照してください

#### デプロイ手順

先ほどのvalues-gateways.yamlからcitadel周りの項目をONにしていきます

```values-citadel.yaml
# ・・・(省略)
security:
  enabled: true
# ・・・(省略)
global:
  disablePolicyChecks: false
  proxy:
    envoyStatsd:
      enabled: false
  useMCP: false
  mlts:
    enabled: true
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-citadel.yaml > citadel.yaml
$ kubectl apply -f citadel.yaml
```

#### テスト

今回は見た目的には変わらないので省略します。
なお、security OFF時に作成したbookinfoアプリを削除してから再度デプロイし直してください。
テスト方法などは私のGitHub(https://github.com/sh-miyoshi/sectest )に載せています。


### Sidecar Injector

Sidecar InjectorはPodの起動を検知し、自動でEnvoy(istio-proxy)を挿入してくれるコンポーネントです。
今までは`istioctl kube-inject`コマンドでEnvoy(istio-proxy)を挿入したyamlファイルを作成し、それを`kubectl apply`コマンドでデプロイしていました。
このコンポーネントがあると自動で挿入してくれるのでとても便利です。

#### デプロイコマンド

先ほどのvalues-citadel.yamlからsidecarInjectorWebhookの項目をenableにします。
※私の環境だとCitadelを有効にした状態でないと起動できませんでした。

```values-injector.yaml
# ・・・(省略)
sidecarInjectorWebhook:
  enabled: true
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-injector.yaml > injector.yaml
$ kubectl apply -f injector.yaml
```

#### 使い方

auto injectするnamespaceにラベルをつけて、kubectlコマンドでデプロイするだけです。

```bash
# 一旦削除
$ ./istio-1.4.1/samples/bookinfo/platform/kube/cleanup.sh
$ kubectl label namespace default istio-injection=enabled
$ kubectl apply -f istio-1.4.1/samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-74f858558f-krpqv       2/2     Running   0          3m36s
productpage-v1-8554d58bff-2l57q   2/2     Running   0          3m33s
ratings-v1-7855f5bcb9-xzcvc       2/2     Running   0          3m36s
reviews-v1-59fd8b965b-7nm8r       2/2     Running   0          3m34s
reviews-v2-d6cfdb7d6-kdmg2        2/2     Running   0          3m35s
reviews-v3-75699b5cfb-86tqh       2/2     Running   0          3m35s
```


### Galley

ユーザが定義したIstio設定のvalidationを実施してくれるコンポーネントらしいです。
> すみません、私もあまりGalleyについて詳しくわかってなくて具体的に何がどうなるのかが説明できないです・・・

## Addons

最後にistioのCoreの部分ではありませんが、役に立ちそうなコンポーネントを紹介します。

### Tracing

簡単に言うとJaegerやzipkinといった分散トレーシングツールを導入してくれます。
複数サービスの通信においてどこにどれだけ時間がかかっているのかを知ることができます。

#### デプロイコマンド

今までと同様にvaluesファイルを修正し、helmコマンドでデプロイします。

```values-tracing.yaml
# ・・・(省略)
tracing:
  enabled: true
  service:
    type: LoadBalancer
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-tracing.yaml > tracing.yaml
$ kubectl apply -f tracing.yaml
```

#### 確認方法

http://localhost:(node-port) にアクセスするとJaegerのダッシュボードが見えます。
※node-portは`kubectl get svc -n istio-system tracing -o json | jq .spec.ports[0].nodePort`などのコマンドで調べられます。
適当にリクエストしてみてデータを見てみてください。

### Kiali

Kialiはマイクロサービスで何が起こっているかを可視化するGUIコンポーネントです。
どのサービスが通信していて今どういう状態なのかということをWeb UI画面から視覚的に確認することができます。

#### デプロイコマンド

今までと同様にvaluesファイルを修正し、helmコマンドでデプロイします。

```values-kiali.yaml
# ・・・(省略)
kiali:
  enabled: true
  createDemoSecret: true
# ・・・(省略)
```

```bash
$ helm template istio-1.4.1/install/kubernetes/helm/istio --name istio --namespace istio-system --values values-kiali.yaml > kiali.yaml
$ kubectl apply -f kiali.yaml
```

#### 確認方法

kialiはNodePortやLoadBalancerでアクセスする方法がないので、`kubectl port-forward`コマンドでアクセスできるようにします。

```bash
$ kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=kiali -o jsonpath='{.items[0].metadata.name}') 20001:20001
```

これで[http://localhost:20001](http://localhost:20001)にアクセスすると様々なグラフが見えるようになります。
なお、ログイン情報はadmin/adminです。(※createDemoSecret: trueの場合)

## まとめ

Istioは全体としては巨大ですが、各コンポーネントが独立しており、必要な分だけインストールすることができます。
今回は1コンポーネントずつ確認しながらインストールする手順を紹介しました。
この記事が少しでもみなさまのIstio理解の手助けになれば幸いです。

## おまけ

最終的なvalues.yamlファイル

```values.yaml
pilot:
  enabled: true
  sidecar: false

gateways:
  istio-ingressgateway:
    autoscaleEnabled: false
    resources:
      requests:
        cpu: 10m
        memory: 40Mi

  istio-egressgateway:
    enabled: false

security:
  enabled: true

sidecarInjectorWebhook:
  enabled: true

galley:
  enabled: false

mixer:
  policy:
    enabled: true
  telemetry:
    enabled: true
    resources:
      requests:
        cpu: 200m
        memory: 500Mi

prometheus:
  enabled: true
  service:
    nodePort:
      enabled: true
  security:
    enabled: true

# Common settings.
global:
  disablePolicyChecks: false
  proxy:
    envoyStatsd:
      enabled: false
  useMCP: true
  mlts:
    enabled: true

tracing:
  enabled: true
  service:
    type: NodePort

kiali:
  enabled: true
  createDemoSecret: true
```