「マイクロサービスパターン」の復習 12章 デプロイ

## 12章 マイクロサービスのデプロイ

- マイクロサービスをデプロイする方法について
- Kubernetesを使ったデプロイ
- Kubernetes+Istioを使ったデプロイ
- AWS Lambdaを使ったデプロイ

## Language-specific packaging formatパターン

- Javaでいうjarやwarなどをデプロイする方法
- 利点
    - デプロイが高速: 転送はjarやwarだけ。起動も各環境に最適化しているので速い。
    - 効率的なリソース使用：tomcatの複数のwarを載せられるので、OSやフレームワークなど各種リソースを共有することになる。見方によっては効率的とも言える。
- 欠点
    - テクノロジスタックをカプセル化できない：デプロイするにもフレームワークの詳細を知らないといけない
    - リソースの制限ができない
    - 1マシンに複数サービスインスタンスをやると密結合：何がしか相手がlocalhostにいる前提のコードが書かれてしまったりする
    - 自由に配置できない

というわけで、今の時代、このやり方は推奨されない

## Sevice as a VMパターン

ビルドパイプラインでVMイメージを作って、VMイメージをデプロイする

- 利点
    - テクノロジスタックをカプセル化できる：VMのデプロイ方法だけ知っていればよい
    - サービスインスタンスの分離：VMは中からみれば、別マシンなので
    - クラウドインフラの機能が使える：同じVMイメージを使って、自動スケーリングなどができる
- 欠点
    - リソースの使い方が非効率：VMのサイズは数パターンしかないので、ピッタリとはいかない
    - デプロイに時間がかかる：VMの転送に時間かかる（OS丸ごと）。VMの起動＝OSの起動なのでやはり時間がかかる
    - システム管理のコスト：各種パッチあてなど。

## Service as a containerパターン

ビルドパイプライでコンテナイメージを作って、コンテナをデプロイする

- 利点
    - テクノロジスタックのカプセル化
    - サービスインスタンスの分離
    - リソースの制限が可能
    - デプロイもそれなりに速い：階層化ファイルシステムのおかげで転送量が少ない。起動時間もOSごと起動するのに比べれば速い
- 欠点
    - コンテナイメージの管理
    - コンテナ内のソフトウェアに対するパッチ当て（通常はコンテナ内は最小限のソフトウェアしか入れないので、VMよりは軽いかもしれない）

## kubenetesによるデプロイ概要

詳細に入るとそれだけで本一冊になるので概要だけ

### 物理or仮想マシン
- マスター: 全体を管理するマシン。一台あればよいのでAutoHeailngくらいしておけば良さそう。以下のコンポーネントを実行する
    - APIサーバー：マスターへの指示を受ける口
    - etcd: クラスタデータ管理
    - スケジューラ： Podをノードに割り当てる
    - コントローラ管理：スケーリングの調整など
- ノード：ポッドを動作させるワーカーマシン。以下のコンポーネントを実行する
    - kubelet: マスターからの指示を受ける
    - kube-proxy: ネットワーク管理、ロードバランス
    - Pod: 一つ、あるいは複数のコンテナを包含する

クラウド環境では、ノード数を自動でスケーリングすることも可能だそうです。

### 重要概念

- Pod
    - デプロイの単位
    - 複数のコンテナを実行可能
    - 1Podに1コンテナでもいいが、アプリ用のコンテナ1つ+補助用途のコンテナで便利に使うこともできる（Sidecarパターン）
        - ログ転送用のfluentdや、後述のistioのenvoyプロキシなどがSidecarの例

- Deployment
    - yamlで定義されるデプロイの定義
    - pod使うコンテナの定義や、podのレプリカ数を記述する
    - kubectl apply -f yamlファイル と指定することで、デプロイできる

- Service
    - yamlで定義して、kubectlで適用するのはDeploymentと同じ
    - 外部からのアクセス情報（ホスト名、ポートなど）を定義して、selectorで指定したコンテナ群にリクエストをロードバランスしながらルーティングする
    - 概ねマイクロサービスでいうところのサービスと、同じくらいの粒度
    - Serviceにはtypeがある：CusluterIP、NodePort、LoadBalancer
        - CusluterIP: クラスタ内での通信用（デフォルト）
        - NodePort: Podのホストとなるノードに、ポートをあける（外部からのアクセス用）
        - LoadBalancer: クラウド固有のロードバランサにマッピングする
        
- ConfigMap
    - 11章で説明された外部設定の定義

### ゼロダウンタイムのデプロイ

- Deploymentのyamlを更新して、kubectl apply -f すれば、ローリングアップデートで新バージョンをデプロイしながら、旧バージョンを徐々に終了させていく
- 途中で問題が発生したらkubectl rollout undoで、ロールバックも可能
- これらをダウンタイムなしでやってくれる

## サービスメッシュ istio を使ったデプロイとリリースの分離

- kubernetes + istioを組み合わせるとデプロイとリリースを分離できるようになる
    - デプロイ：本番環境で実行すること
    - リリース：デプロイされたサービスにリクエストをルーティングする（エンドユーザーが利用できる）
