サーバレスで動く研究室のHPを1年間運用してみて

**指導教員の無茶振り**で作り始めたサーバレス+SPA+GraphQL+ChatOpsで研究室のHPが完成してからだいたい**1年**が経過しました。(参考: [研究室のHPをサーバレス、SPA、GraphQL、ChatOpsで作った](https://qiita.com/asmsuechan/items/17f168f151346ac5cf65))研究室という、人が数年でほとんど入れ替わる特殊な組織内でどのようにHPを**継続的に運用**していったのか振り返ります。

## HPの概要
Slack Botで更新する研究室のHPです。YAMLを添付して`@labbot posts create`のようにメンションを飛ばすと記事が作成されます。更新、削除もできます。

https://moriokalab.com

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131581/c06dedc5-134e-ac79-de16-f0531087c4cc.png)

このHPは、**(1) 研究室のみんなが更新できる**, **(2) 運用、ID/PASS周りの管理コストを最小限にする**, の2点を要件として開発したものです。

大きく分けて**フロントエンド、サーバーサイド、Slack Bot**の3つの部分で構成されており、フロントエンドはNuxt(Vue.js)、Buefy、AtomicDesign、ApolloClientなど、サーバサイドはNodejs、GraphQL、DynamoDBなど、Slack BotはNodejsやjestなどから作られています。そしてこれら全てのインフラを**serverless framework(Lambda)**で動かしています。以下の図は現在のシステム構成概要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131581/2c24768f-62d0-724c-e2ad-45b3b9052363.png)

＿人人人人人人人人人人人人人＿
＞　**全部sererless framework**　＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣

serverless frameworkの開発者達には足を向けて寝られない。

## 数字で見るこの1年間
### (1) アクセス数
GAでトラックしていたのでそのキャプチャです。去年書いた記事がバズった時以外はだいたい**週20アクセス前後**です。いや20人も誰が見とんねんって感じですが。
![image (15).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131581/d93003d3-5e44-3137-e49c-2e505d0dcac5.png)

### (2) 運用費
運用費はRoute53とS3が毎月\$0.5ずつかかって年間\$12です。ドメイン代の\$12を合わせても**年間\$24**で運用できています。とても財布に優しいですね。

たぶん月100万アクセスくらいまではこんな感じの値段になると思います。

### (3) 記事数
週一で記事を追加するので4 x 12 = 48になるはずですが、長期休暇等があって書いてない人もいるので**37件**でした。思ったより多くて満足しています。

## HPの成果
一番大きな目的は、学科内の**研究室配属**の時に「うちの研究室はこんな**アクティビティ**があるよ」というのを紹介するというものでした。これは去年の研究室選択の時期に指導教員がみんなに見せていたので**役割は果たせた**と思います。

あと、うちの研究室に院試受けて入ろうという人の参考にもなったみたいです。

## この1年の開発
以下はこの1年で行った開発に関する取り組みです。

* (1) Vue.jsのSPAから**NuxtでのSSRへリプレイス**
* (2) 毎週担当者をアサインするBotの稼働
* (3) 投稿前にインタラクティブに内容を確認できる
* (4) **Algolia**での記事検索
* (5) **指導教員に内緒でシークレットモードを追加**
* (6) serverless slack botの**バグ修正**

そういえば、今でもちょくちょく指導教員から「ここ**機能追加**して欲しいです」みたいな要望があって興が乗った時にガリガリ作っているのですが、しばらく触っていなくてもすんなり触れる感じのコードになっていて(自分が書いたので当たり前といえば当たり前だけど)大変喜ばしい限りです。

### (1) Nuxtへのリプレイス
上の記事の通り、最初は**Vue.jsによるSPA**として動かしていたのですが、以下の辛みを感じたので勢いに任せてNuxtへ置き換えました。

* 頭を使わずにSPAしてたからレスポンスが返ってくるまでの**0.5秒くらい何も表示されない**
* SPAだから記事ごとに**OG設定できない**
* 勢いだけで書いたから**かなり雑**でテストが書きにくい(リプレイスの後押しとなった要因)

書いて動かしてる感触なんですが、NuxtはプレーンなVue.jsで書くよりも**適度な規約があってとても書きやすかった**です。

