Istio入門 その3 -Blue/Greenデプロイメントによるカナリアリリース-

## はじめに

ついに第三弾 :tada:
前回までのあらすじを簡単に説明すると、はじめにIstioのコンセプトとアーキテクチャを把握し、それを踏まえてIstioの構築とアプリケーションの組み込みを実践するという展開でした。
今回は、Istioを使った柔軟なルーティングについてみていきます。

> **Istio入門**

> * [その1 -Istioとは?-][istio1]
> * [その2 -Istio構築とサンプルアプリのデプロイ-][istio2]
> * [その4 -基礎から振り返る-][istio4]
[istio4]: https://qiita.com/Ladicle/items/4ba57078128d6affadd5

## Blue/Greenデプロイメントによるカナリア:bird:リリース

サービスのデプロイ方式はいくつかの種類がありますが、クラウドと共に流行りだしたのが**Blue/Greenデプロイメント**[^1]です。
これは、Blue(現在動いているバージョン)とGreen(新しいバージョン)の２環境を用意されている場合、ロードバランサの向き先を変えるだけで新バージョンへの**アップグレード**も**ロールバック**も最小のダウンタイムで実現できるというものです。

単純に全てのアクセスをGreenに切り替えるという方法もありますが、新しいバージョンにバグがあった場合にユーザへ大きな影響を与えてしまいます。そのため、一度に新しいバージョンに切り替えるのではなく、先行して一部のみを切り替え、問題がなければ新バージョンの割合を徐々に増やしていく手法が考えられました。このように、バグによるユーザ影響を狭める手法を**カナリア・リリース**[^2]といいます。

