mcpField
A
azure-functions

azure-functions

github.com/bagelholeクラウド・DevOps1.2k312 回インストール23 週間前に更新
リポジトリ

azure-functions について

Azure Functions でサーバーレスアプリを構築する開発者向けの開発・デプロイガイドです。Python と Node.js の例、トリガーやバインディングの設定、本番環境へのデプロイ手順を扱います。

主な機能

  1. 01Function App の作成と設定
  2. 02HTTP、Blob、タイマーなどのトリガー例
  3. 03Python と Node.js のコード例
  4. 04ローカル開発とデプロイの手順
  5. 05デプロイスロット、セキュリティ設定、トラブルシューティング

活用シーン

  1. 01API と Webhook の構築
  2. 02Blob や Service Bus のイベント処理
  3. 03定期タスクやバックグラウンド処理の実行
  4. 04Azure Functions アプリのデプロイ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill azure-functions

インストール後、Claude Code で/azure-functionsを入力して読み込まれたか確認します。

詳細

Azure Functions

Azure Functions を使用して、サーバーレスアプリケーションを構築およびデプロイします。関数アプリの作成、トリガーとバインディングの構成、デプロイ戦略、Python と Node.js の実際のコード例、本番環境でのベストプラクティスを網羅しています。

使用する場面

  • 自動的にゼロまでスケールするイベント駆動型のコンピューティングが必要な場合。
  • API、Webhook、バックグラウンド処理パイプラインを構築する場合。
  • サーバーを管理せずに、実行ごとの課金を利用したい場合。
  • Azure サービスのイベント(Blob Storage、Service Bus、Cosmos DB の変更)に応答する必要がある場合。
  • 軽量なマイクロサービスやスケジュールされたタスクを実装する場合。

前提条件

# Install Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# Install Azure Functions Core Tools v4
npm install -g azure-functions-core-tools@4

# Verify installation
func --version

# Login
az login
az account set --subscription "my-subscription-id"

# Create supporting resources
az group create --name functions-rg --location eastus

az storage account create \
  --name myfuncstorageacct \
  --resource-group functions-rg \
  --location eastus \
  --sku Standard_LRS

関数アプリの作成

従量課金プラン(実行ごとの課金)
# Python function app on Consumption plan
az functionapp create \
  --resource-group functions-rg \
  --consumption-plan-location eastus \
  --runtime python \
  --runtime-version 3.11 \
  --functions-version 4 \
  --name myapp-func \
  --storage-account myfuncstorageacct \
  --os-type Linux

# Node.js function app
az functionapp create \
  --resource-group functions-rg \
  --consumption-plan-location eastus \
  --runtime node \
  --runtime-version 20 \
  --functions-version 4 \
  --name myapp-node-func \
  --storage-account myfuncstorageacct \
  --os-type Linux
Premium プラン(VNet 統合、コールドスタートなし)
# Create Premium plan
az functionapp plan create \
  --resource-group functions-rg \
  --name myapp-premium-plan \
  --location eastus \
  --sku EP1 \
  --is-linux true

# Create function app on Premium plan
az functionapp create \
  --resource-group functions-rg \
  --plan myapp-premium-plan \
  --runtime python \
  --runtime-version 3.11 \
  --functions-version 4 \
  --name myapp-premium-func \
  --storage-account myfuncstorageacct

トリガーとバインディングの例

HTTP トリガー -- Python
# function_app.py (v2 programming model)
import azure.functions as func
import json
import logging

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

@app.route(route="users/{userId}", methods=["GET"])
def get_user(req: func.HttpRequest) -> func.HttpResponse:
    user_id = req.route_params.get("userId")
    logging.info(f"Fetching user: {user_id}")

    if not user_id:
        return func.HttpResponse(
            json.dumps({"error": "userId is required"}),
            status_code=400,
            mimetype="application/json"
        )

    user = {"id": user_id, "name": "Jane Doe", "email": "[email protected]"}
    return func.HttpResponse(
        json.dumps(user),
        status_code=200,
        mimetype="application/json"
    )

