djangoをherokuにdeployしたい

### Django プロジェクト作成
$ python3 –version
Python 3.8.10
$ pip3 –version
pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8)
$ sudo apt install python3-django
$ pip3 install dj-database-url
$ django-admin startproject newsproject
$ cd newsproject
$ python3 manage.py startapp newsapp
templatesフォルダ作成
newsproject/settings.py

ALLOWED_HOSTS = ["192.168.34.10"]

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'newsapp',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR, 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

newsproject/urls.py

from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('newsapp.urls')),
]

newsapp/urls.py

from django.urls import path
from .views import Create, listfunc

urlpatterns = [
	path('', Create.as_view(), name='home'),
	path('list/', listfunc, name='list'),
]

newsapp/views.py

from django.shortcuts import render
from .models import News
from django.views.generic import CreateView
from django.urls import reverse_lazy
import urllib.request
import requests
from bs4 import BeautifulSoup

class Create(CreateView):
	template_name = 'home.html'
	model = News
	fields = ('url'),
	success_url = reverse_lazy('list')

def listfunc(request):
	for post in News.objects.all():
		url = post.url
	list = []
	response = requests.get(url)
	bs = BeautifulSoup(response.text, "html.parser")
	ul_tag = bs.find_all(class_="topicsList_main")
	for tag in ul_tag[0]:
		title = tag.a.getText()
		url2 = tag.a.get("href")
		list.append([title, url2])
	context = {'list': list,}
	return render(request, 'list.html', context)

newsapp/models.py

from django.db import models

class News(models.Model):
	url = models.CharField(max_length=100)

// migration
$ python3 manage.py makemigrations
$ python3 manage.py migrate

templates/base.html

<!doctype html>
<html lang="en">
 <head>
   <!-- Required meta tags -->
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
   <!-- Bootstrap CSS -->
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
   <title>Pachi Data</title>
 </head>
 <body>
   {% block header %}
   {% endblock header %}
   {% block content %}
   {% endblock content %}
   <!-- Optional JavaScript -->
   <!-- jQuery first, then Popper.js, then Bootstrap JS -->
   <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
   <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
   <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
 </body>
</html>

templates/home.html
[htmle]
{% extends ‘base.html’ %}

{% block header %}

News

Enter the URL

{% endblock header %}

{% block content %}

{% csrf_token %}

URL:


{% endblock content %}
[/html]

templates/list.html

{% extends 'base.html' %}

{% block header %}
<div class="jumbotron">
   <div class="container">
     <h1 class="display-4">News</h1>
   </div>
 </div>
{% endblock header %}

{% block content %}
<div class="container">
<ul class="list-group list-group-flush">
 <table border="2">
   <tr>
     <th>タイトル</th>
     <th>URL</th>
   </tr>
   {% for post, post2 in list %} 
   <tr>
     <td>{{ post }}</td>
     <td><a href="{{ post2 }}">{{ post2 }}</a></td>
   </tr>
   {% endfor %}
 </table>
</ul>
</div>
{% endblock content %}

$ python3 manage.py runserver 192.168.34.10:8000

### herokuにDeploy
$ echo web: gunicorn newsproject.wsgi –log-file – > Procfile
$ freeze > requirements.txt
$ python3 –version
Python 3.8.10
$ echo python-3.8.10 > runtime.txt
$ echo -e __pycache__\\ndb.sqlite3\\n.DS_Store\\nlocal_settings.py > .gitignore

newsproject/settings.py
L SECRET_KEYを削除する

import dj_database_url

DEBUG = False
// 省略

ALLOWED_HOSTS = ["*"]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware', // 追加
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'name',
        'USER': 'user',
        'PASSWORD': '',
        'HOST': 'host',
        'PORT': '',
    }
}

try:
    from .local_settings import *
except ImportError:
    pass

if not DEBUG:
    SECRET_KEY = os.environ['SECRET_KEY']
    import django_heroku
    django_heroku.settings(locals())

df_from_env = dj_database_url.config(conn_max_age=600, ssl_require=True)
DATABAES['default'].update(db_from_env)
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

