Micorosft Azureで作るPython製SlackBot開発 ー第3回:Azure FunctionsでPythonスクリプトの定期実行

IT技術 教養としてのテクノロジー入門

この記事について

この記事はプログラミング初心者をターゲットに、Microsoft Azureをつかって、WEBページの更新を検知したら、コミュニケーションアプリサービスのSlackにお知らせを出すBotの作り方を解説しています。

最終回である第3回はMicrosoft AzureのAzure Functionsを利用してこれまで作ってきたPythonのスクリプトをクラウド環境で定期実行させていきます。

第1回はSlackの通知とBeautifulSoup4を使ったスクレイピング処理を作りました。第2回はAzureのCosmos DBを使った最新投稿記事の管理処理を作りました。これまでの記事を読んでおらず、これらの知識に不安がある方はよろしければ以下の記事もあわせて読んでいただけますと幸いです。

目次

Azure Functionsとは

Azure Functionsとは、クラウド環境でPythonなどのスクリプトを実行するサービスです。

AWSではLamda、GCPではCloud FunctionsとAzure以外の3大クラウドでも同様のサービスは提供されています。これらはFaaS(Function as a Service)あるいは、サーバレスと呼ばれて近年注目され始めています。

従来このような処理を実装する場合、たとえちょっとした処理であってもサーバをわざわざ用意して作る必要がありました。しかしながらFaaSを利用することでサーバ構築は不要になり、ソースコードをクラウドサービスにアップロードするだけで作れるようになりました。

今回はAzureが提供するFaaSサービスであるAzure Functionsを使って、SlackBotのPythonスクリプトを定期実行させてみたいと思います。

なおFaaSができるものは今回紹介する定期実行だけではありません。FaaSの処理を実行させるURLを発行してWEBアプリなども作ることができます。Slack Botの機能としては不要のため今回は割愛しますが、もしもAzure Functionsに興味を持っていただけましたらこちらも試してみてください。

Visual Studio CodeでAzure Functionsを使える環境を作る

有料枠であればAzure Functionsの利用はすべてブラウザで完結しますが、無料枠でAzure Functionsを利用するにはVisual Studio Codeが必要になります。Visual Studio Codeで開発していない方はまずこちらをインストールしてください。

Visual Studio Codeが使えるようになったら続いて、Visual Studio CodeにAzure Functions拡張機能をインストールします。

インストールすると左のメニューに三角形のアイコンができます。

 

Azure FunctionsはVisual Studio Codeを利用してソースコードのアップロードやアプリの作成(=デプロイ)を行います。

Azure Functionsのプロジェクトを作成する

開発環境の準備が整いました。それではAzure Functionsの利用を開始していきます。

まず、先ほど拡張機能をインストールして表示された三角形のアイコンをクリックしてAzureの管理メニューを開きます。管理メニューの左上のフォルダアイコン(Create New Project)をクリックしてAzure Functionsのプロジェクトを作成します。

 

まず作業をするフォルダの選択をします。今回私はデスクトップ上にazure-functionというフォルダを作り、作業フォルダとして選択しました。

 

プログラミング言語の選択をします。これまでPythonでコードを書いてきたのでPythonを選択します。

 

ローカルでのPythonの実行環境を選択します。Skipでもいいですが、もしも今実行しているコマンドが表示されるのであれば、そちらを選んでも問題ありません。

 

FaaSで実行させるスクリプトがどのように実行されるか(=トリガー)を選択します。今回は定期実行なのでTimer Triggerを選択します。

 

作成するトリガーの名前を決めます。複数のトリガーを管理する場合は名前管理も重要ですが今回は1つしか作らないのでデフォルトの名前で問題ありません。

 

スクリプトの実行タイミングを設定します。スペースを空けて秒・分・時・日・月・週の実行タイミングを記載して設定します。

 

なお*という文字を入れると毎実行になります。さらに毎実行の*は/を使って割り算することで、XXX分おき、XXX時間おきと間隔をつけることができます。

上記画像の場合、5分おきに実行を意味します。

