Поделиться через


Руководство по мониторингу функций Azure с помощью распределенной трассировки OpenTelemetry

В этой статье демонстрируется поддержка OpenTelemetry в функции Azure, которая обеспечивает распределенную трассировку между несколькими вызовами функций с помощью встроенной поддержки Application Insights и OpenTelemetry. Чтобы помочь вам начать работу, используется шаблон Azure Developer CLI azd для создания вашего проекта и развертывания Azure, в котором будет работать ваше приложение.

В этом руководстве вы будете использовать инструмент azd для:

  • Инициализировать проект с поддержкой OpenTelemetry из шаблона.
  • Просмотрите код, который включает интеграцию OpenTelemetry.
  • Запустите и проверьте локальное приложение с поддержкой OpenTelemetry.
  • Создайте приложение-функцию и связанные ресурсы в Azure.
  • Разверните проект кода в приложении-функции в Azure.
  • Проверьте распределенную трассировку в Application Insights.

Необходимые ресурсы Azure, созданные этим шаблоном, соответствуют текущим рекомендациям по безопасным и масштабируемым развертываниям приложений-функций в Azure. Эта же azd команда также развертывает проект кода в новом функциональном приложении в Azure.

По умолчанию, план потребления Flex следует модели выставления счетов с оплатой по факту использования, что означает, что выполнение этого краткого руководства может стоить вам всего несколько центов США или меньше в вашей учетной записи Azure.

Это важно

В настоящее время эта статья поддерживает только C#, Python и TypeScript. Чтобы завершить краткое руководство, выберите один из поддерживаемых языков в верхней части статьи.

Предпосылки

Инициализируйте проект

Используйте команду azd init для создания кода локального проекта Azure Functions из шаблона, включающего распределенную трассировку OpenTelemetry.

  1. В локальном терминале или командной строке выполните следующую azd init команду в пустой папке:

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

    Эта команда извлекает файлы проекта из репозитория шаблонов и инициализирует проект в текущей папке. Флаг -e задает имя текущей среды. В azd среде поддерживается уникальный контекст развертывания для вашего приложения, и вы можете определить более одного контекста. Имя среды также отображается в имени группы ресурсов, создаваемой в Azure.

  1. В локальном терминале или командной строке выполните следующую azd init команду в пустой папке:

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

    Эта команда извлекает файлы проекта из репозитория шаблонов и инициализирует проект в текущей папке. Флаг -e задает имя текущей среды. В azd среде поддерживается уникальный контекст развертывания для вашего приложения, и вы можете определить более одного контекста. Имя среды также отображается в имени группы ресурсов, создаваемой в Azure.

  1. В локальном терминале или командной строке выполните следующую azd init команду в пустой папке:

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

    Эта команда извлекает файлы проекта из репозитория шаблонов и инициализирует проект в текущей папке. Флаг -e задает имя текущей среды. В azd среде поддерживается уникальный контекст развертывания для вашего приложения, и вы можете определить более одного контекста. Имя среды также отображается в имени группы ресурсов, создаваемой в Azure.

Просмотр кода

Шаблон создает полный сценарий распределенной трассировки с тремя функциями, которые работают вместе. Рассмотрим ключевые аспекты, связанные с OpenTelemetry:

Конфигурация OpenTelemetry

Файл src/otel-sample/host.json включает OpenTelemetry для узла функций:

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

Параметр ключа "telemetryMode": "OpenTelemetry" включает распределенную трассировку между вызовами функций.

Файл src/OTelSample/host.json включает OpenTelemetry для узла функций:

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

Параметр ключа "telemetryMode": "OpenTelemetry" включает распределенную трассировку между вызовами функций.

Зависимости для OpenTelemetry

Файл src/otel-sample/requirements.txt содержит необходимые пакеты для интеграции OpenTelemetry:

azure-functions
azure-monitor-opentelemetry
requests

Пакет azure-monitor-opentelemetry предоставляет интеграцию OpenTelemetry с Application Insights.

Файл src/otel-sample/package.json содержит необходимые пакеты для интеграции 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"
  }
}

@azure/functions-opentelemetry-instrumentation и @azure/monitor-opentelemetry-exporter пакеты обеспечивают интеграцию OpenTelemetry с Application Insights.

