Conduitを使ってみる - サービスメッシュ

この記事は[ドワンゴ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dwango) 8日目の記事です。

昨日は @binzume さんの[【Android】minSdkVersion = 4 でアプリ開発する縛りプレイ【バッドノウハウ】](https://qiita.com/binzume/items/88f5cd95f67fe6c2f511)でした。
仕事ではiOSを書いていて、iOSはOSのアップデート率がとても高く、古いOSをサポートし続ける理由が少なく、古い端末に悩まされるということは（あまり）ありません。時々はありますが・・。Androidは古いOSの対応が大変そうだなといつも横目で見ています。そんななかこの縛りプレイは業務に役立つかは不明ですが、とても面白い試みだと思いました。

この記事ではサービスメッシュについて書こうと思います。
僕は業務ではN予備校のiOSエンジニアをしています。ただ、サービス開発全般的に興味があり、インフラからフロントまでなんでも学ぶ気持ちを持っています。最近注目している技術はコンテナとそれを管理するKubernetesで、サービスメッシュはKubernetesの先にある技術だと思っているため、今回は勉強も兼ねてアドベントカレンダーに書く内容をこれに決めました。

# Conduitを使ってみる
最近のサービス開発ではマイクロサービスが賑わっています。[2年前のアドベントカレンダー](https://qiita.com/kouki_dan/items/105e159f6840cd0dbea8)で僕はCloud Naviteなアプリケーションについて書きましたが、2年も経つとCloud Nativeが指す意味も明確になってきています。[Pivotal](https://pivotal.io/)が発表した [Cloud-Native Maturity Model](https://pivotal.io/replatforming) では、Cloud Nativeなアプリケーションについて、マイクロサービスアーキテクチャが使われていることをあげています。[^1]

マイクロサービスはサービス同士が互いに通信を行うため、今までのサービス管理にはなかった問題が発生します。一番単純な例ではホスト名をサービスに与え、DNS等で名前解決を行いHTTPで通信をします。この方法でサービス同士の通信は可能になりますが、不便な点がいくつもあります。

- このサービスにアクセスしたいけど・・・どうやったらアクセスできるんだろう:thinking_face:
- どのサービスとどのサービスがどれくらい通信しているんだろう:thinking_face:
- リクエストは失敗していないのかな、失敗したらリトライしたいな・・・:thinking_face:
- でも無限にリトライはしたくないな、そして指数的にリトライしたいけど・・その処理どこが持つんだろ:thinking_face:
- サービス間の認証ってどうなってるんだっけ？:thinking_face:
- 本デプロイの前に5%くらいのリクエストを新しいサービスに流して問題ないか確かめたいなぁ・・:thinking_face:

このようにマイクロサービスアーキテクチャでアプリケーションを作っていくと、本来実現したかった機能ではない問題を考える必要がでてきます。これらはモノリシックなアプリケーションでは（多くの場合）無視できた問題ですが、マイクロサービスではこのあたりをしっかりと管理しないとサービス間の依存がわからなくなったり、無限にリクエストを飛ばし続けるサービスがバグにより生まれてしまった時にサービス全体に影響してしまったりします。

これらの問題を解決するため、サービスのロジックに影響せずにサービスの管理や監視等の処理を受け持つプロダクトがあり、それらの一つはサービスメッシュと呼ばれています。サービスメッシュの具体的なプロダクトとしては、[Istio](https://istio.io/)や[Linkerd](https://linkerd.io/)、そして、先日発表されたばかりの[Conduit](https://conduit.io/)があります。

## Conduitについて
ConduitはLinkerdの開発や運用の経験を経て作られたKubernetes用のサービスメッシュです。Linkerdを作った人が開発をしているため、そこで得られた経験や知見が生かされ作られています。
[開発元のConduitを発表した記事](https://buoyant.io/2017/12/05/introducing-conduit/)によると、とても軽いが強力なKubernetes用のサービスメッシュとして[^2]作られています。

まだ発表されたばかりなので機能が十分にあるわけではないですが、Kubernetesさえあれば簡単に試すことができます。

## Conduitはどのようにサービスメッシュを実現するか
Conduitはサービスメッシュを管理するコンテナと、各サービスのPodにデプロイするコンテナによって成り立っています。各Pod内にデプロイされるコンテナはサイドカーと呼ばれています。Conduitではこのサイドカーコンテナは軽量なプロキシとして動作し、サービス同士の通信をプロキシし、同時にメトリクスの収集も行います。
各サービス間の通信はサイドカーのConduitが用意したプロキシコンテナを通し行われることになります。

## KubernetesにConduitを適用する
[Getting Started](https://conduit.io/getting-started/)に沿ってConduitをインストールしてみます。ConduitのGetting StartedではConduitのインストールから、COnduitが用意したマイクロサービスのデモアプリのデプロイまでを行うことができます。

デプロイにはKubernetesが必要なので、[minikube](https://qiita.com/mumoshu/items/8f55ee830d8e5c172dd4)や[GKE](https://cloud.google.com/kubernetes-engine/docs/quickstart?hl=ja)等で`kubectl`コマンドが使える状態のKubernetesを準備しておきましょう。

### 1. 自分のマシンにConduitをインストールする
以下のコマンドでConduitをインストールできます

```
curl https://run.conduit.io/install | sh
```

上記コマンド実行後に`~/.conduit/bin/conduit` に実行可能ファイルが配置されるので、zshrcやbashrcに

```
export PATH="/Users/{your-name}/.conduit/bin:$PATH"```
```

などと記述しパスを通しておきましょう。

### 2. ConduitをKubernetesに適用する
Conduitの動作に必要なプログラムは全てコンテナ化されていて、KubernetesのDeploymentやService等で定義されています。以下のコマンドを適用することでこれらをインストールすることが可能です。
```
conduit install | kubectl apply -f -
```

実際にどのようなコンテナが使われるかを知りたいときは、 `conduit install` と入力することでKubernetesのアプリ定義ymlを確認することができます。
また、上記コマンド適用後はKubernetes上にデプロイされているため、kubectlコマンドでどのようなコンテナが動いているか確認することができます。Conduitの制御用コンテナは`conduit` namespaceで動作するため、以下のように確認できます。

```
kouki_dan% kubectl get deployment -n conduit
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
controller   1         1         1            1           3h
prometheus   1         1         1            1           3h
web          1         1         1            1           3h
```

NAMEから想像すると、

- Conduit全体を管理するために `controller` 
- サービス間通信のメトリクスを保持しておくために `prometheus`
- Web UIを提供するための `web`

の3つのDeploymentが存在するのでしょうか。こちらはあとで見ていこうと思います。

### 3. dashboardを見てみる
Kubernetesを普段から使っている人はkubernetes-dashboardを見たことがあると思います。これはクラスタ内のNodeやPod、ServiceやDeployment等、Kubernetes上の情報を確認できるUIを提供しています。
Conduitもサービスメッシュ用のUIを提供しており、このコマンドを打つことで確認できます
```
conduit dashboard
```
このコマンドを入力すると自動的にブラウザが開き、Conduitのdashboardが開かれるはずなのですが、僕の環境では動きませんでした。その場合は手動で開く必要があります。たいていの人はこのURLがConduitのURLになるはずです。 
http://127.0.0.1:8001/api/v1/namespaces/conduit/services/web:http/proxy/

ブラウザでdashboardを見ると、このような画面が表示されるはずです
<img width="1210" alt="スクリーンショット 2017-12-06 20.12.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/41033/3c567d02-c421-ccf7-8b27-8d2e3acf309c.png">

リクエストの頻度や成功頻度、等を見ることができます。
この時点ではまだアプリをデプロイしていないため有用な情報は表示されていないと思います。次のステップに進みデモアプリをデプロイしてみましょう.

### 4. デモアプリをデプロイしてみる
Conduitが作ったデモアプリをデプロイしてみましょう。デプロイ用のymlと、デプロイされるアプリのコンテナが準備されています。

```
curl https://raw.githubusercontent.com/runconduit/conduit-examples/master/emojivoto/emojivoto.yml | conduit inject - --skip-inbound-ports=80 | kubectl apply -f -
```

[Kubernetesにデプロイするためのyml](https://raw.githubusercontent.com/runconduit/conduit-examples/master/emojivoto/emojivoto.yml)に、`conduit inject`を適用することでConduitの実行に必要なサイドカーコンテナを追加したymlに変換します。それをkubectl applyに渡すことでConduit経由でサービス間通信を行うことができるPodをデプロイすることができます。
デモアプリ以外をデプロイする場合でもDeploymentを定義したymlにconduit injectを使うことでConduitを使ったサービスをデプロイすることが可能です。
どのようなymlが生成されるか興味がある方はパイプで繋いでいるコマンドを一つずつ実行してみてください。

1. サービス定義yml（エンジニアはこのymlを定義する必要がある）
    - `curl https://raw.githubusercontent.com/runconduit/conduit-examples/master/emojivoto/emojivoto.yml`
2. Conduitの設定が注入されたyml（一般的にこれはconduit inject経由で生成される）
    - `curl https://raw.githubusercontent.com/runconduit/conduit-examples/master/emojivoto/emojivoto.yml | conduit inject - --skip-inbound-ports=80`

### 5. デモアプリにアクセスし、dashboardを見てみる
デモアプリにアクセスしてみましょう。デモアプリのIPアドレスは以下のコマンドで確認できます。

```
kouki_dan% kubectl get service web-svc -n emojivoto
NAME      CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
web-svc   10.3.242.127   35.189.148.214   80:31194/TCP   3h
```

この場合、`35.189.148.214` がemojivotoのWebサーバーのアドレスなので、  http://35.189.148.214/ にアクセスします（この値は一人ひとり違うはずです）
<img width="1219" alt="スクリーンショット 2017-12-06 20.28.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/41033/1a2c1df0-ee26-dca8-22c7-d738ed6da352.png">
EMOJI VOTEという絵文字を投票できるサービスが起動しました。このサービスにいくつかアクセスしたあとでdashboardを見ると値に変化が見られるはずです。
サービス間の通信はすべてConduit経由で行われているため、このようなメトリクスを表示することが可能になっています。

## マイクロサービスを考えるときはサービスメッシュも一緒に考えよう
サービスメッシュのプロダクトの1つとして、先日発表されたばかりのConduitを取り上げてインストールし、サービス間の通信やそれらのモニタリングが簡単に行える事を見てきました。
Conduitはまだ新しく、機能も少ないですが、とても軽量で簡単に使いはじめる事ができたのが見て取れたと思います。
Kubernetesで動かすもう少し高機能で開発が進んでいるものとしては、[Istio](https://istio.io/)があり、こちらにはConduitにない機能も実装されています。特に[Handling Failures](https://istio.io/docs/concepts/traffic-management/handling-failures.html)にある、タイムアウトやリトライの管理はとても有用に感じる方が多いのではないでしょうか。
マイクロサービスのアプリを運用していく上でサービスをどこで切るかという切り口も重要ですが、同じようにサービス間はどのように通信され、管理されていくのかも必要です。サービスメッシュはまだ、これといったデファクトスタンダードのプロダクトが存在しませんが[^3]、部分的にでも適用していくことでより信頼性の高いサービスを提供していくことができるのではないでしょうか。

## おかたづけ
GKEを利用した方は余計なお金がかかってしまうのでお片付けをしましょう。ConduitとデモアプリのDeploymentを削除すれば良いので、

```
conduit install | kubectl delete -f -
curl https://raw.githubusercontent.com/runconduit/conduit-examples/master/emojivoto/emojivoto.yml | conduit inject - --skip-inbound-ports=80 | kubectl delete -f -
```

と、applyをdeleteに変えることでおかたづけ終了です

[^1]: 今まで僕がCloud Nativeだと思っていたのはCloud ReadyもしくはCloud Friendlyだったんだなぁとこの記事を読んで思いました。僕的にはこの定義に少し疑問が残るのですが・・・・・。
[^2]: 現在はKubernetesにしか対応していないが、これから別のプラットフォームにも対応していくようです
[^3]: Conduitは2日前に出たばかりだし、Istioもバージョンが1.0に達していない