知識の枝

"All is well"

Django ASGI仕様のアプリをデプロイ①

約159日前 2021年12月9日14:46
デジタル
ホームページ ConoHa Django Python

改訂履歴


2021/12/9 投稿

1. 非同期処理で高速化


Django3.2のAsync Viewを使ったアプリを開発したので、デプロイしてみました。

ASGI云々の設定が必要なのはこの記事の続編です。
ASGI部分だけ知りたい方は、お手数ですが下記の記事へ移動ください。
リンク
Django ASGI仕様のアプリをデプロイ②


Django3.1で追加されたAsync View(非同期ビュー)機能。

3.1以前のDjangoでは、ビューを作る際は関数ベース又はクラスベースで記述する方法しかありませんでした。

例 views.py
# 関数ベース
def IndexPage(request):
return render(request, "index.html")

# クラスベース
class IndexView(TemplateView):
template_name = "index.html"


これらは同期処理ですので、処理を非同期で実行することはできません。


そもそも非同期って何?という方は、

「python async await」「django asgi対応」などググってみると多少分かるようになると思います。


簡単に言うと、非同期化することでアプリの特定の処理を高速で実行できるようになります。

非同期の恩恵を受けられる例としては外部のWeb APIを利用する場合が挙げられます。


以前、外部APIを使用する記事を書きました。
リンク
Python Youtube APIを使って動画検索

この記事で紹介したYoutube APIが外部APIにあたります。


httpリクエストで使用するタイプのAPIを複数実行する場合、例えば上記のYoutube APIを5回連続で実行する場合を考えてみましょう。


同期関数で処理を記述します。
api.py
# APIの使い方の詳細は上記のリンクから
def youtube_api(keyword):

youtube_query = youtube.search().list(
part='id,snippet',
q=keyword,
type='video',
maxResults=50,
order='relevance',
)

youtube_response = youtube_query.execute()
results = youtube_response.get('items', [])

# 動画タイトルを羅列
for video in results:
print(video['snippet']['title'])


if __name__ == "__main__":
start = time.time()

keyword = "minecraft"

for i in range(5):
print("{}回目".format(i+1))
youtube_api(keyword)

print("total:", str(time.time() - start))


実行
コンソール
$ python api.py


結果
コンソール
1回目
Who LIVES IN THIS BIGGEST HOUSE UNDERGROUND in Minecraft ? SECRET HUGE BASE !
Testing Viral Minecraft Builds Hacks That 100% Work
青い化け物 vs セキュリティ【まいくら・マインクラフト】
Monster School : Herobrine and Girl Sad Love Story ( All Episodes Season ) Minecraft Animation
Scary Minecraft Legends Caught On Camera
Minecraft but there is CUSTOM XP
What IF LIGHT PORTAL OF STRANGEST TNT in Minecraft Challenge 100% Trolling
~~~
~~~
NIE WYBIERZ ZŁEGO LABIRYNTU SQUID GAME vs HUGGY WUGGY vs DZIEWCZYNY W MINECRAFT?! GPLAY & LUMI
¿Qué Pasaría si Minecraft BEDROCK no existiera?
Rex Và Bội Đôi Noob Thử Thách Đập 1000 Lucky Block Heal Ball Trong Minecraft Pixelmon !!!
BUNKER SEGURO vs LLUVIA DE FUEGO 😱🔥 MINECRAFT pero LLUEVE FUEGO del CIELO - INVICTOR
total: 2.8407552242279053


APIを5回実行するのに掛かった時間は約2.8秒でした。


これを非同期関数で実行するとおよそ1/5の時間で完了します。

上記の例ではYoutube APIへのリクエスト1回ごとに約0.56秒要しており、合計で2.8秒掛かっています。

1回のデータのやり取りに掛かっている0.56秒のうち、ほとんどがデータの通信時間です。

この通信時間の間はPythonは何もせず、ただ結果が返ってくるのを待ち続けています。


この待ち時間に他のタスクを実行するのが非同期処理です。

待ち時間に残り4回分のAPIリクエストを送ります。

それぞれ並列で0.56秒で結果が返ってきますので、全て完了するのに掛かる時間はトータルでも0.56秒となります。

これが非同期処理によって高速化する分かりやすい例です。



2. 今回のデプロイ内容


前置きが長くなってしまいました。

この記事のメインはあくまでアプリのデプロイなので、非同期ビューの書き方は別途記事を作ろうと思います。

