研究室のHPをサーバレス、SPA、GraphQL、ChatOpsで作った

**Slack botが更新する研究室のHP**を作りました。

## できたもの
https://moriokalab.com
![image.png](https://qiita-image-store.s3.amazonaws.com/0/131581/831b8e3b-7243-ac02-4642-857dcbf610df.png)

Slack botが動いている様子
![image.png](https://qiita-image-store.s3.amazonaws.com/0/131581/aff8fda0-6b38-c2c7-b47a-d43e71bc09ec.png)


## なぜ作ることになったか
〜ある日〜
指導教員「研究室のHPが欲しいから作って。何も管理したくないからWordPressとかCMSはいやだ。Slackから投稿出来るようにしたい。」
後輩くん「分かりました」

〜その後〜
後輩くん「先輩・・・お願いします・・・」
俺「任せろ」

こうして我々はHPを作ることになった。

## 要件整理
使う技術を決める前に、まずは頂いた要件を整理します。

* webページを作る
* 動的に変更されるコンテンツがある
* データのリレーションは特になし
* サーバーやパスワードとか、**何も管理したくない**
* 研究室のみんなで更新したい
* **Slackからアップデート**できるようにしたい

なるほど。どう作るかぼんやりと見えてきました。

## 構成を考える
まず、**「何も管理したくない」**という要件から**サーバレス**にすることにしました。

そして**「動的に変更されるコンテンツがある」**のでデータをDBに保存して**webAPI**から追加参照変更削除できるようにします。

更にwebAPIをサーバレスで作るので**SPA**である必要がありますね。

最後に**Slack bot**からwebAPIを叩いてデータを操作できるようにします。 `@labbot news create <jsonファイル>` みたいなインターフェイスを想定します。
そういえばChatOpsってSlackの事例がめちゃくちゃ多い気がするのでSlackOpsですよねもはや。

こんな感じですね。この段階で思い描いた構成は下の図のような感じです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/131581/c386b423-c782-a48d-ee1b-7df191f053f4.png)

つまりここで手を動かす主な要素は以下の3つです。

* **フロントエンド**
* **webAPI**
* **Slack bot**

## 技術選択
さて、構成はだいたい決まったので次に具体的な技術を決めていきます。なおインフラは**AWS**を使いました。情報が多いので保守しやすいと思ったので。(私以外の人が保守する時は来るのだろうか)

### フロントエンド
Vue.jsかReactかで少し迷ったのですが最近までReactを書いていたので**Vue.js**にしました。CSSフレームワークは**Bulma**を使いました。

`vue-cli`で雛形作ってコード書いてビルドして**S3**にブン投げるだけの簡単な構成です。

#### Here Comes A New Challenger!
後輩くん「先輩、俺もなんかやりたいです」
俺「よかろう」

というわけで後輩くんと二人で**ペアプロ**しながらVue.jsでフロントエンドを作りました。

### webAPI
サーバレスなので`serverless framework`を使って**lambda**にブン投げました。

さて、webAPIなのですが、**GraphQL**を使いました。半分くらい趣味なのですが、一応以下のことを考えた上での選択です。

* リクエストとレスポンスに型を指定できるのでSlack botに変な値を投げてもエラーで落ちてくれる
* 要件にあんまり機能や仕様が多くないからFatになる心配がない
* 1つのlambdaで全ての処理をしたい(RESTだとGET/POST/PUT/DELETEで1つずつ必要)

node.jsでガリガリ書きました。AppSync?知らない子です。

### Slack bot
こいつもサーバレスで動かしました。以下の `serverless framework` のボイラープレートをES6に対応させてものを使いました。

[serverless-slackbot](https://github.com/johnagan/serverless-slackbot)

[asmsuechan/serverless-slackbot-es6](https://github.com/asmsuechan/serverless-slackbot-es6)

botのテスト書こうと思ってreply()の処理をモジュールに分割しようとしたらlambdaのパラメーター(`event`, `payload`, `context`)を毎回引数に入れることになって辛いからクラスのコンストラクタの引数に入れるようにするためにES6で書けるようにしました。

### DB
ここはいろいろ迷いました。結論としては素直に**DynamoDB**を使ったのですが、AuroraDBとか使ってみたかったです(高かった)。

「コネクションプール使い切っちゃうかもしれないけどどうせアクセスそんなに無いだろうし使い慣れてるからRDSにしよう」と思って最初作っていたのですが「そもそもデータにリレーション無いしちょっとお金かかるし(3000円/月くらい)やっぱりDynamoDBだな」と思いDynamoDBにしました。

[Lambda+RDSはアンチパターン](https://qiita.com/teradonburi/items/86400ea82a65699672ad)

[Serverlessを極めるためにDynamoDBデータモデリングを極めよう](https://speakerdeck.com/marcyterui/lets-become-the-master-of-dynamodb-data-modeling-to-become-the-master-of-serverless?slide=7)

## 最終的な構成
serverless frameworkが作成してくれるリソースについてはいろいろ省略しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/131581/6b9f9084-aa61-81e1-03fd-b713bfb3210a.png)

ちなみにデプロイ周りはtravisおじさんが担当してくれます。

## 開発の話
開発にまつわるよしなにごとです。

### テスト
〜最初の俺〜
「まあそんなに大きくないしテストいらねえや」
〜少し経った後の俺〜
「botがイベント駆動だからランタイムエラー拾いにくくてテスト無いと無理・・・・・」

ユニットテストを行うためにまず肥大化したbot本体のコードをモジュールに分割します。この中でES6を使いたくなったのでBabelを導入しました。テストを書くとイケてないコードが分かりやすくなって楽しいです。(イカしたコードになるわけではない)

### セキュリティ対策
Slack botがwebAPIを叩けるようにするためwebAPIをPublicで公開しています。しかしこれだと(エンドポイントとGraphQLのクエリが流出してしまった時に)誰でも記事を操作できるようになってしまいます。これを防ぐために作成・更新・削除には**簡単な認証機能**を付けます。

アラート付けてるし最悪不正アクセスあっても気付けるので(**過信は禁物!**)Slack botからのリクエストヘッダーに固定の文字列入れてwebAPI側で検証するようにしました。下の図のような感じです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/131581/38c368d0-1bb4-29ca-f311-28ad824c6688.png)

よく考えたらPublicなwebAPIとPrivateなwebAPIは完全に分けてAPI GatewayのAuthorize機能を使った方が楽ですね。管理コストとセキュリティリスクの釣り合いを考えて統合した構成のままなのですが、まあイケてないですね。

**参照を呼び出す人**と**更新を呼び出す人**とその**頻度**が完全に違うのに同じwebAPIってのはモニタリングしにくいし何か不都合が生じたら分割します。

ちなみにwebAPIのAPI Gatewayの前には**WAF**を置いています。 こいつを使い倒さなければいけない状況が来ないことを祈ります。
[Amazon API Gateway に AWS WAF のサポートを追加](https://aws.amazon.com/jp/about-aws/whats-new/2018/10/amazon-api-gateway-adds-support-for-aws-waf/)

## 雑談
とりとめもない話です。

### サーバレスとかGraphQLとかどうよ(webアプリケーションにおいて)
サーバレスは規模が小さいものを作るのにはとても適しているのですが、**DBの選択**は注意深く行う必要があります。

アプリケーションは、サービスが小さいうちはサーバレスで作って少し大きくなってきたらコンテナサービスとかに乗っければいいのですが、動いているサービスのDBはそう簡単に乗せ替えができません。ですので乗せ替えを前提とした設計をするか、絶対に回収できるという強い意志を持ってAuroraを使うかなどの選択に迫られます。ちなみに私がサーバレスで個人でwebサービスを作った時は「どうせ人いねえよwww」と思ってRDSにぶち込みました。まだ地獄は見ていません。

GraphQLはREST原理主義者だった私を変えました。GraphQLは楽しいです。しかしGraphQLは出てきてそこそこ時間が経っているはず(2012年くらいに登場したらしい)なのですが情報がそこまで多くありません。ディレクトリ構成などのベストプラクティスみたいなものが出てくれば最高です。

サーバレス+GraphQLはマイクロサービスなどAPIを小さく保ちたい場面に有効な構成だと思います。なぜなら**「管理の手間が大きく省けて、更に管理する対象が少なくなる」**からです。しかし小さなアプリケーションやローンチ初期のサービスや小さいチームなどはこの恩恵を受けることができるのですが、大規模なサービスでチームの規模が大きい場合はそこまでメリットに感じないと思います。

### 後輩くんの感想
ペアプロして一緒にHPを作った後輩くんの感想です。

```
初めてのペアプログラミングを通して学んだことは基礎の大切さです。
正直ペアプログラミングでやってもおそらく基礎がわからないと何もできないですね。がんばります。
```

わかる。

## その他
なんかたまに読み込み遅くね？
-> lambdaが寝てます。そのうち[pingbot](https://github.com/toricls/pingbot)とか使ってよしなにします。
-> (20200624追記) Fargateで動くようになったので読み込み早くなりました

IEでうまく表示されないんだけど
-> IE非対応というのが我々の総意です。

AppSync使えよ
-> 次こそは・・・

ロゴどうしたの
-> 後輩くんが作ってくれました。かっこよい。
-> (20200624追記) 実はロゴの中スペルミスがあるらしい

いくらくらいかかるの？
-> ドメイン代の1500円くらいだけで他の運用費は特にかかりません。(月のアクセス数によるけどそんなアクセスないはず・・・！)
-> (20200624追記) Lambdaメインの構成からFargateメインの構成に変えたのでランニングコストが月2万円くらいになりました。

なんでダンゴムシ？
-> ご自身の目でお確かめください。

## まとめ
このHPに興味を持ったネットワークデザイン学科の学生はぜひ森岡研究室に遊びに来てください。

同じようなwebページを作って欲しいって話があればsuenagaryoutaabc[at]gmail.comまでメールください。

お便りご意見ご感想怪文書などどしどし受け付けています。

## 追記
Fargateで動くようになりました。
https://moriokalab.com/news/52