zappa で hello world するまで

zappa を使って hello world を試してみます。

# zappa とは

https://github.com/Miserlou/Zappa

aws で serverless python web service を実現する flask ベースのフレームワークだそうです。
api gateway + lamdba + cloudwatch events を駆使する模様。

デモの gif 画像が刺激的ですね。

---

# flask とは

http://flask.pocoo.org/

wsgi + jinja2 をバックエンドとした python web application のための microframework とのことです。
first commit が 2010 年となかなか歴史があります。 
公式の top ページのまんまですが、以下のコードで http による hello world ができます。

```py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!\n"

if __name__ == "__main__":
    app.run()
```

```
$ pip install Flask
$ python hello.py &
 * Running on http://localhost:5000/
$ curl http://localhost:5000
Hello World!
```

# 環境構築

今回は vagrant 上の centos7 で動作させてみようと思います。
zappa は virtualenv が必須ですので、その辺りからやってみます。
なお、`vagrant` ユーザで進めていきます。

## 必要なパッケージのインストール

普通に yum でインストールします。

```bash
sudo yum install -y gcc libffi-devel python-devel openssl-devel zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel git
```

## pip のインストール

pip はシステムワイドにインストールしておきます。

```bash
curl -L https://bootstrap.pypa.io/get-pip.py | sudo python
```

## pyenv + virtualenv

pyenv と virtualenv をインストールします。
両方一気にインストールできる素晴らしいスクリプトがありますのでありがたく使わせていただきます。

```bash
curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash
cat << 'EOF' >> ~/.bash_profile

### virtualenv
export PATH="/home/vagrant/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
EOF
```

pyenv と virtualenv については以下のサイトがわかりやすかったです。
http://blog.ieknir.com/blog/pyenv-with-virtualenv/

## virtualenvwrapper

便利そうなので以下の qiita を参考に導入してみます。
http://qiita.com/drafts/505492193317819772c7/edit

```bash
sudo pip install virtualenvwrapper
export WORKON_HOME="~/python-venv"
mkdir -p $WORKON_HOME
virtualenvwrapper_sh=$(whereis virtualenvwrapper.sh | cut -d : -f2)
echo """
if [ -f ${virtualenvwrapper_sh} ]; then
    export WORKON_HOME=$HOME/.virtualenvs
    source ${virtualenvwrapper_sh}
fi
""" >> ~/.bash_profile
source ${virtualenvwrapper_sh}
```

