Katacodeをつかってコンテナ化したMagicOnionのアプリをkubernetes上で動かしてみる

この記事を書いている前日、MagicOnion勉強会で、MagicOnionをLinuxコンテナイメージにしてkubernetes上で動かして、NewRelicで監視する、というLTをしました。資料はこちらです。

https://www.slideshare.net/tanakata/20190604-containerized-magiconion-on-kubernetes-with-observability-with-new-relic

LTということもあり、いきなり動いている様子をデモでお見せしたのですが、どうやって動かすんだろうという疑問をあったような気がします。そこで簡単にkubernetes上で動かす手順を紹介したいと思います。

一番のハードルはkubernetes環境の作成だと思います。AKS,EKS,GKEといったパブリッククラウドのマネージドkubernetesは各社が用意しているコマンドでインストールできますが、それなりにお金がかかります。ローカルにインストールするminikubeというものもありますが、それでも多少の面倒さはあります。そこで探していたら、[Katacoda](https://www.katacoda.com/)というサービスを見つけました。ブログ記事も出ているように、なんとブラウザ上で用意されたkubernetesの環境を操作できるというものです。というわけで今回はKatacodeを使ってMagicOnionのアプリをkubernetes上で動かしてみたいと思います。ただ、1つ制約があってgRPCのエンドポイントをインターネットに公開できないため、クライアント側も同じ環境上で動かす必要があります。

Katacodeはコースを作ることで、手順も込みで提示することができます。もう少し色々調べたら作り込もうと思いますが、まずはこちらの手順をみながら試してみてください。

まず私の作成したコース一覧のページにアクセスしてください。

https://www.katacoda.com/tanaka_733/

このような画面になります。アカウント作成してログインすると履歴が残って便利ですが、作成せずとも利用できるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/a0c56c9f-1b17-f6e1-782b-bedde05a9487.png)

kubernetes sandboxの下のStart Scenarioをクリックします。すると次の画面になります。

<img width="1419" alt="スクリーンショット 2019-06-05 20.34.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/ea187ea3-3b4a-93ec-0ff9-9d1f8695fb45.png">

右下の黒い画面がターミナル、その上の白い画面がエディタとして使える画面です。まず、kubernetesを起動するコマンドを実行します。

```
launch.sh
```

しばらく待つとkubernetesd startedと表示されます。kubernetesのノード一覧を表示するコマンドを実行してみましょう。

```
kubectl get node
```

すると次のようにノード一覧が表示されます。ノードは１台だけのようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/c7286b32-25bc-6822-7b1f-3707249ee393.png)

次にMagicOnionのアプリをデプロイして、外部からアクセスできるようにkubernetesのサービスを作ります。エディタを使うので`app.yaml`という名前で空のファイルを作り、エディタ画面に表示されるのでダブルクリックで開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/60124279-19b9-02e0-ac69-d11034f4dd89.png)

エディタに次のkubernetesのオブジェクト定義をコピーペーストします。自動的に保存されます。

```yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: magiconion
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: magiconion
    spec:
      containers:
      - name: magiconion
        image: tanakatakayoshi/magiconion-server-example:latest
        ports:
        - containerPort: 12345
---
apiVersion: v1
kind: Service
metadata:
  name: magiconion-svc
spec:
  ports:
  - name: magiconion
    port: 12345
  selector:
    app: magiconion
  type: NodePort
```

ここで今から作るものを確認してみましょう。今定義したファイルにはdeploymentとserviceという2つのオブジェクトが存在しています。deploymentにはimageで`tanakatakayoshi/magiconion-server-example:latest`を指定していますが、これが今から動かすコンテナイメージです。

このコンテナイメージの詳細はここに記載されています。

https://hub.docker.com/r/tanakatakayoshi/magiconion-server-example

元となるソースコードはこちらで公開しています。

https://github.com/tanaka-takayoshi/magiconion-k8s-demo

こんなよくわからないコンテナイメージ実行したくない！という場合は、いずれ自分で書いたソースコードをコンテナイメージにして実行する方法を紹介したいと思いますのでしばらくお待ちください。このコンテナはMagicOnionの単純なサーバーとそれに対応するクライアントのプロジェクトおよびそれらをビルドするDockerファイルから構成されています。

