ニフクラのHatoba(β)でdapr + マイクロサービスを動かしてみた

この記事は、 [富士通クラウドテクノロジーズ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/fjct) の**14日目**の記事です。

**13日目**は @morisatoru さんの **[M5Stack とニフクラ mobile backend でお菓子カウンターを作る](https://qiita.com/morisatoru/items/de03a7b4d98f21148297)** でした！
ニフクラ mobile backend + M5Stack を使えば数行のコードを追加するだけでIoT化することができそうですね:yum:
アイデア次第でさまざまなライフハックができそうなので僕もやってみたい…！

磁石が内蔵されているのも便利なので、玄関のドアに貼り付け、外出時に天気予報を配信するモノとか作ってみたいな〜とおもいました！眠っているM5Stackを引っ張りだして来なければ…

## 🌞 はじめに

改めましてこんにちは！DDD + Clean Architecture でAPIを開発している @kzmake と申します。
**2日目**の **[daprでつくるマイクロサービス](https://qiita.com/kzmake/items/4136a11fa301cdec4ccb)** に引き続き、今回は実際にそのサービスをニフクラの**マネージドKubernetesサービス**である**Hatoba（β）**を利用して動かしてみたいとおもいます！

## 📖 Hatoba（β）とは

[Hatoba（β）](https://pfs.nifcloud.com/service/hatoba.htm)とは、ニフクラ上でマネージドkubernetesクラスターを作成することができるサービスです。
2019/03/26からβ版としてリリースされているサービスで、現在(2020/12/13)はなんと**無料**で試すことができます！

※Hatoba（β）はユーザーの評価・検証利用を目的として提供しております。本番サービスでのご利用は想定しておりません。

- [クラウド 機能・サービス（Hatoba（β）） | ニフクラ](https://pfs.nifcloud.com/service/hatoba.htm)
- [マネージドKubernetesサービス　ニフクラ Hatoba(β)リリース！！](https://www.slideshare.net/Fujitsu_CloudTechnologies/kubernetes-hatoba-159713881)

## 💡 [dapr] + [時計サービス:timer:] を [Hatoba（β）] で動かしてみる

前回の記事で[dapr]を使って作成したマイクロサービス
**時計サービス:timer: https://github.com/kzmake/dapr-clock**
をニフクラの[Hatoba（β）]で動かしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48903/ae0385c1-86f3-a223-47e3-16e2c8b3cea1.png)

### kubernetes クラスター作成

kubernetesクラスターを作成します。ユーザーガイドの[Hatoba（β）：クイックスタート]に操作方法を紹介していますのでぜひご利用ください。今回はニフクラ上で作成したサーバーからプライベートLANを経由してリクエストすることを想定し、

- プライベートLAN: `private`
    - DHCPサーバー(ルーター + DHCPコンフィグ): `router`
- ファイアウォールグループ: `fw`
    - `6443/tcp`
    - `32080/tcp`

を事前に作成しました。ファイアウォールグループは、ニフクラ上のサーバーからAPIをリクエストする・daprやkubectl 等で操作するためにINルールを設定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48903/a95e983c-3bad-3e2f-eb58-c3debfdd613e.png)

ニフクラでは、コンパネを利用することで kubernetes クラスターを簡単に作成することができます(今回はノードプールとして `medium` x 3 を設定しました)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48903/67f75d24-4a9f-52c1-cb2e-7355a2caf9da.png)

上記のように、ステータスが**稼働中**となりましたらクラスター作成完了です。最後に、作成したクラスターを選択し、**[選択したクラスターの操作] -> [クレデンシャル表示]** より `~/.kube/config` に設定することでkubectlなどでアクセスできるようになります。

```bash
docker run --rm -it -v ~/.kube/config:/root/.kube/config quay.io/derailed/k9s
```
などでkubernetesクラスターに接続できれば問題ないかとおもいます。

### kubernetesクラスターに [dapr] のインストール

マイクロサービスを実行するruntimeとしてdaprをセットアップします。事前に dapr の cli をインストールしてください。

```bash
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash
```
先程ニフクラ上で作成したkubernetesクラスターに [dapr] をインストールします。現在(2020/12/13)の安定版である `v0.11.3` を利用しています。

```bash
dapr init -k --enable-ha=true --enable-mtls=true --runtime-version=0.11.3
```

```bash
$ dapr status -k
  NAME                   NAMESPACE    HEALTHY  STATUS   REPLICAS  VERSION  AGE  CREATED
  dapr-sentry            dapr-system  True     Running  3         0.11.3   26s  2020-12-13 20:53.07
  dapr-dashboard         dapr-system  True     Running  3         0.3.0    25s  2020-12-13 20:53.08
  dapr-placement         dapr-system  True     Running  1         0.11.3   26s  2020-12-13 20:53.07
  dapr-operator          dapr-system  True     Running  3         0.11.3   26s  2020-12-13 20:53.07
  dapr-sidecar-injector  dapr-system  True     Running  3         0.11.3   26s  2020-12-13 20:53.07
```

### (traefikのデプロイ)

何でもいいんですが僕は [traefik] が好きなので、Helmを利用して IngressRoute を設定できるようにします。今回は `type: NodePort` で `32080/tcp` で待ち受けさせます。(Hatoba(β)にてクラスター作成時に `HTTPロードバランサー` を `利用する` に設定し、 `NGINX Ingress Controller` を利用することも可能です。)

```bash
helm repo add traefik https://containous.github.io/traefik-helm-chart
helm repo update
helm install traefik traefik/traefik --set ports.web.nodePort=32080 --set service.type=NodePort
```

### (redisのデプロイ)

- pub/sub
- state store

にて利用するためRedisを利用します。
(実際にサービス展開する環境では、高可用になるように Redis cluster や kafka、他のクラウドサービスに差し替えての利用をおすすめします。)

今回はとりあえず試せる程度でいいので、下記のマニフェストを kubernetes 上にセットアップします。

```yaml:redis.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis
  labels:
    app: redis
spec:
  ports:
    - port: 6379
      targetPort: 6379
  selector:
    app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: master
          image: redis:alpine
          ports:
            - containerPort: 6379
```

```bash
kubectl apply -f manifests/clock/override/develop/redis.yaml
```

### デプロイしたいアプリケーションの準備

今回は前回の記事([daprでつくるマイクロサービス])で作成した https://github.com/kzmake/dapr-clock を利用します。イメージは下記を利用します。

- [ghcr.io/kzmake/dapr-clock/clock](https://github.com/kzmake?tab=packages&repo_name=dapr-clock)
- [ghcr.io/kzmake/dapr-clock/hour-hand](https://github.com/kzmake?tab=packages&repo_name=dapr-clock)
- [ghcr.io/kzmake/dapr-clock/minute-hand](https://github.com/kzmake?tab=packages&repo_name=dapr-clock)
- [ghcr.io/kzmake/dapr-clock/second-hand](https://github.com/kzmake?tab=packages&repo_name=dapr-clock)
- [ghcr.io/kzmake/dapr-clock/ticker](https://github.com/kzmake?tab=packages&repo_name=dapr-clock)
- [ghcr.io/kzmake/dapr-clock/synchronizer](https://github.com/kzmake?tab=packages&repo_name=dapr-clock)

下記を `kubectl apply` することでアプリケーションのデプロイは完了となります :yum: 

```bash
git clone https://github.com/kzmake/dapr-clock.git && cd dapr-clock
kubectl apply -k manifests/clock/override/develop
```

以下は `kubectl apply -k manifests/clock/override/develop` の要素をピックアップして解説します。

#### コンポーネントの設定

コード中にて抽象化されているビルディングブロックのために、コンポーネントの定義を設定します。

- Redis を pub/sub
- Redis を statestore
- 1sec毎にをカウントするcron
- 24h毎に時刻同期するcron
- サービス間通信でmtls

を利用するために、

```yaml:pubsub.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
spec:
  type: pubsub.redis
  version: v1
  metadata:
    - name: redisHost
      value: redis.default.svc.cluster.local:6379
    - name: redisPassword
      value: ""
scopes:
  - hour-hand
  - minute-hand
  - second-hand
  - synchronizer
  - ticker
```

```yaml:statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
    - name: redisHost
      value: redis.default.svc.cluster.local:6379
    - name: redisPassword
      value: ""
scopes:
  - hour-hand
  - minute-hand
  - second-hand
  - synchronizer
  - ticker
```

```yaml:ticker.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: ticker
spec:
  type: bindings.cron
  metadata:
    - name: schedule
      value: "@every 1s"
scopes:
  - ticker
```

```yaml:synchronizer.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: synchronizer
spec:
  type: bindings.cron
  metadata:
    - name: schedule
      value: "@daily"
scopes:
  - synchronizer
```

```yaml:mtls.yaml
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprsystem
spec:
  mtls:
    enabled: "true"
    workloadCertTTL: "24h"
    allowedClockSkew: "15m"
```

を作成し、

```bash
kubectl apply -f pubsub.yaml
kubectl apply -f statestore.yaml
kubectl apply -f ticker.yaml
kubectl apply -f synchronizer.yaml
```
で設定していきます。 [dapr] では runtime としてこのコンポーネントを元に抽象化されたインターフェースに実装を注入しているようです。

#### マイクロサービス + dapr-sidecar

[dapr] を利用するマイクロサービス毎に sidecar として dapr を設定します。`clock` サービスを例に紹介すると、

``` yaml:clock.yaml
apiVersion: v1
kind: Service
metadata:
  name: clock
  labels:
    app: clock
spec:
  selector:
    app: clock
  ports:
    - port: 80
      targetPort: 3500
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: clock
  labels:
    app: clock
spec:
  replicas: 3
  selector:
    matchLabels:
      app: clock
  template:
    metadata:
      labels:
        app: clock
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "clock"
        dapr.io/app-protocol: "grpc"
        dapr.io/app-port: "3000"
        dapr.io/log-as-json: "true"
        dapr.io/log-level: "debug"
    spec:
      containers:
        - name: clock
          image: ghcr.io/kzmake/dapr-clock/clock:latest
          command: ["/go/bin/app"]
          ports:
            - containerPort: 3000
            - containerPort: 3500
          imagePullPolicy: Always
```
のように `spec.template.metadata.annotations` を記述し、

```bash
kubectl apply -f clock.yaml
```
で設定することで sidecar として [dapr] をデプロイできます。(`dapr-sidecar-injector` がanotationsに対応するdapr-sidecarを展開しているようですね)

### IngressRoute の設定

すべてのアプリケーションのデプロイが完了すれば、[時計サービス:timer:] として動作するはずなので最後に ingress を設定します。

```yaml:ingress.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: dapr-invoke
spec:
  addPrefix:
    prefix: /v1.0/invoke/clock/method
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: clock-ingress
spec:
  entryPoints:
    - web
  routes:
    - match: Method(`GET`) && Path(`/now`)
      kind: Rule
      middlewares:
        - name: dapr-invoke
      services:
        - name: clock
          port: 80
```
上記では traefik を利用し、 `GET /now` のリクエストを `GET /v1.0/invoke/clock/method/now` に流す設定を行いました！

これを、

```bash
kubectl apply -f ingress.yaml
```
して完了です！

## 🚀 リクエストしてみる

```bash
curl -s http://<NodeのIPアドレス>:32080/now | jq .
{
  "hour": 0,
  "minute": 15,
  "second": 42
}
```

バッチリ動作しているようですね！

## 🌜さいごに

どうでしたでしょうか？Hatoba(β) + dapr を利用することで

- Hatoba(β): マネージドkubernetesクラスターを構築
- dapr: ビルディングブロックとして抽象化されたアプリケーションの実行環境
- app: ビジネスロジックに集中して後はつくるだけ :hugging: 

とシンプルになった気がします！

[dapr] には、コンセプトにもあるとおり、**Any language, any framework, anywhere** であることから Any cloud or edge infrastructure な選択も可能になってます。
pub/subやstatestoreとして他のクラウドベンダーさんのサービスを利用しながら、マネージドkubernetesサービスとしてニフクラのHatoba(β)を利用することもできます。Any cloud なアプローチは今後さらに増えていくことになると思うので期待ですね！ :hugging: 

ぜひ、マネージドkubernetesサービスとして Hatoba を利用してもらえると嬉しいです :yum:

明日は、1日目の[ニフクラがTerraformに対応したので使ってみた【基礎編】](https://blog.pfs.nifcloud.com/20201201_terraform_provider_nifcloud) に続き、@miyuush さんが **ニフクラがTerraformに対応したので使ってみた【応用編】**を書いてくれるようです！
お楽しみに！

## 🔖 参考

- https://github.com/dapr/dapr
- https://doc.traefik.io/traefik/
- https://pfs.nifcloud.com/service/hatoba.htm
- https://qiita.com/kzmake/items/4136a11fa301cdec4ccb

[dapr]: https://github.com/dapr/dapr
[traefik]: https://doc.traefik.io/traefik/
[daprでつくるマイクロサービス]: https://qiita.com/kzmake/items/4136a11fa301cdec4ccb
[Hatoba（β）]: https://pfs.nifcloud.com/service/hatoba.htm
[時計サービス:timer:]: https://qiita.com/kzmake/items/4136a11fa301cdec4ccb#%E3%82%93%E3%81%A7%E3%81%AA%E3%81%AB%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%9F%E3%81%AE
[Hatoba（β）：クイックスタート]: https://pfs.nifcloud.com/guide/hatoba/quickstart.htm