Python
Azure
VisualStudioCode
AzureFunctions
CosmosDB

Azure Functions App × Python で Azure CosmosDB にアクセスしてみる

Serverless で何か処理を行いたいときに便利な Azure Function App。

Web API から Azure CosmosDB をアップデートするという操作を行いたく、新しく対応した Python で実装してみましたので、手順を紹介します。


苦労したところ


  • Azure Functions で Python を利用できるのは Linux サーバー、Function App の開発をするのは Windows ということで


    • Visual Studio Code + Azure Functions Extention を利用して開発を進めていたのですが、仮想 Web サーバー(venv) が自動起動せず、結局手動で起動して進めました

    • Azure へデプロイする際に ローカルの Docker (Linux Container) が必要でした




実行環境


手順

1. Azure Function Python App を Azure 上に作成

2. Azure Function Python App をローカルで作成

- 2-1. Function Python App の新規作成

- 2-2. Function Python App のテスト

- 2-3. Function App で CosmosDB にアクセスする

3. ローカルで作成した Azure Function App を Azure にデプロイ


1. Azure Function App を Azure 上に作成

Azure Portal から Azure サブスクリプション が 紐づいたアカウントでサインインします。

[+新規作成] をクリック、Function App と検索して Function App を新規作成します。

Python を選択するには、Linux サーバーを選択する必要があります。

今回は PyCosmosFunc という名前で作成しています。

20181217_01.PNG


2. Azure Function App をローカルで作成


2-1. Function Python App の新規作成

Azure Function Core Tools (ターミナル) を使う方法と、Azure Function ツール (GUI) を使う方法、いずれかで Function の作成を行ってください。


2-1-a. Azure Function Core Tools を使う場合

ターミナルから作成を行います。

Visual Studio Code を開き、Open Folder をクリック、プロジェクトを配置する任意のフォルダーを指定して開きます。

20181217_02.PNG

Azure Function をローカルで作成、起動するには venv の環境が必要です。

ツールバーの [Terminal] > [New Terminal] でターミナルを開き、venv をインストールして起動します。

> python -m venv venv

> venv\scripts\activate

venv を起動した状態で、Azure Function App を作成します。

(venv)> func init

runtime は python を選択します。

Select a worker runtime: python

Azure Function のスターターテンプレートから App (Project) が作成されます。

20181217_03.PNG

Azure Function App に Function を作成します。

(venv)> func new

Template は HTTPTrigger, name は デフォルトのまま (HTTPTrigger) にします。

Select a template: HTTP trigger

Function name: [HttpTrigger]

Azure Function App 内に HttpRequest テンプレートから Function が作成されます。

20181217_04.PNG


2-1-b. Azure Function ツールを使う場合

Visual Studio Code に統合されたツールを利用します。

Visual Studio Code を開き、Azure Tools のタブを開きます。

Azure Function の Create New Project をクリックして Function App を作成します。

20181217_a02.PNG

Runtime は Python を選択します。

20181217_a03.PNG

今の Windows で開くには Open in current window を選択します。

20181217_a04.PNG

Azure Function をローカルで作成、起動するには venv の環境が必要です。

.env フォルダに venv がインストールされているので、Terminal を開いてコマンドプロンプトで起動します。

> .env\scripts\activate

再び Azure Tools のタブにある Azure Function の Create New Function をクリックして Function を作成します。

20181217_a05.PNG

作成した App のフォルダを選択します。

20181217_a06.PNG

Function の種類は HTTPTrigger を選択します。

20181217_a07.PNG

Function Name はデフォルト (HTTPTrigger) のままにします。

20181217_a08.PNG

Authorization Level は Functions を選択します。

20181217_a09.PNG

Azure Function App 内に HttpRequest テンプレートから Function が作成されます。

20181217_a10.PNG


2-2. Function Python App のテスト

Function App が起動すると、HttpTrigger > __init__.py が実行されます。

HTTP GET または POST を行うと、送信されたパラメータ(name)を取得して Hello {name}! と表示するという簡単なものです。


__init__.py

import logging

import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')

name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')

if name:
return func.HttpResponse(f"Hello {name}!")
else:
return func.HttpResponse(
"Please pass a name on the query string or in the request body",
status_code=400
)


ターミナルから起動して動作を確認します。

(venv)> func start

以下のようなメッセージが表示されたら起動完了です。ブラウザーを起動して HttpTrigger の URLにアクセスします。

Now listening on: http://0.0.0.0:7071

Application started. Press Ctrl+C to shut down.

Http Functions:

HttpTrigger: [GET,POST] http://localhost:7071/api/HttpTrigger

まず、なにもパラメータがない場合はメッセージが表示されます。

20181217_06.PNG

このようにパラメーターを追加すると名前が表示されるようになります。

http://localhost:7071/api/HttpTrigger?user=annie

20181217_07.PNG

ターミナルから Ctrl+C で一旦アプリを停止します。


2-3. Function App で CosmosDB にアクセスする

Azure Cosmos DB を操作する API を作成していきます。

パラメーターが取得できた場合、Azure Cosmos DB のデータベース、コレクション(コンテナー)を参照して、ドキュメント数を表示するようにします。

パラメーターは database, container とします。


Azure CosmosDB 用パッケージの追加

CosmosDB を操作するには、Python 用のモジュール(azure-cosmos)が公開されていますので、それを利用します。コマンドプロンプトから pip でインストールします。

(venv)> pip install azure-cosmos

このとき表示される azure-cosmos のバージョン(2018年12月現在、v3.0.2)を確認します。

(venv)> pip install azure-cosmos

Collecting azure-cosmos
 :
