heroku で python 動かすチュートリアルをやったメモ

自分用メモ

HerokuのPythonチュートリアルをやりました。
https://devcenter.heroku.com/articles/getting-started-with-python
メモ用と思って残しましたが特につまづかなかったので、ノウハウ的なものではなくコマンドメモになりました。

# 前提
ローカルで python は動いてる
virtualenv もある

# macにpostgresqlをインストール

```
brew intall postgresql
```
postgres用に設定

```
echo 'export PGDATA=/usr/local/var/postgres' >> ~/.zshrc # それぞれのprofileに
```
これで ` pg_ctl start` や `pg_ctl stop` で起動・停止できる。
(普通だと `postgres -D /usr/local/var/postgres` とからしいので面倒)
最初からあるpostgresユーザで入ってみたり、データベース一覧を表示したりして確認

``` 
psql -l # DB表示
```
``` 
psql postgres # postgresユーザでログイン(出るときは \q)
```

# Heroku にアカウント作成

https://www.heroku.com/

# Heroku用CLIツールをインストール

```
brew install heroku-toolbelt
```
その後 Heroku にログイン

```
$ heroku login
Enter your Heroku credentials.
Email: [email]
Password (typing will be hidden):
Logged in as [email]
```


# サンプル app の用意から deploy まで
サンプルアプリをcloneする。

```
git clone https://github.com/heroku/python-getting-started.git
$ cd python-getting-started
```
できたら、Herokuにアプリを登録する

```
$ heroku create
Creating morning-plateau-xxxx... done, stack is cedar-14
https://morning-plateau-xxxx.herokuapp.com/ | https://git.heroku.com/morning-plateau-xxxx.git
Git remote heroku added
```
適当な名前(今回はmorning-plateau-xxxx)でアプリが登録される。
名前を付けたい場合は `create` の後ろにかく。 `heroku create sanmple-name-app`
この時点でローカルの git に heroku という名前で remote が登録される。
URLにアクセスすると初期ページみたいなのが出た。