Файл .csproj содержит необходимые пакеты для интеграции 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" />

Эти пакеты обеспечивают интеграцию OpenTelemetry с Application Insights и инструментированием HTTP для распределенной трассировки.

Реализация функции

Функции в src/otel-sample/function_app.py демонстрируют процесс распределенного трассирования.

Первая функция 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"
    )

Вторая функция 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
    )

Триггер очереди сервисной шины

@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')

Конфигурация OpenTelemetry настроена в 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()],
});

Функции определяются в папке src/otel-sample/src/functions :

Первая функция 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" }),
    };
  }
}

Вторая функция 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,
  };
}

Триггер очереди Azure 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");
}

Конфигурация OpenTelemetry настроена в 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();

Функции определяются в отдельных файлах классов:

Первая функция 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}");
    }
}

Вторая функция 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; }
}

Триггер очереди Azure 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);
    }
}

Распределенный поток трассировки

Эта архитектура создает полный сценарий распределенной трассировки с таким поведением:

  1. Первая функция HTTP получает HTTP-запрос и вызывает вторую функцию HTTP
  2. Вторая функция HTTP отвечает и отправляет сообщение в служебную шину
  3. Триггер служебной шины обрабатывает сообщение с задержкой для имитации работы обработки

Ключевые аспекты реализации OpenTelemetry:

  • Интеграция OpenTelemetry: host.json файл активирует OpenTelemetry с "telemetryMode": "OpenTelemetry"
  • Цепочка функций: первая функция вызывает вторую с помощью HTTP-запросов, создавая коррелированные трассировки.
  • Интеграция служебной шины: вторая функция выводится в служебную шину, которая активирует третью функцию.
  • Анонимная проверка подлинности: используются auth_level=func.AuthLevel.ANONYMOUSфункции HTTP, поэтому ключи функций не требуются

Вы можете просмотреть полный проект шаблона здесь.

  • Интеграция OpenTelemetry: index.ts файл настраивает OpenTelemetry с экспортерами Azure Monitor для трассировок и журналов.
  • Цепочка функций: первая функция вызывает вторую с помощью axios с автоматическим распространением трассировки.
  • Интеграция служебной шины: вторая функция выводится в служебную шину с помощью выходных привязок, которая активирует третью функцию.
  • Управляемое удостоверение: все подключения к служебной шине используют управляемое удостоверение вместо строк подключения
  • Моделирование обработки: 5-секундная задержка в триггере служебной шины имитирует работу обработки сообщений

Вы можете просмотреть полный проект шаблона здесь.

  • Интеграция OpenTelemetry: Program.cs файл настраивает OpenTelemetry с помощью экспортера Azure Monitor
  • Цепочка функций: первая функция вызывает вторую через HttpClient с инструментированием OpenTelemetry
  • Интеграция служебной шины: вторая функция выводится в служебную шину с помощью выходных привязок, которая активирует третью функцию.
  • Управляемое удостоверение: все подключения Шины Сервисов используют управляемое удостоверение вместо строк подключения
  • Изолированная рабочая модель .NET 8. Использует последнюю изолированную модель рабочего процесса Azure Functions .NET для повышения производительности и гибкости.

Вы можете просмотреть полный проект шаблона здесь.

После локальной проверки функций пора опубликовать их в Azure.

Развертывание в Azure

Этот проект сконфигурирован для развертывания в новом функциональном приложении в плане Flex Consumption в Azure с поддержкой OpenTelemetry.

Подсказка