ここまでを [github](https://github.com/rriifftt/zappa-vagrant-init) に置きました。

## python のインストール

```
### install python
pyenv install 2.7.12
pyenv local 2.7.12
```
2.7.12 になっています。

```
$ pyenv versions
  system
* 2.7.12 (set by PYENV_VERSION environment variable)
```

# zappa のセットアップ

## mkvirtualenv

mkvirtualenv で専用の環境を作ってみます。
プロンプトが変わってオシャレです。

```
vagrant 07:42:55 ~$ mkvirtualenv zappa_2.7.12
(zappa_2.7.12) vagrant 07:42:55 ~$
```

セットアップしてみます。

```
$ mkdir app
$ cd app
$ cat << EOF >> requirements.txt
zappa
flask
EOF
$ pip install -r requirements.txt
$ zappa init

███████╗ █████╗ ██████╗ ██████╗  █████╗
╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗
  ███╔╝ ███████║██████╔╝██████╔╝███████║
 ███╔╝  ██╔══██║██╔═══╝ ██╔═══╝ ██╔══██║
███████╗██║  ██║██║     ██║     ██║  ██║
╚══════╝╚═╝  ╚═╝╚═╝     ╚═╝     ╚═╝  ╚═╝

Welcome to Zappa!

Zappa is a system for running server-less Python web applications on AWS Lambda and AWS API Gateway.
This `init` command will help you create and configure your new Zappa deployment.
Let's get started!

Your Zappa configuration can support multiple production environments, like 'dev', 'staging', and 'production'.
What do you want to call this environment (default 'dev'): 
```

なんか出た。
環境名を求められていますが、dev としておきます。

```
Your Zappa deployments will need to be uploaded to a private S3 bucket.
If you don't have a bucket yet, we'll create one for you too.
What do you want call your bucket? (default 'zappa-*****'):
```

lambda にデプロイするための S3 バケット名を求められていますが、ここはデフォルトにしておきます。

```
It looks like this is a Flask application.
What's the modular path to your app's function?
This will likely be something like 'your_module.app'.
Where is your app's function?: 
```

test.app としておきます。

```
Okay, here's your zappa_settings.js:

{
    "dev": {
        "app_function": "test.app",
        "s3_bucket": "zappa-******"
    }
}

Does this look okay? (default y) [y/n]: y

Done! Now you can deploy your Zappa application by executing:

	$ zappa deploy dev

After that, you can update your application code with:

	$ zappa update dev

To learn more, check out the Zappa project page on GitHub: https://github.com/Miserlou/Zappa
or stop by our Slack channel: http://bit.do/zappa

Enjoy!
```

初期化が完了しました。

## zappa_settings.json

デフォルトで region が us-east-1 だったので、編集します。
また、 credentials で profile を分けているのであれば profile_name で指定します。

```
$  cat zappa_settings.json
{
    "dev": {
        "app_function": "test.app",
        "s3_bucket": "zappa-*****",
        "aws_region": "ap-northeast-1",
        "profile_name": "profile_name" //必要あれば
    }
}
```

## サンプルの配置

[こちら](https://github.com/Miserlou/Zappa/blob/master/example/app.py) のサンプルを動かしてみたいと思います。

```
$  cat test.py
import logging
from flask import Flask

app = Flask(__name__)
logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

@app.route('/', methods=['GET', 'POST'])
def hello(event=None, context=None):
    logger.info('Lambda function invoked index()')
    return 'hello from Flask!\n'


if __name__ == '__main__':
    app.run(debug=True)
```

フォルダ構成はこのようになりました。

```
~/app$  tree
.
├── requirements.txt
├── test.py
└── zappa_settings.json

0 directories, 3 files
```

## aws credential の設定

aws にアクセスするので、以下のような方法で credential を設定します。

* awscli をインストールして `aws configure` する
* `~/.aws/config` を作成する


## deploy ！

いよいよ deploy です。

```
(zappa_2.7.12-2) vagrant 08:05:26 ~/app$  zappa deploy dev
Packaging project as zip...
Uploading zip (2.7MiB)...
100%|█████████████████████████████████████████████████████████████████████████████████████████| 2.81M/2.81M [00:00<00:00, 2.97Mit/s]
Scheduling keep-warm..
Creating API Gateway routes (this only happens once)..
1008it [01:39, 10.17it/s]
Deploying API Gateway..
Deployed! https://*****.execute-api.ap-northeast-1.amazonaws.com/dev
```

叩いてみます。

```
(zappa_2.7.12-2) vagrant 08:07:45 ~/app$  curl -l https://*****.execute-api.ap-northeast-1.amazonaws.com/dev
hello from Flask!
```

でた！

## ログの確認

`zappa tail dev` で cloudwatch logs の確認ができて便利です。
(なにやらエラーがでてる。。)

```
[1470384650490] [DEBUG] 2016-08-05T08:10:50.490Z    1f3ccb88-5ae4-11e6-b4c6-c9ecf09594ff    Zappa Event: {u'body': u'e30=', u'headers': {u'Via': u'1.1 9736f79fa942ea72a1eee114f49093dd.cloudfront.net (CloudFront)', u'CloudFront-Is-Desktop-Viewer': u'true', u'CloudFront-Is-SmartTV-Viewer': u'false', u'CloudFront-Forwarded-Proto': u'https', u'X-Forwarded-For': u'1.1.1.1, 2.2.2.2', u'CloudFront-Viewer-Country': u'JP', u'Accept': u'*/*', u'User-Agent': u'curl/7.43.0', u'Host': u'q14421ik6h.execute-api.ap-northeast-1.amazonaws.com', u'X-Forwarded-Proto': u'https', u'X-Amz-Cf-Id': u'RdcP863CiLrem7LekuwSQrt2YvNw29a9m3Es55O2Db6E9YGjqWQdCg==', u'CloudFront-Is-Tablet-Viewer': u'false', u'X-Forwarded-Port': u'443', u'CloudFront-Is-Mobile-Viewer': u'false'}, u'params': {}, u'method': u'GET', u'query': {}}
[1470384650491] [INFO]  2016-08-05T08:10:50.491Z    1f3ccb88-5ae4-11e6-b4c6-c9ecf09594ff    Lambda function invoked index()
[1470384650491] [INFO]  2016-08-05T08:10:50.491Z    1f3ccb88-5ae4-11e6-b4c6-c9ecf09594ff    1.1.1.1 - - [05/Aug/2016:08:10:50 +0000] "GET / HTTP/1.1" 200 18 "" "curl/7.43.0" 0/0.798
[1470384659083] need more than 1 value to unpack: ValueError
Traceback (most recent call last):
  File "/var/task/handler.py", line 324, in lambda_handler
    return LambdaHandler.lambda_handler(event, context)
  File "/var/task/handler.py", line 121, in lambda_handler
    return cls().handler(event, context)
  File "/var/task/handler.py", line 146, in handler
    app_function = self.import_module_and_get_function(whole_function)
  File "/var/task/handler.py", line 114, in import_module_and_get_function
    module, function = whole_function.rsplit('.', 1)
ValueError: need more than 1 value to unpack
```

## アプリケーションの更新

`zappa update dev` でアプリケーションの更新ができます。

## アプリケーションの削除

`zappa undeploy dev` で deploy した内容を全て削除できます。

```
$ zappa undeploy dev
Are you sure you want to undeploy? [y/n] y
Deleting API Gateway..
Removing keep-warm..
Deleting Lambda function..
Done!
```

## さいごに

なんだかよくわからない内に hello world する web アプリケーションが構築できました。
すごいですね。