Herokuの使い方を勉強した。【Pythonを定期的に自動実行したい】


**Python**を定期実行したかったので、**Heroku**の使い方を勉強してみました！
　
具体的には、Herokuの公式チュートリアル
「**Getting Started on Heroku with Python**（PythonでHerokuをはじめよう）」

https://devcenter.heroku.com/articles/getting-started-with-python#set-up

を日本語に翻訳しつつ、勉強した内容をまとめました。


#①Herokuの公式チュートリアルを勉強する

**Heroku**の公式チュートリアルは、**「HerokuでPythonプログラムの定期実行」する方法ではない**です。

しかし、まず公式チュートリアルをやると**Heroku**の使い方がなんとなく分かります。
そして、その後に**Python**プログラムの定期実行をやってみるとスムーズに理解できた気がします。

したがって、この記事では「**①Heroku**の公式チュートリアル」→「**②Python**プログラムの定期実行」の順で書いています。

##セットアップ

###Gitを使えるようにする。
まず、**Heroku CLI**を使うには、バージョン管理システムである**Git**が必要です。
まだインストールしていない場合は、インストールして設定をします。

https://git-scm.com/book/en/v2/Getting-Started-Installing-Git

https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup

###Heroku Command Line Interface (CLI)をインストールする。

**Heroku**のインストーラーは**[こちら](https://devcenter.heroku.com/articles/getting-started-with-python#set-up)**のページから入手できます。

https://devcenter.heroku.com/articles/getting-started-with-python#set-up

僕はWindowsを使っているので、Windows用のインストーラーの表示に従ってインストールしました。
![ScreenShot-2021108_112223.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/554a3d0c-244f-e58f-2777-c44660adfc12.png)


インストールが完了すると、コマンドシェルからherokuコマンドを使用可能になります。

###Herokuへログインする。

僕はWindowsなので、PowerShellに`heroku login`コマンド

```
$ heroku login
```

を入力します。

すると、ブラウザが起動して「Herokuにログインしますか？」的な確認画面が出てくるので、受け入れます。
![ScreenShot-2021108_11203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/a06ce99e-67b3-dfd0-2bb7-bc646bb65e86.png)


##アプリを準備する
https://devcenter.heroku.com/articles/getting-started-with-python#prepare-the-app


Heroku側が、**勉強用サンプルアプリ**↓を作ってくれているので

https://github.com/heroku/python-getting-started

これを自分のローカル環境に`clone`（コピー）します。

具体的には、まずサンプルアプリを保存したい任意の場所（デスクトップとか）に移動させます。

PowerShellに`cd`コマンドを入力して移動します。

```
※例
$ cd 任意の場所
```

その後

```
$ git clone https://github.com/heroku/python-getting-started.git
```

を実行して、ローカル環境にサンプルアプリのクローンを作成します。
「python-getting-started」というサンプルアプリのディレクトリ（フォルダ）が作成されるはずです。

###生成されたファイルについて
![python-getting-started.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/eb6a3275-56bf-f340-e724-d9097ccbc59c.png)

「python-getting-started」内には、いろいろなファイルが生成されているはずです。

主なファイルの役割↓

- **.env**
  環境変数を書いておくファイル。
　「env」は、「environment（環境）」の略。

- **.gitignore**
  「.env」など**Git**の管理に含めたくないファイルを指定するためのファイル。
　（「ignore」の意味は「無視」）

- [**app.json**](https://devcenter.heroku.com/ja/articles/app-json-schema)
　**Heroku**でアプリを実行するために必要な環境変数、アドオン、その他の情報を宣言するファイル。

- **manage.py**
　Django プロジェクトの設定ファイル。

- **README.md**
  説明書的なファイル。

- **Procfile（プロクファイル）**
  **Heroku**上で動かしたいコマンドの一覧を指定するファイル。（拡張子は無し）

- **Procfile.windows**
  Windowsでローカル開発を行うための**Procfile**（プロクファイル）。

- **README.md**
  説明書的なファイル。

- **requirements.txt**
  ローカルにインストールしてある**Python**のライブラリを書いておくファイル。
  （**Heroku**へデプロイ時に指定した**Python**のライブラリがインストールされる。）

- **runtime.txt**
  インストールする**Python**のバージョンを書いておくファイル。
  （**Heroku**へデプロイ時に指定したバージョンの**Python**がインストールされる。）




##アプリをデプロイする
https://devcenter.heroku.com/articles/getting-started-with-python#deploy-the-app


続いて、そのまま`cd`コマンドでディレクトリ（フォルダ）の中へ移動します。

```
$ cd python-getting-started
```

そこで`heroku create`コマンドを実行すると…

```
$ cd heroku create
Creating app... done, ⬢ serene-caverns-82714
https://serene-caverns-82714.herokuapp.com/ | https://git.heroku.com/serene-caverns-82714.git
```
みたいな感じで、gitリモート（herokuと呼ばれる）も作成され、ローカルのgitリポジトリに関連付けられます。

アプリにランダムな名前（ここではseren-caverns-82714）になります。

ちなみに

```
$ heroku create アプリ名
```

とすると任意のアプリ名を指定できます。
（しかし、数字や大文字始まりのアプリ名や、既に他のユーザー使用されているアプリ名と被っているのはダメっぽいです。）


###コードをデプロイする

そのまま`push heroku main`コマンドを実行すると、コードをデプロイしてくれます。

```
$ git push heroku main
```

実行が終わったら`heroku ps:scale web=1`コマンドで

```
$ heroku ps:scale web=1
```

アプリのインスタンスが、少なくとも1つ実行されているかを確認します。
「now running」みたいなメッセージが返ってきたら成功だと思います。


###実際に確認する

次に、先ほどアプリ名で生成されたURL

```
例
https://serene-caverns-82714.herokuapp.com/ 
```

でアプリにアクセスします。

ショートカットとして、`heroku open`コマンドでもWebサイトを開けます。

```
$ heroku open
```

こんな感じの画面になっていたら成功だと思います。
![ScreenShot-2021108_12932.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/e0f850eb-af03-23ab-8835-2b1ac7edffc5.png)


###ログの表示方法を知る
https://devcenter.heroku.com/articles/getting-started-with-python#view-logs

公式チュートリアルによると

>「Herokuはログを、すべてのアプリとHerokuコンポーネントの出力ストリームから集約された、時間順に並べられたイベントのストリームとして扱い、すべてのイベントに対して単一のチャンネルを提供します。」

とのこと。

…正直、ちょっと何言ってるかよく分からない。笑
「アプリを複数作っても、ログはまとめて時系列順で表示されるよ。」ってことでしょうか。

とりあえず`heroku logs --tail`コマンド

```
$ heroku logs --tail
```

で直近の実行中アプリのログが見れるっぽいです。

###Procfile（プロクファイル）を定義する

https://devcenter.heroku.com/articles/getting-started-with-python#define-a-procfile


先ほども紹介した「python-getting-started」の中にある「**Procfile**（プロクファイル）」。
Heroku 上で動かしたいコマンドの一覧を指定するファイルみたいです。（拡張子は無し）

※Procfileは必ずルートディレクトリに置く。

##アプリの拡張

ここまで確認してきたアプリは、"**Dyno（仮想サーバー）**"上で動作しているみたいです。
いくつの"Dyno"が稼働しているかは、`ps`コマンドで確認できます。

```
$ heroku ps
```

"Dyno"は複数使えるので、アプリの負荷が高くなってきたら"Dyno"を増して対応します。
（**スケールアウト**って手法だと思います。）

※"Dyno"の数は、こんな感じ↓で設定・変更する。

```
$ heroku ps:scale web=1
```

###無料用Dynoと課金について

デフォルトでは、アプリは**"無料用Dyno"**にデプロイされます。

"無料用Dyno"は、30分ほど活動しないと（トラフィックを受け取らないと）スリープします。
そして、スリープ解除後の最初のリクエストに遅延が生じます。

「そうならない"Dyno"を使いたい場合は課金してね」ってわけです。

※"無料用Dyno"でも使用時間が1000時間/月以上使いたい場合は、課金が必要みたいです。

###アプリの依存関係を宣言する

先ほども紹介した「python-getting-started」の中にある「**requirements.txt**」。
ローカルにインストールしてあるPythonのライブラリを書いておくファイル。

サンプルアプリには

```
django
gunicorn
django-heroku
```

が記述されています。
**Heroku**へデプロイ時に指定した**Python**のライブラリがインストールされるみたいです。

ローカル環境にも同じライブラリをインストールしたい場合は

```
$ pip install -r requirements.txt
```

で実行できるみたいです。

（上手くいかない場合、**Postgres**を正しくインストールしてある必要があるみたいです。）

https://devcenter.heroku.com/articles/heroku-postgresql#local-setup


###アプリをローカルで実行する

ここらへんは、**Heroku**というより**Django**の使い方です。

まず、collectstatic を実行します。

```
$ python manage.py collectstatic
```

**Django**の使い方詳しくないので分かりませんけど、必要なファイルを生成するコマンドだと思います。

そして、以下のコマンドを実行します。

```
heroku local web -f Procfile.windows
```

（この時に、Windowsでローカル開発を行うためのProcfileである「Procfile.windows」を使うわけですね。）

しばらくすると、ローカルサーバーが立ち上がって「localhost:5000」から先ほどと同じページが閲覧できます。
![ScreenShot-2021108_12932.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/809596d4-3a19-123d-2606-d47026571ecc.png)

ローカルサーバーの停止は`Ctrl+C`で行えます。

##ローカル環境での変更をHerokuに反映させる方法

###試しに変更してみる

まず、公式チュートリアル通りにローカルに**python**のライブラリ「**requests**」をインストールします。
（僕は既に入れてありました。）

```
$ pip install requests
```

次に、requirements.txtに「**requests**」を書き加えます。

![スクリーンショット 2021-10-08 164335.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/88dff343-8bb4-201b-8dfb-b490dbcad941.png)

そして、hello/views.pyを書き換えます。
![スクリーンショット 2021-10-08 164443.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/fdf3dae7-4bac-2c07-d64b-d28e9a1a923e.png)
ここにある↑ファイルですね。

**requests**をインポートして、関数「index」を指定通りに書き換えます。

```views.py
from django.shortcuts import render
from django.http import HttpResponse

from .models import Greeting

import requests #　←　「requests」をインポートする！！！！

# Create your views here.


# def index(request):
#     # return HttpResponse('Hello from Python!')
#     return render(request, "index.html")

#　↓関数「index」を書き換える！！！！！

def index(request):
    r = requests.get('http://httpbin.org/status/418')
    print(r.text)
    return HttpResponse('<pre>' + r.text + '</pre>')
```

再びローカルサーバーを立ち上げて、localhost:5000 にアクセスすると…

![ティーポット画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/02b4cbd7-ae41-4f07-fbd9-096dd9048861.png)

ページの表示がティーポットのアスキーアートになっています。

###変更をHerokuへデプロイ

この状態を**Heroku**へデプロイします。

まず、変更した全てのファイルをローカルの git リポジトリに追加します。

```
$ git add .
```

次に、変更内容をリポジトリにコミットします。
コミットメッセージは「Demo」とします。

```
$ git commit -m "Demo"
```

Herokuへデプロイします。

```
$ git push heroku main
```

Herokuへデプロイが完了したら、確認します。

```
$ heroku open
```

で、アプリのURLへアクセスできましたよね。
アクセスすると…

![ティーポット画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/9e37e93f-f563-7dbf-0698-826cbc960a1e.png)

ちゃんとティーポットになっていたら成功！

##git cloneしない場合

ここまで公式チュートリアル通りにサンプルアプリをgit cloneして進めてきました。

任意のフォルダ内のアプリをHerokuへデプロイしたい場合は

```
$ heroku create アプリ名
```

で新規アプリを作った後、
自分でruntime.txt　と　requirements.txtを用意して

```
$ git init
$ git remote add heroku https://git.heroku.com/アプリ名.git
$ git add . 
$ git commit -m "コミットメッセージ"
$ git push heroku master
```

でいけるはずです。

#②ファイルの定期実行がしたい

さて。なんとなく**Heroku**の概念は理解できてきた気がします。

しかし、今回は**Django**で作ったWebアプリをデプロイしたいのではなく、**Pythonファイルを定期実行したい**わけです。

いよいよ、定期実行をするための設定をしていきたいと思います。

##定期実行するファイルを用意する

僕は自分に**LINEメッセージ**を送るプログラムを定期実行させてみます。

```LineSend.py
import requests
import datetime

TOKEN = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'  #アクセストークン
# APIのURL(エンドポイント)を変数に代入。
api_url = 'https://notify-api.line.me/api/notify'

# -------------------------------------------
# メッセージを送信する関数
def send_message(send_text, token):
    # 情報を辞書型にする
    TOKEN_dic = {'Authorization': 'Bearer' + ' ' + token}
    # 送りたいコンテンツ
    send_contents = f'{send_text}'
    send_dic = {'message': send_contents}
    # LINE通知を送る（200: 成功時、400: リクエストが不正、401: アクセストークンが無効：公式より）
    requests.post(api_url, headers=TOKEN_dic, data=send_dic)

# 現在時刻を取得
time = datetime.datetime.now()
time = time.strftime('%Y年%m月%d日 %H:%M:%S')

# 送りたいテキスト
send_text = f'\n{time}\nテスト送信'

# メッセージを送信する
send_message(send_contents, TOKEN)

```
※アクセストークンを発行するためには、**LINE Notify**に登録する必要があります。

https://notify-bot.line.me/ja/

↓詳しくは、こちらの動画が参考になると思います。

https://youtu.be/FuCJd0ftVsU

アクセストークンはスクリプトにベタ打ちするのではなく、**環境変数**に設定した方が良いと思います。

https://cream-kuchen.hatenablog.com/entry/heroku_set_config_vars


##Herokuへデプロイする

先ほどと同じ要領で、用意したpyファイルを**Heroku**へデプロイします。

（もう一度新たに定期実行用のフォルダを作ってHerokuの設定をしても良いですけど…
とりあえず、練習ならそのまま「python-getting-started」の中に入れると手っ取り早そうです。）

##アドオン（拡張機能）を設置する方法

Herokuへデプロイしたファイルの定期実行をするには、「**スケジューラ**」のアドオンを使います。

まず、スケジューラーを使うには、クレジットカード情報の登録が必要です。
Herokuの**Account settings**→**Billing**タブを開いてクレジットカード情報を登録します。

![Billing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/864a6824-2a50-3b7f-3000-b7424e6e04a7.png)

（※登録したからといってプランを変えない限り、勝手には課金されないみたいです。）

https://devcenter.heroku.com/articles/scheduler

コマンドを使って「スケジューラ」のアドオンを追加します。

```
$ heroku addons:add scheduler:standard
```

コマンドを実行して、「Created scheduler-アプリ名」みたいなのが出てきたらOKだと思います。

##スケジューラのジョブを設定をする

スケジューラのアドオンを追加したら、ジョブを設定するページへ移動します。

```
$ heroku addons:open scheduler
```

を実行すると、ブラウザで設定ページが開かれます。

![スケジューラ　ジョブの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/1fbbe9c4-a7d9-1f5c-ad84-cb0fc5e25bf1.png)

Create jobボタンを押して、ジョブを作成します。

![定期実行ジョブ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/8814426d-9111-eb18-a9e1-d1d4e3c878dc.png)


ここに実行する間隔と、実行したいpyファイルを設定するわけですね。

設定時は、dyno sizeの「**Free**」が選択肢に出てこなくて、「え？これ有料のDynoじゃね？」と思いつつ「**Standard-1X**」にしてみました。
しかし、1度でも実行すると管理画面で「**Free**」に表示に変わりました。

どうやら、スケジューラで使うdynoは「[**One-off dyno**](https://devcenter.heroku.com/ja/articles/one-off-dynos#one-off-dyno-type)」というものらしいです。

>Free または Hobby dyno タイプを使用するアプリでは、対応する Free または Hobby タイプが One-off dyno で使用されます。その他の dyno タイプは使用できません。

つまり、自分の使用プランに合わせて勝手にOne-off dynoの種類が選択される（？）みたいです。ややこしい…。

最後に、時刻を**UTC（協定世界時）**で指定します。
（日本標準時から9時間マイナスした時刻になります。）

https://www.jisakeisan.com/?t1=utc&t2=jst

これで指定通りにプログラムが実行されれば成功です！
（微妙に実行時間がズレるのは仕様のようです。）

今回の場合は、LINEメッセージが来れば成功だと分かりますけど、チュートリアルでやった`heroku logs`でも情報を確認できます。


#参考にさせていただいたページ

https://hashikake.com/heroku-scheculer

https://creepfablic.site/2019/06/15/heroku-procfile-matome/

https://qiita.com/aocattleya/items/53388f5915cf38d6c97b

https://python.keicode.com/django/how-to-serve-static-files.php

https://qiita.com/okoppe8/items/38688fa9259f261c9440

https://kino-code.com/pythonline/

https://cream-kuchen.hatenablog.com/entry/heroku_set_config_vars

https://qiita.com/mzmz__02/items/64db94b8fc67ee0a9068

https://e-tec-memo.herokuapp.com/article/277/