[^1]: 詳しくはMartin Fowler氏による[BlueGreenDeployment](https://martinfowler.com/bliki/BlueGreenDeployment.html)参照
[^2]: 一酸化炭素を検出するために鉱山へカナリアを連れて行ったことから、異常を検知するために先行して動作させるものをカナリアと呼ぶ

<img width="886" alt="B4C0A210-EC90-4A0D-9027-494553B986E7 2.png" src="https://qiita-image-store.s3.amazonaws.com/0/26683/91f4079e-f256-94bd-9a7b-07414940541c.png">

Istioではアクセスの何%だけルーティング先を変えたいということが簡単にできるので、これを使ってカナリア・リリースを実現することができます。今回も、引き続きBookInfoを使ってこの動作を確認していきます。まだIstioとBookInfoの構築が終わっていない方は、[前回の記事][istio2]をご参照ください。

### バージョンを固定する

前回のBookInfoの構築では、各バージョンのレビューコンポーネントに対して、ラウンドロビンでアクセスするようになっていました。このままではカナリア・リリースによるバージョンアップを試すことができないので、バージョン1に固定します。

1. まずは、前回ダウンロードしたistioのディレクトリに移動します
2. 次に、route-rule-all-v1.yamlを適用します

    ```bash
$ kubectl create -f samples/bookinfo/kube/route-rule-all-v1.yaml
routerule "productpage-default" created
routerule "ingress-deny-all" created
routerule "ingress-products-api" created
routerule "ingress-productpage-logout" created
routerule "ingress-productpage-login" created
routerule "ingress-productpage-landing" created
routerule "reviews-default" created
routerule "ratings-default" created
routerule "details-default" created
```
> 本来は、istictlのManifestのバリデーション機能を活用することをお勧めしたいのですが、現在バリデーション[^3]エラーで弾かれてしまうため`kubectl`を利用しています。

3. kubectlから、実際にリソースが作成されたことを確認します

    ```bash
$ kubectl get routerules
NAME                          AGE
details-default               5m
ingress-deny-all              5m
ingress-productpage-landing   5m
ingress-productpage-login     5m
ingress-productpage-logout    5m
ingress-products-api          5m
productpage-default           5m
ratings-default               5m
reviews-default               5m
```
> ルーティング設定は非同期に設定されるので、全て適用されるまでには数秒程度かかることがあります。

4. ブラウザからもアクセスし、何度リロードしてもバージョン1が表示されることを確認します

    ```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}')
$ open $GATEWAY_URL/productpage
```
    ![0fe62468-2828-4cd5-9a22-1af88712decb.png](https://qiita-image-store.s3.amazonaws.com/0/26683/2fa7a57d-b5d1-e075-edaa-2e9f7bc60d9f.png)

[^3]: Istio[#1375](https://github.com/istio/istio/issues/1375) 参照

### 20%のアクセスを新バージョンへルーティングする

バージョンが固定できたところで、実際にカナリア・リリースを試してみましょう。まずはカナリアとして20%のアクセスだけバージョン2にルーティングされるように変更してみます。

1. 以下のようなルーティングManifestを作成してください

    ```yaml:canary-release-test.yaml
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: canary-release-test
spec:
  destination:
    name: reviews
  precedence: 2
  route:
  - labels:
      version: v1
    weight: 80
  - labels:
      version: v2
    weight: 20
```
> 先ほどバージョン1に固定するために適用したManifestにもこのRouteRuleが定義されていました。このカスタムリソースの定義については[こちら](https://istio.io/docs/reference/config/traffic-rules/routing-rules.html#destinationweight)をご参照ください

2. 作成したManifestを適用します

    ```bash
$ kubectl create -f canary-release-test.yaml
routerule "canary-release-test" created
```
3. ブラウザから何度もリロードしてみてください。数回に一度、バージョン2にルーティングされるようになったかと思います。
    ![1f23cd0b-f6e6-4343-a22a-ef4427cc04ae.png](https://qiita-image-store.s3.amazonaws.com/0/26683/ae36f436-a2b9-b344-bf65-6dcf01718006.png)
4. 問題がなければ、先ほどのファイルを編集して徐々に割合を変えて適用してみてください。ブラウザからアクセスして見ると実際に割合が変わることを実感できると思います

このように、カナリアリリースによる緩やかなアップグレードが実現できることが分かりました。このようなフローを手動でやり続けるのは辛いので、モニタリングツールと連動したアップグレードとロールバックフローを自動化するのもよいかもしれません。

## βテスター専用ページ

サービスのテストのために、特定のユーザだけ新しいバージョンを表示させたいと思う事があるかもしれません。ここでは、Cookieの中に入っているユーザ情報を使って、テスターの`jason`さんのみバージョン3にを表示させる方法を見ていきます。

1. 以下のようなルーティングManifestを作成してください

    ```yaml:add-tester.yaml
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: add-tester
spec:
  destination:
    name: reviews
  precedence: 3
  match:
    request:
      headers:
        cookie:
          regex: "^(.*?;)?(user=jason)(;.*)?$"
  route:
  - labels:
      version: v3
```
> ちなみに、このリソースはKubernetesのCustomResourceDefinitionを利用して定義されています。この定義ファイルは、前回のIstio構築時に適用したManifestに含まれています。気になった方はPilotのコードと合わせて見ると面白いかと思います。

2. 作成したManifestを適用します

    ```bash
$ kubectl create -f add-tester.yaml
routerule "add-tester" created
```
3. `Sign in`ボタンから`jason`さんとしてログインします(パスワードは不要です)
    ![b7bd30a6-850d-4687-8374-949923246708.png](https://qiita-image-store.s3.amazonaws.com/0/26683/55079114-58ab-83c4-1112-0e11614fd5aa.png)

4. ブラウザから何度もリロードしてみてください。バージョン3のみにルーティングされるかと思います

今回はCookieのユーザ名でルーティング先を振り分けましたが、HTTPヘッダの中身であれば何でも条件を設定できます。例えば、User-agentを見てAndroidとiPhoneで表示するページを変えるということもできます。

## おわりに

今回は、Istioを使って重みやHTTPヘッダに応じたルーティング先の変更を試していきました。他にもやChaos Engineeringのための障害発生機能があります。これらについては気が向いたら続きを書こうと思います。

明日は、@hatotakaさんによる「RaspberryPiとkubeadmで自宅Kubernetesクラスタを構築する」お話です。それでは引き続き[Z Lab Advent Calendar 2017](https://qiita.com/advent-calendar/2017/zlab)をお楽しみください。

[istio1]: https://qiita.com/Ladicle/items/979d59ef0303425752c8
[istio2]: https://qiita.com/Ladicle/items/e949b0f68ac18b7a95d8