![初期ページっぽいの](https://gyazo.com/4169a334202a4df94369d360721461ed.png)

次に、サンプルアプリを deploy する

```
$ git push heroku master
Counting objects: 185, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (91/91), done.
Writing objects: 100% (185/185), 30.52 KiB | 0 bytes/s, done.
Total 185 (delta 80), reused 185 (delta 80)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: -----> Installing runtime (python-2.7.9)
remote: -----> Installing dependencies with pip
# 長いので省略
remote: -----> Compressing... done, 42.9MB
remote: -----> Launching... done, v4
remote:        https://morning-plateau-xxxx.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy.... done.
```

deploy完了。
出力を見ると、単にpushするだけでなく、何やら環境構築したりコマンド実行されていることが分かる。

アプリを動かすためのプロセスを下記コマンドで起動する。
scaleを1に指定しているが、大きくするとお金かかるんだろうか？
とりあえず動かすために、"Ensure that at least one instance of the app is running"とのこと。

```
$ heroku ps:scale web=1
Scaling dynos... done, now running web at 1:Free.
```

ps コマンドを実行すると確かに動いているようだ。

```
$ heroku ps
=== web (Free): gunicorn gettingstarted.wsgi --log-file -
web.1: up 2016/01/06 02:36:49 (~ 8m ago)
```

ちなみにweb=2にするとこうなった。うーん。(あとでしらべる)

```
$ heroku ps:scale web=2
Scaling dynos... failed
 !    Cannot update to more than 1 Free size dynos per process type.
```

最後に、下記でアプリを開く

```
heroku open
```
簡単。ここでとりあえずページを開くところまで完了。

# ログを見る

```
heroku logs
heroku logs --tail # tail で見れる
```

# Procfile?

アプリケーションのrootディレクトリにあるテキストファイルで、アプリケーションの立ち上げに必要なコマンドを書く。
PythonのサンプルアプリケーションのProcfileは下記のようになっていた。

```
web: gunicorn gettingstarted.wsgi --log-file -
```

`web` がプロセスタイプで、その後がコマンド。
普通に起動すると一つのdynoでプロセスが走る。
([dynoって？](https://devcenter.heroku.com/articles/dynos) ここではProcfileのコマンドを走らせる軽量コンテナ)
ちゃんとみたい場合は `heroku ps`。

デフォルトではdynoは1つ&freeで、30分アクセスがないor一日18時間以上動くととスリープモードに入る。
18時間以内ならアクセスが来ると毎回立ち上がるが、スリープ後だと立ち上げ処理入るので若干重い。
スリープがいやorスケールしたい場合はProfessionalに変えてねとのこと。

# アプリの依存関係
requirements.txt が root ディレクトリにあれば、Herokuはpython appであると認識するようだ。
サンプルアプリの requirements.txt の中身は次のようになっている。

```
$ cat requirements.txt
dj-database-url==0.3.0
Django==1.8.1
django-postgrespool==0.3.0
gunicorn==19.3.0
psycopg2==2.6
SQLAlchemy==1.0.4
whitenoise==1.0.6
```
チュートリアルにのっていたものと若干違うが、Herokuはこれを元に必要なライブラリをインストールする。
これは御存知の通り `pip freeze` と同じ形式。
ダウンロードしてきたサンプルアプリなので、とりあえず同じ環境をローカルに作る。
pythonのバージョンは、ルートディレクトリにある `runtime.txt` に書いてある。
(pyenvを使っていたためそれで環境作成した。)

```
$ cat runtime.txt
python-2.7.9
$ pyenv install 2.7.9
$ pyenv virtualenv 2.7.9 sample-app
$ cd [workspace]
$ pyenv local sample-app
$ pip install -r requirements.txt --allow-all-external
```

# ローカルで動かす

herokuで動かしたものをローカルでも動かす。

```
$ python manage.py collectstatic # プロジェクト内の静的ファイルを一箇所に集める
$ heroku local web
```
http://localhost:5000/ にアクセスして確かに動いていることを確認。

# ローカルの変更を heroku に反映
適当にローカルを変更してherokuにpushする流れ。requestsをimportして、適当なサイトにリクエストを飛ばしてそのレスポンスを表示するような変更を加えて、pushしている。
まず、`requirements.txt` に次を加える。

```
requests==2.3.0
```
`hello/views.py` の頭に次を足し、

```
import requests
```
indexという関数を次のように書き換える

```
def index(request):
    r = requests.get('http://httpbin.org/status/418')
    print r.text
    return HttpResponse('<pre>' + r.text + '</pre>')
```
ローカルで実行

```
$ pip install -r requirements.txt --allow-all-external
$ heroku local
```
http://localhost:5000/ にいってみると、、http://httpbin.org/status/418 の内容が。

```
    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
```
かわいい。

無事変わっていたので、herokuに反映させる。

```
$ git add .
$ git commit -m "Demo"
$ git push heroku masterg
$ heroku open
```

deploy完了、同じteapotのページが開く。
herokuへのdeployは基本的にこの流れ。

# アドオンの追加

例として、papertrailを追加する。

```
$ heroku addons:create papertrail
 !    Please verify your account to install this add-on plan (please enter a credit card) For more information, see https://devcenter.heroku.com/categories/billing Verify now at https://heroku.com/verify
```

怒られた。悪用防止のためaddonはverifyがいるようだ。
びびりつつクレカ情報を登録して再度試すといけた。

```
$ heroku addons:create papertrail
Creating papertrail-rigid-xxxx... done, (free)
Adding papertrail-rigid-xxxx to morning-plateau-xxxx... done
Setting PAPERTRAIL_API_TOKEN and restarting morning-plateau-xxxx... done, v6
Welcome to Papertrail. Questions and ideas are welcome (support@papertrailapp.com). Happy logging!
Use `heroku addons:docs papertrail` to view documentation.
```

これでdeployed. 現在のaddon一覧は下記コマンドで見れる。

```
$ heroku addons
Add-on                                         Plan       Price
─────────────────────────────────────────────  ─────────  ─────
heroku-postgresql (postgresql-octagonal-xxxx)  hobby-dev  free
 └─ as DATABASE

papertrail (papertrail-rigid-xxxx)             choklad    free
 └─ as PAPERTRAIL

The table above shows add-ons and the attachments to the current app (morning-plateau-xxxx) or other apps.
```

papertrail で早速見てみる

```
heroku addons:open papertrail
```
ブラウザが開いてログが確認できる。

# コンソール

`heroku run COMMAND` コマンドでherokuのコンソールが使える。
ここではコマンドはone-off dynoという一時的なdyno上で動いているらしい。

```
$ heroku run pwd
Running pwd on morning-plateau-xxxx... up, run.5945
/app
$ heroku run python manage.py shell
Running python manage.py shell on morning-plateau-xxxx... up, run.4047
Python 2.7.9 (default, Dec 11 2014, 17:18:51)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import requests
>>> print requests.get('http://httpbin.org/status/418').text

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

```
同様に、dynoのbashを下記で動かせる。

```
$ heroku run bash
Running bash on morning-plateau-xxxx... up, run.3252
$ ls
app.json  gettingstarted  hello  manage.py  Procfile  Procfile.windows	README.md  requirements.txt  runtime.txt  staticfiles
$ exit
exit
```

# 設定用変数の定義

外部に置きたい secret key のような設定用変数や外部リソース。

実行時、設定変数は環境変数に入る。

`hello/view.py` の最初に次を追加。

```
import os
```
また、`index`を次のように書き換える

```
def index(request):
    times = int(os.environ.get('TIMES',3))
    return HttpResponse('Hello! ' * times)
```

local の .env ファイルに次を追加。ここを見て環境変数が設定される。

```
TIMES=2
```
`heroku config:set`でもHeroku上での設定変数を設定できる。また`heroku config`で確認できる。

```
$ heroku config:set TIMES=2
Setting config vars and restarting morning-plateau-xxxx... done
TIMES: 2
$ heroku config
=== morning-plateau-xxxx Config Vars
DATABASE_URL:         xxxx
PAPERTRAIL_API_TOKEN: xxxx
TIMES:                2
```

# データベースの準備(FreeのPostgresアドオン)

データベースもaddonで色々用意されている。addonなので現在のものを`heroku addons`で確認できる。
`heroku config`の`DATABASE_URL`で、接続先データベースのURLが登録されているのが見れる。
```
heroku config
=== morning-plateau-xxxx Config Vars
DATABASE_URL:         postgres://~~~ #略
```
また、`heroku pg` コマンドは詳細が得られる。

```
$ heroku pg
=== DATABASE_URL
Plan:        Hobby-dev
Status:      Available
Connections: 0/20
PG Version:  9.4.4
Created:     2016-01-05 17:36 UTC
Data Size:   6.4 MB
Tables:      0
Rows:        0/10000 (In compliance)
Fork/Follow: Unsupported
Rollback:    Unsupported
Add-on:      postgresql-octagonal-xxxx
```

サンプルプロジェクトにもDBは既に有効化されていて、URL末尾に `/db` とつけることでアクセスできるが、今の状態ではまだテーブルが作成されていないため、アクセスするとエラーになる。
djangoでテーブルを作るために、Herokuで`manage.py migrate`を実行する。

```
$ heroku run python manage.py migrate
Running python manage.py migrate on morning-plateau-xxxx... up, run.6683
Operations to perform:
  Synchronize unmigrated apps: messages, hello, staticfiles
  Apply all migrations: admin, sessions, auth, contenttypes
  # ...
  # 略
```

もう一度`URL+/db`にアクセスすると、今度はエラーではなく、アクセスする度にレコードが増えていくようなシンプルなページが表示される。

DBのモデルは`hello/models.py`に、設定は`gettingstarted/settings.py`にある。
(djangoについての説明はしません)

ローカルにpostgresqlがはいっているなら、プロジェクトのrootディレクトリからherokuのdbに次でアクセスできる。

```
$ heroku pg:psql
---> Connecting to DATABASE_URL
psql (9.4.5, server 9.4.4)
Type "help" for help.

morning-plateau-xxxx::DATABASE=> select * from hello_greeting;
 id |             when
----+-------------------------------
  1 | 2016-01-11 16:09:35.860529+00
  2 | 2016-01-11 16:10:28.385479+00
(2 rows)
```

Heroku Postgres の詳細については https://devcenter.heroku.com/articles/heroku-postgresql を参照とのこと。

# 次のステップ

ここまででチュートリアルは終わり。最後に、次のステップとして2つのリンクが用意されている。
- How Heroku Works https://devcenter.heroku.com/articles/how-heroku-works 
Heroku自体についての詳細知りたい人向けリンク。
- Python on Heroku https://devcenter.heroku.com/categories/python
Heroku内のPython情報ポータル。