下記がこの記事で解説するデプロイ環境の構成です。
  • ubuntu 20.04.3
  • nginx 1.18.0
  • gunicorn 20.1.0
  • uvicorn 0.15.0
  • Django 3.2.9
  • Python 3.9.5
  • PostgreSQL 12.9

手順としては
  1. 開発環境でアプリを作る(ここまで終わっている前提)
  2. サーバー契約
  3. ubuntuの初期設定
  4. ssh接続
  5. 最新版のPythonを入れる
  6. データベース(postgresql)を入れる
  7. nginxの動作確認
  8. Pythonの仮想環境を作る
  9. Django etc..をインストール
  10. gunicornの設定
  11. 静的ファイルの配信設定
  12. ドメインの取得&設定
  13. 常時SSL化
  14. 動作確認

かなりボリュームが大きくなるので、記事を分けて書きたいと思います。


本記事では1~7までを解説します。



3. デプロイ


3.1 - サーバー契約


何を始めるにしろまずは本番環境を構築するサーバーが必要です。

今回は VPS を選びました。

支払い料金が明確で、1,000円/月以下で運用できるなんて素晴らしい。

使いすぎたら課金、ストップし忘れたら課金・・・なんてことは有りませんので、安心してアプリをデプロイしたい方はVPS一択ですね。


VPSを契約できる業者は複数ありますが、私はいつもConoHaVPSを使っています。

リンク
ConoHa VPS
(上記は紹介リンクです。使って頂けると、このブログのサーバー維持費約2ヶ月分に充てられます)

選べるプランがいくつかありますが、VPSを選びましょう。


はじめて利用する方はアカウント登録、登録済みの方はログインしましょう。


サーバーは下記画像の構成でOKです。



あとはrootパスワードを設定しましょう。

このパスワードはサーバーへのログイン時に使用します。


ネームタグは自分が分かりやすい名前を付ければ大丈夫です。
例えば ”Django App” など


契約が完了したらubuntuの設定を行います。


3.2 - ubuntuの初期設定


契約したサーバーの管理画面からコンソールを起動します。



別ウィンドウでブラウザが起動し、コンソール画面が表示されます。

ログイン待ち状態になっていますので、最初はrootとしてログインします。



login: root
Password: サーバー契約時に設定した"rootパスワード"


ログインが成功すると入力待機モードになります。
root@IP:~# 



画面に文字がたくさん映っていて見にくい場合は、clearコマンドでリフレッシュできます。
root@IP:~# clear



rootは何でも出来るすごいアカウントですが、セキュリティ面で危険でもあります。

ですので、作業用のアカウントを別途作り、rootではログイン出来ない設定に変更します。

まずはユーザーアカウントの作成から。
root@IP:~# adduser ユーザー名


パスワードを設定します。
New password: 設定したいパスワード
Retype new password: 確認用にパスワードを再入力


ユーザー情報を登録します。
全部空欄でエンター連打でも問題ないです。
Enter the new value, or press ENTER for the default
Full name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n] # Yで確定



続いて作成したユーザーアカウントに管理者権限を与えます。
root@IP:~# gpasswd -a ユーザー名 sudo

成功すると下記のようにログが出力されます。
Adding user ユーザー名 to group sudo



作成したユーザーでログインします。
root@IP:~# su ユーザー名

ログインに成功すると表示が変わります。
ユーザー名@IP:/root$


もう一度rootに戻りたいときは
ユーザー名@IP:/root$ sudo su
で戻れます。


補足
以降、「ユーザー名@IP:/root$」の部分は「$」と省略します。
※ディレクトリを移動する際など必要に応じてそのまま書くこともあります。



aptというコマンドを使い、インストールされているソフトをアップデート情報を取得します。
$ sudo apt update
1分くらい待つと完了します。

続いて更新可能なソフトを更新します。
$ sudo apt upgrade
こちらは少々掛かります。


ここからは好みですが、Ubuntuの言語設定を日本語に変更可能です。

変更しなくても良いという方は3.3項まで飛ばして頂いて結構です。

日本語パックをインストールします。
$ sudo apt install -y language-pack-ja-base
ずらーっと出てきてインストールが完了します。

言語設定を確認します。
$ sudo localectl
System Locale: LANG=en_US.UTF-8
デフォルトでは英語です。

日本語設定に変更します。
$ sudo localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
$ sudo localectl
System Locale: LANG=ja_JP.UTF-8
LANGUAGE=ja_JP:ja
これでOKです。

タイムゾーンはデフォルトでAsia/Tokyoになっていますので、特にイジる必要は無いです。


