zmoog / public-notes

Apache License 2.0
0 stars 1 forks source link

Write a Azure Function in Python #17

Closed zmoog closed 1 year ago

zmoog commented 1 year ago

Start with something simple, learn how Azure Functions work.

Relates: #16


TL;DR

Final working version. You can still read the whole thread to see all my mistakes.

Prerequisites

Check the comment.

Note: As of today the Azure Functions toolchain works on x86 machines only. No ARM Macs support yet, follow https://github.com/Azure/azure-functions-python-worker/issues/915 for updates and workarounds.

v1 vs v2

Using v1 because it’s mature. Caught some v2 rough edges.

Setup

Create a new project:

$ func init hello-world --python
Found Python version 3.8.10 (python3).
Did you know? There is a new Python programming model in public preview. For fewer files and a decorator based approach, learn how you can try it out today at https://aka.ms/pythonprogrammingmodel
Writing requirements.txt
Writing getting_started.md
Writing .gitignore
Writing host.json
Writing local.settings.json
Writing /home/zmoog/code/projects/zmoog/esf-for-azure/v1/hello-world/.vscode/extensions.json

Move into the project:

cd hello-world

Create a function:

$ func new --name hello --template "HTTP trigger" --authlevel "anonymous"
Select a number for template:HTTP trigger
Function name: [HttpTrigger] Writing /home/zmoog/code/projects/zmoog/esf-for-azure/v1/hello-world/hello/__init__.py
Writing /home/zmoog/code/projects/zmoog/esf-for-azure/v1/hello-world/hello/function.json
The function "hello" was created successfully from the "HTTP trigger" template.

Create a virtualenv:


python3 -m venv venv

. venv/bin/activate

Install dependencies:

$ pip install -r requirements.txt
Collecting azure-functions
  Using cached azure_functions-1.13.3-py3-none-any.whl (163 kB)
Installing collected packages: azure-functions
Successfully installed azure-functions-1.13.3
(venv) zmoog@nuc:~/code/projects/zmoog/esf-for-azure/v1/hello-world

Run (locally)

Test locally:

$ func start
Found Python version 3.8.10 (python3).

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

Functions:

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

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

From another terminal:

$ curl -i http://localhost:7071/api/hello\?name\=John
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Mon, 06 Mar 2023 05:51:19 GMT
Server: Kestrel
Transfer-Encoding: chunked

Hello, John. This HTTP triggered function executed successfully.%

Run (on Azure)

Create the resources

Create the Azure Function on Azure:

az group create --name azfqssa-rg --location eastus

az storage account create --name azfqssa --sku Standard_LRS --resource-group azfqssa-rg

az functionapp create --consumption-plan-location eastus --runtime python --runtime-version 3.9 --functions-version 4 --name azfqs --os-type linux --storage-account azfqssa --resource-group azfqssa-rg

Deploy

Deploy the function code:

func azure functionapp publish azfqs

Testing

Quick test:

$ curl -i https://azfqs.azurewebsites.net/api/hello?name=AzureFunctions
HTTP/2 200
content-type: text/plain; charset=utf-8
date: Tue, 07 Mar 2023 05:32:34 GMT
server: Kestrel
request-context: appId=cid-v1:f6bd1760-dc78-4fc3-bae5-0a0c70af385e

Hello, AzureFunctions. This HTTP triggered function executed successfully.

Cleanup

Remove the whole resource group with everything inside:

az group delete --name azfqssa-rg
zmoog commented 1 year ago

Let's dig into https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-triggers-python

zmoog commented 1 year ago

A few things a noticed.

triggers

Bindings

Programming model

https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-python?tabs=asgi%2Capplication-level&pivots=python-mode-configuration#programming-model

Azure Functions expects a function to be a stateless method in your Python script that processes input and produces output. By default, the runtime expects the method to be implemented as a global method called main() in the init.py file.

zmoog commented 1 year ago

The next step is to walk through the standard Quickstart: Create a Python function in Azure from the command line.

zmoog commented 1 year ago

The article asks to pick Python programming model v1 or v2. I don't know a lot about the differences, but I guess that if I start something new I should pick v2.

Dependencies

Prerequisites check

$ func --version
4.0.4483
$ az --version
azure-cli                         2.44.1 *

core                              2.44.1 *
telemetry                          1.0.8

Extensions:
azure-iot                         0.18.3
application-insights              0.1.16

Dependencies:
msal                              1.20.0
azure-mgmt-resource             21.1.0b1

Python location '/opt/homebrew/Cellar/azure-cli/2.44.1/libexec/bin/python'
Extensions directory '/Users/zmoog/.azure/cliextensions'

