birabiraのめも

忘れっぽいのでここにメモをしていきます

Azure Functions、Python、VSCodeで動くところまでやってみた

Azure Functionsを個人的に使う機会がありましたので備忘録的にもブログに残しておきたいと思います。サーバーレスアプリケーションの開発って楽と聞いていただけに実際に触って理解することの重要さを学ぶことができました。そして私はクラウド初心者のため、初心者がググりまくってたどり着いたものだということを心に留めていただけると助かります。

この記事では次のトピックについて取り扱います。

  • Azure Functionsの概要&セットアップ
  • Azure DevOps&VSCode&Pythonを用いた開発環境の構築
  • ローカルでの実行&デバッグ
  • Pipeline&Releaseを利用したデプロイ
  • 本番環境での動作確認

Azure Functionsの概要

Azure FunctionとはAWSでいうlambdaのようなサーバーレスでスクリプトを動作させることができるPaaSです。サーバーやランタイムの管理を行う必要がなく、必要なスクリプトだけを実行することができるので様々な管理の手間が省けます。

詳細については公式サイトを読んで見てください。

Azure Functions – コンピューティングでのサーバーレス関数 | Microsoft Azure

これを見てもよくわからないという人は「Azure アーキテクチャを参照する」のページを見てみましょう。このページにはAzureの各コンポーネントを利用した際の構成図集がまとまったサイトで様々なアーキテクチャを視覚的に確認することができます。

Azure アーキテクチャを参照する - Azure Architecture Center | Microsoft Learn

例えば次のURLのアーキテクチャを見てみましょう。

PDF フォーム処理の自動化 - Azure Architecture Center | Microsoft Learn