そのほか詳細な書き方は公式リファレンスに記載されていますので確認ください。

開発中は5分おき実行で問題ありませんが、頻繁に実行されすぎてしまうと、あっという間にAzure Functionsの無料枠を使い切り、当月分の実行ができなくなってしまいます。実際に稼働させるときは3時間おき実行など間隔を空けて使うようにしましょう。

定期実行の設定は変更可能ですので今回はデフォルトの5分おき実行で問題ありません。

最後にVisual Studio CodeのWindowを新しく開くか選択します。特に現在のWindowで作業をしていなければ「Open in current window」で支障はありません。

 

以上で設定が完了し、プロジェクトが作成されます。ソースコードや設定ファイルが自動的に作成されていきます。

 

以上でプロジェクトの作成は完了です。

Azure Functionsにスクリプトをデプロイする

プロジェクトができました。まずは作成されたプロジェクトのファイルの内容を確認しつつ、お試しでSlackにAzure Functionsが定期実行されていることを確認するだけのアプリをデプロイしてみましょう。

プロジェクトの自動作成ファイルの確認

プロジェクトで作成されたファイルの中で特に重要なものは以下の3つのファイルです。

  • __init__.py
  • function.json
  • requirements.txt

__init__.py

Azure Functionsで実行されるPythonコードのエントリポイントです。初期では以下のようになっており、単純にログを出すだけの処理となっています。


import datetime
import logging

import azure.functions as func

def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')

    logging.info('Python timer trigger function ran at %s', utc_timestamp)


このmain関数に実行したい処理を記載していくことが重要になっていきます。

function.json

Azure Functionsで実行されるスクリプトの設定が書かれています。プロジェクト作成時に定義したトリガーや最初に実行するPythonコードファイルが定義されています。実行間隔の変更や__init__.py以外のファイルから処理を実行させたい場合はこちらを修正していきます。


{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "mytimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 0 */3 * * *"
    }
  ]
}

requirements.txt

pipでインストールが必要なPythonのライブラリを定義します。デフォルトはazure-functionsしか記載されていません。


# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues

azure-functions

定期実行確認のお試しアプリのデプロイ

それではお試しアプリをデプロイしてみましょう。

まず、スクリプト実行のためにこれまでpipでインストールしたライブラリを追記していきましょう。※beautifulsoup4以下はお試しアプリでは不要ですがこの機会に追記しています。


# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues

azure-functions
requests
beautifulsoup4
azure-cosmos

続いて__init__.pyを以下のように修正します。このプログラムが実行されるとslack通知が飛ぶようにしています。


import datetime
import logging

import azure.functions as func
import requests
import json

def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')

    logging.info('Python timer trigger function ran at %s', utc_timestamp)

    # 以下Slack通知の処理を追加
    url = "https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXXXXXXXX";
    data = {"text": "Azure Functionが動きました。"}
    headers = {"Content-Type": "application/json"}

    response = requests.post(url, headers=headers, data=json.dumps(data))

    print(response.text)

コードの修正ができたらAzureにデプロイします。三角形アイコンのAzure管理メニューから、左上の「↑」アイコン(Deploy to Function App)を押してください。ここからデプロイにあたっての設定を定義していきます。

 

上の「Create new Function App in Azure」を選択してください。※なおこれまでAzureにVisual Studio Codeを通してログインしてなかった場合、先にログインのためのアカウント・パスワード入力が求められます。

 

作成するAzure Functionの名前を入力してください。任意の名前で大丈夫です。

 

Pythonの実行バージョンを指定します。基本はPCで実行しているバージョンと同一で問題ありません。

 

実行する場所を選択します。Cosmos DBと同様、日本でも問題ありませんが、アメリカにしておくと従量課金枠になったときにコストが下がります。Bot自体は日本からの実行速度を要求するものではないのでアメリカでも大きな支障はありません。

 