3.3 - ssh接続


今後、アプリのメンテナンスなどを行っていく上で毎回ConoHaのダッシュボードからコンソールを起動するのは若干面倒くさいですよね。

そこで便利なのがSSH接続です。

SSH接続の設定を済ませておくと、パソコンから直接コンソールを起動したり、スマホからコンソールを使用できたりと何かと便利です。

SSH接続の設定ファイル"sshd_config"は /etc/ssh ディレクトリにあります。
ユーザー名@IP:~$ cd /etc/ssh
ユーザー名@IP:/etc/ssh$
目的のディレクトリに移動しました。

lsコマンドでファイルを確認してみます。
sshd_configファイルがありました。

このファイルをvimというエディタで編集します。

エディタをインストールします。
$ sudo apt install vim
インストールが完了したら早速先程のファイルを開いてみます。
$ sudo vim sshd_config
ファイルの中身が表示されます。


スクロールして内容を確認していくと、rootログインの許可項目があります。
PermitRootLogin yes
初期で"yes"になっているので、"no"に変更します。

vimエディタで文字を編集する際は i キーを押して編集モードに移ります。
PermitRootLogin no


書き換え終えたら ESC キーを押して編集モードを抜けます。

上書き保存してファイルを閉じます。: w qEnter

上書き保存が終わったら、sshdを再起動しておきましょう。
$ sudo systemctl restart sshd



続いてファイアウォールの設定です。

SSH接続を行うにはSSH接続用のポートを開ける必要があります。

Ubuntuでは標準でufwというファイアウォールが入っていますが、動いていません。
$ sudo ufw status
Status: inactive
ステータスコマンドで確認してみても、やはり動いていないことが分かります。

早速起動させましょう。
$ sudo ufw enable
Firewall is active and enabled on system startup


再度ステータスを確認してみます。
$ sudo ufw status
Status: active
起動しました。

以降の操作でufwの設定を変更するので、一旦ストップさせておきます。
$ sudo ufw disable
Firewall stopped and disabled on system startup


まずはSSH接続するポート番号を決めましょう。

SSH接続はデフォルトで22番のポートで行われます。

この番号は全世界共通なので、セキュリティ対策として別の番号を使用することをおすすめします。

番号の決め方は下記の記事が参考になると思います。

リンク
ホームページの作り方 - SSH接続の設定

具体的には49152番~65535番の中から適当に選べば問題ありません。
(ここでは例として55555番を使用します)


allowコマンドでufwのホワイトリストに55555番を追加します。
$ sudo ufw allow 55555
Rules updated

ついでにhttp接続(80番)とhttps接続(443番)のポートも開放しておきましょう。
$ sudo ufw allow 80
$ sudo ufw allow 443

改めてufwを起動し、ステータスを確認してみます。
$ sudo ufw enable
$ sudo ufw status
Status: active

