Skip to content

Commit e04ccef

Browse files
authored
Add OTEL metrics continuous pipeline for continuous monitoring of dev branch. (#11057)
1 parent 7adf9e4 commit e04ccef

File tree

6 files changed

+264
-0
lines changed

6 files changed

+264
-0
lines changed

eng/ci/host.metrics-monitor.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# No triggers for code push to any branch.
2+
trigger: none
3+
4+
# No PR triggers.
5+
pr: none
6+
7+
schedules:
8+
- cron: "0 6,18 * * *"
9+
displayName: Daily Schedule (6 AM & 6 PM UTC)
10+
branches:
11+
include:
12+
- dev
13+
always: true
14+
15+
resources:
16+
repositories:
17+
- repository: 1es
18+
type: git
19+
name: 1ESPipelineTemplates/1ESPipelineTemplates
20+
ref: refs/tags/release
21+
- repository: eng
22+
type: git
23+
name: engineering
24+
ref: refs/tags/release
25+
26+
variables:
27+
- template: /ci/variables/cfs.yml@eng
28+
29+
extends:
30+
template: v1/1ES.Unofficial.PipelineTemplate.yml@1es
31+
parameters:
32+
pool:
33+
name: 1es-pool-azfunc-benchmarking
34+
image: 1es-windows-2022-benchmark-runner-vanilla
35+
os: windows
36+
37+
stages:
38+
- stage: RunWindows
39+
displayName: Collect Windows metrics
40+
jobs:
41+
- template: /eng/ci/templates/official/jobs/run-metrics-monitor.yml@self
42+
parameters:
43+
description: .NET9 Timer Application
44+
functionAppName: TimerAppNet9
45+
azureMonitorConnectionString: $(WINDOWS_AZURE_MONITOR_CONNECTION)
46+
47+
- stage: RunLinux
48+
dependsOn: []
49+
displayName: Collect Linux metrics
50+
jobs:
51+
- template: /eng/ci/templates/official/jobs/run-metrics-monitor.yml@self
52+
parameters:
53+
os: Linux
54+
description: .NET9 Timer Application
55+
functionAppName: TimerAppNet9
56+
azureMonitorConnectionString: $(LINUX_AZURE_MONITOR_CONNECTION)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
parameters:
2+
- name: description
3+
type: string
4+
- name: functionAppName
5+
type: string
6+
- name: os
7+
type: string
8+
default: Windows
9+
values:
10+
- Windows
11+
- Linux
12+
- name: azureMonitorConnectionString
13+
type: string
14+
15+
jobs:
16+
- job: ${{ parameters.functionAppName }}_${{ parameters.os }}
17+
displayName: ${{ parameters.os }} ${{ parameters.description }}
18+
pool:
19+
name: 1es-pool-azfunc-benchmarking
20+
${{ if eq(parameters.os, 'Linux') }}:
21+
image: 1es-ubuntu-22.04-benchmark-runner-vanilla
22+
os: linux
23+
${{ else }}:
24+
image: 1es-windows-2022-benchmark-runner-vanilla
25+
os: windows
26+
27+
variables:
28+
functionAppOutputPath: $(Build.BinariesDirectory)/Published/${{ parameters.functionAppName }}
29+
hostOutputPath: $(Build.BinariesDirectory)/Published/HostRuntime
30+
hostAssemblyPath: $(hostOutputPath)/Microsoft.Azure.WebJobs.Script.WebHost.dll
31+
logsDirectory: $(Build.ArtifactStagingDirectory)/Logs
32+
stdOutLogsFilePath: $(logsDirectory)/std_out_logs.txt
33+
stdErrorLogsFilePath: $(logsDirectory)/std_error_logs.txt
34+
FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated'
35+
FUNCTIONS_WORKER_RUNTIME_VERSION: '9.0'
36+
AzureFunctionsWebHost__hostid: '${{ parameters.functionAppName }}_${{ parameters.os }}'
37+
AzureWebJobsScriptRoot: '$(functionAppOutputPath)'
38+
${{ if eq(parameters.os, 'Linux') }}:
39+
publishRid: linux-x64
40+
${{ if eq(parameters.os, 'Windows') }}:
41+
publishRid: win-x64
42+
43+
steps:
44+
- template: /eng/ci/templates/install-dotnet.yml@self
45+
46+
- task: CopyFiles@2
47+
displayName: Copy benchmark apps to temp location
48+
inputs:
49+
SourceFolder: '$(Build.SourcesDirectory)/test/Performance/Apps'
50+
Contents: '**/*'
51+
TargetFolder: '$(Build.ArtifactStagingDirectory)/PerformanceTestApps'
52+
CleanTargetFolder: true
53+
54+
- task: DotNetCoreCLI@2
55+
displayName: Publish function app
56+
inputs:
57+
command: publish
58+
publishWebProjects: false
59+
zipAfterPublish: false
60+
modifyOutputPath: false
61+
projects: '$(Build.ArtifactStagingDirectory)/PerformanceTestApps/${{ parameters.functionAppName }}/App.csproj'
62+
arguments: -c Release -o $(functionAppOutputPath) -f net9.0 -r $(publishRid)
63+
workingDirectory: $(Build.ArtifactStagingDirectory)/PerformanceTestApps/${{ parameters.functionAppName }}
64+
65+
- task: DotNetCoreCLI@2
66+
displayName: Publish host
67+
inputs:
68+
command: publish
69+
publishWebProjects: false
70+
zipAfterPublish: false
71+
modifyOutputPath: false
72+
projects: '$(Build.SourcesDirectory)/src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj'
73+
arguments: -c Release -o $(hostOutputPath) -r $(publishRid) -f net8.0 -p:PlaceholderSimulation=true
74+
workingDirectory: $(Build.SourcesDirectory)/src/WebJobs.Script.WebHost
75+
76+
- pwsh: |
77+
Write-Host "Creating log directory: $(logsDirectory)"
78+
New-Item -ItemType Directory -Path $(logsDirectory)
79+
displayName: Create log directories
80+
81+
- ${{ if eq(parameters.os, 'Windows') }}:
82+
- pwsh: |
83+
$env:APPLICATIONINSIGHTS_CONNECTION_STRING = "${env:AZ_MON_CONNECTION_STRING}"
84+
$errorLogFilePath = "$(Build.ArtifactStagingDirectory)/Logs/std_err_logs.txt"
85+
$process = Start-Process -FilePath "$(hostOutputPath)/Microsoft.Azure.WebJobs.Script.WebHost.exe" -RedirectStandardOutput $(stdOutLogsFilePath) -RedirectStandardError $(stdErrorLogsFilePath) -PassThru
86+
Write-Host "Started process ID: $($process.Id)"
87+
echo $process.Id > $(Build.ArtifactStagingDirectory)/hostProcessId.txt
88+
displayName: Start functions host
89+
env:
90+
AZ_MON_CONNECTION_STRING: ${{ parameters.azureMonitorConnectionString }}
91+
- ${{ else }}:
92+
- script: |
93+
# In Azure DevOps, when variables convert into environment variables, variable names become uppercase, and periods turn into underscores.
94+
# This works for windows when getting the env variable value, but fails on linux. So we need to pass the variable value using correct case.
95+
# https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#environment-variables
96+
export AzureWebJobsScriptRoot="$(functionAppOutputPath)"
97+
export APPLICATIONINSIGHTS_CONNECTION_STRING="$AZ_MON_CONNECTION_STRING"
98+
nohup dotnet $(hostAssemblyPath) > $(stdOutLogsFilePath) 2> $(stdErrorLogsFilePath) &
99+
echo $! > $(Build.ArtifactStagingDirectory)/hostProcessId.txt
100+
displayName: Start functions host
101+
env:
102+
AZ_MON_CONNECTION_STRING: ${{ parameters.azureMonitorConnectionString }}
103+
104+
- pwsh: |
105+
$url = "http://localhost:5000/api/warmup"
106+
Write-Host "Checking if host is ready at $url..."
107+
$maxAttempts = 5
108+
109+
for ($attempt = 0; $attempt -lt $maxAttempts; $attempt++) {
110+
try {
111+
$response = Invoke-RestMethod -Uri $url -Method Get -TimeoutSec 5 -ErrorAction Stop
112+
Write-Host "Response: $response"
113+
break
114+
} catch {
115+
Write-Host "Attempt $($attempt+1) failed: $_.Exception.Message. Retrying in 10 seconds..."
116+
Start-Sleep -Seconds 10
117+
}
118+
}
119+
displayName: Wait until host is ready
120+
121+
- pwsh: |
122+
$helloUrl = "http://localhost:5000?forcespecialization=1"
123+
Write-Host "Calling $helloUrl"
124+
Invoke-WebRequest -Uri $helloUrl -Method Get -ErrorAction Stop
125+
displayName: Specialize
126+
127+
- pwsh: |
128+
$appRunDurationInSeconds = $env:RUN_DURATION_IN_SECONDS
129+
Start-Sleep -Seconds $appRunDurationInSeconds
130+
displayName: Run for $(APP_RUN_DURATION_IN_SECONDS) seconds
131+
env:
132+
RUN_DURATION_IN_SECONDS: $(APP_RUN_DURATION_IN_SECONDS)
133+
134+
- ${{ if eq(parameters.os, 'Windows') }}:
135+
- pwsh: |
136+
$processIdFile = "$(Build.ArtifactStagingDirectory)/hostProcessId.txt"
137+
if (Test-Path $processIdFile) {
138+
$processId = Get-Content $processIdFile
139+
Write-Host "Stop functions host process with process ID: $processId"
140+
Stop-Process -Id $processId -Force
141+
} else {
142+
Write-Host "Process ID file not found."
143+
}
144+
displayName: Stop host process
145+
condition: always()
146+
147+
- ${{ if eq(parameters.os, 'Linux') }}:
148+
- script: |
149+
processIdFile="$(Build.ArtifactStagingDirectory)/hostProcessId.txt"
150+
if [ -f "$processIdFile" ]; then
151+
processId=$(cat $processIdFile)
152+
echo "Sending SIGTERM to functions host process process ID: $processId"
153+
kill -SIGTERM $processId
154+
else
155+
echo "Process ID file not found."
156+
fi
157+
displayName: Stop host process
158+
condition: always()
159+
160+
- pwsh: |
161+
Write-Host "Logs:"
162+
Get-Content $(stdOutLogsFilePath)
163+
Write-Host "----"
164+
Write-Host "Error logs:"
165+
Get-Content $(stdErrorLogsFilePath)
166+
displayName: Print logs
167+
condition: always()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net9.0</TargetFramework>
4+
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
5+
<OutputType>Exe</OutputType>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
<ItemGroup>
10+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
11+
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
12+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.0" />
13+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.3.1" />
14+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.0" />
15+
</ItemGroup>
16+
</Project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using Microsoft.Azure.Functions.Worker.Builder;
2+
using Microsoft.Extensions.Hosting;
3+
4+
var builder = FunctionsApplication.CreateBuilder(args);
5+
builder.ConfigureFunctionsWebApplication();
6+
7+
await builder.Build().RunAsync();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Microsoft.Azure.Functions.Worker;
2+
using Microsoft.Extensions.Logging;
3+
4+
namespace App
5+
{
6+
public sealed class TimerFunctions(ILoggerFactory loggerFactory)
7+
{
8+
private readonly ILogger _logger = loggerFactory.CreateLogger<TimerFunctions>();
9+
10+
[Function("TimerFunction1")]
11+
public void Run([TimerTrigger("%TIMER_RUN_SCHEDULE_CRON_EXPRESSION%")] TimerInfo timer)
12+
=> _logger.LogInformation($"C# Timer trigger executed at:{DateTime.Now}. IsPastDue:{timer.IsPastDue}");
13+
}
14+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"version": "2.0",
3+
"telemetryMode": "OpenTelemetry"
4+
}

0 commit comments

Comments
 (0)