الدرس التعليمي: تحليل نصوص مطول مع مشاركة ملفات Azure المثبتة

في هذا الدرس، تقوم بنشر تطبيق Python دالات Azure يستخدم Durable Functions لتنسيق تحليل ملفات النص المتوازي. يقوم تطبيق الوظائف الخاص بك بتركيب مشاركة ملفات Azure، ويحلل عدة ملفات نصية بالتوازي (المعرض)، ويجمع النتائج (المروحة الداخلة)، ويعيدها إلى المتصل. يظهر هذا النهج ميزة رئيسية لتركيبات التخزين: الوصول المشترك للملفات عبر عدة نسخ وظيفية دون عبء شبكة لكل طلب.

في هذا البرنامج التعليمي، سوف تتعلّم:

  • استخدم Azure Developer CLI لنشر تطبيق Durable Functions في خطة استهلاك مرنة مع مشاركة ملفات Azure مركبة
  • تفعيل تنسيق لمعالجة ملفات نصية نموذجية بالتوازي
  • تحقق من نتائج التحليل المجمعة

ملاحظة

عينات الكود لهذه المقالة متوفرة في مستودع Azure Functions Flex Consumption مع ملفات Azure OS Mount Samples Samples مستودع GitHub.

المتطلبات المسبقه

أمثلة واجهة السطر في هذا الدرس تستخدم بناء جملة Bash وقد تم اختبارها في أجهزة Azure Cloud Shell (Bash) وطرفيات لينكس/ماك أو إس.

تهيئة مشروع العينة

يمكنك العثور على نموذج الكود لهذا الدرس في مستودع Azure Functions Flex Consumption مع ملفات Azure OS Mount Samples Samples Repository على GitHub. يحتوي المجلد durable-text-analysis على كود تطبيق الوظيفة، وقالب Bicep الذي يوفر الموارد المطلوبة لأزور، وسكريبت بعد النشر يرفع ملفات نصية نموذجية.

  1. افتح طرفية واذهب إلى الدليل الذي تريد استنساخ المستودع فيه.

  2. استنساخ المستودع:

    git clone https://github.com/Azure-Samples/Azure-Functions-Flex-Consumption-with-Azure-Files-OS-Mount-Samples.git
    
  3. اذهب إلى مجلد المشروع:

    cd Azure-Functions-Flex-Consumption-with-Azure-Files-OS-Mount-Samples/durable-text-analysis
    
  4. قم بتهيئة azd البيئة. عند طلب ذلك، أدخل اسم البيئة مثل durable-text:

    azd init
    

مراجعة الرمز

الأجزاء الثلاثة الرئيسية التي تجعل هذه العينة تعمل هي البنية التحتية التي تنشئ التركيب، والنص الذي يرفع ملفات العينة، وكود الدالة الذي ينظم التحليل.

عند وقت التشغيل، تتبع التوزيع الموسيقي هذا التدفق:

  1. يبدأ منشور HTTP إلى /api/start-analysis توزيع Durable Functions.
  2. يقوم المنسق باستدعاء list_text_files النشاط للعثور على جميع .txt الملفات على التركيب.
  3. يتفرق المنسق، ينادي analyse_text_file على كل ملف بالتوازي.
  4. بمجرد الانتهاء من جميع المهام المتوازية، يقوم المنسق بدمج aggregate_results المقاييس لكل ملف في ملخص واحد.
  5. يتم إرجاع النتيجة المجمعة كمخرج الأوركسترا.

تقوم الوحدة mounts.bicep بتكوين تركيب ملفات Azure SMB على تطبيق الوظائف. تحدد القيمة mountPath المسار المحلي الذي تظهر فيه الملفات أثناء وقت التشغيل. تمرر مفتاح الوصول إلى حساب التخزين كمعلمة، وتقوم المنصة بحلها أثناء التشغيل من خلال مرجع لخزنة المفاتيح:

@description('Function app name')
param functionAppName string

@description('Storage account name')
param storageAccountName string

@description('Storage account access key or app setting reference for Azure Files SMB mount')
param accessKey string

@description('Array of mount configurations')
param mounts array

// Function app reference
resource functionApp 'Microsoft.Web/sites@2023-12-01' existing = {
  name: functionAppName
}

// Azure Files OS mount configuration
// Deploys azureStorageAccounts site config with all mounts in one shot
resource mountConfig 'Microsoft.Web/sites/config@2023-12-01' = {
  parent: functionApp
  name: 'azurestorageaccounts'
  properties: reduce(mounts, {}, (cur, mount) => union(cur, {
    '${mount.name}': {
      type: 'AzureFiles'
      shareName: mount.shareName
      mountPath: mount.mountPath
      accountName: storageAccountName
      accessKey: accessKey
    }
  }))
}