newsproject/local_settings.py
L SECRET_KEYをコピーする

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

SECRET_KEY = 'django-insecure-bm-*'

DATABASES = {
	'default': {
		'ENGINE' : 'django.db.backends.sqlite3',
		'NAME': os.path.join(BASE_DIR, 'db.sqlite3')
	}
}

DEBUG = True

if not DEBUG:
    SECRET_KEY = os.environ['SECRET_KEY']

$ git init
$ git add -A
$ git commit -m “initial commit”
$ heroku login
$ heroku create
$ heroku config:set SECRET_KEY=’django-insecure-*’
$ git push heroku master

ERROR: Could not find a version that satisfies the requirement cliapp==1.20180812.1 (from versions: 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 1.0.6, 1.0.7, 1.0.8, 1.0.9)
remote: ERROR: No matching distribution found for cliapp==1.20180812.1

git pushできない…
なかなか難儀やな

Getting started Heroku with Ubuntu20.04, python

$ sudo snap install heroku –classic
heroku v7.59.1 from Heroku✓ installed
$ heroku login
// ブラウザにアクセス
$ git clone https://github.com/heroku/python-getting-started.git
$ cd python-getting-started
$ heroku create
$ git push heroku main
$ heroku ps:scale web=1
$ heroku open

ソースファイルを見るとdjangoで作られていますね

procfile
web: gunicorn gettingstarted.wsgi

$ heroku ps
=== web (Free): gunicorn gettingstarted.wsgi (1)
web.1: up 2021/11/28 13:00:59 +0000 (~ 10m ago)

requirements.txt

django
gunicorn
django-heroku

in local
$ pip install -r requirements.txt

うむー まとまり過ぎていて細かな手順がわからん

herokuで実行されない時

app logsを見ます。
[vagrant@localhost heroku]$ heroku logs -t
2019-08-17T06:29:07.714312+00:00 app[web.1]: [17-Aug-2019 06:29:07 UTC] PHP Fatal error: Uncaught Error: Call to undefined function mb_convert_encoding() in /app/backlog.php:23

ん? 
mb_convert_encoding()?

とりあえずcomposer.jsonをupdateして、herokuにpush

{
	"require" : {
		"ext-mbstring": "*",
		"google/apiclient": "^2.0"
	}
}

きたーーーーーーーーーーー 結局、GCP→Heroku→Google Spread sheetもOKでした。

Herokuの料金体系とは

さて、Herokuの料金体系はどうなっているのでしょうか?
Herokuというと、何となく「無料」のサーバーという概念が強かったのですが、Heroku Schedulerを使用しようとしたところ、クレジットカードの登録を要求されて、無料版と有料版があるということを理解。

Herokuの運営がSalesforceなので、やや不安が残るというか、変な課金のされ方をしないか心配ではある。

では早速見ていこう。以下のように○○ free dyno hours(○%) used this monthとあり、AWSのように容量課金ではなく、時間単位の課金のようだ。

ちなみに、このfree dynoは月550時間使えることが見て取れる。

そしてこのfree dynoはクレジットカードを登録すると、月1000時間使えるようになる。約2倍。何んかだいぶいい加減だな。

では早速クレジットカードを登録してみましょう。
登録後は、以下のように、free dyno houresが1000に増えていることがわかります。

で、Heroku Scheduler は Web Dynoとは別のOne-Off Dynoという仮想環境で実行される。で、One-Off Dynoで使用された時間はfree dyno houresから引かれる。つまり、cronだけで使うなら、約1/2時間になるイメージだろう。

続いて、Heroku Schedulerを登録していこう。

Heroku scheduler

[vagrant@localhost heroku]$ heroku addons:create scheduler:standard
Creating scheduler:standard on ⬢ peaceful-garden-15590… !
▸ 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
[vagrant@localhost heroku]$ heroku addons:open scheduler
▸ Couldn’t find that add on.

あれ、何で?
コンソールにログインして見ましょう。

Heroku schedulerって金かかるの?

Herokuにdeploy

