Closed52

AzureにDjangoで作ったwebアプリのContainerをデプロイしてみる

いばらきいばらき
  • djangoインストール
poetry add django
  • プロジェクト作る
poetry run django-admin startproject sampleProject .
  • sampleアプリ追加
poetry run python manage.py startapp sample
sampleProject/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'sample', # add
]
  • sampleアプリを適当に書く
sample/views.py
from django.http import HttpResponse

def index(request):
    return HttpResponse('Hello Sample')
  • ルーティング
sampleProject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('sample/', include('sample.urls')) # add
]
sample/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index),
]
いばらきいばらき
  • migrate
poetry run python manage.py migrate
  • 起動
poetry run python manage.py runserver

OK

いばらきいばらき
  • docker
Dockerfile
FROM python:3.8-slim-buster

ENV PYTHONUNBUFFERED=1
EXPOSE 8000

WORKDIR /app
COPY . .

RUN pip install poetry
RUN poetry config virtualenvs.create false
RUN poetry update
RUN python manage.py migrate
RUN chmod 744 ./startup.sh

ENTRYPOINT ["./startup.sh"]
startup.sh
#!/bin/bash
python manage.py runserver 0.0.0.0:8000
docker build -t sample-django .
docker run -p 8000:8000 sample-django
いばらきいばらき
  • gunicornを使う
poetry add uvicorn gunicorn
startup.sh
#!/bin/bash
#python manage.py runserver 0.0.0.0:8000
gunicorn sampleProject.asgi:application -w 2 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
いばらきいばらき
  • App Service Plan作る

  • 料金プランが無料だとたぶん積むのでB1にしておく

    • 本格的に使うならP系のプラン必須ですが、試すだけだと高いので、、、
  • 結局P1V2プランにしました。

    • P系でないとVNet統合が使えなくてこの先で詰んだので
    • ちょっと試したいだけなのに月額1万です。こういうところ、Azureの学習コストって最悪ですよね!
いばらきいばらき
  • ContainerRegistryの管理ユーザー有効化
    • これしないとWebAppsと連携できない
いばらきいばらき

いや、これだと、直接WebAppsにデプロイするのか。
それでもいいけど、、、

いばらきいばらき
  • AzureにWebApps追加
    コンソールを日本語にしてると逆に検索しづらいんだよなぁ。。。


いばらきいばらき

フロントエンド・ルーティング・バックエンドを適当に設定

  • 本当はhttpsにするべきだけど、今回はサンプルなのでhttpで。
  • WAFも今回は使わない。高いので。
いばらきいばらき

Djangoにhostの設定を入れる必要があるっぽい

sampleProject/settings.py
ALLOWED_HOSTS = ['*.azurewebsites.net']

これで行けるか?
→ダメでした。。。

いばらきいばらき

とりあえず、

sampleProject/settings.py
ALLOWED_HOSTS = ['*']

にしたら、WebApps上は大丈夫になったんだけど、Application Gatewayからだと502になるわ。
なんでよ、、、

いばらきいばらき

よく分からんけど、Application Gatewayのレベルを'Standard'でなくて'Standard V2'で作り直したらうまくいった

あ、このキャプチャは、後から説明用に撮ったんですけど、リージョンがEast Asiaになってますね。
Japan Eastで作ってます。

  • 「正常性プルーブ設定したから動くようになった。」が正しそう。
    • ルーティングの設定で直下は404だったから、ヘルスチェックでエラーになってたっぽい
    • こんな感じで、200になるsample配下に正常性プルーブを向けたら動いた
いばらきいばらき

Django初めて触ったんだけど、settings.pySECRET_KEYとかいう不穏な項目があるじゃないですか。。。
これgitに上げちゃダメなやつじゃないですか。。。

  • もう遅いけど、環境変数に隠しました。
    • ついでにローカル用にpython-dotenv入れました
poetry add python-dotenv
sampleProject/settings.py
import os
from dotenv import load_dotenv
load_dotenv(verbose=True)

SECRET_KEY = os.getenv('SECRET_KEY')

(参考) :
DjangoのSECRET_KEYを再生成
https://qiita.com/frosty/items/bb5bc1553f452e5bb8ff

いばらきいばらき

DjangoのHOSTの設定はこれで良さそう。

sampleProject/settings.py
ALLOWED_HOSTS = ['(WebApps名).azurewebsites.net']
いばらきいばらき
  • VNetにAzureWebAppsからのVNET統合用のサブネットを作る
  • VNetにAzure StorageへのPrivate Endpoint用のサブネットを作る
いばらきいばらき

Storageのリソース作って、ネットワーク->プライベートエンドポイント接続で設定

いばらきいばらき

Storageのネットワーク->ファイアウォールと仮想ネットワークで、VNET統合用のサブネットからのアクセスを許可

いばらきいばらき