@app.route(route="users", methods=["POST"])
def create_user(req: func.HttpRequest) -> func.HttpResponse:
    try:
        body = req.get_json()
    except ValueError:
        return func.HttpResponse(
            json.dumps({"error": "Invalid JSON"}),
            status_code=400,
            mimetype="application/json"
        )

    logging.info(f"Creating user: {body.get('name')}")
    return func.HttpResponse(
        json.dumps({"id": "new-id", **body}),
        status_code=201,
        mimetype="application/json"
    )
HTTP トリガー -- Node.js
// src/functions/httpTrigger.js (v4 programming model)
const { app } = require("@azure/functions");

app.http("getUser", {
  methods: ["GET"],
  authLevel: "function",
  route: "users/{userId}",
  handler: async (request, context) => {
    const userId = request.params.userId;
    context.log(`Fetching user: ${userId}`);

    if (!userId) {
      return { status: 400, jsonBody: { error: "userId is required" } };
    }

    const user = { id: userId, name: "Jane Doe", email: "[email protected]" };
    return { status: 200, jsonBody: user };
  },
});

app.http("createUser", {
  methods: ["POST"],
  authLevel: "function",
  route: "users",
  handler: async (request, context) => {
    const body = await request.json();
    context.log(`Creating user: ${body.name}`);

    return { status: 201, jsonBody: { id: "new-id", ...body } };
  },
});
Blob トリガー -- Python
@app.blob_trigger(arg_name="blob", path="uploads/{name}",
                   connection="AzureWebJobsStorage")
def process_upload(blob: func.InputStream):
    logging.info(f"Processing blob: {blob.name}, Size: {blob.length} bytes")
    content = blob.read()
    # Process file content here
Timer トリガー -- Python
@app.timer_trigger(schedule="0 */5 * * * *", arg_name="timer",
                    run_on_startup=False)
def cleanup_job(timer: func.TimerRequest):
    if timer.past_due:
        logging.warning("Timer is past due")
    logging.info("Running scheduled cleanup")
    # Cleanup logic here
Service Bus トリガー -- Python
@app.service_bus_queue_trigger(arg_name="msg", queue_name="orders",
                                connection="ServiceBusConnection")
@app.cosmos_db_output(arg_name="doc", database_name="mydb",
                       container_name="processed-orders",
                       connection="CosmosDBConnection")
def process_order(msg: func.ServiceBusMessage, doc: func.Out[func.Document]):
    order = json.loads(msg.get_body().decode("utf-8"))
    logging.info(f"Processing order: {order['id']}")

    processed = {
        "id": order["id"],
        "status": "processed",
        "items": order["items"],
        "total": sum(item["price"] for item in order["items"])
    }
    doc.set(func.Document.from_dict(processed))
Cosmos DB Change Feed トリガー -- Python
@app.cosmos_db_trigger_v3(arg_name="documents", database_name="mydb",
                           container_name="orders",
                           connection="CosmosDBConnection",
                           lease_container_name="leases",
                           create_lease_container_if_not_exists=True)
def on_order_change(documents: func.DocumentList):
    for doc in documents:
        logging.info(f"Document changed: {doc['id']}")

ローカル開発

# Initialize a new Python function project
func init MyFunctionProject --python
cd MyFunctionProject

# Create a new function from template
func new --name HttpExample --template "HTTP trigger" --authlevel function

# Run locally
func start

# Run locally with specific port
func start --port 7072

# Test locally
curl http://localhost:7071/api/HttpExample?name=World

デプロイ

# Deploy using Core Tools
func azure functionapp publish myapp-func

# Deploy with build step for Python
func azure functionapp publish myapp-func --build remote

# Deploy using ZIP package
zip -r function.zip . -x ".git/*" ".venv/*" "__pycache__/*"
az functionapp deployment source config-zip \
  --resource-group functions-rg \
  --name myapp-func \
  --src function.zip

# Deploy via CI/CD with GitHub Actions
az functionapp deployment github-actions add \
  --resource-group functions-rg \
  --name myapp-func \
  --repo "myorg/myrepo" \
  --branch main \
  --runtime python \
  --login-with-github

デプロイスロット

# Create a staging slot
az functionapp deployment slot create \
  --resource-group functions-rg \
  --name myapp-func \
  --slot staging

# Deploy to staging slot
func azure functionapp publish myapp-func --slot staging