[vagrant@localhost local]$ mkdir heroku
[vagrant@localhost local]$ cd heroku
[vagrant@localhost heroku]$ ls
[vagrant@localhost heroku]$ touch index.php
[vagrant@localhost heroku]$ touch composer.json
[vagrant@localhost heroku]$ ls
composer.json index.php
[vagrant@localhost heroku]$ git init
Initialized empty Git repository in /home/vagrant/local/heroku/.git/
[vagrant@localhost heroku]$ git add .
[vagrant@localhost heroku]$ git commit -m “Hello heroku commit”
[master (root-commit) 683c29e] Hello heroku commit
Committer: vagrant
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:

git config –global user.name “Your Name”
git config –global user.email you@example.com

If the identity used for this commit is wrong, you can fix it with:

git commit –amend –author=’Your Name

1 files changed, 3 insertions(+), 0 deletions(-)
create mode 100644 composer.json
create mode 100644 index.php
[vagrant@localhost heroku]$ heroku create
Creating app… done, ⬢ peaceful-garden-15590
https://peaceful-garden-15590.herokuapp.com/ | https://git.heroku.com/peaceful-garden-15590.git
[vagrant@localhost heroku]$ git push heroku master
Counting objects: 4, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 295 bytes, done.
Total 4 (delta 0), reused 0 (delta 0)
remote: Compressing source files… done.
remote: Building source:
remote:
remote: —–> PHP app detected
remote:
remote: NOTICE: Your ‘composer.json’ is completely empty!
remote:
remote: A completely empty file is not a valid JSON document.
remote:
remote: Heroku automatically corrected this problem, but it is strongly
remote: recommended you change the contents to at least ‘{}’.
remote:
remote: For documentation on Composer and dependency management, check
remote: out the introduction at https://getcomposer.org/doc/00-intro.md
remote:
remote: —–> Bootstrapping…
remote: —–> Installing platform packages…
remote: NOTICE: No runtime required in composer.lock; using PHP ^7.0.0
remote: – php (7.3.8)
remote: – apache (2.4.39)
remote: – nginx (1.16.0)
remote: —–> Installing dependencies…
remote: Composer version 1.9.0 2019-08-02 20:55:32
remote: —–> Preparing runtime environment…
remote: NOTICE: No Procfile, using ‘web: heroku-php-apache2’.
remote: —–> Checking for additional extensions to install…
remote: —–> Discovering process types
remote: Procfile declares types -> web
remote:
remote: —–> Compressing…
remote: Done: 15.7M
remote: —–> Launching…
remote: Released v3
remote: https://peaceful-garden-15590.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy… done.
To https://git.heroku.com/peaceful-garden-15590.git
* [new branch] master -> master

ほう、rubyじゃなくてもいけるやんか。
で、Herokuでcronってどうやるんだ?

あれ、Heroku Schedulerで行けそう?? もしかして^^

ターミナルベースでherokuにログイン

[vagrant@localhost ~]$ heroku –version
heroku/7.27.1 linux-x64 node-v11.14.0
[vagrant@localhost ~]$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/browser/54d1a025-4f83-4774-93e8-afe0faba719a
› Warning: Cannot open browser.
heroku: Waiting for login… ⣯
^C
[vagrant@localhost ~]$ heroku login –interactive
heroku: Enter your login credentials
Email:

Herokuを使おう

やっと戻って来ました

toolbeltはCLIのようですね。
https://devcenter.heroku.com/articles/heroku-cli