元に戻って、このdeploymentはimageに指定しているコンテナ1つから構成されるPodというkubernetesのオブジェクトを配置します。Podには複数のコンテナおよびvolumeと呼ばれるストレージを配置できますが、今定義したPodは1つのコンテナのみで構成されています。`containerPort: 12345`というのは、このPodはポート12345番をコンテナ外に公開するという意味です。MagicOnionのサーバー側のコードでListenするポートと対応させます。

```cs
await MagicOnionHost.CreateDefaultBuilder()
    .UseMagicOnion(new[] {
        new ServerPort("0.0.0.0", 12345, ServerCredentials.Insecure) })
    .RunConsoleAsync();
```

もう一つ作成したサービスというオブジェクトはPodへのアクセスを公開するためのオブジェクトです。（後述する理由で今回はこの環境外からアクセスできないのですが、）外部からアクセスするためのエンドポイントであり、1つのアプリが複数のPodで負荷分散している場合[^1]にロードバランサになります。

[^1]: 先ほどのdeployment定義の中の`replicas`に指定した数だけPodが生成されます。

ターミナル画面に戻って、今定義したオブジェクト定義を作成するコマンドを実行します。

```
kubectl create -f app.yaml
```

deploymentとserviceが作成されたと表示されます。deploymentによりPodが配置されるのですが、少し時間がかかるので完了を待ちます。`kubectl get`コマンドは`-w`オプションをつけると変化があると更新情報が表示されるようになります。これでPodの様子を確認します。

```
kubectl get po -w
```

次の画面のようにSTATUSがRunningになればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/6c9277cf-2328-b181-3e79-b06b317466ad.png)

次にサービスの様子を確認します。

```
kubectl get svc
```

magiconion-svcというNAMEのサービスのCLUSTER-IPをメモしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/1ee70744-6dd8-4583-7a85-c317f9c8f2da.png)

さて、これで無事にMagicOnionのサーバー側のアプリがコンテナとしてkubernetes上で動きました。では接続したいところなのですが、Katacodeを使う上での制約が一つあります。HTTP（S)ではないTCPのトラフィックを外部から受け付ける方法がなさそう[^2]ということです。先ほどの作成したサービスは`NodePort`というタイプで、指定したポートでノードでアクセスすると、Podと通信できるというものです。しかしKatacodeのkubernetesではノードにアクセスできないためこの方法は使えません。

[^2]: 調べた限りではなさそう。確実にないかどうかまではわからず。 https://www.katacoda.com/docs/scenarios/displaying-ports

そこで、今kubernetesを動かしているサーバー上でMagicOnionのクライアントを動かすことにします。今動いているサーバーに対応したテストクライアントアプリを同じdocker repositoryで公開しているのでそれを使います。

ちょっと邪道ですが、kubernetesを動かしているサーバー上でdockerイメージを直接実行します。このクライアントはサーバーのホストを`GRPC_HOST`という環境変数で外から渡すことができるように作っているので、先ほどメモしたCLUSTER-IPの値を指定した次のコマンドをターミナルで実行します。実際に動かす場合は`10.99.143.216`を置き換えてください。

```
docker run --name client-demo -e GRPC_HOST=10.99.143.216 tanakatakayoshi/magiconion-server-example:client
```

すると一気にログが流れます。両方とも公開されているdockerイメージを使って、ログが流れているのを眺めるだけなのでいまいち動かした感がないかもしれませんがこれでMagicOnionのサーバーアプリをkubernetes上で動かすことができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405931/57e413c4-ae42-e655-4d8b-c9399e3bbfd7.png)

もちろんminikubeをはじめ、通常のkubernetesで動かせば、kubernetes外からアクセスできて、Unityから繋ぐということもできます。Katacodeはkubernetesをお試しで動かすという用途ではすごくお手軽に使えていいと思います。今後も調べながらいろんなものを試せるコースを作ろうかなと思っています。