WebAppsのリソースに移動して、[構成]->[パスのマッピング]から、Azure Storageマウントを追加する

  • privateで繋ぐ場合、Basicだとダメなので詳細設定

  • BLOBはRead only、書き込みしたい時はAzure Files

  • WebAppsで設定するAzure Filesは、AzureStorageの「ファイル共有」のこと(分かるかこんなもん!!!)

  • WebAppsで設定する「共有名」は、 AzureStorageのファイル共有の「名前」にあわせる(分かるかこんなもん!!!)

いばらきいばらき

これでつながった。

なお設定をミスると、Containerが起動すらしないので注意。
起動すらしないので、ログも見れません。。。「共有名」の設定間違って立ち上がらなくて意味不明でつらかった。

あと、なんでプライベート接続なのに、アクセスキー必要なんだろう?
ロールベースのアクセスをしてほしい。

いばらきいばらき

AzureなのでMSSQL使う。
ローカル用にdocker立ち上げる

docker-compose.yml
version: "3"

services:
  db:
    platform: linux/x86_64
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment:
      ACCEPT_EULA: Y
      MSSQL_SA_PASSWORD: P@ssw0rd
      TZ: "Asia/Tokyo"
    volumes:
      - ./volumes/db/data:/var/opt/mssql
    ports:
      - 1433:1433

ローカルだから適当でいいやと思ってパスワードをpasswordにしたら起動出来なかった。
ちゃんとチェックしていやがる。

いばらきいばらき

aldjemyを入れる

% poetry add aldjemy
Using version ^2.6 for aldjemy

Updating dependencies
Resolving dependencies... (6.0s)

Writing lock file

Package operations: 3 installs, 0 updates, 0 removals

  - Installing greenlet (1.1.2)
  - Installing sqlalchemy (1.4.34)
  - Installing aldjemy (2.6)
いばらきいばらき

mssql-djangoを入れる

% poetry add mssql-django
Using version ^1.1.2 for mssql-django

Updating dependencies
Resolving dependencies... (0.9s)

Writing lock file

Package operations: 3 installs, 0 updates, 0 removals

  - Installing pyodbc (4.0.32)
  - Installing pytz (2022.1)
  - Installing mssql-django (1.1.2)
いばらきいばらき

settings.pyを編集

settings.py
ALDJEMY_ENGINES = {
    "mssql": 'mssql+pyodbc'
}
DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.getenv('MS_DB_NAME', ''),
        "USER": os.getenv('MS_DB_USER', ''),
        "PASSWORD": os.getenv('MS_DB_PASSWORD', ''),
        "HOST": os.getenv('MS_DB_HOST', 'localhost'),
        "PORT": os.getenv('MS_DB_PORT', '1433'),
        "OPTIONS": {
            "driver": os.getenv('MS_DB_DRIVER', 'ODBC Driver 18 for SQL Server'),
        },
    },
}
いばらきいばらき

Djangoモデルに対して、、、

models.py
class User(models.Model):
    id = models.IntegerField(primary_key=True)
    username = models.CharField(max_length=16)
    email = models.EmailField(max_length=254)
    password = models.CharField(max_length=128)

SAが生えていて、SQLAlchemyモデルとして使える!!

users.py
from sample.models import User

User.sa.query().all()
いばらきいばらき

中身を取り出し。もうちょっとキレイに書けないか?

users.py
users = [{k: v for k, v in u.__dict__.items() if k != "_sa_instance_state"}
         for u in User.sa.query().all()]
いばらきいばらき

それはそうとローカルで立ち上げたdockerのSQL Serverだとエラーになるんだよなぁ、、、

django.db.utils.OperationalError: ('08001', '[08001] [Microsoft][ODBC Driver 18 for SQL Server]SSL Provider: [error:0A000086:SSL routines::certificate verify failed:self-signed certificate] (-1) (SQLDriverConnect)')

Azure上のSQL Databaseだと動くのに!!

なんで?

いばらきいばらき

localのsql serverでsslのエラーになる件の対応。
DATABASESOPTIONS"extra_params": "TrustServerCertificate=yes",を追加。

全然情報なくて、くっそ強敵でした。。。TrustServerCertificate=yesのオプションを入れればいいことは割とすぐに分かったのですが、オプションを入れる場所がさっぱり分からんかったわ。。。

setting.py
ALDJEMY_ENGINES = {
    "mssql": 'mssql+pyodbc'
}
DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.getenv('MS_DB_NAME', ''),
        "USER": os.getenv('MS_DB_USER', ''),
        "PASSWORD": os.getenv('MS_DB_PASSWORD', ''),
        "HOST": os.getenv('MS_DB_HOST', 'localhost'),
        "PORT": os.getenv('MS_DB_PORT', '1433'),
        "OPTIONS": {
            "driver": os.getenv('MS_DB_DRIVER', 'ODBC Driver 18 for SQL Server'),
            "extra_params": "TrustServerCertificate=yes",
        },
    },
}
このスクラップは2022/11/03にクローズされました