# Test staging slot
curl https://myapp-func-staging.azurewebsites.net/api/health

# Swap staging to production
az functionapp deployment slot swap \
  --resource-group functions-rg \
  --name myapp-func \
  --slot staging \
  --target-slot production

# Roll back by swapping again
az functionapp deployment slot swap \
  --resource-group functions-rg \
  --name myapp-func \
  --slot staging \
  --target-slot production

アプリケーション設定とセキュリティ

# Set application settings
az functionapp config appsettings set \
  --resource-group functions-rg \
  --name myapp-func \
  --settings \
    ServiceBusConnection="Endpoint=sb://..." \
    CosmosDBConnection="AccountEndpoint=https://..." \
    CUSTOM_SETTING="my-value"

# Set settings as slot-specific
az functionapp config appsettings set \
  --resource-group functions-rg \
  --name myapp-func \
  --slot-settings \
    ENVIRONMENT="staging"

# Enable managed identity
az functionapp identity assign \
  --resource-group functions-rg \
  --name myapp-func

# Configure CORS
az functionapp cors add \
  --resource-group functions-rg \
  --name myapp-func \
  --allowed-origins "https://myapp.example.com"

# Set minimum TLS version
az functionapp config set \
  --resource-group functions-rg \
  --name myapp-func \
  --min-tls-version 1.2

# Enable Application Insights
az functionapp config appsettings set \
  --resource-group functions-rg \
  --name myapp-func \
  --settings APPINSIGHTS_INSTRUMENTATIONKEY="your-key"

Terraform の構成

resource "azurerm_service_plan" "functions" {
  name                = "myapp-func-plan"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  os_type             = "Linux"
  sku_name            = "Y1"  # Consumption plan
}

resource "azurerm_linux_function_app" "main" {
  name                       = "myapp-func"
  location                   = azurerm_resource_group.main.location
  resource_group_name        = azurerm_resource_group.main.name
  service_plan_id            = azurerm_service_plan.functions.id
  storage_account_name       = azurerm_storage_account.func.name
  storage_account_access_key = azurerm_storage_account.func.primary_access_key

  identity {
    type = "SystemAssigned"
  }

  site_config {
    application_stack {
      python_version = "3.11"
    }
    cors {
      allowed_origins = ["https://myapp.example.com"]
    }
  }

  app_settings = {
    FUNCTIONS_WORKER_RUNTIME       = "python"
    WEBSITE_RUN_FROM_PACKAGE       = "1"
    APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.main.instrumentation_key
  }

  tags = var.tags
}

トラブルシューティング

症状原因対処法
コールドスタートの遅延が 10 秒を超える従量課金プランのコールドスタートPremium プラン(EP1 以上)を使用するか、WEBSITE_RUN_FROM_PACKAGE=1 を有効にします
関数がトリガーされない接続文字列の設定ミスaz functionapp config appsettings list を実行して、バインディング値が正しいことを確認します
Python で ModuleNotFoundError が発生するデプロイ時に依存関係がインストールされていない--build remote フラグを使用するか、パッケージに requirements.txt を含めます
HTTP 401 Unauthorized認証レベルの不一致、または関数キーがないコード内の認証レベルが想定と一致していることを確認し、x-functions-key ヘッダーを渡します
Blob トリガーが起動しないストレージアカウントの接続先が誤っているAzureWebJobsStorage が正しいアカウントを指していることを確認します
Timer トリガーが 2 回実行されるPremium プランで複数のインスタンスが動作しているWEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT=1 を設定するか、シングルトンロックを使用します
デプロイスロットのスワップに失敗するスロットの設定が構成されていないスロット固有の設定が --slot-settings で指定されていることを確認します
メモリ不足エラーが発生するペイロードが大きい、またはメモリリークがあるデータをすべて読み込まずにストリーミングし、プランのティアを引き上げます

関連スキル

  • azure-networking -- プライベートリソースにアクセスする Premium プランの関数での VNet 統合。
  • azure-sql -- 関数バインディングからのデータベース接続。
  • terraform-azure -- 関数アプリのプロビジョニングに使用する Infrastructure as Code。
  • arm-templates -- Bicep ベースの関数アプリのデプロイ。

コメント