なお、このNuxtアプリケーションについてもLambdaの上で動かしています。NuxtをLambda上で動かす仕組みは[Nuxt.js on AWS Lambda with Serverless Framework](https://mya-ake.com/posts/nuxtjs-on-aws-lambda/)とほとんど同じ物を採用しました。

### (2) 週次アサインBotの作成
研究室の仲間たち(合計20人くらい)で継続的に更新を続けていくために、週に一回担当者を決めて更新してもらうようにしました。この週一の担当者決めはSlack Botが担当します。

これも**サーバレス**で動いています。去年のre:InventでLambdaのRubyサポートが始まったので試してみたかったのでRubyで書きました。

### (3) 投稿前の確認
ある日研究室で隣の席の人が**「投稿時に確認画面が欲しい」**と言ったのでガガっと実装したのがこの機能です。

こういう風に、Slack BotにSnipetとして送信すると**プレビュー画像と共に「これ本当に投稿する？キャンセルする？」の2択を迫ってくる**ようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131581/d98a2e76-d86e-f041-4066-4c055c48ef65.png)

投稿プレビューの仕組みは**擬似的**なもので、実は**プレビュー段階で本番環境にデータが作成されます**。そしてPublishボタンを押すと作成済みデータの`published_at`に現在時刻が代入されてニュース一覧で見られるようになり、Cancelボタンを押すとこのデータが削除されます。

つまり**プレビュー時はURL直打ちだと見える**状態になっており、プレビューのスクリーンショットはここに直接アクセスして取得したものを返すようにしています。

ここで必要になったのが、**任意のWebページのスクリーンショットを撮影してその画像を取得する**という仕組みです。

