ほぼ無料（完全サーバレス）でガチャァをまわせるようにした。Slack上で。

アドベントカレンダー二本目です。
改めて、@morin_river です。
完全に無計画な進め方のせいで、当日に大慌てでアドベントカレンダーを書いてます。
果たして、間に合うのか。
寝るまでが12/8日だ！！

# はじめに

みなさん、ガチャ好きですよね？
ですが、ガチャを回すと当然お金がかかる……。
そこで、お手軽にslack上でガチャを回せるようにしてみました。
極力お金を掛けないような仕組みで。

# デモ

こんなふうにSlack上で使えます。

![スクリーンショット 2018-12-09 05.27.20.png](https://qiita-image-store.s3.amazonaws.com/0/146781/72896dc8-fc9b-f5e6-ad6c-d74fe7ac2904.png)

……マイルド、かつ著作権に配慮した、結果さらに凶悪な感じが。

# 使っている技術スタック

さて、ここから真面目な話です。

Amazon CloudFront
Amazon API Gateway
AWS Lambda
Python
Pillow（PIL・Python Image Libraryのフォーク)
Serverless Framework 
Ansible
Slack Webhook

# 構成図

![Untitled Diagram (1).png](https://qiita-image-store.s3.amazonaws.com/0/146781/d7e45532-1164-f157-2c55-89b04ad7e1c3.png)

雑ですが、構成図はこのようになります。
また、サンプルは以下となります。
https://github.com/cahlchang/gachalink

# 解説
## Amazon CloudFront

とてもお手軽で有名なAWSのCDN。
ですが、今回は**CDNの役割ではなく、特定ヘッダーを付ける為だけに利用**します。
理由は後述。

また、最近CloudFrontからLambdaを直接コール出来る機能が実装されました！
（Lambda@Edge）
ですが、現時点ではリージョンに制限があるのと、NodeJSでしか動かないようなので
今回はAPI Gatewayを使います。
（また、Lambda@Edgeがバイナリを返せるかまでは見れていません）

## API Gateway

Cloud FrontとAWS Lambdaの接続用に使います。
ルーティングとかGatewayとして色々使えるのですが、今回は一点のみ。
Lambdaから、バイナリオプションを使う為に利用します。
細かい~~ちゃんとした~~使い方は、前に書いた記事を参照して下さい。
~~私が今回忘れていたので、過去の記事を参考に実装しました~~
https://qiita.com/morin_river/items/0662a939d336028b483f

## Ansible

ここまでの処理を、コード化してわかるようにAnsible化しておきます。
https://github.com/cahlchang/gachalink/blob/master/site.yml
キモとなるのは、cloudfrontのデプロイ時に以下のヘッダーを付けている事ですね。

```
            custom_headers:
              - header_name: Accept
                header_value: image/png,image/jpeg,image/gif
              - header_name: Content-Type
                header_value: image/png,image/jpeg,image/gif
```
このHTTPヘッダーがないと、API Gatewayから帰ってきた画像をSlack上で表示させる事ができません。
（Slackのクライアント上で画像を展開するにはこの２つのヘッダーが必要です）。
https://github.com/cahlchang/gachalink/blob/master/templates/swagger_spec.yml#L74-L76
また、API Gatewayでバイナリを使う事を明示する必要があります。

……結構、たどり着くまで大変だったのですが、コード化すると簡単そうに見えますね。

## AWS Lambda 

単純な処理系として使っています。
https://github.com/cahlchang/gachalink/blob/master/handler.py#L49-L55
Lambdaで、しっかりと覚えておかないのはレスポンスの値でしょう。

```
"isBase64Encoded": True
```
このパラメータをレスポンスに加えることで、base64で返却したbodyの値を画像としてAPI Gatewayが認識できるようになります。
あと、ガチャの処理とかが超雑ですが、これはまぁ……いつかなんとかしようと思って放置していますね。
きちんとするならソースにハードコートするのではなくデータストアに入れないといけないのですが
今回の目標は、出来るだけ安く。
その為に敢えてこのような形を取っています。
保守や運用を考えず、値段と性能だけで見たらソースコードに直埋め込みが一番ですからね……。
（インフラエンジニアとしてはどうなんだ、という話なのですが）。
名前が、アルファとかベータとかなのは、この資料書く時の後ろで流していたアニメの影響ですね……。
~~大当たりはSGにすべきだったかな~~

Aurora Serverlessは、お安く使った分の課金になりそうなので
気合があったら使ってみるかもしれません。

また、稀に（？）CloudWatch上だとeventの値がNoneになっているようです。
（デプロイ直後の1アクセス目？）
この辺りも無駄にハマったのですが、ちゃんとした場所で使う場合は、ちゃんとしておいた方が良いかもしれません。
（2アクセス目以降は出て無さそうなので、まぁいいかと放置しています）

## Python + Pillow

ここでは、PILの更新が止まっているようなのでPillowライブラリを使おう、という事ぐらいでしょうか。
本当は、もうちょっとちゃんとした画像処理にしようと思っていたのですが、なんやかんやで……。
コメントにしている部分は
いつかちゃんとしたコードにしていい感じの画像を返すようにしよう、と思ってはいます。
気持ちだけは。

## Serveless Framework
Lambdaのデプロイ用に使いました。
色々な候補はあるのですが、今回PythonのAmazonLinuxの共用ライブラリが必要だったため
Serveless Frameworkにしました。
（本当はシェルスクリプトを叩くぐらいでもいいのかな、と思っていたのですが）。
https://github.com/cahlchang/gachalink/blob/master/serverless.yml#L12-L13
ここで指定する事によって、いい感じに共用ライブラリをまとめてパッケージングしてくれます。
まぁ、本当はIAMとかもServeless Framework使わずに管理したかったのですが
その辺りも含めてアプリケーションのデプロイという事で、今回はまとめちゃっています。

# 終わりに

駆け足でしたが、いかがでしょうか。
このような仕組みを整える事で、**ほぼ**無料でガチャを回し続ける事が出来ます。
よっぽど酷い回し方をしない限りは、無料枠の中に収まるのではないでしょうか。
（前に試算した時は月に2万回ぐらい回さないと料金は発生しない計算だった気がします）

これで、実質お金をかけないで永遠とガチャを回し続ける事が出来るようになりました。

……また、私はこのガチャを回して当たりが来たら「なんか来た！」と思ってしまい
結果課金額が上がってしまいました。
そして、ガチャの女神かなんかに怒られたのか、この仕組みを作ったあと
星5レアが一体も出なくなりました。
やっぱり、ガチャは回すもので作るものじゃないかもしれません。

（補足でフェアの為に言っておきますが、元ゲーム業界の人間の発言です）

## ちゃんとした終わり

こういう無駄に新し目の技術だけで組むのはやっぱり楽しいな、と思いました。
また、今回のアドベントカレンダーに向けてきちんとコード化したのですが
色々忘れている事が多くてインフラのコード化は大事だな、と改めて実感しました。
（バイナリをAPI Gatewayで取り回すのや、CloudFrontは普段使わないのですっかりと忘れていました）。
ネタっぽい投稿ですが、色々と再認識出来たのでよかったな、と思いました。