Python Tools for Visual Studio による Python Web ロールと Python worker ロール

重要

現在、Cloud Services (クラシック) は新しいお客様に対して非推奨となっており、2024 年 8 月 31 日に、すべてのお客様に対して廃止される予定です。 新しいデプロイでは、新しい Azure Resource Manager ベースのデプロイ モデル、 Azure Cloud Services (延長サポート) を使用してください。

この記事では、Python Tools for Visual Studio で Python Web ロールと Python worker ロールを扱う方法について概説します。 Python を使用する基本的なクラウド サービスを、Visual Studio を使用して作成およびデプロイする方法を学習します。

前提条件

Note

このチュートリアルを完了するには、Azure アカウントが必要です。 Visual Studio サブスクライバーの特典を有効にするか、無料試用版にサインアップしてください。

Python Web ロールと Python worker ロールについて

Azure には、アプリケーションの実行用として、Azure App Service の Web Apps 機能Azure Virtual MachinesAzure Cloud Services という 3 つのコンピューティング モデルが用意されています。 これら 3 つのモデルはすべて、Python をサポートしています。 Cloud Services には、Web ロールと worker ロールが含まれ、"サービスとしてのプラットフォーム (PaaS)" を提供します。 クラウド サービス内で、Web ロールは、フロント エンド Web アプリケーションのホスト専用のインターネット インフォメーション サービス (IIS) Web サーバーを提供します。worker ロールは、ユーザーの操作や入力とは関係なく、長期間または恒久的な非同期タスクを実行できます。

詳細については、「クラウド サービスとは」を参照してください。

Note

単純な Web サイトを構築する場合 シナリオが単純な Web サイトのフロント エンドにのみ関係している場合は、Azure App Service の軽量の Web Apps 機能を使用することを検討してください。 Web サイトの規模が増大し、要件が変化したときには、容易にクラウド サービスにアップグレードできます。 Azure App Service の Web Apps 機能の開発に関する記事については、Python デベロッパー センターを参照してください。

プロジェクトの作成

Visual Studio で、[新しいプロジェクト] ダイアログ ボックスの [Python] から [Azure クラウド サービス] を選択します。

[新しいプロジェクト] ダイアログ

Azure クラウド サービス ウィザードでは、新しい Web ロールまたは worker ロールを作成できます。

Azure クラウド サービス ダイアログ

worker ロール テンプレートには、Azure ストレージ アカウントまたは Azure Service Bus に接続するための定型コードが含まれています。

クラウド サービス ソリューション

Web ロールまたは worker ロールは、既存のクラウド サービスにいつでも追加することができます。 既存のプロジェクトをソリューションに追加するか、または新たに作成するかを選択できます。

ロール コマンドの追加

クラウド サービスには、異なる言語で実装されたロールを含めることができます。 たとえば、Django を使用して実装された Python Web ロールを、Python worker ロールや C# worker ロールと共存させることができます。 ロール間のやり取りは、Service Bus キューまたはストレージ キューを使用することで簡単に行うことができます。

クラウド サービスに Python をインストールする

警告

(この記事が最後に更新された時点で) Visual Studio と共にインストールされるセットアップ スクリプトは動作しません。 このセクションでは、回避策について説明します。

セットアップ スクリプトの一番の問題は、Python がインストールされないことです。 最初に、2 つのスタートアップ タスクServiceDefinition.csdef ファイルに定義します。 最初のタスク (PrepPython.ps1) は、Python ランタイムをダウンロードしてインストールします。 2 番目のタスク (PipInstaller.ps1) は、pip を実行して、すべての依存関係をインストールします。

以下のスクリプトは、Python 3.8 を対象に書かれています。 Python のバージョン 2.x を使用する場合は、2 つのスタートアップ タスクとランタイム タスクの PYTHON2 変数ファイルを on に設定してください: <Variable name="PYTHON2" value="<mark>on</mark>" />

<Startup>

  <Task executionContext="elevated" taskType="simple" commandLine="bin\ps.cmd PrepPython.ps1">
    <Environment>
      <Variable name="EMULATED">
        <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
      </Variable>
      <Variable name="PYTHON2" value="off" />
    </Environment>
  </Task>

  <Task executionContext="elevated" taskType="simple" commandLine="bin\ps.cmd PipInstaller.ps1">
    <Environment>
      <Variable name="EMULATED">
        <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
      </Variable>
      <Variable name="PYTHON2" value="off" />
    </Environment>

  </Task>

</Startup>