output mountPaths array = [for mount in mounts: mount.mountPath]

نظرا لأن تركيبات ملفات Azure SMB لا تدعم بعد إدارة التحقق من هويتها، تحتاج إلى مفتاح حساب تخزين. كأفضل ممارسة، قم بتخزين هذا المفتاح في Azure Key Vault واستخدم مرجع Key Vault في إعداد التطبيق. إعداد التثبيت يشير إلى إعداد التطبيق باستخدام @AppSettingRef()، لذا لا يظهر المفتاح أبدا في قوالب العضلة ذات الرأس. تقوم الوحدة keyvault.bicep بإنشاء القبو، وتخزين المفتاح، وتمنح أدوار RBAC:

@description('Key Vault name')
param name string

@description('Location')
param location string

@description('Tags')
param tags object = {}

@description('Storage account name')
param storageAccountName string

@description('Principal ID of the function app identity (receives Key Vault Secrets User role)')
param functionAppPrincipalId string

@description('Principal ID of the deploying user (receives Key Vault Secrets Officer role)')
param deployerPrincipalId string = ''

// Storage account reference
resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' existing = {
  name: storageAccountName
}

// Key Vault with RBAC authorization
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: name
  location: location
  tags: tags
  properties: {
    sku: {
      family: 'A'
      name: 'standard'
    }
    tenantId: tenant().tenantId
    enableRbacAuthorization: true
    enabledForTemplateDeployment: true
    enableSoftDelete: true
    softDeleteRetentionInDays: 7
  }
}

// Store storage account key as a secret (Azure Files mounts require shared key)
resource storageKeySecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
  parent: keyVault
  name: 'storageAccountKey'
  properties: {
    value: storage.listKeys().keys[0].value
    contentType: 'Storage account access key for Azure Files SMB mount'
  }
}

// Built-in Key Vault RBAC role IDs
var roles = {
  KeyVaultSecretsOfficer: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')
  KeyVaultSecretsUser: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
}

// Grant the function app identity read access to secrets
resource functionAppSecretsUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(keyVault.id, functionAppPrincipalId, roles.KeyVaultSecretsUser)
  scope: keyVault
  properties: {
    roleDefinitionId: roles.KeyVaultSecretsUser
    principalId: functionAppPrincipalId
    principalType: 'ServicePrincipal'
  }
}

// Grant the deployer manage access to secrets
resource deployerSecretsOfficer 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(deployerPrincipalId)) {
  name: guid(keyVault.id, deployerPrincipalId, roles.KeyVaultSecretsOfficer)
  scope: keyVault
  properties: {
    roleDefinitionId: roles.KeyVaultSecretsOfficer
    principalId: deployerPrincipalId
    principalType: 'User'
  }
}

output name string = keyVault.name
output uri string = keyVault.properties.vaultUri
output storageKeySecretUri string = storageKeySecret.properties.secretUri

يستدعي الملف main.bicep وحدات التثبيت وKey Vault:


// Key Vault for secure storage of Azure Files access key
module keyVault './app/keyvault.bicep' = {
  name: 'keyVault'
  scope: rg
  params: {
    name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}'
    location: location
    tags: tags
    storageAccountName: storage.outputs.name
    functionAppPrincipalId: processorIdentity.outputs.principalId
    deployerPrincipalId: principalId
  }
}

// Azure Files mount configuration (access key resolved via Key Vault reference)
module azureFilesMount './app/mounts.bicep' = {
  name: 'azureFilesMount'
  scope: rg
  params: {
    functionAppName: functionApp.outputs.name
    storageAccountName: storage.outputs.name
    accessKey: '@AppSettingRef(MOUNT_SECRET_REFERENCE)'
    mounts: [
      {
        name: 'data'
        shareName: 'data'
        mountPath: '/mounts/data/'
      }
    ]
  }
  dependsOn: [
    functionAppRoleAssignments
  ]

النشر باستخدام Azure Developer CLI

هذه العينة هي قالب Azure Developer CLI (azd). بنية تحتية واحدة azd up لتوفير الأوامر، تنشر كود الوظيفة، وترفع ملفات نصية نموذجية إلى مشاركة ملفات Azure.

  1. سجل الدخول إلى Azure. سكريبت ما بعد النشر يستخدم أوامر Azure CLI، لذا تحتاج إلى المصادقة باستخدام كلا الأداتين:

    azd auth login
    az login
    
  2. توفير ونشر كل شيء:

    azd up
    

    عند الطلب، اختر اشتراك Azure والموقع المناسب. ثم الأمر:

    • ينشئ مجموعة موارد، حساب تخزين، خزنة مفاتيح، تطبيق دوال استهلاك مرن مع تكوين الوظائف المتينة، مثيل Application Insights وهوية مدارة
    • ينشر كود دالة بايثون
    • يرفع ملفات نصية نموذجية إلى مشاركة ملفات Azure
    • يجري فحصا صحيا