Этот проект включает набор файлов Bicep, которые azd используются для создания безопасного развертывания в плане потребления Flex, следуя передовым практикам, включая интеграцию с управляемыми удостоверениями.

  1. Выполните следующую команду, чтобы azd создать необходимые ресурсы Azure в Azure и развернуть проект кода в новом приложении-функции:

    azd up
    

    Корневая папка содержит файл определения, необходимый azure.yamlazd.

    Если вы еще не вошли, вам будет предложено выполнить проверку подлинности с помощью учетной записи Azure.

  2. При появлении запроса укажите следующие необходимые параметры развертывания:

    Параметр Description
    Подписка Azure Подписка, в которой создаются ресурсы.
    Расположение Azure Регион Azure, в котором создается группа ресурсов, содержащая новые ресурсы Azure. Отображаются только регионы, поддерживающие план потребления Flex.

    Команда azd up использует ответ на эти запросы с файлами конфигурации Bicep для выполнения следующих задач развертывания:

    • Создайте и настройте эти необходимые ресурсы Azure (эквивалентно azd provision):

      • Приложение функции Azure с планом Flex Consumption и поддержкой OpenTelemetry
      • служба хранилища Azure (обязательно) и Application Insights (рекомендуется)
      • Пространство имен и очередь Service Bus для демонстрации распределенной трассировки
      • Политики и роли доступа для учетной записи
      • Подключения между службами с помощью управляемых удостоверений (вместо хранимых строка подключения)
    • Упаковайте и разверните код в контейнере развертывания (эквивалентно azd deploy). Затем приложение запускается и запускается в развернутом пакете.

    После успешного завершения команды вы увидите ссылки на созданные ресурсы.

Тестирование распределенной трассировки

Теперь вы можете протестировать возможности распределенной трассировки OpenTelemetry, вызывая развернутые функции и наблюдая телеметрию в Application Insights.

Вызов функции в Azure

Вы можете вызвать конечные точки функций в Azure, выполнив HTTP-запросы к их URL-адресам. Так как функции HTTP в этом шаблоне настроены с анонимным доступом, ключи функций не требуются.

  1. В локальном терминале или командной строке выполните следующую команду, чтобы получить имя приложения-функции и создать URL-адрес:

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

    Команда azd env get-value получает имя приложения-функции из локальной среды.

  2. Проверьте функцию в браузере, перейдя по URL-адресу:

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

    Замените your-function-app фактическим именем приложения-функции на предыдущем шаге. Этот один запрос создает распределенную трассировку, которая проходит через все три функции.

Просмотр распределенной трассировки в Application Insights

После вызова функции можно наблюдать полную распределенную трассировку в Application Insights:

Замечание

Может потребоваться несколько минут, чтобы данные телеметрии отображались в Application Insights после вызова функции. Если данные не отображаются немедленно, подождите несколько минут и обновите представление.

  1. Перейдите к ресурсу Application Insights на портале Azure (его можно найти в той же группе ресурсов, что и приложение-функция).

  2. Откройте карту приложения , чтобы увидеть распределенную трассировку во всех трех функциях. Вы должны увидеть поток от HTTP-запроса через ваши функции и до Service Bus.

  3. Проверьте поиск транзакций , чтобы найти запрос и просмотреть полную временную шкалу трассировки. Выполните поиск транзакций в функциональном приложении.

  4. Выберите определенную транзакцию, чтобы увидеть сквозную трассировку, которая показывает:

    • HTTP-запрос на first_http_function
    • Внутренний HTTP-вызов second_http_function
    • Сообщение служебной шины, которое отправляется
    • Обработка servicebus_queue_trigger сообщения из Service Bus
  5. В сведениях о трассировке можно увидеть следующее:

    • Сведения о времени: как долго каждый шаг занял
    • Зависимости: соединения между функциями
    • Логи: журналы приложений, связанные с трассировкой
    • Метрики производительности: время отклика и пропускная способность

В этом примере демонстрируется сквозная распределенная трассировка для нескольких функций Azure с интеграцией OpenTelemetry, обеспечивающая полную видимость поведения и производительности приложения.

Повторное развертывание кода

Выполните команду azd up столько раз, сколько потребуется, чтобы развернуть обновления кода в функции-приложении и настроить ресурсы Azure.

Замечание

Последний пакет развертывания всегда перезаписывает развернутые файлы кода.

Исходные ответы на azd запросы и любые переменные среды, созданные с помощью azd , хранятся локально в именованной среде. azd env get-values Используйте команду, чтобы просмотреть все переменные в вашей среде, которые команда использует при создании ресурсов Azure.

Очистите ресурсы

Когда вы закончите работу с приложением-функцией и связанными ресурсами, используйте эту команду, чтобы удалить приложение-функцию и связанные с ним ресурсы из Azure и избежать дополнительных затрат:

azd down --no-prompt

Замечание

Параметр --no-prompt указывает azd удалить группу ресурсов без подтверждения.

Эта команда не влияет на локальный проект кода.