#### スクリーンショット撮影部分
欲しかったのは**「あるURLを渡すとそのサイトのスクリーンショットが返ってくる」**機能だったので、作りました([**asmsuechan/sshot**](https://github.com/asmsuechan/sshot))。

これも**サーバレス**で動かしています。ヘッドレスchromeを無理やりLambdaに押し込んでAPI Gateway経由で呼ぶような形式です。

以下のURLは公開されているもので、誰でもアクセスすることができます。あ、このままだとスクショは研究室のAWSアカウントのS3に保存されるので、保存されたくない方は自分で立てるかbase64オプション使うかで。

```shell
$ curl "https://vckvs9l162.execute-api.ap-northeast-1.amazonaws.com/production/screenshot?url=https://github.com/asmsuechan"
{"screenshot":{"url":"https://s3-ap-northeast-1.amazonaws.com/sshot-images/kvd6ajor2hbprpb9.png"}}
```

urlをパラメータに入れたcurlを投げるとそのサイトのスクリーンショットがアップロードされたS3のURLが返ってきます(このバケットは研究室のAWSアカウント上のものです)。また、base64パラメータをtrueに設定する事によって**base64**にエンコードした画像を返すようにする事もできます。

また、設定ファイルをよしなにいじる事で**自分のAWSアカウント上にデプロイ**することもできます。

開発で一番大変だったのが**日本語フォントへの対応**で、かなり無理くりやって何とか対応しました。が、上の画像を見る通り**フォントがダサい**のでここもうちょっとどうにかならんかったんか・・・って感想です。

### (4) Algoliaでの記事検索
Algolia使ってみたくて、なんとなく興が乗った時に**記事検索機能**を https://moriokalab.com/news に追加しました。誰が使うんだろう、って感じですが。。。

NuxtへのAlgoliaの導入は[公式のexample](https://github.com/algolia/vue-instantsearch/blob/master/examples/nuxt/pages/search.vue)と公式ドキュメント以外特に参照せず実装できました。すごく簡単。**Algoliaめちゃくちゃ速くてびっくりです。Algoliaすごい。**

### (5) 指導教員に内緒でシークレットモードを追加
トップページにある**指導教員の顔を30回クリック**すると**シークレットモード**が発動し、**イカしたデザイン**になります。**なお画面から元に戻す方法はありません**。戻したい方は`LocalStorage`でそれっぽいフラグをdeleteしてください。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">研究室のHP、指導教員には内緒で指導教員の顔を30回クリックしたらイカしたデザインになるようにした <a href="https://t.co/kdxvheWfqh">pic.twitter.com/kdxvheWfqh</a></p>&mdash; asmsuechan (@suenaga_ryota) <a href="https://twitter.com/suenaga_ryota/status/1131462880086220800?ref_src=twsrc%5Etfw">May 23, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### (6) serverless slack botのバグ修正
Slack Botは[johnagan/serverless-slackbot](https://github.com/johnagan/serverless-slackbot)を元に作ったのですが、画像を2回以上連続でSlackにアップロードするとBotが落ちてしまうという問題がありました。最初は放置していたのですがあまりにも不便だったので原因を探したところserverless-slackbot側のバグだったので直してPRを送りました。
https://github.com/johnagan/serverless-slackbot/pull/3

## サーバレスで良かったこと・悪かったこと
良かったことは

* serverless frameworkを使うとコマンド1つで**簡単にデプロイ**できる
* インフラの**メンテコストがかからない**
* **料金が安い**

悪かったことは

* **ローカル環境構築**が大変
* **DB選択**が悩ましい
* **初回アクセスが遅い** 

1年経ちましたが、あんまりずっと面倒を見続けるようなシステムではないので**サーバレスを選択して良かった**な、と思っています。

## 運用を振り返る
また、更新操作を**Slackで完結**させることで、CMSと比べて誰でも手軽に更新できる仕組みとなったのが良かったです。CMSだとどうしてもID/Passの問題があって、ここの管理が面倒でだんだん更新されなくなる未来が見えていました。

また、サーバレスな構成にして**サーバーサイドのインフラの面倒を見なくても動き続けるようにした**ので**「管理/運用したくない」という目標は達成した**と思います。

## 更新を続ける仕組み
研究室の愉快な仲間たちみんなで更新していくための仕組みとして、**毎週月曜日の18時にその週の更新担当者をアサインするCronを動かす**ようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131581/81d0c783-3cce-e415-54ad-3baeb537c64b.png)

これ最初は「みんな無視するんじゃね」って思ってたんですが案外そんなことはなく、当てられた人は結構マジメに更新してくれています。

## 打ち捨てられないための仕組み
このようなHPというのは**作ったっきりで打ち捨てられてしまう**ことがままあります。これはだいたいの場合**「めんどくさい」**という気持ちと**「優先度が低い」**という状況のどちらか、またはどちらもが原因になります。この辺りで意識したことについて運用面と開発面の両方で書きます。

### 運用面
Botに指名されているのにも関わらずHPの更新をしていない人には指導教員が面談中に「書いてくれ」と言っていたことで、(その人の中での)優先度をあげていました。とにかくあたった人に、**書くまでpingを送るという戦法**ですね。

なるべく**記事のハードルを下げる**というのも気をつけたことです。目的は「アクティビティがあることを示すこと」なので内容のクオリティはそもそも問うていません。**クオリティは必要になってから**考えます。

あ、あと、こういうのはやる気がありすぎても飽きて打ち捨てられてしまう事があるのでほどほどのやる気で取り組んだというのも良かったと思います。指導教員に「これ欲しいから作って」と言われた時に「興が乗ったら作ります」と言って数カ月後ガリガリ作り出す、みたいな感じのゆるさ。

### 開発面
開発上では研究室のGitHubオーガナイゼーションを作りそこに全てのコードを置いてどこからでもコマンド1つでデプロイできるような仕組みを最初から作り、**デプロイがめんどくさくてコードいじる気がなくなる**という状況を緩和しました。

そしてコードも最初から予めある程度の**決まった枠組み**を作り、何か機能を追加する時には既存のものを見ながら**思考停止でも基本はコピペで動くものができるようにすることで「コードの書き始めのだるさ、めんどくささ」を軽減**しました。既存システムへの機能追加って、どうしても**「壊すかもしれない」**という気持ちがあって最初は億劫なものなので。最初さえ頭を使わずサクッとできればあとは流れに乗って書けるはずです。

## 現状の問題点
去年も同じこと言ってたじゃねえか！って話もありますが、今の問題点です。

### (1) ローカル環境が適当
インフラのローカル環境が全然構築できていなくて、毎回デプロイしてテスト用Slackチャンネルで直接いじるという形にしていて非常にめんどくさいです。いい感じにDocker環境立ててやるとうまくできるのでしょうが、そこまでやる体力がなくてそのままにしています・・・

### (2) 後任の不在
研究室は人間の流動性が高く、1人の学生がずっと面倒を見続けるわけにはいきません。このHPももちろんそうなのですが、指導教員と話していてこれから先Web人材が研究室に入ってきて自分の残したコードを読み保守運用してくれる人が現れる確率は低そうという結論になったので、卒業後はたまに報酬**(うまい焼肉)**を貰いながら私が運用していくという感じの話をしています。

### (3) LambdaでSSRすると初回アクセス遅い
Lambdaがcold standbyしている時にアクセスすると表示に5秒くらいかかってしまうという残念な問題があります・・・解決策は10分に1回だかヘルスチェック回して常にHot standbyな状態にしてやることです。そんなに難しくはないと思うのですが気持ちが乗らないので手をつけていません。

### (4) UIの改善は特になし
やはり仕事のコードではないのでUI上の細かい部分はおざなりになりがちです。どこかであまり良くない部分を改善したいとは思っているのですが後回しにしています。もっとカッコよくしていきたい。

## まとめ
この手のHPの目的はだいたい**「自分たちの組織がマトモに活動していることを対外的にアピールして印象を悪くしないこと」**であることが多いので、作って公開することより**公開して更新し続けることがよっぽど重要**だと思います。ですのでここで書いた**継続する仕組み**や**打ち捨てられないための仕組み**というのが、私と似たような状況に置かれた人の助けになれば幸いです。