Sdílet prostřednictvím


Kurz: Monitorování azure Functions s využitím distribuovaného trasování OpenTelemetry

Tento článek ukazuje podporu OpenTelemetry ve službě Azure Functions, která umožňuje distribuované trasování napříč více voláními funkcí pomocí integrované podpory Application Insights a OpenTelemetry. Abyste mohli začít, použije se šablona Azure Developer CLI (azd) k vytvoření projektu kódu a nasazení Azure, ve kterém se má aplikace spouštět.

V tomto kurzu použijete azd nástroj k:

  • Inicializace projektu s podporou OpenTelemetry ze šablony
  • Zkontrolujte kód, který umožňuje integraci OpenTelemetry.
  • Spusťte a ověřte aplikaci s podporou OpenTelemetry místně.
  • Vytvoření aplikace funkcí a souvisejících prostředků v Azure
  • Nasaďte projekt kódu do aplikace funkcí v Azure.
  • Ověřte distribuované trasování ve službě Application Insights.

Požadované prostředky Azure vytvořené touto šablonou se řídí aktuálními osvědčenými postupy pro zabezpečená a škálovatelná nasazení aplikací funkcí v Azure. Stejný azd příkaz také nasadí projekt kódu do nové aplikace funkcí v Azure.

Ve výchozím nastavení se plán Flex Consumption řídí fakturačním modelem platíš za to, co používáš, což znamená, že dokončení tohoto rychlého průvodce způsobuje malé náklady ve výši několika centů USD nebo méně na vašem účtu Azure.

Důležité

Tento článek aktuálně podporuje pouze C#, Python a TypeScript. Rychlý start dokončíte tak, že v horní části článku vyberete některý z těchto podporovaných jazyků.

Požadavky

Inicializace projektu

azd init Pomocí příkazu vytvořte místní projekt kódu Azure Functions ze šablony, která zahrnuje distribuované trasování OpenTelemetry.

  1. V místním terminálu nebo příkazovém řádku spusťte tento azd init příkaz v prázdné složce:

    azd init --template functions-quickstart-python-azd-otel -e flexquickstart-otel
    

    Tento příkaz načte soubory projektu z úložiště šablony a inicializuje projekt v aktuální složce. Příznak -e nastaví název aktuálního prostředí. V azdprostředí udržuje jedinečný kontext nasazení pro vaši aplikaci a můžete definovat více než jeden. Název prostředí se také zobrazí v názvu skupiny prostředků, kterou vytvoříte v Azure.

  1. V místním terminálu nebo příkazovém řádku spusťte tento azd init příkaz v prázdné složce:

    azd init --template functions-quickstart-typescript-azd-otel -e flexquickstart-otel
    

    Tento příkaz načte soubory projektu z úložiště šablony a inicializuje projekt v aktuální složce. Příznak -e nastaví název aktuálního prostředí. V azdprostředí udržuje jedinečný kontext nasazení pro vaši aplikaci a můžete definovat více než jeden. Název prostředí se také zobrazí v názvu skupiny prostředků, kterou vytvoříte v Azure.

  1. V místním terminálu nebo příkazovém řádku spusťte tento azd init příkaz v prázdné složce:

    azd init --template functions-quickstart-dotnet-azd-otel -e flexquickstart-otel
    

    Tento příkaz načte soubory projektu z úložiště šablony a inicializuje projekt v aktuální složce. Příznak -e nastaví název aktuálního prostředí. V azdprostředí udržuje jedinečný kontext nasazení pro vaši aplikaci a můžete definovat více než jeden. Název prostředí se také zobrazí v názvu skupiny prostředků, kterou vytvoříte v Azure.

Kontrola kódu

Šablona vytvoří kompletní scénář distribuovaného trasování se třemi funkcemi, které spolupracují. Pojďme se podívat na klíčové aspekty související s OpenTelemetry:

Konfigurace OpenTelemetry

Soubor src/otel-sample/host.json povolí OpenTelemetry pro hostitele Functions:

{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry",
  "extensions": {
    "serviceBus": {
        "maxConcurrentCalls": 10
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  }
}

Nastavení "telemetryMode": "OpenTelemetry" klíče umožňuje distribuované trasování napříč voláními funkcí.

Soubor src/OTelSample/host.json povolí OpenTelemetry pro hostitele Functions:

{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry",
  "logging": {
    "OpenTelemetry": {
      "logLevel": {
        "Host.General": "Warning"
      }
    }
  }
}

Nastavení "telemetryMode": "OpenTelemetry" klíče umožňuje distribuované trasování napříč voláními funkcí.

Závislosti pro OpenTelemetry

Soubor src/otel-sample/requirements.txt obsahuje potřebné balíčky pro integraci OpenTelemetry:

azure-functions
azure-monitor-opentelemetry
requests

Balíček azure-monitor-opentelemetry poskytuje integraci OpenTelemetry s Application Insights.

Soubor src/otel-sample/package.json obsahuje potřebné balíčky pro integraci OpenTelemetry:

{
  "dependencies": {
    "@azure/functions": "^4.0.0",
    "@azure/functions-opentelemetry-instrumentation": "^0.1.0",
    "@azure/monitor-opentelemetry-exporter": "^1.0.0",
    "axios": "^1.6.0"
  }
}

Balíčky @azure/functions-opentelemetry-instrumentation a @azure/monitor-opentelemetry-exporter poskytují integraci OpenTelemetry s Application Insights.

Soubor .csproj obsahuje potřebné balíčky pro integraci OpenTelemetry:

<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.OpenTelemetry" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.10.0" />

Tyto balíčky poskytují integraci OpenTelemetry s Application Insights a instrumentací HTTP pro distribuované trasování.

Implementace funkce

Funkce v src/otel-sample/function_app.py demonstrují tok distribuovaného sledování:

První funkce HTTP

@app.function_name("first_http_function")
@app.route(route="first_http_function", auth_level=func.AuthLevel.ANONYMOUS)
def first_http_function(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function (first) processed a request.')

    # Call the second function
    base_url = f"{req.url.split('/api/')[0]}/api"
    second_function_url = f"{base_url}/second_http_function"

    response = requests.get(second_function_url)
    second_function_result = response.text

    result = {
        "message": "Hello from the first function!",
        "second_function_response": second_function_result
    }

    return func.HttpResponse(
        json.dumps(result),
        status_code=200,
        mimetype="application/json"
    )

Druhá funkce HTTP

@app.function_name("second_http_function")
@app.route(route="second_http_function", auth_level=func.AuthLevel.ANONYMOUS)
@app.service_bus_queue_output(arg_name="outputsbmsg", queue_name="%ServiceBusQueueName%",
                              connection="ServiceBusConnection")
def second_http_function(req: func.HttpRequest, outputsbmsg: func.Out[str]) -> func.HttpResponse:
    logging.info('Python HTTP trigger function (second) processed a request.')

    message = "This is the second function responding."

    # Send a message to the Service Bus queue
    queue_message = "Message from second HTTP function to trigger ServiceBus queue processing"
    outputsbmsg.set(queue_message)
    logging.info('Sent message to ServiceBus queue: %s', queue_message)

    return func.HttpResponse(
        message,
        status_code=200
    )

Spouštěč fronty služby Service Bus

@app.service_bus_queue_trigger(arg_name="azservicebus", queue_name="%ServiceBusQueueName%",
                               connection="ServiceBusConnection") 
def servicebus_queue_trigger(azservicebus: func.ServiceBusMessage):
    logging.info('Python ServiceBus Queue trigger start processing a message: %s',
                azservicebus.get_body().decode('utf-8'))
    time.sleep(5)  # Simulate processing work
    logging.info('Python ServiceBus Queue trigger end processing a message')

Konfigurace OpenTelemetry je nastavená v src/otel-sample/index.ts:

import { AzureFunctionsInstrumentation } from '@azure/functions-opentelemetry-instrumentation';
import { AzureMonitorTraceExporter, AzureMonitorLogExporter } from '@azure/monitor-opentelemetry-exporter';
import { getNodeAutoInstrumentations, getResourceDetectors } from '@opentelemetry/auto-instrumentations-node';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { detectResources } from '@opentelemetry/resources';
import { LoggerProvider, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
import { NodeTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';

const resource = detectResources({ detectors: getResourceDetectors() });

const tracerProvider = new NodeTracerProvider({ 
  resource, 
  spanProcessors: [new SimpleSpanProcessor(new AzureMonitorTraceExporter())] 
});
tracerProvider.register();

const loggerProvider = new LoggerProvider({
  resource,
  processors: [new SimpleLogRecordProcessor(new AzureMonitorLogExporter())],
});

registerInstrumentations({
    tracerProvider,
    loggerProvider,
    instrumentations: [getNodeAutoInstrumentations(), new AzureFunctionsInstrumentation()],
});

Funkce jsou definovány ve src/otel-sample/src/functions složce:

První funkce HTTP

export async function firstHttpFunction(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
  context.log("TypeScript HTTP trigger function (first) processed a request.");

  try {
    // Call the second function
    const baseUrl = request.url.split("/api/")[0];
    const secondFunctionUrl = `${baseUrl}/api/second_http_function`;

    const response = await axios.get(secondFunctionUrl);
    const secondFunctionResult = response.data;

    const result = {
      message: "Hello from the first function!",
      second_function_response: secondFunctionResult,
    };

    return {
      status: 200,
      body: JSON.stringify(result),
      headers: { "Content-Type": "application/json" },
    };
  } catch (error) {
    return {
      status: 500,
      body: JSON.stringify({ error: "Failed to process request" }),
    };
  }
}

Druhá funkce HTTP

export async function secondHttpFunction(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
  context.log("TypeScript HTTP trigger function (second) processed a request.");

  const message = "This is the second function responding.";

  // Send a message to the Service Bus queue
  const queueMessage =
    "Message from second HTTP function to trigger ServiceBus queue processing";

  context.extraOutputs.set(serviceBusOutput, queueMessage);
  context.log("Sent message to ServiceBus queue:", queueMessage);

  return {
    status: 200,
    body: message,
  };
}

Trigger fronty služby „Service Bus“

export async function serviceBusQueueTrigger(
  message: unknown,
  context: InvocationContext
): Promise<void> {
  context.log("TypeScript ServiceBus Queue trigger start processing a message:", message);

  // Simulate processing time
  await new Promise((resolve) => setTimeout(resolve, 5000));

  context.log("TypeScript ServiceBus Queue trigger end processing a message");
}

Konfigurace OpenTelemetry je nastavená v src/OTelSample/Program.cs:

using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.OpenTelemetry;
using OpenTelemetry.Trace;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Logging.AddOpenTelemetry(logging =>
{
    logging.IncludeFormattedMessage = true;
    logging.IncludeScopes = true;
});

builder.Services.AddOpenTelemetry()    
    .WithTracing(tracing =>
    {
        tracing.AddHttpClientInstrumentation();
    });

builder.Services.AddOpenTelemetry().UseAzureMonitorExporter();
builder.Services.AddOpenTelemetry().UseFunctionsWorkerDefaults();

builder.Services.AddHttpClient();

builder.Build().Run();

Funkce jsou definovány v samostatných souborech tříd:

První funkce HTTP

public class FirstHttpTrigger
{
    private readonly ILogger<FirstHttpTrigger> _logger;
    private readonly IHttpClientFactory _httpClientFactory;

    public FirstHttpTrigger(ILogger<FirstHttpTrigger> logger, IHttpClientFactory httpClientFactory)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;
    }

    [Function("first_http_function")]
    public async Task<IActionResult> Run(
         [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
    {
        _logger.LogInformation("first_http_function function processed a request.");

        var baseUrl = $"{req.Url.AbsoluteUri.Split("/api/")[0]}/api";
        var targetUri = $"{baseUrl}/second_http_function";

        var client = _httpClientFactory.CreateClient();
        var response = await client.GetAsync(targetUri);
        var content = await response.Content.ReadAsStringAsync();

        return new OkObjectResult($"Called second_http_function, status: {response.StatusCode}, content: {content}");
    }
}

Druhá funkce HTTP

public class SecondHttpTrigger
{
    private readonly ILogger<SecondHttpTrigger> _logger;

    public SecondHttpTrigger(ILogger<SecondHttpTrigger> logger)
    {
        _logger = logger;
    }

    [Function("second_http_function")]
    public MultiResponse Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
    {
        _logger.LogInformation("second_http_function function processed a request.");

        return new MultiResponse
        {
            Messages = new string[] { "Hello" },
            HttpResponse = req.CreateResponse(System.Net.HttpStatusCode.OK)
        };
    }
}

public class MultiResponse
{
    [ServiceBusOutput("%ServiceBusQueueName%", Connection = "ServiceBusConnection")]
    public string[]? Messages { get; set; }

    [HttpResult]
    public HttpResponseData? HttpResponse { get; set; }
}

Trigger fronty služby „Service Bus“

public class ServiceBusQueueTrigger
{
    private readonly ILogger<ServiceBusQueueTrigger> _logger;

    public ServiceBusQueueTrigger(ILogger<ServiceBusQueueTrigger> logger)
    {
        _logger = logger;
    }

    [Function("servicebus_queue_trigger")]
    public async Task Run(
        [ServiceBusTrigger("%ServiceBusQueueName%", Connection = "ServiceBusConnection")]
        ServiceBusReceivedMessage message,
        ServiceBusMessageActions messageActions)
    {
        _logger.LogInformation("Message ID: {id}", message.MessageId);
        _logger.LogInformation("Message Body: {body}", message.Body);

        // Complete the message
        await messageActions.CompleteMessageAsync(message);
    }
}

Distribuovaný tok trasování

Tato architektura vytvoří kompletní scénář distribuovaného trasování s tímto chováním:

  1. První funkce HTTP přijme požadavek HTTP a zavolá druhou funkci HTTP.
  2. Druhá funkce HTTP odpoví a odešle zprávu do služby Service Bus.
  3. Service Bus trigger zpracovává zprávu se zpožděním, aby simuloval práci při zpracování.

Klíčové aspekty implementace OpenTelemetry:

  • Integrace OpenTelemetry: Soubor host.json umožňuje použití OpenTelemetry s "telemetryMode": "OpenTelemetry"
  • Řetězení funkcí: První funkce volá druhou pomocí požadavků HTTP a vytváří korelované trasování.
  • Integrace služby Service Bus: Druhá funkce se vypíše do služby Service Bus, která aktivuje třetí funkci.
  • Anonymní ověřování: Funkce HTTP používají auth_level=func.AuthLevel.ANONYMOUS, takže nejsou vyžadovány žádné klíče funkcí.

Tady si můžete projít kompletní projekt šablony.

  • Integrace OpenTelemetry: Soubor index.ts konfiguruje OpenTelemetry s exportéry azure Monitoru pro trasování a protokoly.
  • Zřetězení funkcí: První funkce volá druhou pomocí axiosu s automatickým šířením trasování.
  • Integrace služby Service Bus: Druhá funkce vypíše do služby Service Bus pomocí výstupních vazeb, které aktivují třetí funkci.
  • Spravovaná identita: Všechna připojení služby Service Bus používají spravovanou identitu místo připojovacích řetězců.
  • Simulace zpracování: 5sekundové zpoždění triggeru služby Service Bus simuluje práci zpracování zpráv.

Tady si můžete projít kompletní projekt šablony.

  • Integrace OpenTelemetry: Soubor Program.cs konfiguruje OpenTelemetry s exportérem služby Azure Monitor.
  • Řetězení funkcí: První funkce volá druhou pomocí HttpClient s instrumentací OpenTelemetry.
  • Integrace služby Service Bus: Druhá funkce vypíše do služby Service Bus pomocí výstupních vazeb, které aktivují třetí funkci.
  • Spravovaná identita: Všechna připojení služby Service Bus používají spravovanou identitu místo připojovacích řetězců.
  • Izolovaný pracovní proces .NET 8: Používá nejnovější model izolovaného pracovního procesu Azure Functions pro zajištění lepšího výkonu a flexibility.

Tady si můžete projít kompletní projekt šablony.

Po místním ověření funkcí je čas je publikovat do Azure.

Nasazení do Azure

Tento projekt je nakonfigurovaný tak, aby pomocí azd up příkazu nasadil tento projekt do nové aplikace funkcí v plánu Flex Consumption v Azure s podporou OpenTelemetry.

Návod

Tento projekt zahrnuje sadu souborů Bicep, které azd používají k vytvoření zabezpečeného nasazení do plánu Flex Consumption, který dodržuje osvědčené postupy, včetně připojení spravovaných identit.

  1. Spuštěním tohoto příkazu vytvořte azd požadované prostředky Azure v Azure a nasaďte projekt kódu do nové aplikace funkcí:

    azd up
    

    Kořenová složka obsahuje azure.yaml definiční soubor vyžadovaný azd.

    Pokud ještě nejste přihlášení, zobrazí se výzva k ověření pomocí účtu Azure.

  2. Po zobrazení výzvy zadejte tyto požadované parametry nasazení:

    Parameter Description
    Předplatné Azure Předplatné, ve kterém se vaše prostředky vytvářejí.
    Umístění Azure Oblast Azure, ve které se má vytvořit skupina prostředků, která obsahuje nové prostředky Azure. Zobrazí se pouze oblasti, které aktuálně podporují plán Flex Consumption.

    Příkaz azd up použije k dokončení těchto úloh nasazení odpověď na tyto výzvy s konfiguračními soubory Bicep:

    • Vytvořte a nakonfigurujte tyto požadované prostředky Azure (ekvivalentní azd provision):

      • Plán Flex Consumption služby Azure Functions a aplikace funkcí s povoleným OpenTelemetry
      • Azure Storage (povinné) a Application Insights (doporučeno)
      • Service Bus namespace a fronta pro ukázku distribuovaného trasování
      • Zásady přístupu a role pro váš účet
      • Připojení mezi službami pomocí spravovaných identit (místo uložených připojovací řetězec)
    • Zabalte a nasaďte kód do kontejneru nasazení (ekvivalentní).azd deploy Aplikace se pak spustí a spustí v nasazeném balíčku.

    Po úspěšném dokončení příkazu se zobrazí odkazy na prostředky, které jste vytvořili.

Testování distribuovaného trasování

Teď můžete otestovat funkci distribuovaného trasování OpenTelemetry voláním nasazených funkcí a pozorováním telemetrie v Application Insights.

Vyvolání funkce v Azure

Koncové body funkcí v Azure můžete vyvolat tak, že na jejich adresy URL odešlete požadavky HTTP. Vzhledem k tomu, že funkce HTTP v této šabloně jsou nakonfigurovány s anonymním přístupem, nejsou vyžadovány žádné klíče funkcí.

  1. V místním terminálu nebo příkazovém řádku spusťte tento příkaz, abyste získali název aplikace funkcí a vytvořili adresu URL:

    APP_NAME=$(azd env get-value AZURE_FUNCTION_NAME)
    echo "Function URL: https://$APP_NAME.azurewebsites.net/api/first_http_function"
    

    Příkaz azd env get-value získá název aplikace funkcí z místního prostředí.

  2. Otestujte funkci v prohlížeči tak, že přejdete na adresu URL:

    https://your-function-app.azurewebsites.net/api/first_http_function
    

    Nahraďte your-function-app skutečným názvem aplikace funkcí z předchozího kroku. Tento jediný požadavek vytvoří distribuované trasování, které prochází všemi třemi funkcemi.

Zobrazení distribuovaného trasování v Application Insights

Po vyvolání funkce můžete sledovat kompletní distribuované trasování v Application Insights:

Poznámka:

Po vyvolání funkce může trvat několik minut, než se telemetrická data zobrazí v Application Insights. Pokud se data nezobrazují okamžitě, počkejte několik minut a aktualizujte zobrazení.

  1. Přejděte k prostředku Application Insights na webu Azure Portal (najdete ho ve stejné skupině prostředků jako vaše aplikace funkcí).

  2. Otevřete mapu aplikace a zobrazte distribuované trasování napříč všemi třemi funkcemi. Měli byste vidět tok z požadavku HTTP prostřednictvím vašich funkcí a do služby Service Bus.

  3. Zkontrolujte hledání transakcí a vyhledejte svou žádost a podívejte se na úplnou časovou osu trasování. Vyhledejte transakce z vaší funkční aplikace.

  4. Výběrem konkrétní transakce zobrazíte kompletní trasování, které ukazuje:

    • Požadavek HTTP na first_http_function
    • Interní volání HTTP na second_http_function
    • Zpráva služby Service Bus, která je odesílána
    • Zpracování servicebus_queue_trigger zprávy ze služby Service Bus
  5. V podrobnostech trasování můžete vidět:

    • Informace o načasování: Jak dlouho jednotlivé kroky trvaly
    • Závislosti: Propojení mezi funkcemi
    • Protokoly: Protokoly aplikací korelují s trasami
    • Metriky výkonu: Doby odezvy a propustnost

Tento příklad ukazuje kompletní distribuované trasování napříč několika službami Azure Functions s integrací OpenTelemetry a poskytuje úplný přehled o chování a výkonu vaší aplikace.

Opětovné nasazení kódu

azd up Spusťte příkaz tolikrát, kolikrát potřebujete zřizovat prostředky Azure a nasazovat aktualizace kódu do aplikace funkcí.

Poznámka:

Nejnovější balíček nasazení vždy přepíše nasazené soubory kódu.

Vaše počáteční odpovědi na azd výzvy a všechny proměnné prostředí vygenerované azd pomocí se ukládají místně ve vašem pojmenovaném prostředí. azd env get-values Pomocí příkazu zkontrolujte všechny proměnné ve vašem prostředí, které příkaz používá při vytváření prostředků Azure.

Vyčistěte zdroje

Po dokončení práce s aplikací funkcí a souvisejícími prostředky pomocí tohoto příkazu odstraňte aplikaci funkcí a související prostředky z Azure a vyhněte se dalším nákladům:

azd down --no-prompt

Poznámka:

Tato --no-prompt možnost dává azd pokyn k odstranění skupiny prostředků bez potvrzení od vás.

Tento příkaz nemá vliv na místní projekt kódu.