PYTHON2 変数と PYPATH 変数は、worker スタートアップ タスクに追加する必要があります。 PYPATH 変数は、PYTHON2 変数を on に設定した場合にのみ使用します。

<Runtime>
  <Environment>
    <Variable name="EMULATED">
      <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
    </Variable>
    <Variable name="PYTHON2" value="off" />
    <Variable name="PYPATH" value="%SystemDrive%\Python27" />
  </Environment>
  <EntryPoint>
    <ProgramEntryPoint commandLine="bin\ps.cmd LaunchWorker.ps1" setReadyOnProcessStart="true" />
  </EntryPoint>
</Runtime>

サンプルの ServiceDefinition.csdef

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="AzureCloudServicePython" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2015-04.2.6">
  <WorkerRole name="WorkerRole1" vmsize="Small">
    <ConfigurationSettings>
      <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" />
      <Setting name="Python2" />
    </ConfigurationSettings>
    <Startup>
      <Task executionContext="elevated" taskType="simple" commandLine="bin\ps.cmd PrepPython.ps1">
        <Environment>
          <Variable name="EMULATED">
            <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
          </Variable>
          <Variable name="PYTHON2" value="off" />
        </Environment>
      </Task>
      <Task executionContext="elevated" taskType="simple" commandLine="bin\ps.cmd PipInstaller.ps1">
        <Environment>
          <Variable name="EMULATED">
            <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
          </Variable>
          <Variable name="PYTHON2" value="off" />
        </Environment>
      </Task>
    </Startup>
    <Runtime>
      <Environment>
        <Variable name="EMULATED">
          <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated" />
        </Variable>
        <Variable name="PYTHON2" value="off" />
        <Variable name="PYPATH" value="%SystemDrive%\Python27" />
      </Environment>
      <EntryPoint>
        <ProgramEntryPoint commandLine="bin\ps.cmd LaunchWorker.ps1" setReadyOnProcessStart="true" />
      </EntryPoint>
    </Runtime>
    <Imports>
      <Import moduleName="RemoteAccess" />
      <Import moduleName="RemoteForwarder" />
    </Imports>
  </WorkerRole>
</ServiceDefinition>

次に、PrepPython.ps1 ファイルと PipInstaller.ps1 ファイルをロールの ./bin フォルダーに作成します。

PrepPython.ps1

このスクリプトは、Python をインストールします。 PYTHON2 環境変数を on に設定すると Python 2.7 がインストールされ、それ以外の場合は Python 3.8 がインストールされます。