構成図(https://learn.microsoft.com/ja-jp/azure/architecture/ai-ml/architecture/automate-pdf-forms-processingより引用)

この構成図ではAzure Logic AppsからAzure Functionsが呼び出されています。④、⑤の説明を読んでみると、Azure Logic AppsからAzureFunctionsを呼び出して、PDFファイルに対して処理を行うことが説明されています。

このような形で大量にあるアーキテクチャからどれがどのようなことをしているのかを紐解いていくと個人的にはわかりやすかったです。

トリガーとバインド

Azure Functions のトリガーとバインド | Microsoft Learn

Azure Functionsでは、なんのイベントを持って実行されるのかを取りが、のAzureサービスに接続するかをバインドと言っているようです。まあ、ドキュメントを読んでもらったほうが早いので読んでみてください。

Azure Functionsのセットアップ

なにはともあれ、まずはAzure Functionsのセットアップを行ってみましょう。すでにMicrosoftアカウントが設定されており、サブスクリプションが設定されている前提で話を進めていきます。マイクロソフトのドキュメントではいきなりVSCodeからAzure Functionsのプロジェクトを作っているようですが私はWebから行きたいと思います。

Visual Studio Code を使用して Python 関数を作成する - Azure Functions | Microsoft Learn

関数アプリの作成

画面上部の検索窓から関数アプリを選択し、関数アプリの画面を開きます。そして、「作成+」をクリックします。

関数アプリの検索

適当に内容を入力し、作成します。本来であればネットワーク等の設定も行うべきですが、テストなので一旦割愛します。

作成画面

デプロイ完了後、再度関数アプリ画面を開くと追加した関数アプリが作成されているはずです。ここではまだ関数が追加されていないため、関数を作成のようなメッセージが出ています。

関数アプリ画面

指定されているURLにアクセスしてみてください。次の画像の通りつながればOKです。

 

さらに、Azure Functionの本番環境の構成を設定していきます。必要な設定は次のとおりです。

  • ストレージアカウントの設定が必須です。ストレージアカウントを作成後に、接続文字列を取得してから、関数アプリの構成のAzureWebJobsStorageとして設定してください
  • AzureWebJobsSecretStorageTypeblobがv2では推奨値なので修正します
  • AzureWebJobsFeatureFlagsEnableWorkerIndexing

設定完了後、保存をクリック。ここの設定が必要かどうなのかを全く知らず超絶時間を浪費しました。

ローカルの開発環境のセットアップ

VSCodeのインストールはすでに済んでいると言うことにして、拡張機能を入れていきます。気がついたら入っていたものもありましたが、主に次の3つをインストールすればOKです。

  • Azure Account
  • Azure Functions
  • Azurite

また、Pythonのコードの開発を行うため、Pythonに関連する拡張機能を入れておくと便利でしょう。

Azureの拡張機能をインストールすると画面左にAzureのマークが現れるのでクリックし、「Sign in to Azure」からサインインしてください。

サインイン後、「Function App」から先程作成したAppが見えていればうまく行っているでしょう。

Azure DevOps

まあ、GitHubみたいなもんです。

先程の関数Appと同様に画面上部の検索窓からDevOpsと検索し、「Azure DevOps Organizations」を選択し、「Azure DevOps Organizations」へ行きます。

画面が切り替わると思うので、新しい組織を作成し、プロジェクトを作成します。だいぶ端折りましたが、次の画面のようになればOK.

プロジェクト画面

画面左の「Repos」から「Files」を開きローカルにCloneしておくと良きです。クレデンシャルはVSCode側で利用するのでボタンを押して発行しておきましょう。

任意のフォルダで構いませんので、VSCodeでCloneできたらOKです。

関数の作成とデバッグ

ここまでくればコードを作ってローカルで動かしていきたいと思います。

ツールを使ったテンプレートの生成

Cloneしたフォルダを開いた状態で、VSCodeのAzureの拡張画面を開きます。画面下の方に「WORKSPACE」があるので、Azure Functionsのマークから、Create New Projectを選択します。プロンプトが現れるので適当に回答します。

設定内容

  1. Select the folder that will contain your function project: すでにcloneしたフォルダが選ばれていると思うので選択
  2. Select a language: Python
  3. Select a Python programming model: Model V2
  4. Select a Python interpreter to create a virtual environment: PCにインストールされているPythonベースでvenvが作られます
  5. Select a template for your project's first function: HTTP trigger
  6. Name of the function ~~~: http_trigger
  7. Authorization level ~~~~: FUNCTION

VSCodeエクスプローラーを確認し、function_app.pyを始めファイルが生成されていればOK.

Azure FunctionsのPythonバージョンではこの、function_app.pyに含まれるコードから実行を始めます。functionを分割するblueprintなど機能は存在していますが、今回はお試しのためそこまでは行きません。

ここまでくればあとは普通のPythonコードなのでモジュール分割しても普通に動きます。

ローカルの設定変更

local.settings.jsonが生成されているので、AzureWebJobsStorageに値を挿入します。UseDevelopmentStorage=trueの固定値でOKです。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "AzureWebJobsFeatureFlags": "EnableWorkerIndexing"
  }
}

ローカルでの実行

先程のVSCodeの拡張を使ったテンプレートの生成を行うと、.vscodeが生成され、launch.jsonを生成してくれます。なので、普通に実行してみます。

Failed to ~~~とか出ますが、Debug anywayしか押したことがありません。※このエラーは本来Azure Functionsではストレージアカウントの接続が必要ですが、ローカルの設定変更で変更した通りローカルのStorageを使います。これを制御してくれるのが拡張機能としてインストールしたAzuriteというツールなのですが、HTTP Triggerでは使わないようなので割愛!

ターミナル画面で、venvのセットアップが行われ、ローカルで実行されます。このとき実行されるコマンドはfunc host startfuncがazureのコマンドラインツールです。HTTP triggerのため、localhostで待ち受けてくれたようです。

 *  実行するタスク: .venv\Scripts\python -m pip install -r requirements.txt 

Collecting azure-functions
  Using cached azure_functions-1.17.0-py3-none-any.whl (165 kB)