Python (Darwin) 3.10.9 (main, Dec 15 2022, 10:44:50) [Clang 14.0.0 (clang-1400.0.29.202)]

Legal docs and information: aka.ms/AzureCliLegal

You have 2 update(s) available. Consider updating your CLI installation with 'az upgrade'
$ python --version
Python 3.10.10
zmoog commented 1 year ago
python -m venv venv

. venv/bin/activate
# func init LocalFunctionProj --python -m V2
Found Python version 3.8.16 (python3.8).
Writing requirements.txt
Writing getting_started.md
Writing .gitignore
Writing host.json
Writing local.settings.json
Writing /Users/zmoog/code/projects/zmoog/esf-for-azure/LocalFunctionProj/.vscode/extensions.json
$ pip install -r requirements.txt
Collecting azure-functions
  Using cached azure_functions-1.13.2-py3-none-any.whl (162 kB)
Installing collected packages: azure-functions
Successfully installed azure-functions-1.13.2

[notice] A new release of pip available: 22.3.1 -> 23.0.1
[notice] To update, run: pip install --upgrade pip
func start
Found Python version 3.8.16 (python3.8).

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

[2023-03-02T06:20:30.399Z] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
For detailed output, run func with --verbose flag.
[2023-03-02T06:20:35.333Z] Host lock lease acquired by instance ID '00000000000000000000000034F53DFF'.

Oh crap, the expected output doesn't appear :-(

It should display something like this:

...

 Now listening on: http://0.0.0.0:7071
 Application started. Press Ctrl+C to shut down.

 Http Functions:

         HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
 ...
zmoog commented 1 year ago

I turns out I was using an old version of azure-functions-core-tools, upgrading it:

$ brew upgrade azure/functions/azure-functions-core-tools@4
==> Upgrading 1 outdated package:
azure/functions/azure-functions-core-tools@4 4.0.4483 -> 4.0.5030
==> Fetching azure/functions/azure-functions-core-tools@4
==> Downloading https://functionscdn.azureedge.net/public/4.0.5030/Azure.Functions.Cli.osx-arm64.4.0.5030.zip
######################################################################## 100.0%
==> Upgrading azure/functions/azure-functions-core-tools@4
  4.0.4483 -> 4.0.5030

The next problem is this toolchain does not support apple silicon Macs 🙄

$ func start
Found Python version 3.10.10 (python3).

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

[2023-03-02T08:51:48.338Z] Failed to initialize worker provider for: /opt/homebrew/Cellar/azure-functions-core-tools@4/4.0.5030/workers/python
[2023-03-02T08:51:48.338Z] Microsoft.Azure.WebJobs.Script: Architecture Arm64 is not supported for language python.
[2023-03-02T08:51:48.826Z] Failed to initialize worker provider for: /opt/homebrew/Cellar/azure-functions-core-tools@4/4.0.5030/workers/python
[2023-03-02T08:51:48.826Z] Microsoft.Azure.WebJobs.Script: Architecture Arm64 is not supported for language python.
[2023-03-02T08:51:48.975Z] A host error has occurred during startup operation '23e9e2ec-c1ab-4375-aefb-ace8aca3c54d'.
[2023-03-02T08:51:48.975Z] Microsoft.Azure.WebJobs.Script: WorkerConfig for runtime: python not found.
[2023-03-02T08:51:48.980Z] Failed to stop host instance '69e46943-3a45-43eb-adda-fcae228c957c'.
[2023-03-02T08:51:48.981Z] Microsoft.Azure.WebJobs.Host: The host has not yet started.
Value cannot be null. (Parameter 'provider')
zmoog commented 1 year ago

Time to search how to run this tool using Rosetta, here are a few links:

zmoog commented 1 year ago

More on running azure functions locally https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Cmacos%2Cpython%2Cportal%2Cbash

zmoog commented 1 year ago

I don't wanna mess my laptop up, running this on an Intel NUC with Ubuntu is just fine:

Image

zmoog commented 1 year ago

Both v1 and v2 models require the use of the azurite storage account emulator.

I installed it using npm:

npm install -g azurite

The I started azurite in a pane, and func start in another one:

Image

zmoog commented 1 year ago

I guess the last step is to deploy and run this simple function in Azure.

zmoog commented 1 year ago
az group create --name azfqssa-rg --location eastus

az storage account create --name azfqssa --sku Standard_LRS --resource-group azfqssa-rg

az functionapp create --consumption-plan-location eastus --runtime python --runtime-version 3.9 --functions-version 4 --name azfqs --os-type linux --storage-account azfqssa --resource-group azfqssa-rg

Deploy

func azure functionapp publish azfqs

Cleanup

az group delete --name azfqssa-rg