chaliceを使って簡単にPythonでサーバーレスしよう

[Python Advent Calendar 2016](http://qiita.com/advent-calendar/2016/python_python)の5日目及び
[Fusic Advent Calendar 2016](http://qiita.com/advent-calendar/2016/fusic)の5日目の記事です。

はじめまして、seike460と申します。
普段は主にPHPとサーバー触っています。

ふと流行りのサーバーレスしたい！と思いましたが、
僕が懇意に使用しているAWSでは、PHPでサーバーレス出来ないのが現状です。
(正確には頑張れば出来ますが、その話は置いておきます。)

出来ないものはしょうがないので、他の言語でサーバーレスしようと思いたち、
最近魅力を感じているPythonを使ってサーバーレスする事にしました。

何も使用せずにLambdaとAPIGatewayの設定をしようとすると、
~~面倒臭すぎて気が狂いそうになるので~~ 効率が悪く、実運用に耐えられない印象を受けました。

そこで安心のAmazon公式 Python ライブラリ「chalice」を選択して実装しました。

- [awslabs/chalice](https://github.com/awslabs/chalice)

AWSアカウント持っているなら、Quickstartセクションを実行するだけで10分かからずサーバーレスHello World出来ます。
~~あと何と言っても厨二心を刺激する名前が最高に良い、聖杯ですよ!聖杯!~~
既にREADMEで導入方法が書かれていますので導入方法は省きます。

## 今回のゴール設定

今回は、APIGatewayにJsonを送信して、S3にJsonを保存することをゴールとしました。
データを受信するAPIを設置して、そのデータをS3経由で利用する事を想定しています。

<img width="608" alt="qiita-2016-12-05_1.png" src="https://qiita-image-store.s3.amazonaws.com/0/120576/d63aea70-e0b3-49d1-949f-ee3dc3b565ec.png">

## 開発環境
OS：macOS v10.12(Sierra)
Python:2.7.10

## AWS側の準備

まず、chalice用のIAMロールを用意します。
今回は特に権限に拘らず設定していきます。ひとまずエイヤって権限振ります。
**※実際は考えて設定すべきです!自己責任!!**

- AWSLambdaFullAccess
- IAMFullAccess 
- AmazonS3FullAccess
- AmazonAPIGatewayAdministrator

↓こんな感じです。

<img width="709" alt="qiita-2016-12-05_2.png" src="https://qiita-image-store.s3.amazonaws.com/0/120576/c8bf6a05-12f4-3f1f-03f9-6d5daa486e04.png">

その後、開発環境にcredentialsを設定します。

```vim:~/.aws/config
[default]
aws_access_key_id=【見せられないよ！！！】
aws_secret_access_key=【見せられないよ！！！】
region=ap-northeast-1 ←Tokyoリージョン
```

Hello world出来る準備が整いました。

あとAWS触るので、boto3も入れておきます。
この時virtualenvsを使う場合はchaliceを使用する環境にインストールしましょう。

```zsh:boto3
(chalice) $ pip install boto3
```

- 参考
    - [boto3](https://boto3.readthedocs.io/en/latest/)
    - [virtualenvs](https://virtualenv.pypa.io/en/stable/)

## Let's hello world
save2S3プロジェクトを作成します。

```zsh:new-project
(chalice) $ chalice new-project save2S3
```

すると以下の様なディレクトリ構成が作成されます。

```zsh:save2S3
save2S3
├── .chalice
│   └── config.json
├── .gitignore
├── app.py
└── requirements.txt
```

このapp.pyを編集してサーバーレスするわけです。
app.pyを開いて見ましょう

```py:app.py

from chalice import Chalice

app = Chalice(app_name='save2S3')


@app.route('/')
def index():
    return {'hello': 'world'}


# The view function above will return {"hello": "world"}
# whenver you make an HTTP GET request to '/'.
#
# Here are a few more examples:
#
# @app.route('/hello/{name}')
# def hello_name(name):
#    # '/hello/james' -> {"hello": "james"}
#    return {'hello': name}
#
# @app.route('/users', methods=['POST'])
# def create_user():
#     # This is the JSON body the user sent in their POST request.
#     user_as_json = app.json_body
#     # Suppose we had some 'db' object that we used to
#     # read/write from our database.
#     # user_id = db.create_user(user_as_json)
#     return {'user_id': user_id}
#
# See the README documentation for more examples.
#
```

既にhello world出来そうな雰囲気が凄いですね。
chaliceがhello worldしたそうなので、早速AWS上にデプロイしましょう。

```zsh:deploy
(chalice) $ chalice deploy

Initial creation of lambda function.
Creating role
Creating deployment package.
Lambda deploy done.
Initiating first time deployment...
Deploying to: dev
https://【あなた固有のPath】.execute-api.ap-northeast-1.amazonaws.com/dev/
```

なんとも簡単にデプロイされました。初期設定だと/devというPathに作成されます。
apiというPathに作成したい場合、以下のコマンドでOKです。

```zsh:deploy_to_api
(chalice) $ chalice deploy api
```

早速URLにアクセスしてみます。

```zsh:deploy
(chalice) $ curl https://【あなた固有のPath】.execute-api.ap-northeast-1.amazonaws.com/dev/
{"hello": "world"}
```

これであなたのchaliceデビューは完了です。
隣の人に「俺、聖杯使えるぜ」と自慢しましょう。
**結果キモがられても僕のせいじゃないです。自己責任**

## ゴールに辿り着くコード
こまけえことはいいんだよ！って感じで、ちゃっちゃかコード書いちゃいます。

```py:app.py
from chalice import Chalice
import boto3
import json

# for S3
clientS3 = boto3.client('s3')
yourBucketPath = '【ここにあなたのバケットを書くんだ！】'

app = Chalice(app_name='save2S3')

@app.route('/save', methods=['POST'], content_types=['application/json'])
def save():
  saveJson = app.current_request.json_body
  if (validateKey(saveJson) == False):
    return {'error':'please input key'}
  SavePath = saveJson['key'] + '.json'
  clientS3.put_object(Bucket=yourBucketPath, Key=SavePath, Body=json.dumps(saveJson, ensure_ascii=False))
  return {'save':saveJson['key']}

def validateKey(saveJson):
  if (saveJson.has_key("key") == False):
      return False
  return True
```
最低限のKeyValidateだけして保存しちゃいます。

こいつ確認するためのコードも、ちゃっちゃか書きます。
**※ここはpython3で書きました。** (lambdaのpython3対応はよ　)

```py:postJson.py
import urllib.request
import json

url = "https://【あなた固有のPath】.execute-api.ap-northeast-1.amazonaws.com/dev/save"
method = "POST"
headers = {"Content-Type" : "application/json"}
obj = {"key" : "seike460", "val" : "オレ、聖杯使えるぜ"}
json_data = json.dumps(obj).encode("utf-8")

request = urllib.request.Request(
  url,
  data=json_data,
  method=method,
  headers=headers
)
with urllib.request.urlopen(request) as response:
  response_body = response.read().decode("utf-8")
print(response_body)
```
こいつを実行すると **※python3コマンドです!!**

```zsh:deploy
$ python3 postJson.py
{"save": "seike460"}
```

S3にもバッチリ保存されているはずです。
ここまでくれば、もうあなたはサーバーレスラーです。
思う存分サーバーレスしましょう。
弊社でこの段階でサーバーレス出来ますぶると、
弊社サーバーレス軍曹にこってり理論的に説き伏せられそうですので、僕はやめておきます。

## まとめと所感

今回はサーバーレスをする目的で、PHPerがPythonを使ってみました。
その中で、

* Python書きやすい！
* 機械学習とか流行ってるし楽しそう！
* とりあえず基本どのOSも最初からPython入ってるからいざという時に便利そう(ほぼ2.7だけど)

と感じており、今回はAWSに寄った話になってしまいましたが、
これからもPythonネタで何か書いてみようかと思います。

Djangoを触ってみたいな