fix(windows): start telegraf on Windows Server 2025 hosts - #1782
Open
zanejohnson-azure wants to merge 1 commit into
Open
zanejohnson-azure wants to merge 1 commit into
zanejohnson-azure wants to merge 1 commit into
Conversation
Test-FluentbitTcpListener detected the fluent-bit listener by scanning
`netstat -an` for a LISTENING row on port 25229. Inside the ama-logs
container on a Windows Server 2025 host, both `netstat -an` and
`Get-NetTCPConnection` return zero connection rows, even though the
listener is present and accepting connections.
The check therefore never succeeded on WS2025, so Start-Telegraf always
took the failure path and logged "Telegraf not started since Fluentbit
tcp listener is not up and running on port 25229". The service was
installed but never started, and no custom Prometheus metrics were
collected from WS2025 nodes at all.
Detect the listener by connecting to it instead of enumerating sockets.
A TcpClient BeginConnect/WaitOne probe reflects what the caller actually
needs to know and works on both WS2022 and WS2025.
Also bound the retry loop by wall-clock time. A failed connect can
consume its full timeout, so counting attempts would have stretched the
configured wait (e.g. 45s) to roughly 3x that. The deadline keeps the
configured window accurate.
Verified on an AKS cluster with WS2022 and WS2025 node pools:
- probe inside the WS2025 container: netstat method False,
connect method True, closed-port control False
- WS2025 produced zero InsightsMetrics rows across the cluster
lifetime; after the listener was detected and telegraf started,
scraped samples began flowing to the workspace
- WS2022 behaviour unchanged (listener still detected, telegraf runs)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 09e0348b-9670-4407-8126-48bcc0c91a87
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
| # previous netstat-based check fail forever and prevented telegraf from ever starting. | ||
| $client = New-Object System.Net.Sockets.TcpClient | ||
| try { | ||
| $asyncResult = $client.BeginConnect('127.0.0.1', $port, $null, $null) |
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Test-FluentbitTcpListenerdetects the fluent-bit listener by scanningnetstat -anfor aLISTENINGrow on port 25229.Inside the ama-logs container on a Windows Server 2025 host, both
netstat -anandGet-NetTCPConnectionreturn zero connection rows — even though the listener is present and accepting connections. TCP itself works fine; only enumeration is blind.So the check never succeeds on WS2025,
Start-Telegrafalways takes the failure path, and the agent logs:Because
--service installruns unconditionally, the telegraf service exists but is never started. No custom Prometheus metrics are collected from WS2025 nodes at all.Measured inside the agent container on both OS versions:
netstat -anLISTENING rowsGet-NetTCPConnection -State ListenFix
Detect the listener by connecting to it instead of enumerating sockets. A
TcpClient.BeginConnect/WaitOneprobe tests exactly what the caller needs to know, and works on both WS2022 and WS2025.Also bound the retry loop by wall-clock time. A failed connect can consume its full timeout, so counting attempts would have stretched the configured wait (e.g. 45s) to roughly 3x that. The deadline keeps the configured window accurate.
Validation
Run on an AKS cluster (K8s 1.35.7) with both WS2022 and WS2025 node pools, agent
ciprod:win-3.6.0:False, new connect methodTrue, closed-port controlFalse(no false positive).InsightsMetricsrows across the entire ~12h cluster lifetime (verified withfirstSeen=min(TimeGenerated)). After the listener was detected and telegraf started, scraped samples began flowing to the workspace within ~20 seconds and sustained ~18-20 per 5-minute bin.main.ps1parses with 0 syntax errors.Independently reproduced on a second cluster
The same defect was confirmed on a different cluster, in a different region, running a newer agent image (
win-3.8.0-ci-prod-09-03-2026-fd42f68c):netstatandGet-NetTCPConnectionboth returned zero rows, a real connect to127.0.0.1:25229succeeded, a closed port (39999) was correctly refused, and the telegraf service was leftStoppedwith no process. Line 60 still usesnetstatin 3.8.0, so this fix is still required on current images.Data-side impact on that cluster: the WS2022 node ingests custom Prometheus metrics normally while the WS2025 node produces none at all.
Important for sequencing - this fix alone is not sufficient for WS2025
Starting telegraf on WS2025 also exposes it to a separate, pre-existing defect that affects WS2022 today, caused by the old telegraf version shipped on Windows (1.24.2, verified by
--versioninside the container).The defect: the kubelet credential is captured once and never refreshed
In telegraf v1.24.2,
plugins/inputs/prometheus/kubernetes.gopasses the in-cluster bearer token straight through, with no re-read:and
cAdvisorbuilds the request once and reuses it:So Kubernetes service-account token rotation (~hourly) is never picked up, and roughly an hour after telegraf starts all pod discovery fails permanently:
Measured by manually starting telegraf on a WS2025 node to simulate post-fix behaviour: process start 16:26:49Z, cached token expiry 17:14:36Z, first 401 at 17:16:51Z, then ~57 errors/min indefinitely. A fixed WS2025 node therefore gets roughly 48 minutes of working discovery before failing permanently.
The failure is silent: telegraf keeps scraping its cached pod list, so throughput never drops while newly created pods become permanently undiscoverable. Confirmed on WS2022: 13 pods running, only 11 scraped, frozen for 10 hours.
Root cause was proven rather than inferred: from inside the same container while telegraf returned 401, the current on-disk token returned HTTP 200 from the kubelet
/podsendpoint and an anonymous call returned 401 — eliminating RBAC, reachability, and cluster configuration.This is already fixed upstream - the existing chart comment is correct
From v1.28.5 onward (verified in v1.28.5 and v1.39.1), the outer loop re-reads the token file before every
cAdvisorcall:Because
cAdvisorreturns on its first failed poll, the outer loop re-reads the current token ~1s later and rebuilds the request — so the failure is self-healing rather than permanent.This validates the existing comment in
charts/azuremonitor-containers/templates/ama-logs-daemonset-windows.yaml:Recommendation: upgrade Windows telegraf from 1.24.2 to >= 1.28.5 (Linux already ships 1.39.1, which is why Linux is unaffected). Note the fix calls
os.ReadFileunconditionally rather than checking file metadata — which matters, because on Windows the projected token file'sLastWriteTimeUtcdoes not advance when the token rotates (observed content dated Sept 15 with an mtime of Sept 11). A metadata-based cache-invalidation approach would not work here.A second, still-unfixed upstream bug worth reporting separately
Present in v1.24.2, v1.28.5 and v1.39.1 — the response body is leaked on any non-200:
The connection is never released and retains its bound ephemeral port. On a canary cluster running 1.24.2 this was measured as a sustained leak of one bound port per failed request (handles/error = 1.0010, distinct bound ports/error = 1.0000, R² = 0.9999994 across 8 points, no plateau) while thread count stayed flat.
Upgrading to >= 1.28.5 makes this largely moot in practice, since the 401 storm that drives it stops recurring — but the leak remains latent for any other non-200 (kubelet restart, transient failure) and is worth an upstream issue.
Secondary change
test/scenario/log-gen-all-nodes-daemonset.yaml— dropped thekubernetes.azure.com/os-sku: Windows2022selector so the Windows log generator schedules on all Windows nodes. Theltsc2022image runs with process isolation on both WS2022 and WS2025 hosts, so no second DaemonSet is needed. This is what made WS2025 testable. (WS2019 hosts cannot run this image and would need a separate one.)