Installing collected packages: azure-functions
Successfully installed azure-functions-1.17.0
WARNING: You are using pip version 22.0.4; however, version 23.2.1 is available.
You should consider upgrading via the 'C:\Users\XXXXXX\XXXXXXXXX\biratest_functions\.venv\Scripts\python.exe -m pip install --upgrade pip' command.
 *  ターミナルはタスクで再利用されます、閉じるには任意のキーを押してください。 

 *  実行するタスク: .venv\Scripts\activate ; func host start 

Found Python version 3.10.5 (py).

Azure Functions Core Tools
Core Tools Version:       4.0.5348 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.24.5.21262

[2023-10-01T08:57:32.481Z] Worker process started and initialized.

Functions:

        http_trigger:  http://localhost:7071/api/http_trigger

For detailed output, run func with --verbose flag.

表示されたURLに対してアクセスすると次の通り、うまく動くはずです。

この方法で開発を進められればいいですが、VSCode側で実行している状態でソースコードを修正するとエラーで終了してしまいます。その際にはVSCode側でデバッグはできなくなるものの、ターミナルでfunc host startをしてやるとソースコードを保存しても終了することなく再起動してくれます。何かいい方法があれば教えてください。

PS C:\Users\bira\asdf\biratest_functions> .\.venv\Scripts\activate
(.venv) PS C:\Users\bira\asdf\biratest_functions> 
(.venv) PS C:\Users\bira\asdf\biratest_functions> 
(.venv) PS C:\Users\bira\asdf\biratest_functions> 
(.venv) PS C:\Users\bira\asdf\biratest_functions> func host start
Found Python version 3.10.5 (py).

Azure Functions Core Tools
Core Tools Version:       4.0.5348 Commit hash: N/A  (64-bit)
Function Runtime Version: 4.24.5.21262

[2023-10-01T12:41:47.463Z] Worker process started and initialized.

Functions:

        http_trigger:  http://localhost:7071/api/http_trigger

For detailed output, run func with --verbose flag.
[2023-10-01T12:41:53.166Z] Worker process started and initialized. # ソースコードを修正して保存すると再スタートする
[2023-10-01T12:41:58.932Z] Worker process started and initialized. # ソースコードを修正して保存すると再スタートする

デプロイメント

コードができたと思うのでデプロイしてみましょう。デプロイする方法としてはVSCodeからデプロイすることも可能ですが、せっかくDevOpsを用意しているので、PipelineとReleaseの機能を使って見ようと思います。

先程までの手順で用意したコードをまずはリポジトリ上にプッシュします。プッシュ後にリポジトリを見ると「Set up build」というボタンが出てきているので、セットアップしましょう

Pipelineのセットアップ

基本的には次のMSのページの内容を参考に進めていきます。

Azure Pipelines を使用して、関数アプリ コードを継続的に更新する | Microsoft Learn

「Set up build」ボタンを押すとどのようにビルドしますか?みたいな選択画面が出ますが、あとでazure-pipelines.ymlの内容はすべて書き換えるのでなんでもいいので設定します。そして、上記ページを少し改変したものをペーストします。

trigger:
- master

pool:
  vmImage: ubuntu-latest

steps:
- task: UsePythonVersion@0
  displayName: "Setting Python version to 3.10 as required by functions"
  inputs:
    versionSpec: '3.10'
    architecture: 'x64'
- bash: |
    pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt
- task: ArchiveFiles@2
  displayName: "Archive files"
  inputs:
    rootFolderOrFile: "$(System.DefaultWorkingDirectory)"
    includeRootFolder: false
    archiveFile: "$(System.DefaultWorkingDirectory)/build$(Build.BuildId).zip"
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(System.DefaultWorkingDirectory)/build$(Build.BuildId).zip'
    artifactName: 'drop'

このpipelineは、masterブランチに対してPushされると、pipelineが動くようになっています。ubuntu-latestのイメージを使い、stepの通りにビルドしていきます。本来であればテストなども併せて行うと良いでしょう。

