Microsoft AzureのApp ServiceにPostgreSQLを使ったDjangoアプリをデプロイしようしたのですが、けっこう苦労したのでわかったことを書いておきます。
基礎知識
- Microsoft Azure
- Microsoftが提供するクラウドプラットフォーム(PaaS/IaaS)
- App Service
- AzureでWebアプリを提供するためのプラットフォーム (PaaS)
わかったこと
ドキュメントと色々試行錯誤してわかったことです。
- App ServiceのOSはデフォルトではWindows
- OSはLinuxも選択できるけど現在はプレビュー版という扱いで制限がある
- LinuxではDockerでのデプロイが可能
- デフォルトのデプロイメントスクリプトではDjangoの静的ファイル配置に対応している
- 選択可能なPythonバージョンは2.7または3.4のみ
- WindowsのPython3.4ではpipでインストールできないモジュールがある(psycopg2など)
- 拡張機能を使えば他のPythonバージョンをインストールできる
- この場合デプロイコマンド(pipを実行したりするコマンド)は自分で作る必要がある
OSがWindowsなのはMicrosoftなので当たり前なのですが、運用環境はLinuxというのが常識の世界で育った人だと見落としがちです。
公式ドキュメント
メニューからPythonに関するページを探すと以下4つがありました。
まずは1をやってみてから4を見るのがいいと思います。
2はチュートリアルとなってますが、プレビュー版であるLinuxにDockerイメージでデプロイするという特殊な使い方なので注意が必要です。
3はVisualStudioを使わないなら見なくてもいいです。
1. クイック スタート > Python アプリの作成
- Flaskを使ったシンプルなアプリをデプロイする手順が書かれています。
- 必要なファイルは全てGitのサンプルコードに含まれています。
- Azure上の操作はAzure CLI を使って行います。
- デプロイはローカルGitという方法で、git push するとデプロイが実行されます。
2. Tutorials (チュートリアル) > 1 - Python と PostgreSQL
Azure で Docker Python と PostgreSQL アプリを構築する
- FlaskでPostgreSQLを使ったアプリの例です。
- Dockerイメージでデプロイする方法が書かれています。
- サンプルコード
3. ハウツー ガイド > アプリの開発 > Python > MySQL を使用した Django アプリ
Python Tools 2.2 for Visual Studio を使用した Azure 上の Django と MySQL
- Python Tools 2.2 for Visual Studio(PTVS)を使った開発方法が書かれています。
4. ハウツー ガイド > アプリの開発 > ランタイムの構成 > Python
Azure App Service Web Apps による Python の構成
- デプロイに必要な以下のファイルの説明が書かれています。
- requirements.txt
- runtime.txt
- web.config
- ptvs_virtualenv_proxy.py
- .deployment
- deploy.cmd
Python3.6/Django/PostgreSQLのWebアプリをデプロイする
公式ドキュメント+αの情報で次のような手順でデプロイすることができました。
- ローカルでDjangoアプリを作成する
- AzureにPostgreSQLデータベースを作成する
- Azureに App Service を作成する
- App Service に拡張機能でPython3.6をインストールする
- デプロイ用ファイルを作成する
- Djangoアプリをデプロイする
※Azure では PostgreSQL の使用は有償になります。
1. ローカルでDjangoアプリを作成する
ふつうにDjangoアプリを作ります。
デプロイする際には以下の設定が必要です。
DEBUG = False
ALLOW_HOST = ['*']
STATIC_ROOT = 'static'
2. AzureにPostgreSQLデータベースを作成する
Azure で Docker Python と PostgreSQL アプリを構築する の「運用 PostgreSQL データベースを作成する」を参考に AzureにPostgreSQLデータベースを作成します。
AZ_GROUP="<resource_group>"
AZ_LOCATION="japanwest"
AZ_PG="<postgresql_name>"
AZ_PG_USER="<admin_username>"
AZ_PG_PASS="<admin_password>"
az postgres server create -g $AZ_GROUP -n $AZ_PG -l $AZ_LOCATION -u $AZ_PG_USER -p $AZ_PG_PASS
az postgres server firewall-rule create -g $AZ_GROUP --server-name $AZ_PG --start-ip-address=0.0.0.0 --end-ip-address=255.255.255.255 --name AllowAllIPs
データベースとユーザーを作成します。
$ psql -h ${AZ_PG}.postgres.database.azure.com -U ${AZ_PG_USER}@${AZ_PG} postgres
CREATE DATABASE <database_name>;
CREATE USER <user> WITH PASSWORD '<password>';
GRANT ALL PRIVILEGES ON DATABASE <database_name> TO <user>;
3. Azureに App Service を作成する
Azure に Python Web アプリを作成する を参考に以下のコマンドを実行します。
AZ_GROUP="<resource_group>"
AZ_LOCATION="japanwest"
AZ_APPSERVICE_PLAN="<plan_name>"
AZ_APPSERVICE_PLAN_TYPE="Free"
AZ_APP_NAME="<app_name>"
az group create -n $AZ_GROUP -l $AZ_LOCATION
az appservice plan create -n $AZ_APPSERVICE_PLAN -g $AZ_GROUP --sku $AZ_APPSERVICE_PLAN_TYPE
az webapp create -n $AZ_APP_NAME -g $AZ_GROUP --plan $AZ_APPSERVICE_PLAN --deployment-local-git
4. App Service に拡張機能でPython3.6をインストールする
Upgrading Python on Azure App Service – Python Engineering at Microsoft
Azure Portal の画面から作成した App Service を選択 > 拡張機能を選択 > インストールしたいPythonバージョンを選択してインストールします。
5. デプロイ用ファイルを作成する
以下のファイルを作成します。
- requirements.txt
- .deployment
- deploy.cmd
- web.config
Django
psycopg2
[config]
command = deploy.cmd
deploy.cmd
はデフォルトのものをコピーしてpython.exeやpipのパスをバージョンに合わせて変更します。
@if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off
:: ----------------------
:: KUDU Deployment Script
:: Version: 1.0.15
:: ----------------------
SET PYTHON_HOME=D:\home\python361x86
SET PYTHON=%PYTHON_HOME%\python.exe
SET PIP=%PYTHON% -m pip
:: Prerequisites
:: -------------
:: Verify node.js installed
where node 2>nul >nul
IF %ERRORLEVEL% NEQ 0 (
echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.
goto error
)
:: Setup
:: -----
setlocal enabledelayedexpansion
SET ARTIFACTS=%~dp0%..\artifacts
IF NOT DEFINED DEPLOYMENT_SOURCE (
SET DEPLOYMENT_SOURCE=%~dp0%.
)
IF NOT DEFINED DEPLOYMENT_TARGET (
SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot
)
IF NOT DEFINED NEXT_MANIFEST_PATH (
SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest
IF NOT DEFINED PREVIOUS_MANIFEST_PATH (
SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest
)
)
IF NOT DEFINED KUDU_SYNC_CMD (
:: Install kudu sync
echo Installing Kudu Sync
call npm install kudusync -g --silent
IF !ERRORLEVEL! NEQ 0 goto error
:: Locally just running "kuduSync" would also work
SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
)
goto Deployment
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Deployment
:: ----------
:Deployment
echo Handling python deployment.
:: 1. KuduSync
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
IF !ERRORLEVEL! NEQ 0 goto error
)
IF NOT EXIST "%DEPLOYMENT_TARGET%\requirements.txt" goto postPython
pushd "%DEPLOYMENT_TARGET%"
:: 4. Install packages
echo Pip install requirements.
%PIP% install -r requirements.txt
IF !ERRORLEVEL! NEQ 0 goto error
REM Add additional package installation here
REM -- Example --
REM env\scripts\easy_install pytz
REM IF !ERRORLEVEL! NEQ 0 goto error
:: 5. Copy web.config
IF EXIST "%DEPLOYMENT_SOURCE%\web.%PYTHON_VER%.config" (
echo Overwriting web.config with web.%PYTHON_VER%.config
copy /y "%DEPLOYMENT_SOURCE%\web.%PYTHON_VER%.config" "%DEPLOYMENT_TARGET%\web.config"
)
:: 6. Django collectstatic
IF EXIST "%DEPLOYMENT_TARGET%\manage.py" (
echo Collecting Django static files. You can skip Django specific steps with a .skipDjango file.
IF NOT EXIST "%DEPLOYMENT_TARGET%\static" (
MKDIR "%DEPLOYMENT_TARGET%\static"
)
%PYTHON% manage.py collectstatic --noinput --clear
)
popd
:postPython
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto end
:: Execute command routine that will echo out when error
:ExecuteCmd
setlocal
set _CMD_=%*
call %_CMD_%
if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%
exit /b %ERRORLEVEL%
:error
endlocal
echo An error has occurred during web site deployment.
call :exitSetErrorLevel
call :exitFromFunction 2>nul
:exitSetErrorLevel
exit /b 1
:exitFromFunction
()
:end
endlocal
echo Finished successfully.
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="PYTHONPATH" value="D:\home\site\wwwroot" />
<add key="WSGI_HANDLER" value="django.core.wsgi.get_wsgi_application()" />
<add key="WSGI_LOG" value="D:\home\LogFiles\wfastcgi.log" />
<add key="DJANGO_SETTINGS_MODULE" value="project.settings" />
</appSettings>
<system.webServer>
<handlers>
<remove name="Python27_via_FastCGI" />
<remove name="Python34_via_FastCGI" />
<add name="Python FastCGI"
path="handler.fcgi"
verb="*"
modules="FastCgiModule"
scriptProcessor="D:\home\python361x86\python.exe|D:\home\python361x86\wfastcgi.py"
resourceType="Unspecified"
requireAccess="Script" />
</handlers>
<rewrite>
<rules>
<rule name="Static Files" stopProcessing="true">
<conditions>
<add input="true" pattern="false" />
</conditions>
</rule>
<rule name="Configure Python" stopProcessing="true">
<match url="(.*)" ignoreCase="false" />
<conditions>
<add input="{REQUEST_URI}" pattern="^/static/.*" ignoreCase="true" negate="true" />
</conditions>
<action type="Rewrite" url="handler.fcgi/{R:1}" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
DJANGO_SETTINGS_MODULE
の部分はDjangoプロジェクトに合わせて変更してください。
6. Djangoアプリをデプロイする
AZ_USER="<user>"
AZ_PASS="<password>"
az webapp deployment user set --user-name $AZ_USER --password $AZ_PASS
git remote add azure <git_url>
git push azure master
gitのURLはaz webapp create
したときに表示されます。
ポータルの App Service の概要でも確認できます。
ブラウザで<app_name>.azurewebsites.net/admin
にアクセスしてadminのログイン画面が表示されれば完了です。
感想
公式ドキュメントの構成のせいでかなり振り回されました。
チュートリアルの部分が通常と全く違うやり方を説明しているので混乱しました。。。