    ملاحظة

    نظرا لأن تركيبات ملفات Azure SMB لا تدعم بعد إدارة التحقق من هويتها، تحتاج إلى مفتاح حساب تخزين. كأفضل ممارسة، يخزن النشر هذا المفتاح في Azure Key Vault ويستخدم مرجعا لخزنة المفاتيح حتى لا يتم كشف المفتاح أبدا في إعدادات التطبيق. يوفر هذا النهج إدارة مركزية للأسرار، والتدقيق، ودعم تدوير المفاتيح.

    يستغرق النشر بضع دقائق. عند الانتهاء، ترى ملخصا للموارد التي تم إنشاؤها.

  3. احفظ أسماء الموارد كمتغيرات shell للخطوات المتبقية:

    RESOURCE_GROUP=$(azd env get-value AZURE_RESOURCE_GROUP)
    FUNCTION_APP_NAME=$(azd env get-value AZURE_FUNCTION_APP_NAME)
    FUNCTION_APP_URL=$(azd env get-value AZURE_FUNCTION_APP_URL)
    

تشغيل التوزيع الموسيقي

  1. احصل على مفتاح المضيف الوظيفي:

    HOST_KEY=$(az functionapp keys list \
      --resource-group $RESOURCE_GROUP \
      --name $FUNCTION_APP_NAME \
      --query "functionKeys.default" \
      -o tsv)
    
  2. ابدأ التوزيع الموسيقي. يمكنك اختياريا تمرير جسم JSON لتجاوز {"mount_path": "/mounts/data/"} مسار التركيب الافتراضي:

    curl -s -X POST "${FUNCTION_APP_URL}/api/start-analysis?code=${HOST_KEY}" | jq .
    

    يتضمن الرد معرف المثيل وURIs إدارة يمكنك استخدامها للتحقق من الحالة، إرسال الأحداث، أو إنهاء التنسيق:

    {
      "id": "abc123def456",
      "statusQueryGetUri": "https://<your-app>.azurewebsites.net/...",
      "sendEventPostUri": "https://...",
      "terminatePostUri": "https://..."
    }
    

تحقق من النتائج

  1. تحقق من حالة التوزيع باستخدام statusQueryGetUri الاستجابة من المحفز:

    STATUS_URL="<statusQueryGetUri-from-trigger-response>"
    
    curl -s "${STATUS_URL}" | jq .
    

    أثناء تشغيل التوزيع الأوركسترالي، يكون runtimeStatus.Running عند الانتهاء، يبدو الرد كما يلي:

    {
      "instanceId": "abc123def456",
      "runtimeStatus": "Completed",
      "output": {
        "total_files": 3,
        "total_words": 45,
        "total_lines": 12,
        "total_chars": 303,
        "overall_avg_word_length": 4.82,
        "overall_top_characters": [["e", 42], ["t", 38], ["a", 35]],
        "per_file": [
          {
            "file_path": "/mounts/data/sample1.txt",
            "word_count": 15,
            "line_count": 4,
            "char_count": 98,
            "avg_word_length": 5.1,
            "top_characters": [["e", 14], ["t", 12]]
          },
          {
            "file_path": "/mounts/data/sample2.txt",
            "word_count": 18,
            "line_count": 5,
            "char_count": 120,
            "avg_word_length": 4.6,
            "top_characters": [["a", 16], ["e", 15]]
          },
          {
            "file_path": "/mounts/data/sample3.txt",
            "word_count": 12,
            "line_count": 3,
            "char_count": 85,
            "avg_word_length": 4.9,
            "top_characters": [["e", 13], ["t", 11]]
          }
        ]
      }
    }
    

    كما يعرض التطبيق نقطة نهاية مريحة عند /api/status/{instance_id} تعيد استجابة حالة مبسطة.

نصيحة

تطبيق الوظائف الخاص بك يصل إلى الملفات الثلاثة بالتوازي عبر حامل التخزين. التطبيق لا يحتاج إلى مكالمات شبكية لكل طلب. تقرأ الوظيفة هذه البطاقات مباشرة من المشاركة المركبة باستخدام إدخال/إخراج الملفات القياسي. يظهر هذا النهج قوة قواعد التخزين مع الوظائف المتينة.

تنظيف الموارد

لتجنب الرسوم المستمرة، احذف جميع الموارد التي أنشأها هذا الدرس:

azd down --purge

تحذير

يقوم هذا الأمر بحذف مجموعة الموارد وجميع الموارد فيها، بما في ذلك تطبيق الدالة، وحساب التخزين، ونسخة Application Insights الخاصة بها.