以上でAzureへのデプロイの設定は完了しデプロイが始まります。デプロイには5-10分ほど時間がかかります。デプロイが完了するとAzure Functionsのコンソールに作成されたアプリが表示されます。※私は「slack-practice」という名前で作ったため、「slack-practice」と表示されています。

 

初期設定、または、function.jsonに設定した実行間隔で以下のようにSlack通知がされれば成功です。

 

なお、デプロイされたPythonコードは手動実行も可能です。コンソールから作成したアプリをクリックして詳細を出した後、各のメニューから「関数」→「Timer Trigger1(作成したトリガー名が別名であればその名前)」→「コードとテスト」と移動してください。以下の画面になりアップロードしたコードが表示されます。

 

デプロイしたコードの上部の「テストと実行」をクリックすると手動実行ができます。ログも閲覧できるのでもしも動作がうまくいかない場合、こちらからエラーを調べることができます。

Slack通知Botをデプロイする

それでは最後の仕上げに前回まで開発してきた新着記事検知処理のPythonスクリプトを__init__.pyに追記しましょう。以下のように追記します。


import datetime
import logging

import azure.functions as func
import requests
import json
from bs4 import BeautifulSoup
from azure.cosmos import exceptions, CosmosClient, PartitionKey


def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')

    logging.info('Python timer trigger function ran at %s', utc_timestamp)

    #スクレイピング 最新記事タイトルを取得
    blogResponse = requests.get('https://miyau5555.info/')
    beautifulSoup = BeautifulSoup(blogResponse.text, 'html.parser')
    latestEntry = beautifulSoup.find_all("a", class_="blog-entry")[0].find("h2").get_text()

    #COSMO DBにアクセス
    endpoint = "https://xxxx.documents.azure.com:443/"
    key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=="

    #接続
    client = CosmosClient(endpoint, key)
    database = client.get_database_client("DATABASE_ID")
    container = database.get_container_client("CONTAINER_ID")

    #IDの値を取得
    query = "SELECT * FROM c WHERE c.id = 'latest-entry'"
    items = list(container.query_items(query, enable_cross_partition_query=True));
    item = items[0]


    # 最新記事が変わったかどうか確認
    if item.get("title") != latestEntry:
        # COSMODBの最新記事名を更新
        item["title"] = latestEntry
        container.replace_item("latest-entry", body=item)

        #Slack 通知
        slackApi = "https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXX";
        data = {"text": "最新記事 " + latestEntry + " が投稿されました。"}
        headers = {"Content-Type": "application/json"}

        response = requests.post(slackApi, headers=headers, data=json.dumps(data))


コードの修正が完了したらAzure管理メニューの「↑」アイコン(Deploy to Function App)を再度クリックしてください。一度デプロイしたので以下の画像のように先ほどデプロイしたAzure Functionsアプリ名が表示されています。今度は作成したアプリ名を選択してください。

 

上書きしてよいかの確認がでます。問題なければ「Deploy」をクリックします。クリックするとデプロイが始まります。

 

デプロイが成功すると、定期的に最新記事の投稿検知を行うようになります。

なお、記事更新がされないとSlack通知は来なくなるので、強制的にSlack通知を出したい場合は前回行ったようにCosmos DBの値を変えて強制的に更新がかかるようにしてください。

 

全三回にわたりましたが、これでSlackBotは完成しました。

終わりに

改めてシステム構成図を再掲しますが、様々なシステムと連携していることが改めてわかると思います。

 

今回はSlackBotという小規模なシステムでしたが、自前でサーバを立てたり、Azureのようなクラウドサービスを使いこなすことによって大きなシステムまで作ることができることがイメージできたのではと思います。

もしもシステム構築についてさらなる興味がありましたら、以下の記事でシステム構成のパターンなどを紹介しています。さらなる学習として読んでみていただけますと幸いです。

全3回にわたりましたが、最後まで読んでいただきありがとうございました。この記事を通じて少しでもPythonを使ったアプリ開発、システム構築の理解が深まっていただければ幸いです。

Photo by Artem Sapegin on Unsplash

 みやうデジタルラボ - にほんブログ村