[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
$is_emulated = $env:EMULATED -eq "true"
$is_python2 = $env:PYTHON2 -eq "on"
$nl = [Environment]::NewLine

if (-not $is_emulated){
    Write-Output "Checking if Python is installed...$nl"
    if ($is_python2) {
        & "${env:SystemDrive}\Python27\python.exe"  -V | Out-Null
    }
    else {
        py -V | Out-Null
    }

    if (-not $?) {

        $url = "https://www.python.org/ftp/python/3.8.8/python-3.8.8-amd64.exe"
        $outFile = "${env:TEMP}\python-3.8.8-amd64.exe"

        if ($is_python2) {
            $url = "https://www.python.org/ftp/python/2.7.18/python-2.7.18.amd64.msi"
            $outFile = "${env:TEMP}\python-2.7.18.amd64.msi"
        }

        Write-Output "Not found, downloading $url to $outFile$nl"
        Invoke-WebRequest $url -OutFile $outFile
        Write-Output "Installing$nl"

        if ($is_python2) {
            Start-Process msiexec.exe -ArgumentList "/q", "/i", "$outFile", "ALLUSERS=1" -Wait
        }
        else {
            Start-Process "$outFile" -ArgumentList "/quiet", "InstallAllUsers=1" -Wait
        }

        Write-Output "Done$nl"
    }
    else {
        Write-Output "Already installed"
    }
}

PipInstaller.ps1

このスクリプトは、pip を呼び出し、requirements.txt ファイル内のすべての依存関係をインストールします。 PYTHON2 環境変数を on に設定すると Python 2.7 が使用され、それ以外の場合は Python 3.8 が使用されます。

$is_emulated = $env:EMULATED -eq "true"
$is_python2 = $env:PYTHON2 -eq "on"
$nl = [Environment]::NewLine

if (-not $is_emulated){
    Write-Output "Checking if requirements.txt exists$nl"
    if (Test-Path ..\requirements.txt) {
        Write-Output "Found. Processing pip$nl"

        if ($is_python2) {
            & "${env:SystemDrive}\Python27\python.exe" -m pip install -r ..\requirements.txt
        }
        else {
            py -m pip install -r ..\requirements.txt
        }

        Write-Output "Done$nl"
    }
    else {
        Write-Output "Not found$nl"
    }
}

LaunchWorker.ps1 を変更する

Note

worker ロール プロジェクトの場合は、スタートアップ ファイルを実行するために LauncherWorker.ps1 ファイルが必要です。 Web ロール プロジェクトでは、スタートアップ ファイルがプロジェクト プロパティで定義されています。

bin\LaunchWorker.ps1 は本来多くの準備作業を行うために作成されていますが、実際には機能しません。 このファイルの内容を次のスクリプトに置き換えます。

このスクリプトは、Python プロジェクトの worker.py ファイルを呼び出します。 PYTHON2 環境変数を on に設定すると Python 2.7 が使用され、それ以外の場合は Python 3.8 が使用されます。

$is_emulated = $env:EMULATED -eq "true"
$is_python2 = $env:PYTHON2 -eq "on"
$nl = [Environment]::NewLine

if (-not $is_emulated)
{
    Write-Output "Running worker.py$nl"

    if ($is_python2) {
        cd..
        iex "$env:PYPATH\python.exe worker.py"
    }
    else {
        cd..
        iex "py worker.py"
    }
}
else
{
    Write-Output "Running (EMULATED) worker.py$nl"

    # Customize to your local dev environment

    if ($is_python2) {
        cd..
        iex "$env:PYPATH\python.exe worker.py"
    }
    else {
        cd..
        iex "py worker.py"
    }
}

ps.cmd

Visual Studio テンプレートによって、ps.cmd ファイルが ./bin フォルダーに作成されています。 このシェル スクリプトは、上記の PowerShell ラッパー スクリプトを呼び出し、呼び出された PowerShell ラッパーの名前に基づくログを提供します。 このファイルが作成されていない場合は、次の内容のファイルを作成します。

@echo off

cd /D %~dp0

if not exist "%DiagnosticStore%\LogFiles" mkdir "%DiagnosticStore%\LogFiles"
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Unrestricted -File %* >> "%DiagnosticStore%\LogFiles\%~n1.txt" 2>> "%DiagnosticStore%\LogFiles\%~n1.err.txt"

ローカルで実行する

クラウド サービス プロジェクトをスタートアップ プロジェクトとして設定し、F5 キーを押した場合、ローカルの Azure エミュレーター内でクラウド サービスが実行されます。

PTVS (Python Tools for Visual Studio) はエミュレーターでの起動をサポートしていますが、デバッグ操作 (ブレークポイントなど) は機能しません。

Web ロールまたは worker ロールをデバッグするには、対象となるロール プロジェクトをスタートアップ プロジェクトに設定したうえで、デバッグするようにしてください。 複数のスタートアップ プロジェクトを設定することもできます。 ソリューションを右クリックし、[スタートアップ プロジェクトの設定] を選択します。

ソリューション スタートアップ プロジェクト プロパティ

Azure に発行する

クラウド サービス プロジェクトを発行するには、対象のクラウド サービス プロジェクトをソリューション内で右クリックし、[発行] を選択します。

Microsoft Azure 発行サインイン

ウィザードに従って操作します。 必要に応じて、リモート デスクトップを有効にします。 リモート デスクトップは、デバッグの必要があるときに便利です。

設定が済んだら、[発行] をクリックします。

出力ウィンドウにいくつかの進行状況が表示された後、[Microsoft Azure のアクティビティ ログ] ウィンドウが表示されます。

Microsoft Azure Activity Log Window

数分経過するとデプロイが完了し、Web ロールまたは worker ロールが Azure 上で稼働状態となります。

ログを調査する

クラウド サービスの仮想マシンが起動され、Python がインストールされた後、ログにエラー メッセージが含まれているかどうかを確認します。 これらのログは、C:\Resources\Directory\{role}\LogFiles フォルダーに格納されます。 PrepPython.err.txt には、Python がインストールされているかどうかを検出しようとしたとき以降のエラーが少なくとも 1 つ含まれます。PipInstaller.err.txt には、pip のバージョンが古いことに関するエラーが含まれる場合があります。

次のステップ

Python Tools for Visual Studio で Web ロールまたは worker ロールを扱う方法の詳細については、次の PTVS 関連ドキュメントを参照してください。

Web ロールまたは worker ロールから Azure Storage や Service Bus などの Azure サービスを使用する方法の詳細については、次の記事を参照してください。