Installing collected packages: idna, certifi, chardet, urllib3, requests, azure-cosmos
Successfully installed azure-cosmos-3.0.2 certifi-2018.11.29 chardet-3.0.4 idna-2.8 requests-2.21.0 urllib3-1.24.1

この情報を requirements.txt に追加します。


requirements.txt

azure-cosmos==3.0.2



外部スクリプトの作成

HTTPTrigger と同じ階層に SharedScripts という名前でフォルダを追加、その中に Cosmos.py という名前でファイルを作成します。

Cosmos.py を記述します。ポイントは


  • import azure.cosmos.cosmos_client でモジュールをインポートしています

  • CosmosDBの接続情報は冒頭、config で設定しています。(※本来であれば、別途 config ファイル等に記載すべきですが、動作確認のため直接記載しています)


    • ENDPOINT および PRIMARYKEY にお持ちの Azure CosmosDB の設定を入力してください



  • CountDocuments で database および container を引数として、document の数を返します


cosmos.py

import azure.cosmos.cosmos_client as cosmos_client

def CountDocuments(database,container):
config = {
'ENDPOINT': 'YOUR_COSMOSDB_ENDPOINT',
'PRIMARYKEY': 'YOUR_COSMOSDB_KEY',
'DATABASE': database,
'CONTAINER': container
}

# Initialize the Cosmos client
client = cosmos_client.CosmosClient(url_connection=config['ENDPOINT'], auth={
'masterKey': config['PRIMARYKEY']})

# Connect to a database
database_link = 'dbs/' + config['DATABASE']
db = client.ReadDatabase(database_link)

# Connect to a container
container_definition = {
'id': config['CONTAINER']
}
container_link = database_link + '/colls/{0}'.format(config['CONTAINER'])
container = client.ReadContainer(container_link)

# Query these items in SQL
query = {'query': 'SELECT * FROM server s'}

options = {}
options['enableCrossPartitionQuery'] = True
options['maxItemCount'] = 2

result_iterable = client.QueryItems(container['_self'], query, options)
count = 0
for item in iter(result_iterable):
count = count +1

#return count
msg = str(count) + ' document(s) found in DB'
return msg



init.py の修整

database および container というパラメーターを取得して処理を行うように __init.py__ を修正します。ポイントは


  • 作成した SharedScripts > Cosmos.py から CountDocuments を import します

  • database および container のパラメーターが取得できた場合は CountDocuments を呼び出します

  • どちらかのパラメーターが取得できない場合は、パラメーターが必要な旨のメッセージを表示します


__init__.py

import logging

import azure.functions as func
from ..SharedScripts import cosmos

def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')

database = req.params.get('database')
container = req.params.get('container')
if not database:
try:
req_body = req.get_json()
except ValueError:
pass
else:
database = req_body.get('database')
if not container:
try:
req_body = req.get_json()
except ValueError:
pass
else:
container = req_body.get('container')

if database is not None and container is not None:
msg = cosmos.CountDocuments(database,container)
return func.HttpResponse(
msg,
status_code=200
)
else:
return func.HttpResponse(
"Please pass database and container name on the query string or in the request body",
status_code=400
)



再びテスト実行

ここで再度実行します。

(venv)> func start

HTTPTrigger の URL をブラウザーで開きます。

http://localhost:7071/api/HttpTrigger

パラメーターがないときは、パラメーターが必要な旨のメッセージが表示されます。

20181217_09.PNG

パラメーターとしてお持ちの Azure CosmosDB の Database 名と Container 名をセットします。

http://localhost:7071/api/HttpTrigger?database=YOUR_COSMOSDB&container=YOUR_CONTAINER

CosmosDB 内のドキュメント数が返されれば OK です。

20181217_10.PNG


3. ローカルで作成した Azure Function App を Azure にデプロイ

Linux 環境が必要となるため、Docker for Windows (Linux Container) を起動しておきます。

Visual Studio Code のターミナルから Azure CLI で Azure にサインインします。

> az login

ブラウザーが開くので、1. と同じアカウントでサインインします。

Login Success と表示されたら Visual Studio Code に戻ります。

複数サブスクリプションを持っている場合は、使用するサブスクリプション(YOUR_SUBSCRIPTION)をセットします。

> az account set --subscription YOUR_SUBSCRIPTION

Visual Studio Code のターミナルから Azure Function App (今回は PyCosmosFunc) へ Azure Function Core Tools を使ってデプロイします。

ポイントは、--nozip オプション および --build-native-deps オプションで、(Docker を利用して)ビルド後にデプロイすることです。

> func azure functionapp publish PyCosmosFunc --nozip --build-native-deps

以下のようなメッセージが表示されたらデプロイ完了です。

Upload completed successfully.

Functions in PyCosmosFunc:
HttpTrigger - [httpTrigger]
Invoke url: http://YOUR_FUNCTION_NAME.azurewebsites.net/api/httptrigger?code=YOUR_FUNCTION_ACCESSCODE

表示されている URL にブラウザーからアクセスします。code というパラメーターでアクセスコードが付与されています。

http://YOUR_FUNCTION_NAME.azurewebsites.net/api/httptrigger?code=YOUR_FUNCTION_ACCESSCODE

database と container のパラメーターを入力していないので、パラメーターが必要な旨のメッセージが表示されればOKです。

20181217_11.PNG

URL の後ろに &database=YOUR_DATABASE&container=YOUR_CONTAINER の形式でパラメーターを入力して再度アクセスします。

http://YOUR_FUNCTION_NAME.azurewebsites.net/api/httptrigger?code=YOUR_FUNCTION_ACCESSCODE&database=YOUR_DATABASE&container=YOUR_CONTAINER

Document の数が表示されれば完了です。

20181217_12.PNG