Istio入門 その2 - Istio構築とサンプルアプリのデプロイ -

## はじめに

本記事はIstio入門シリーズの第二弾です！

前回の [Istio入門 その1 -Istioとは?-][0] では、Istioのざっくりとしたアーキテクチャを解説しました。ぼんやりと全体像が見えてきたかと思いますので、今回は公式サンプルアプリケーションを試しつつ実際の動きを見ていきたいと思います。

> **Istio入門シリーズ**
> * [その1 -Istioとは?-][istio1]
> * [その2 -Istio構築とサンプルアプリのデプロイ-][istio2]
> * [その3 -Blue/Greenデプロイメントによるカナリアリリース-][istio3]
> * [その4 -基礎から振り返る-][istio4]
[istio4]: https://qiita.com/Ladicle/items/4ba57078128d6affadd5
[istio1]: https://qiita.com/Ladicle/items/979d59ef0303425752c8
[istio2]: https://qiita.com/Ladicle/items/e949b0f68ac18b7a95d8
[istio3]: https://qiita.com/Ladicle/items/5a68ff85e15e86781453

## Hello Istio!

bookinfoを試す前に、まずはIstioの構築です。フル機能のIstioを使えるプラットフォームは現時点でKubernetesのみ[^1]なので、[Minikube](https://github.com/kubernetes/minikube)[^2]を使って構築したいと思います。

### 環境情報

* **OS**: macOS Sierra
* **Hypervisor**: VirtualBox

### KubernetesにIstioをデプロイする

1. はじめにMinikubeをダウンロードしてパスを通します

    ```bash
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.23.0/minikube-darwin-amd64
$ chmod +x minikube
$ sudo mv minikube /usr/local/bin/
```

2. 続いてメモリを4G確保してMinikubeを起動します

    ```bash
$ minikube start --memory 4096 --extra-config=apiserver.Admission.PluginNames="Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,GenericAdmissionWebhook,ResourceQuota"
```
> **注意**
> Istio本体の起動には2Gで十分なのですが、BookInfoの動作には4G以上のメモリが必要なことに注意してください。

3. ここから、本題のIstioをダウンロードしてistioctlのパスを通します

    ```bash
$ curl -L https://git.io/getLatestIstio | sh - 
$ cd istio-0.2.7
$ sudo mv bin/istioctl /usr/local/bin
```

4. KubernetesにダウンロードしてきたIstioのManifestを適用します

    ```bash
$ kubectl apply -f install/kubernetes/istio-auth.yaml
$ kubectl apply -f install/kubernetes/istio-initializer.yaml
```

5. 最後にkubectlを使って、作成されたIstioのリソースを確認します

    ```bash
$ kubectl get --all-namespaces all
NAMESPACE      NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
istio-system   deploy/istio-ca            1         1         1            1           15m
istio-system   deploy/istio-egress        1         1         1            1           15m
istio-system   deploy/istio-ingress       1         1         1            1           15m
istio-system   deploy/istio-initializer   1         1         1            1           12m
istio-system   deploy/istio-mixer         1         1         1            1           15m
istio-system   deploy/istio-pilot         1         1         1            1           15m
kube-system    deploy/kube-dns            1         1         1            1           34m
NAMESPACE      NAME                             DESIRED   CURRENT   READY     AGE
istio-system   rs/istio-ca-57f9bd7ddb           1         1         1         15m
istio-system   rs/istio-egress-6c6b84cd5d       1         1         1         15m
istio-system   rs/istio-ingress-79cf84458b      1         1         1         15m
istio-system   rs/istio-initializer-865bc594c   1         1         1         12m
istio-system   rs/istio-mixer-6fdc6784c7        1         1         1         15m
istio-system   rs/istio-pilot-5f4865659b        1         1         1         15m
kube-system    rs/kube-dns-6fc954457d           1         1         1         34m
NAMESPACE      NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
istio-system   deploy/istio-ca            1         1         1            1           15m
istio-system   deploy/istio-egress        1         1         1            1           15m
istio-system   deploy/istio-ingress       1         1         1            1           15m
istio-system   deploy/istio-initializer   1         1         1            1           12m
istio-system   deploy/istio-mixer         1         1         1            1           15m
istio-system   deploy/istio-pilot         1         1         1            1           15m
kube-system    deploy/kube-dns            1         1         1            1           34m
NAMESPACE      NAME                             DESIRED   CURRENT   READY     AGE
istio-system   rs/istio-ca-57f9bd7ddb           1         1         1         15m
istio-system   rs/istio-egress-6c6b84cd5d       1         1         1         15m
istio-system   rs/istio-ingress-79cf84458b      1         1         1         15m
istio-system   rs/istio-initializer-865bc594c   1         1         1         12m
istio-system   rs/istio-mixer-6fdc6784c7        1         1         1         15m
istio-system   rs/istio-pilot-5f4865659b        1         1         1         15m
kube-system    rs/kube-dns-6fc954457d           1         1         1         34m
NAMESPACE      NAME                                   READY     STATUS    RESTARTS   AGE
istio-system   po/istio-ca-57f9bd7ddb-4j7h5           1/1       Running   0          15m
istio-system   po/istio-egress-6c6b84cd5d-dzgxx       1/1       Running   0          15m
istio-system   po/istio-ingress-79cf84458b-s4wx6      1/1       Running   0          15m
istio-system   po/istio-initializer-865bc594c-kw2xx   1/1       Running   0          12m
istio-system   po/istio-mixer-6fdc6784c7-hl8sf        2/2       Running   0          15m
istio-system   po/istio-pilot-5f4865659b-hbw5d        1/1       Running   0          15m
kube-system    po/kube-addon-manager-minikube         1/1       Running   0          34m
kube-system    po/kube-dns-6fc954457d-qfczj           3/3       Running   0          34m
kube-system    po/kubernetes-dashboard-55t8g          1/1       Running   0          34m
NAMESPACE     NAME                      DESIRED   CURRENT   READY     AGE
kube-system   rc/kubernetes-dashboard   1         1         1         34m
NAMESPACE      NAME                       TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)                                                  AGE
default        svc/kubernetes             ClusterIP      10.0.0.1     <none>        443/TCP                                                  34m
istio-system   svc/istio-egress           ClusterIP      10.0.0.60    <none>        80/TCP                                                   15m
istio-system   svc/istio-ingress          LoadBalancer   10.0.0.175   <pending>     80:32706/TCP,443:32034/TCP                               15m
istio-system   svc/istio-mixer            ClusterIP      10.0.0.184   <none>        9091/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP   15m
istio-system   svc/istio-pilot            ClusterIP      10.0.0.189   <none>        8080/TCP,443/TCP                                         15m
kube-system    svc/kube-dns               ClusterIP      10.0.0.10    <none>        53/UDP,53/TCP                                            34m
kube-system    svc/kubernetes-dashboard   NodePort       10.0.0.76    <none>        80:30000/TCP                                             34m
```


### お疲れ様でした:beer:

以上でIstioの構築は完了です。環境差分など何か問題がありましたら公式の[Setup](https://istio.io/docs/setup/)ガイドをご参照ください。


## サンプルアプリケーションを試す

前章でIstioが立ち上げることができました。しかし、これだけではどの様な機能が使えるのかわかりません。この章では、[公式サンプルアプリケーション](https://github.com/istio/istio/tree/master/samples)を使ってIstioを組み込む方法を見ていきます。

### アプリケーションにIstioを組み込む

Istioを構成するコンポーネントの一つであるEnvoy[^3]は、アプリケーションのサイドカーとして組み込まれるためアプリケーションコードを修正する必要がありません。

1. まずは前章でダウンロードしたIstioのディレクトリに移動します

    ```bash
$ cd path/to/istio-0.2.7
```

2. アプリケーションのManifestに対してistioctlのkube-injectを実行します。すると、Istioの設定が組み込まれたManifestが生成されます。

    ```bash
$ istioctl kube-inject -f samples/bookinfo/kube/bookinfo.yaml > out
```

3. `kube-inject`で生成されたManifestと元のManifestのdiffをみると、annotationやサイドカーの設定が追加されていることがわかります![Pasted image at 2017_12_07 10_40 AM.png](https://qiita-image-store.s3.amazonaws.com/0/26683/ebaa40bb-c8ba-6dff-6349-314cd2040d5e.png)

> 全てのdiffが見たい方は[gists](https://gist.github.com/Ladicle/8ea73d991f074c80adf7c9b8d8eceb7f)をご参照ください。


#### Automatic sidecar injection

今回は差分をみるためistioctlで明示的にIstioをアプリケーションに対して組み込みました。

より簡単にデプロイする方法として、DeploymentのPodにサイドカーを自動で組み込む機能[^4]も提供されています。ただし、この機能はKubernetesの[Initializer](https://kubernetes.io/docs/admin/extensible-admission-controllers/#what-are-initializers)を利用しているので、Kubernetesクラスタの立ち上げ時にフラグをオンにする必要があります。その上でistio-initializerをデプロイすることで利用することができます。

実は、前章の構築で既にこの機能を有効にしています。minikube立ち上げ時に付与していた`extra-config`、Istio Manifest適用時に指定していた`istio-initializer.yml`が自動組み込み機能の有効化方法です。

### Istioを組み込んだManifestをKubernetesに適用する

<img width="1254" alt="degital gerimanda - 4.png" src="https://qiita-image-store.s3.amazonaws.com/0/26683/528ab317-f586-068e-5ba2-0ce69bb1c399.png">
**図1** IstioをBookinfoに組み込んだ前と後の比較

実際にアプリケーションを立ち上げる前に、軽くBookinfoのアーキテクチャをみていきましょう。このBookinfoは、本の情報を掲載しているマイクロサービスなアプリケーションです。図1のような構成を取っており、機能は以下のようになっています。

* **商品ページ (Python)**:  他のコンポーネントから取得した本の情報を提供する。
* **レビュー (Java)**: 本のレビュー情報を提供する。1~3までのバージョンがある。
* **詳細情報 (NodeJS)**: 本の詳細情報を提供する。商品ページから呼び出される。
* **レーティング (Ruby)**: 本のレートを提供する。レビューのv2から呼び出される。

では、前章で作成したIstio組み込み済みのManifestを使ってBookinfoをKubernetes上に立ちあげましょう。

1. はじめに、作成したManifestをKubernetesに適用します

    ```bash
$ kubectl apply -f out
service "details" created
deployment "details-v1" created
service "ratings" created
deployment "ratings-v1" created
service "reviews" created
deployment "reviews-v1" created
deployment "reviews-v2" created
deployment "reviews-v3" created
service "productpage" created
deployment "productpage-v1" created
ingress "gateway" created
```

2. 次に、正しく立ち上がっていることをkubectlで確認します

    ```bash
$ kubectl get svc
NAME          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
details       ClusterIP   10.0.0.190   <none>        9080/TCP   5s
kubernetes    ClusterIP   10.0.0.1     <none>        443/TCP    1h
productpage   ClusterIP   10.0.0.207   <none>        9080/TCP   5s
ratings       ClusterIP   10.0.0.116   <none>        9080/TCP   5s
reviews       ClusterIP   10.0.0.92    <none>        9080/TCP   5s
$ kubectl get po
NAME                              READY     STATUS    RESTARTS   AGE
details-v1-cf8bdb7f7-dwpqw        2/2       Running   0          1m
productpage-v1-6bb58b5bd5-r6mkf   2/2       Running   0          1m
ratings-v1-598779769-s6msj        2/2       Running   0          1m
reviews-v1-6f49f7dbd7-dznm7       2/2       Running   0          1m
reviews-v2-8659c664ff-xh29g       2/2       Running   0          1m
reviews-v3-bcbd4f7f9-kcm7z        2/2       Running   0          1m
```

### BookInfoにアクセスする

最後に、立ち上げたBookinfoにアクセスしてみましょう。まずは取得したゲートウェイのURLにアクセスして正しいレスポンスが返ってくることを確認します。

```bash
$ export GATEWAY_URL=$(kubectl get po -n istio-system -l istio=ingress -o 'jsonpath={.items[0].status.hostIP}'):$(kubectl get svc istio-ingress -n istio-system -o 'jsonpath={.spec.ports[0].nodePort}')
$ curl -o /dev/null -s -w "%{http_code}\n" http://$GATEWAY_URL/productpage
200
```

200が返ってきたでしょうか? 問題なければ次はブラウザからアクセスしてみます。

```bash
open http://$GATEWAY_URL/productpage
```

ブラウザが立ち上がったら、ページを何回かリロードしてみてください。レビュー機能を持つコンポーネントに対してラウンドロビンでルーティングされるので、図2-4のような画面複数のバージョンページが表示されるかと思います。

![1510561185204.png](https://qiita-image-store.s3.amazonaws.com/0/26683/0dc22781-5d1f-2bf4-4fe8-ec685c16ece1.png)
**図2** Bookinfoバージョン1のトップ画面

![1510561051636.png](https://qiita-image-store.s3.amazonaws.com/0/26683/a34422cd-16f7-12cc-ac77-18ea477d4097.png)
**図3** Bookinfoバージョン2のトップ画面

![1510561213211.png](https://qiita-image-store.s3.amazonaws.com/0/26683/a6edc8ff-7512-63c2-2ed1-4ed5388d7b34.png)
**図4** Bookinfoバージョン3のトップ画面

## おわりに

今回はIstioの構築とアプリケーションにIstioを組み込む方法について見ていきました。アーキテクチャは少々複雑でしたが、Kubernetesへの組み込みは簡単だったのではないでしょうか？
次回は、Istioの機能を使ってBookInfoをより高度にコントロールする方法を見ていきたいと思います。それではお楽しみに 👋

[0]: https://qiita.com/Ladicle/items/979d59ef0303425752c8
[1]: https://istio.io/docs/guides/bookinfo.html

[^1]: [Istioとは](https://qiita.com/Ladicle/items/979d59ef0303425752c8#istioとは)参照
[^2]: Localで簡単にKubernetesクラスタを動かすためのツール
[^3]: [システム構成](https://qiita.com/Ladicle/items/979d59ef0303425752c8#istioを構成するコンポーネント)参照
[^4]: [automatic-sidecar-injection](https://istio.io/docs/setup/kubernetes/sidecar-injection.html#automatic-sidecar-injection)参照