이 문서에서는 통합 Application Insights 및 OpenTelemetry 지원을 사용하여 여러 함수 호출에서 분산 추적을 가능하게 하는 Azure Function의 OpenTelemetry 지원을 보여 줍니다. 시작하는 데 도움이 되도록 Azure 개발자 CLI(azd) 템플릿을 사용하여 코드 프로젝트와 앱을 실행할 Azure 배포를 만듭니다.
이 자습서에서는 azd 도구를 사용하여 다음을 수행합니다.
- 템플릿에서 OpenTelemetry 지원 프로젝트를 초기화합니다.
- OpenTelemetry 통합을 사용하도록 설정하는 코드를 검토합니다.
- OpenTelemetry 사용 앱을 로컬로 실행하고 확인합니다.
- Azure에서 함수 앱 및 관련 리소스를 만듭니다.
- Azure의 함수 앱에 코드 프로젝트를 배포합니다.
- Application Insights에서 분산 추적을 확인합니다.
이 템플릿에서 만든 필수 Azure 리소스는 Azure에서 안전하고 확장 가능한 함수 앱 배포에 대한 현재 모범 사례를 따릅니다. 또한 동일한 azd 명령은 Azure의 새 함수 앱에 코드 프로젝트를 배포합니다.
기본적으로 Flex Consumption 계획은 사용한 만큼 지불하는 청구 모델을 따릅니다. 즉, 이 빠른 시작을 완료하면 Azure 계정에서 소액의 비용, 약 몇 센트 미만의 USD, 이 발생합니다.
중요합니다
이 문서에서는 현재 C#, Python 및 TypeScript만 지원합니다. 빠른 시작을 완료하려면 문서 맨 위에 있는 지원되는 언어 중 하나를 선택합니다.
필수 조건
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
프로젝트 시작
이 azd init 명령을 사용하여 OpenTelemetry 분산 추적을 포함하는 템플릿에서 로컬 Azure Functions 코드 프로젝트를 만듭니다.
로컬 터미널이나 명령 프롬프트에서 빈 폴더에서 이
azd init명령을 실행합니다.azd init --template functions-quickstart-python-azd-otel -e flexquickstart-otel이 명령은 템플릿 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다.
-e플래그는 현재 환경의 이름을 설정합니다. 환경에서azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 환경 이름은 Azure에서 만든 리소스 그룹의 이름에도 표시됩니다.
로컬 터미널이나 명령 프롬프트에서 빈 폴더에서 이
azd init명령을 실행합니다.azd init --template functions-quickstart-typescript-azd-otel -e flexquickstart-otel이 명령은 템플릿 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다.
-e플래그는 현재 환경의 이름을 설정합니다. 환경에서azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 환경 이름은 Azure에서 만든 리소스 그룹의 이름에도 표시됩니다.
로컬 터미널이나 명령 프롬프트에서 빈 폴더에서 이
azd init명령을 실행합니다.azd init --template functions-quickstart-dotnet-azd-otel -e flexquickstart-otel이 명령은 템플릿 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다.
-e플래그는 현재 환경의 이름을 설정합니다. 환경에서azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 환경 이름은 Azure에서 만든 리소스 그룹의 이름에도 표시됩니다.
코드 검토
템플릿은 함께 작동하는 세 가지 함수를 사용하여 완전한 분산 추적 시나리오를 만듭니다. OpenTelemetry 관련 주요 측면을 검토해 보겠습니다.
OpenTelemetry 구성
이 파일은 src/otel-sample/host.json Functions 호스트에 대해 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 Functions 호스트에 대해 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 Application Insights와 OpenTelemetry 통합을 제공합니다.
파일에는 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 패키지는 Application Insights와 OpenTelemetry 통합을 제공합니다.
파일에는 .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" />
이러한 패키지는 분산 추적을 위해 Application Insights 및 HTTP 계측과 OpenTelemetry 통합을 제공합니다.
함수 구현
이 함수는 분산 추적 흐름을 보여 줍니다 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
)
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')
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,
};
}
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; }
}
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);
}
}
분산 추적 흐름
이 아키텍처는 다음 동작을 사용하여 전체 분산 추적 시나리오를 만듭니다.
- 첫 번째 HTTP 함수 는 HTTP 요청을 수신하고 두 번째 HTTP 함수를 호출합니다.
- 두 번째 HTTP 함수 가 응답하고 Service Bus에 메시지를 보냅니다.
- Service Bus 트리거 는 처리 작업을 시뮬레이션하기 위해 지연으로 메시지를 처리합니다.
OpenTelemetry 구현의 주요 측면:
-
OpenTelemetry 통합: 파일은
host.json"telemetryMode": "OpenTelemetry"을(를) 통해 OpenTelemetry를 활성화합니다. - 함수 연결: 첫 번째 함수는 HTTP 요청을 사용하여 두 번째 함수를 호출하여 상관 관계가 있는 추적을 만듭니다.
- Service Bus 통합: 두 번째 함수는 세 번째 함수를 트리거하는 Service Bus에 출력됩니다.
-
익명 인증: HTTP 함수가 사용
auth_level=func.AuthLevel.ANONYMOUS하므로 함수 키가 필요하지 않습니다.
여기에서 전체 템플릿 프로젝트를 검토할 수 있습니다.
-
OpenTelemetry 통합: 파일은
index.ts추적 및 로그에 대한 Azure Monitor 내보내기를 사용하여 OpenTelemetry를 구성합니다. - 함수 연결: 첫 번째 함수는 자동 추적 전파와 함께 axios를 사용하여 두 번째 함수를 호출합니다.
- Service Bus 통합: 두 번째 함수는 세 번째 함수를 트리거하는 출력 바인딩을 사용하여 Service Bus에 출력합니다.
- 관리 ID: 모든 Service Bus 연결은 연결 문자열 대신 관리 ID를 사용합니다.
- 처리 시뮬레이션: Service Bus 트리거의 5초 지연은 메시지 처리 작업을 시뮬레이션합니다.
여기에서 전체 템플릿 프로젝트를 검토할 수 있습니다.
- OpenTelemetry 통합: 이 파일은 Azure Monitor 내보내기 프로그램을 사용하여 OpenTelemetry를 구성합니다.
- 함수 연결: 첫 번째 함수는 OpenTelemetry 계측과 함께 HttpClient를 사용하여 두 번째 함수를 호출합니다.
- Service Bus 통합: 두 번째 함수는 세 번째 함수를 트리거하는 출력 바인딩을 사용하여 Service Bus에 출력합니다.
- 관리 ID: 모든 Service Bus 연결은 연결 문자열 대신 관리 ID를 사용합니다.
- .NET 8 격리 작업자: 성능 및 유연성을 높이기 위해 최신 Azure Functions .NET 격리 작업자 모델을 사용합니다.
여기에서 전체 템플릿 프로젝트를 검토할 수 있습니다.
로컬에서 함수를 확인한 후에는 Azure에 게시할 차례입니다.
Azure에 배포
이 프로젝트는 명령을 사용하여 azd up OpenTelemetry를 지원하는 Azure의 Flex Consumption 계획의 새 함수 앱에 이 프로젝트를 배포하도록 구성됩니다.
팁 (조언)
이 프로젝트에는 관리 ID 연결을 비롯한 모범 사례를 따르는 Flex 사용 계획에 보안 배포를 만드는 데 사용하는 Bicep 파일 azd 집합이 포함되어 있습니다.
azd가 Azure에서 필요한 Azure 리소스를 만들고 코드 프로젝트를 새 함수 앱에 배포하려면 이 명령을 실행합니다.azd up루트 폴더에는
azure.yaml에 필요한azd정의 파일이 포함되어 있습니다.아직 로그인하지 않은 경우 Azure 계정으로 인증하라는 메시지가 표시됩니다.
메시지가 표시되면 다음과 같은 필수 배포 매개 변수를 제공합니다.
매개 변수 Description Azure 구독 리소스가 만들어지는 구독. Azure 위치 새 Azure 리소스가 포함된 리소스 그룹을 만들 Azure 지역입니다. 현재 Flex 사용량 플랜을 지원하는 지역만 표시됩니다. azd up명령은 이러한 프롬프트에 대한 응답을 Bicep 구성 파일과 함께 사용하여 이러한 배포 작업을 완료합니다.다음과 같은 필수 Azure 리소스를 만들고 구성합니다(
azd provision과 동등):- OpenTelemetry가 사용하도록 설정된 Azure Functions Flex 사용량 플랜 및 함수 앱
- Azure Storage(필수) 및 Application Insights(권장)
- 분산 추적 데모를 위한 Service Bus 네임스페이스 및 큐
- 사용자의 계정에 대한 액세스 정책 및 역할
- 관리 ID를 사용한 서비스 간 연결(저장된 연결 문자열 대신)
코드를 패키지하여 배포 컨테이너(
azd deploy와 동등)에 배포합니다. 그러면 앱이 시작되어 배포된 패키지에서 실행됩니다.
명령이 성공적으로 완료되면, 만든 리소스에 대한 링크가 표시됩니다.
분산 추적 테스트
이제 배포된 함수를 호출하고 Application Insights에서 원격 분석을 관찰하여 OpenTelemetry 분산 추적 기능을 테스트할 수 있습니다.
Azure에서 함수 호출
URL에 대한 HTTP 요청을 만들어 Azure에서 함수 엔드포인트를 호출할 수 있습니다. 이 템플릿의 HTTP 함수는 익명 액세스로 구성되므로 함수 키가 필요하지 않습니다.
로컬 터미널 또는 명령 프롬프트에서 다음 명령을 실행하여 함수 앱 이름을 가져와서 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명령은 로컬 환경에서 함수 앱 이름을 가져옵니다.URL로 이동하여 브라우저에서 함수를 테스트합니다.
https://your-function-app.azurewebsites.net/api/first_http_function이전 단계의 실제 함수 앱 이름으로
your-function-app을(를) 바꿉니다. 이 단일 요청은 세 가지 함수를 모두 통과하는 분산 추적을 만듭니다.
Application Insights에서 분산 추적 보기
함수를 호출한 후 Application Insights에서 전체 분산 추적을 관찰할 수 있습니다.
비고
함수를 호출한 후 Application Insights에 원격 분석 데이터가 표시되는 데 몇 분 정도 걸릴 수 있습니다. 데이터가 즉시 표시되지 않으면 몇 분 정도 기다렸다가 보기를 새로 고칩니다.
Azure Portal에서 Application Insights 리소스로 이동합니다(함수 앱과 동일한 리소스 그룹에서 찾을 수 있습니다).
애플리케이션 맵을 열어 세 함수 모두에 분산된 추적을 확인합니다. HTTP 요청에서 시작하여 함수 및 Service Bus로 이어지는 흐름을 확인할 수 있어야 합니다.
트랜잭션 검색을 확인하여 요청을 찾고 전체 추적 타임라인을 확인합니다. Function 앱에서 트랜잭션을 검색합니다.
특정 트랜잭션을 선택하여 다음을 보여 주는 엔드 투 엔드 추적을 확인합니다.
- 에 대한 HTTP 요청
first_http_function - 에 대한 내부 HTTP 호출
second_http_function - 전송 중인 Service Bus 메시지
- Service Bus에서 온 메시지를 처리하는
servicebus_queue_trigger
- 에 대한 HTTP 요청
추적 세부 정보에서 다음을 확인할 수 있습니다.
- 타이밍 정보: 각 단계의 소요 시간
- 종속성: 함수 간의 연결
- 로그: 추적과 상관 관계가 있는 애플리케이션 로그
- 성능 메트릭: 응답 시간 및 처리량
이 예제에서는 OpenTelemetry 통합을 사용하여 여러 Azure Functions에서 종단 간 분산 추적을 보여 줍니다. 애플리케이션의 동작 및 성능에 대한 완전한 가시성을 제공합니다.
코드 다시 배포
필요에 따라 Azure 리소스를 프로비전하고 함수 앱에 코드 업데이트를 배포하기 위해 azd up 명령을 여러 번 실행합니다.
비고
최신 배포 패키지는 항상 배포된 코드 파일을 덮어씁니다.
azd 프롬프트에 대한 초기 응답과 azd에서 생성된 모든 환경 변수는 명명된 환경에 로컬로 저장됩니다. 명령을 azd env get-values 사용하여 Azure 리소스를 만들 때 명령이 사용하는 환경의 모든 변수를 검토합니다.
자원을 정리하세요
함수 앱 및 관련 리소스 작업을 마쳤으면 이 명령을 사용하여 Azure에서 함수 앱 및 관련 리소스를 삭제하고 추가 비용이 발생하지 않도록 합니다.
azd down --no-prompt
비고
--no-prompt 옵션은 사용자의 확인 없이 azd에게 리소스 그룹을 삭제하도록 지시합니다.
이 명령은 로컬 코드 프로젝트에는 영향을 미치지 않습니다.