- これで何が嬉しいかといえば、サービスを旧バージョンから新バージョンに移行させるときに、以下のように新バージョンのリリースを調整できる
    - 新バージョンをデプロイした直後は、ルーティングの100%を旧バージョンに対して行う（つまり新バージョンはデプロイはしてもリリースはしない）
    - 開発者だけが、新バージョンにアクセスできるようにする
    - エンドユーザーからのリクエストのうち5%を新バージョンにルーティングする
    - 新バージョンへの転送比率を徐々に上げていって、最終的に100%にする
- kubernetesだけだと、新旧両バージョンに等しくルーティングしてしまう

### istioの構造

サービスメッシュistioは、11章でも紹介されたが多くの機能を持っている.
ここでは主にルーティングの部分を解説している
kubernetesと組み合わせなくてもいいらしいが、kubernetesとの組み合わせの場合で記述する

istioは以下で構成される
- コントロールプレーン: おそらく単一ホスト
    - Pilot: データプレーンへの指示
    - mixer: 分散トレーシング用のデータ収集など
- データプレーン: kubernetesの各pod内で動作する（Nodeではなく、Podであることに注意）
    - envoyプロキシ: データプレーンの実体。各pod内でサイドカーとして動作するコンテナ。pilotからの指示に従いルーティングを行う

kubernetes+istioの環境では、各サービス間（Pod間）の通信は、envoyを通じて行うことになる。
サーキットブレイカーや分散トレーシングもこの構造のおかげでできるようになる

kubernetesにマスターとノードがあるように、istioにはコントロールプレーンとデータプレーンがある。
ただし、一つのノード上には、複数のpodが実行されるので、ノードとデータプレーンの粒度は違う

### istioでのデプロイ


kubernetes+istio環境でのサービスのデプロイは基本的にはkubernetesの流儀にしたがって、DeploymentとServiceをyamlで定義するが、若干の拡張が入る
- Service: portにプロトコル名を追加する
- Deployment: 名前にバージョン名を入れる。つまり別デプロイになるので、v1とv2が同時にデプロイされる。また後述のルーティングの設定で、v1とv2が同じサービスの別バージョンであることが分かるように、labelづけが必要。具体的には、app：同じサービス名、version：それぞれのバージョン とする。

kubernetes側で自動サイドカー注入が有効になっていれば、通常通りkubectl apply -fでyamlを読み込ませるだけで、各podにenvoyコンテナが注入された状態でデプロイされる

### istioでのルーティング

上記のデプロイだけだと、複数バージョンが同時にデプロイされて、全部にルーティングされるだけなので、別途ルーティングの定義が必要です。

istioは、以下の概念をkubernetesのyamlと同じ形式で定義できます。

- VirtualService
    - istioにおけるサービス定義。
    - kubernetesのServiceだと、selectorで指定したコンテナすべてのルーティングしてしまうため、v1とv2の両方に等しくルーティングしてしまう。
    - 一方でistioのVirtualServiceは、バージョンごとのweightをつけてルーティングを定義できる
- DestinationRule
    - kubernetesのDeploymentだと、v1のDeploymentとv2のDeploymentは別名がついている別のモノなだけ。
    - istioのDestinationRuleは、v1とv2のDeploymentが同じサービスの別バージョンであることを定義する
        - 先述のDeploymentのラベル付の情報をもとに定義する

kubenetesにServiceとDeploymentがあるように、istioにはVirtualServiceとDestinationRuleがある。istioの方がレイヤーは1段上で粒度も違う。

## Serverles deplymentパターン

AWS Lambdaや、それに類するサービスを使ったパターン。

- 利点
    - OS、ランタイム、フレームワークの保守が不要
    - 設定なしで自動でスケーリングしてくれる
    - Lambda関数実行時のみにしか課金されない
    - クラウドプラットフォーム上の他のサービスとの連携が容易
- 欠点
    - レイテンシーが発生する場合がある
        - コンテナやランタイムをオンデマンドでデプロイして、不要になったら破棄されるので、予測不可能なタイミングで大きなレイテンシが発生する
    - 外部からのリクエストやほかサービスからのイベント起点でしか動作できない


デプロイするには、Serverlessというツール（ https://www.serverless.com/framework ）を使うと楽になる
serverless.ymlというファイルを用意して、serverless deployとコマンドを打てば、API Gatewayの設定も含めてデプロイしてくれるらしい。


## まとめ

- kubernetesやistioは今回初めて解説を読んだが、どういうものかは大体わかった
    - 他の資料は見てないので記述に間違いがあるかもしれない
- kubernetesやistioは色々できて便利なのだろうけど、やりたいことができるようになるまで理解して、かつ障害時の対処方法まで身につけるには、かなりの労力がかかりそう
    - 使いこなせれば多くの管理業務が楽になるのは、そのとおりなのだろうけど、そこに至るまでには多くの失敗も必要な気がする
    - そして使いこなせるようになったころには別のテクノロジに置き換わっている可能性も。。。
- 著者が言うように要件を満たすもののなかで、軽いものから選択するとよいとのことなので、第一の選択肢はserverlessになるのは、そのとおり
- 第二の選択肢がいきなりkubenetes+istioと言われると、うーんと思う。
- 同じコンテナ系の技術でも、AppRunnerやECS+Fagateあたりならもっと軽いかもしれないので、そのあたりも気になる。