その後、「Save and run」で動かしてみましょう。Jobが実行されるのでしばらく待ちます。Jobに✓が入ればOKです。また、「1 published」を押すと、ビルドした結果のファイルをDLすることができます。ArtifactsはこのあとのReleaseで使います。

ジョブ結果画面

ビルド結果のダウンロード

Releaseのセットアップ

今度はReleaseをセットアップしていきます。releaseはその名の通り、リリースを行います。「Pipelines」からReleasesを選び「New pipeline」をクリックします。

まず、Artifactsから「+ Add」をクリックし、どのartifactからリリースするかを選択します。今回はAzure DevOps内でビルドしているので、Buildを選択します。あとは適当に設定してください。基本そのままでも動くっちゃ動くはずです。

Artifactsの雷マークをクリックすると、トリガーを選択することができます。今回はテストなので、buildされ次第リリースしてしまいます。

続いて、Stagesを選びます。開発環境や本番環境を分けてデプロイするなど柔軟に設定できるようです。今回は面倒なのでいきなりデプロイしてしまいます。「Stages」の「+Add」を選択し、「Deploy a function app to Azure Functions」を選びます。

Jobタスクをクリックすると、Deploy Azure Function Appの設定が行えるます。Azure subscriptonのauthorizeなどを行い設定します。タスクのバージョンは2.*です!なお、App typeはPythonを使っていると、Function App on Linuxになるようです。設定が終わり次第画面上からSaveしましょう。

ここまでくれば一旦手動でリリースしてみましょう。Create releaseのボタンをクリックしReleaseを作成、作成したReleaseの中のStageからデプロイできるはずです。

やったー

動作確認

本番環境にデプロイまでできているはずですので、Azureの画面から確認します。関数アプリの画面の概要から先ほど作成したfunctionが新しくできていることがわかります。

名前をクリックし、関数のURLの取得からURLを取得してアクセスします。今回auth levelをFUNCTIONにしているためAPIKEYのようなパラメーターがURLにくっついています。

VSCodeからソースコードのPushと自動ビルド・デプロイ

色々設定しましたので、VSCode側からソースコードを編集して、リポジトリにPushしてみましょう。やっほーと表示されるよう修正してPushしました。もちろんAzure DevOps側に反映されています。

ビルドのJobも成功しているようです。

リリースも成功しているようです。

画面表示も変わっていました。やったー

以上が一通りの流れです。テスト用の設定はテストが完了したあとに必ず削除しましょう。

最後に

初めてがサーバーレス関連のサービスのお初がAzureでしたが、クラウドサービスのコンポーネントを合わせることで非常に効率的に環境を構築することができました。今までオンプレしか触っておらず、WebはNginxとPHPや!、スケジュール実行はCronや!という世界に住んでいましたが、基盤のメンテナンスをしなくてもいいというのは改めてメリットだなと思います。(HTTPSやったー)

ですが、そこそこハマった落とし穴が多くここまでたどり着くのに多くの時間を使いました。また、各コンポーネントにアクセスするためのネットワークの設定も行う必要がありますが、仮想ネットワークが従量課金プランでは利用できないようで、非常に困っています。今回構築に当たり次の方の記事を拝読させていただきました。本当につらく、私も泣きそうです。AWSだとこうはならないんでしょうか。

zenn.dev

私はセキュリティエンジニアなのでセキュリティ観点でコメントを一応入れておくと、クラウドにおける設定ミスを起因とする事故を見たことがあります。今回私が実際に試してみて、結構複雑でしょうがない一面もあるかもと思ってしまいました。事故の原因としてはミスというシンプルなものですが、問題を解決するために実際にサービスを構築している側がクラウド自体のアーキテクチャを理解し、各設定項目がどのようなリスクを生む可能性があるかを把握するというのはとてつもない作業だと思いました。

クラウドに関する設定ミスを予防するガイドラインや、CSPMのようなミスを検知するような製品もあるにはありますが、製品の仕様とミス起因の脆弱性をうまく見分けられるか甚だ疑問です。(使ったことないので正直良くわからないので有識者の方教えてください。)