To Action From
-- ------ ----
80 ALLOW Anywhere
443 ALLOW Anywhere
55555 ALLOW Anywhere
80 (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
55555 (v6) ALLOW Anywhere (v6)
全て問題無く追加されました。

SSH接続ポートをデフォルトの22から55555に変更したので、その変更を設定ファイルに反映します。

再度sshd_configファイルを開いて下記の部分を編集して上書き保存しましょう。
$ sudo vim sshd_config
# 変更前
#Port 22
# 変更後
Port 55555


これでSSH接続の設定は完了です。

試しにTera TermなどでSSH接続を試してみて下さい。



上手く接続できればこの項はOKです。


3.4 - 最新版のPythonを入れる


あえて "最新版のPython" というタイトルにしているのには理由があります。

このubuntuにはデフォルトでPythonがインストールされています。

が、最新版ではありません。確認してみましょう。
 python3 -V
Python 3.8.10
この記事を書いている時点でPython3.10までリリースされていますので、3.8.10はちょっと古いです。


Python3.10系をインストールできるか確認してみましょう。
$ apt show python3.10
N: Unable to locate package python3.10
N: Couldn't find any package by glob 'python3.10'
N: Unable to locate package python3.10
N: Couldn't find any package by glob 'python3.10'
E: No packages found
残念ながらPython3.10系は見つかりませんでした。

3.9系で探してみます。
$ apt show python3.9
Package: python3.9
Version: 3.9.5-3~20.04.1
...
こちらは見つかりました。このPython3.9の最新版(3.9.5)をインストールしたいと思います。
$ sudo apt install -y python3.9

インストールが終わったらpip関係もインストールしましょう。
$ sudo apt install -y python3-pip python3-dev


インストールは完了しましたが、このubuntuには2種類のPythonがインストールされたままの状態です。

新しくインストールしたPython3.9.5を優先して使用する設定に変更したいと思います。

Python3.9の優先度を1, Python3.8の優先度を2に設定します。
$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1
$ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 2
$ sudo update-alternatives --config python3
alternative python3 (/usr/bin/python3 を提供) には 2 個の選択肢があります。

選択肢 パス 優先度 状態
------------------------------------------------------------
0 /usr/bin/python3.8 2 自動モード
1 /usr/bin/python3.8 2 手動モード
* 2 /usr/bin/python3.9 1 手動モード

現在の選択 [*] を保持するには <Enter>、さもなければ選択肢の番号のキーを押してく ださい: 2
Python3.9の番号(上記の場合は2)を入力して Enter を押します。

設定が完了すると、
update-alternatives: /usr/bin/python3 (python3) を提供するためにマニュアルモードで /usr/bin/python3.9 を使います
と出力されます。

改めてPythonのバーションを確認します。
 python3 -V
Python 3.9.5
Python3.9系を使う設定になりました。


3.5 - データベース(postgresql)を入れる


DjangoはデフォルトでSQLite3というデータベースを使えますが、今回は使用しません。

代わりにPostgreSQLというオープンソースのデータベースを使います。

リンク
PostgreSQL 公式

ubuntuのコマンドでインストール可能です。
$ sudo apt install -y libpq-dev  postgresql postgresql-contrib
PostgreSQL本体とモジュール等をインストールしました。

インストールされたPostgreSQLのバージョンを確認してみましょう。
$ psql -V
psql (PostgreSQL) 12.9 (Ubuntu 12.9-0ubuntu0.20.04.1)
12.9でした。


早速起動してみます。
$ sudo -u postgres psql
psql (12.9 (Ubuntu 12.9-0ubuntu0.20.04.1))
Type "help" for help.

postgres=#
入力待機中になります。

データベースを作成します。名前は仮で「testdb」とします。
postgres=# CREATE DATABASE testdb;
CREATE DATABASE
コマンドを入力する際は、コマンドの最後に「セミコロン ";"」を忘れないようにしましょう。

次にユーザーを作成します。名前は仮で「testuser」とします。
postgres=# CREATE USER testuser WITH PASSWORD 'testpassword';
CREATE ROLE
こちらも同様に最後のセミコロンを忘れないように。

続いてユーザー設定を2つ変更します。

まずは文字セットをUTF-8に変更します。
postgres=# ALTER ROLE testuser SET client_encoding TO 'utf8';


次にタイムゾーンをAsia/Tokyoに変更します。
postgres=# ALTER ROLE testuser SET timezone TO 'Asia/Tokyo';


最後に先程作成したデータベースの全ての権限をユーザーに許可します。
postgres=# GRANT ALL PRIVILEGES ON DATABASE testdb TO testuser;
GRANT
これで完了です。

PostgreSQLのコンソールから抜けましょう。
postgres=# \q
バックスラッシュキーが¥マークになってしまっても問題ないです。

データベースの作成は一旦完了です。


3.6 - nginxの動作確認


Nginxの動作確認が取れたら一旦休憩です。

あと少しなので頑張りましょう!

Nginxとはウェブアプリケーションにおいてユーザー(クライアント)とアプリケーション(サーバー)を繋ぐ最初の窓口となる"ウェブサーバー"のことです。

ubuntuにインストールします。
$ sudo apt install -y nginx


単純に動作確認を行うだけであれば設定は簡単です。

3.3項のSSH接続設定の際にufwというファイアウォールの設定を行いましたよね?

その設定ファイルを編集し、Nginxの接続を許可したいと思います。
$ sudo ufw allow Nginx Full
Rules updated
これでNginxの接続が許可されました。

試しにブラウザにサーバーのIPアドレスを打ち込んでアクセスしてみましょう。
http://123.456.78.910 ←あなたのサーバーのIPアドレスに変更して下さい。


動作確認がとれました。



4. 一旦休憩


ここまでお疲れ様でした。

残り半分の作業は次の記事 Django ASGI仕様のアプリをデプロイ② で解説致します。


正直、前半の設定関係が面倒なだけなので後半はスムーズに進めば1時間も掛からず完了すると思います。

あと少しでアプリをデプロイできます!

休憩が終わったら、この勢いのまま全部終わらせましょう。