[vagrant@localhost ~]$ curl https://cli-assets.heroku.com/install.sh | sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
105 1892 105 1892 0 0 2942 0 –:–:– –:–:– –:–:– 19306
This script requires superuser access.
You will be prompted for your password by sudo.
Your path is missing /usr/local/bin, you need to add this to use this installer.
[vagrant@localhost ~]$ sudo curl https://cli-assets.heroku.com/install.sh | sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
105 1892 105 1892 0 0 5341 0 –:–:– –:–:– –:–:– 25567
This script requires superuser access.
You will be prompted for your password by sudo.
Your path is missing /usr/local/bin, you need to add this to use this installer.
[vagrant@localhost ~]$ wget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz
–2019-08-09 00:02:07– https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz
cli-assets.heroku.com をDNSに問いあわせています… 13.32.54.88, 13.32.54.49, 13.32.54.116, …
cli-assets.heroku.com|13.32.54.88|:443 に接続しています… 接続しました。
HTTP による接続要求を送信しました、応答を待っています… 301 Moved Permanently
場所: https://cli-assets.heroku.com/heroku-linux-x64.tar.gz [続く]
–2019-08-09 00:02:08– https://cli-assets.heroku.com/heroku-linux-x64.tar.gz
cli-assets.heroku.com:443 への接続を再利用します。
HTTP による接続要求を送信しました、応答を待っています… 200 OK
長さ: 29106114 (28M) [application/gzip]
`heroku-linux-amd64.tar.gz’ に保存中

100%[==============================================================================>] 29,106,114 1.58M/s 時間 16s

2019-08-09 00:02:24 (1.76 MB/s) – `heroku-linux-amd64.tar.gz’ へ保存完了 [29106114/29106114]

[vagrant@localhost ~]$ ls
centos6 heroku-linux-amd64.tar.gz index.php zend

[vagrant@localhost ~]$ ls
centos6 heroku-linux-amd64.tar.gz index.php zend
[vagrant@localhost ~]$ tar xf heroku-linux-amd64.tar.gz
[vagrant@localhost ~]$ ls
centos6 heroku heroku-linux-amd64.tar.gz index.php zend
[vagrant@localhost ~]$ sed -i -e “/^export PATH$/i PATH=\$HOME/heroku/bin:\$PATH” ~/.bash_profile
[vagrant@localhost ~]$ export PATH=$HOME/heroku-client/bin:$PATH
[vagrant@localhost ~]$ heroku –version
bash: heroku: コマンドが見つかりません
[vagrant@localhost ~]$ export PATH=$HOME/heroku/bin:$PATH
[vagrant@localhost ~]$ heroku –version
heroku/7.27.1 linux-x64 node-v11.14.0

おいおいおい

[vagrant@localhost .ssh]$ ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
65:0a:17:62:84:01:83:3e:c4:44:43:b5:e1:d0:63:0a vagrant@localhost.localdomain
The key’s randomart image is:
+–[ RSA 4096]—-+
|=B=+.++ . |
|Eo+=+. . . |
|+ oo. . . o |
| + o + |
| . S |
| |
| |
| |
| |
+—————–+
[vagrant@localhost .ssh]$ ls
authorized_keys id_rsa id_rsa.pub
なんか眠くなって来た。

Heroku account

get start Heroku account
Heroku

Heroku CLI
https://devcenter.heroku.com/articles/heroku-cli

To verify your CLI installation use the heroku –version command.

heroku-cli/5.6.12-1708b34 (windows-386) go1.7.4

heroku create

> heroku create
Creating app... done, radiant-dawn-72472
https://radiant-dawn-72472.herokuapp.com/ | https://git.heroku.com/radiant-dawn-72472.git

To ensure you have properly created this remote, run the git remote -v command, you should see something output that reveals the two remotes listed, origin and heroku.

> git remote -v
heroku  https://git.heroku.com/radiant-dawn-72472.git (fetch)
heroku  https://git.heroku.com/radiant-dawn-72472.git (push)
origin  https://github.com/udacity/galeria.git (fetch)
origin  https://github.com/udacity/galeria.git (push)

Once you have your application deployed, Heroku will provide you with a URL you can visit to see your site. After this you can run heroku open. This will open up a browser window and navigate to the deployed application URL.

https://dashboard.heroku.com/apps

If you want to change the URL of your application in the future you can do this by logging into your Heroku Dashboard. Many other actions are also available on the dashboard.

Deployment pipeline
development->staging->production

Heroku will allow you one “dyno” or single command container for free. This means that on a free tier Heroku account, you can run one command on the container and you will not be charged for it.