はじめに
ちょっとおもしろい記事を見つけました。
Python のフルスタックな Web アプリケーション開発フレームワークである「Django」で作ったアプリケーションを、まるごと Azure Functions に乗せてしまうというものです。
フルスタックな Web アプリケーションフレームワークで作った重厚なアプリが、サーバーレスな関数上で動くということで、なかなかおもしろそうです。
実際にやってみた
この記事をベースに、実際に Azure 上で動くか試してみます。
Azure SQL Database の作成
まず DB を Azure 上に作ります。元記事と同じように、サーバーレスで作っていきます。
サーバーは新規作成で、構成は以下の通り。
データベースの構成は下記のような感じで。
作成されたら、ファイアウォール設定を変えておきます。
これから作る Django のアプリケーションをローカルで動かす際もこの Azure 上の DB を見に行くので、ローカルからも接続できるようにしておきます。
ローカルで Azure Functions のプロジェクト作成
VS Code を開き、Azure Functions 拡張機能から Python の Function App を作っていきます。
関数の名前は DjangoTrigger
で。
承認レベルは必ず Anonymous
にします。
Functions プロジェクト内に Django アプリ作成
つづいて Django アプリを作っていきます。
venv を作ってから、
python -m venv venv
requirements.txt
を編集します。下3行を追記。
azure-functions django==4.0.2 mssql-django whitenoise
まとめて pip install します。
pip install -r requirements.txt
Django プロジェクトをつくります。
.
を末尾につければ直下に直接作ってくれます。
django-admin startproject config .
ここまででこのようになっていれば OK です。
Function → Django をつなぐ設定
Functions から Django が呼ばれるように設定していきます。このあたりは元記事にかいてあるとおりです。
DjangoTrigger/function.json
を以下のようにします("route":
の行を追加)。
{ "scriptFile": "__init__.py", "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get", "post" ], "route": "DjangoTrigger/{*path_info}" }, { "type": "http", "direction": "out", "name": "$return" } ] }
DjangoTrigger/__init__.py
を丸ごと書き換えます。
import azure.functions as func from config.wsgi import application def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse: return func.WsgiMiddleware(application).handle(req, context)
ここでいったん動作確認してみます。
func host start
ブラウザで開いてみても、まだ動きません。
より細かい設定をしていきます。Azure の SQL DB への接続もやってしまいます。
config/settings.py
の内容を編集していきます。
# (略) import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) FUNCTION_APP_PATH = 'api/DjangoTrigger' # 追加 # (略) ALLOWED_HOSTS = ['*'] # 変更 # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'whitenoise.runserver_nostatic', # 追加 ] 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': 'mssql', 'NAME': 'xxxx-xxxx-xxxx', # SQL DB の名称 'USER': 'xxxxxxxx', # サーバー管理者ログイン 'PASSWORD': '************', # サーバー管理者パスワード 'HOST': 'xxxxxxx.database.windows.net', # ホスト名 'PORT': '', 'OPTIONS': { 'driver': 'ODBC Driver 17 for SQL Server', }, } } # (略) STATIC_URL = '/' + FUNCTION_APP_PATH + '/static/' # 変更
つづいて config/urls.py
を書き換えます。
from django.contrib import admin from django.urls import path from config.settings import FUNCTION_APP_PATH # <- import our FUNCTION_APP_PATH constant from settings.py file urlpatterns = [ path(FUNCTION_APP_PATH + '/admin/', admin.site.urls), # <- change "path" to include FUNCITION_APP_PATH ]
ここまでできたら、もう一度起動してみます。
func host start
このタイミングで DB につなぐので、ODBC Driver がインストールされていないと起動できません。
インストールされていなければ、入れておきましょう。
自分が Mac でやったときは Xcode を最新化する必要があったりで結構面倒でした。
エラーなく起動したら、http://localhost:7071/api/DjangoTrigger/admin/
をブラウザで開いてみます。
以下のようなログイン画面が表示されれば、OK です(Django が動いてますね!)。
データベースへのテーブル作成
より本格的に使えるか試すため、SQL DB にテーブルを作っていきます。
python manage.py migrate
Mac でやったときはここで
[08001] [Microsoft][ODBC Driver 17 for SQL Server]Client unable to establish connection (0) (SQLDriverConnect)
というエラーが出てしまいました。どうも Mac に入っている OpenSSL が悪いらしく、LibreSSL から OpenSSL@1.1 にしたり
を参考にいろいろやったりしてなんとか解消しました(ちなみに Windows の環境でやったときはすんなりでした)。
ログインユーザー作成
うまく動いたら、続いてアプリの管理者ユーザーを作ります。
コマンドで作成します。
python manage.py createsuperuser
指示に従って、ユーザー名、メールアドレス、パスワードを設定します。
モデルクラス&テーブル作成
元記事にはないですが、新しいモデルクラスとテーブルも作ってみます。
作るテーブルは公式チュートリアルのものを使います。
python manage.py startapp polls
ファイルが作られるので、その中の polls/models.py
を以下のようにします。
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
config/settings.py
にさらに追記します。
INSTALLED_APPS = [ 'polls.apps.PollsConfig', # これを追加 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'whitenoise.runserver_nostatic', ]
コマンドを実行します。
python manage.py makemigrations polls
python manage.py sqlmigrate polls 0001
python manage.py migrate
Azure 上の SQL DB に、作ったモデルクラスにあったテーブルが作成されます。
最後に polls/admin.py
を以下のようにします。
from django.contrib import admin from .models import Question from .models import Choice admin.site.register(Question) admin.site.register(Choice)
ローカルでの動作確認
func host start
でローカルで実行して、以下の http://localhost:7071/api/DjangoTrigger/admin/
にアクセスしてみます。
createsuperuser
で作った ユーザー名とパスワードでログインすると、以下のようになります。
ちゃんと動いてるっぽいです!
Function App のデプロイ
ではいよいよ Azure に Function App としてデプロイしてみます。
Azure にサインインします。
「Deploy to Function App...」をクリック。
あとはプロジェクト、サブスクリプション、リージョンなどを指示通りに指定していけば作成・デプロイされます。
Azure 上での動作確認
デプロイ後は、Azure ポータルにいかずとも VS Code 上で関数 URL を得られます。
コピーした URL をもとにして、https://xxxxxxxxxx.azurewebsites.net/api/DjangoTrigger/admin/
となるように編集(大文字小文字に注意!)してアクセスしてみます。
ログイン画面
管理画面
データ追加
ちゃんと動いてます!
おわりに
このままでは URL に api などが入っていていまいちなのですが、Web アプリがまるごと Function App に乗るのはなかなかおもしろいですね。
Consumption プランで乗せてしまえば、コールドスタートの問題はあるものの低価格&スケーラブルに Django アプリが運用できます。
ちなみにログイン機能で使われるセッションキーについてですが、サーバー側はデータベースに保存されるようになっているので、サーバーレスでも運用できそうです。