Skip to content

Commit dde3441

Browse files
committed
feat: Add CNI-telemtry sidecar in CNS pod
1 parent d9c845c commit dde3441

File tree

7 files changed

+466
-33
lines changed

7 files changed

+466
-33
lines changed

.pipelines/pipeline.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ stages:
153153
arch: amd64
154154
name: npm
155155
os: windows
156+
cni_telemetry_sidecar_linux_amd64:
157+
arch: amd64
158+
name: cni-telemetry-sidecar
159+
os: linux
160+
cni_telemetry_sidecar_windows_amd64:
161+
arch: amd64
162+
name: cni-telemetry-sidecar
163+
os: windows
156164
steps:
157165
- template: containers/container-template.yaml
158166
parameters:
@@ -190,6 +198,10 @@ stages:
190198
arch: arm64
191199
name: npm
192200
os: linux
201+
cni_telemetry_sidecar_linux_arm64:
202+
arch: arm64
203+
name: cni-telemetry-sidecar
204+
os: linux
193205
steps:
194206
- template: containers/container-template.yaml
195207
parameters:
@@ -228,6 +240,9 @@ stages:
228240
azure_ip_masq_merger:
229241
name: azure-ip-masq-merger
230242
platforms: linux/amd64 linux/arm64
243+
cni_telemetry_sidecar:
244+
name: cni-telemetry-sidecar
245+
platforms: linux/amd64 linux/arm64 windows/amd64
231246
steps:
232247
- template: containers/manifest-template.yaml
233248
parameters:

Makefile

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ ACN_PACKAGE_PATH = github.com/Azure/azure-container-networking
8181
CNI_AI_PATH=$(ACN_PACKAGE_PATH)/telemetry.aiMetadata
8282
CNS_AI_PATH=$(ACN_PACKAGE_PATH)/cns/logger.aiMetadata
8383
NPM_AI_PATH=$(ACN_PACKAGE_PATH)/npm.aiMetadata
84+
CNI_TELEMETRY_SIDECAR_DIR = $(REPO_ROOT)/cns/cni-telemetry-sidecar
85+
CNI_TELEMETRY_SIDECAR_BUILD_DIR = $(BUILD_DIR)/cni-telemetry-sidecar
86+
CNI_TELEMETRY_SIDECAR_AI_ID = $(CNI_AI_ID) # Reuse CNI AI ID
87+
CNI_TELEMETRY_SIDECAR_VERSION = $(CNS_VERSION) # Version follows CNS
8488

8589
# Tool paths
8690
CONTROLLER_GEN := $(TOOLS_BIN_DIR)/controller-gen
@@ -107,6 +111,7 @@ NPM_ARCHIVE_NAME = azure-npm-$(GOOS)-$(GOARCH)-$(NPM_VERSION).$(ARCHIVE_EXT)
107111
AZURE_IPAM_ARCHIVE_NAME = azure-ipam-$(GOOS)-$(GOARCH)-$(AZURE_IPAM_VERSION).$(ARCHIVE_EXT)
108112
AZURE_IP_MASQ_MERGER_ARCHIVE_NAME = azure-ip-masq-merger-$(GOOS)-$(GOARCH)-$(AZURE_IP_MASQ_MERGER_VERSION).$(ARCHIVE_EXT)
109113
IPV6_HP_BPF_ARCHIVE_NAME = ipv6-hp-bpf-$(GOOS)-$(GOARCH)-$(IPV6_HP_BPF_VERSION).$(ARCHIVE_EXT)
114+
CNI_TELEMETRY_SIDECAR_ARCHIVE_NAME = azure-cni-telemetry-sidecar-$(GOOS)-$(GOARCH)-$(CNI_TELEMETRY_SIDECAR_VERSION).$(ARCHIVE_EXT)
110115

111116
# Image info file names.
112117
CNI_IMAGE_INFO_FILE = azure-cni-$(CNI_VERSION).txt
@@ -123,7 +128,7 @@ all-binaries-platforms: ## Make all platform binaries
123128

124129
# OS specific binaries/images
125130
ifeq ($(GOOS),linux)
126-
all-binaries: acncli azure-cni-plugin azure-cns azure-npm azure-ipam azure-ip-masq-merger ipv6-hp-bpf
131+
all-binaries: acncli azure-cni-plugin azure-cns azure-npm azure-ipam azure-ip-masq-merger ipv6-hp-bpf cni-telemetry-sidecar
127132
all-images: npm-image cns-image cni-manager-image azure-ip-masq-merger-image ipv6-hp-bpf-image
128133
else
129134
all-binaries: azure-cni-plugin azure-cns azure-npm
@@ -139,7 +144,7 @@ azure-npm: azure-npm-binary npm-archive
139144
azure-ipam: azure-ipam-binary azure-ipam-archive
140145
ipv6-hp-bpf: ipv6-hp-bpf-binary ipv6-hp-bpf-archive
141146
azure-ip-masq-merger: azure-ip-masq-merger-binary azure-ip-masq-merger-archive
142-
147+
cni-telemetry-sidecar: cni-telemetry-sidecar-binary cni-telemetry-sidecar-archive
143148

144149
##@ Versioning
145150

@@ -213,6 +218,10 @@ azure-vnet-ipamv6-binary:
213218
azure-vnet-telemetry-binary:
214219
cd $(CNI_TELEMETRY_DIR) && CGO_ENABLED=0 go build -v -o $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) -ldflags "-X main.version=$(CNI_VERSION) -X $(CNI_AI_PATH)=$(CNI_AI_ID)" -gcflags="-dwarflocationlists=true"
215220

221+
# Build the Azure CNI Telemetry Sidecar binary.
222+
cni-telemetry-sidecar-binary:
223+
cd $(CNI_TELEMETRY_SIDECAR_DIR) && CGO_ENABLED=0 go build -v -o $(CNI_TELEMETRY_SIDECAR_BUILD_DIR)/azure-cni-telemetry-sidecar$(EXE_EXT) -ldflags "-X main.version=$(CNI_TELEMETRY_SIDECAR_VERSION) -X $(CNI_AI_PATH)=$(CNI_TELEMETRY_SIDECAR_AI_ID)" -gcflags="-dwarflocationlists=true"
224+
216225
# Build the Azure CLI network binary.
217226
acncli-binary:
218227
cd $(ACNCLI_DIR) && CGO_ENABLED=0 go build -v -o $(ACNCLI_BUILD_DIR)/acn$(EXE_EXT) -ldflags "-X main.version=$(ACN_VERSION)" -gcflags="-dwarflocationlists=true"
@@ -275,6 +284,7 @@ CNI_IMAGE = azure-cni
275284
CNS_IMAGE = azure-cns
276285
NPM_IMAGE = azure-npm
277286
AZURE_IP_MASQ_MERGER_IMAGE = azure-ip-masq-merger
287+
CNI_TELEMETRY_SIDECAR_IMAGE = azure-cni-telemetry-sidecar
278288

279289
## Image platform tags.
280290
ACNCLI_PLATFORM_TAG ?= $(subst /,-,$(PLATFORM))-$(ACN_VERSION)
@@ -287,7 +297,7 @@ CNS_PLATFORM_TAG ?= $(subst /,-,$(PLATFORM))-$(CNS_VERSION)
287297
CNS_WINDOWS_PLATFORM_TAG ?= $(subst /,-,$(PLATFORM))-$(CNS_VERSION)-$(OS_SKU_WIN)
288298
NPM_PLATFORM_TAG ?= $(subst /,-,$(PLATFORM))-$(NPM_VERSION)
289299
AZURE_IP_MASQ_MERGER_PLATFORM_TAG ?= $(subst /,-,$(PLATFORM))-$(AZURE_IP_MASQ_MERGER_VERSION)
290-
300+
CNI_TELEMETRY_SIDECAR_PLATFORM_TAG ?= $(subst /,-,$(PLATFORM))-$(CNI_TELEMETRY_SIDECAR_VERSION)
291301

292302
qemu-user-static: ## Set up the host to run qemu multiplatform container builds.
293303
sudo $(CONTAINER_RUNTIME) run --rm --privileged multiarch/qemu-user-static --reset -p yes
@@ -542,6 +552,35 @@ npm-image-pull: ## pull cns container image.
542552
IMAGE=$(NPM_IMAGE) \
543553
TAG=$(NPM_PLATFORM_TAG)
544554

555+
# cni-telemetry-sidecar
556+
557+
cni-telemetry-sidecar-image-name: # util target to print the CNI telemetry sidecar image name
558+
@echo $(CNI_TELEMETRY_SIDECAR_IMAGE)
559+
560+
cni-telemetry-sidecar-image-name-and-tag: # util target to print the CNI telemetry sidecar image name and tag.
561+
@echo $(IMAGE_REGISTRY)/$(CNI_TELEMETRY_SIDECAR_IMAGE):$(CNI_TELEMETRY_SIDECAR_PLATFORM_TAG)
562+
563+
cni-telemetry-sidecar-image: ## build cni-telemetry-sidecar container image.
564+
$(MAKE) container \
565+
DOCKERFILE=cns/cni-telemetry-sidecar/Dockerfile \
566+
IMAGE=$(CNI_TELEMETRY_SIDECAR_IMAGE) \
567+
EXTRA_BUILD_ARGS='--build-arg CNI_AI_PATH=$(CNI_AI_PATH) --build-arg CNI_AI_ID=$(CNI_TELEMETRY_SIDECAR_AI_ID)' \
568+
PLATFORM=$(PLATFORM) \
569+
TAG=$(CNI_TELEMETRY_SIDECAR_PLATFORM_TAG) \
570+
TARGET=$(OS) \
571+
OS=$(OS) \
572+
ARCH=$(ARCH)
573+
574+
cni-telemetry-sidecar-image-push: ## push cni-telemetry-sidecar container image.
575+
$(MAKE) container-push \
576+
IMAGE=$(CNI_TELEMETRY_SIDECAR_IMAGE) \
577+
TAG=$(CNI_TELEMETRY_SIDECAR_PLATFORM_TAG)
578+
579+
cni-telemetry-sidecar-image-pull: ## pull cni-telemetry-sidecar container image.
580+
$(MAKE) container-pull \
581+
IMAGE=$(CNI_TELEMETRY_SIDECAR_IMAGE) \
582+
TAG=$(CNI_TELEMETRY_SIDECAR_PLATFORM_TAG)
583+
545584
## Reusable targets for building multiplat container image manifests.
546585

547586
IMAGE_ARCHIVE_DIR ?= $(shell pwd)
@@ -681,6 +720,21 @@ npm-skopeo-archive: ## export tar archive of multiplat container manifest.
681720
IMAGE=$(NPM_IMAGE) \
682721
TAG=$(NPM_VERSION)
683722

723+
cni-telemetry-sidecar-manifest-build: ## build cni-telemetry-sidecar multiplat container manifest.
724+
$(MAKE) manifest-build \
725+
PLATFORMS="$(PLATFORMS)" \
726+
IMAGE=$(CNI_TELEMETRY_SIDECAR_IMAGE) \
727+
TAG=$(CNI_TELEMETRY_SIDECAR_VERSION)
728+
729+
cni-telemetry-sidecar-manifest-push: ## push cni-telemetry-sidecar multiplat container manifest
730+
$(MAKE) manifest-push \
731+
IMAGE=$(CNI_TELEMETRY_SIDECAR_IMAGE) \
732+
TAG=$(CNI_TELEMETRY_SIDECAR_VERSION)
733+
734+
cni-telemetry-sidecar-skopeo-archive: ## export tar archive of cni-telemetry-sidecar multiplat container manifest.
735+
$(MAKE) manifest-skopeo-archive \
736+
IMAGE=$(CNI_TELEMETRY_SIDECAR_IMAGE) \
737+
TAG=$(CNI_TELEMETRY_SIDECAR_VERSION)
684738

685739
########################### Archives ################################
686740

@@ -783,6 +837,14 @@ ifeq ($(GOOS),linux)
783837
cd $(IPV6_HP_BPF_BUILD_DIR) && $(ARCHIVE_CMD) $(IPV6_HP_BPF_ARCHIVE_NAME) ipv6-hp-bpf$(EXE_EXT)
784838
endif
785839

840+
# Create a CNI Telemetry Sidecar archive for the target platform.
841+
.PHONY: cni-telemetry-sidecar-archive
842+
cni-telemetry-sidecar-archive: cni-telemetry-sidecar-binary
843+
ifeq ($(GOOS),linux)
844+
$(MKDIR) $(CNI_TELEMETRY_SIDECAR_BUILD_DIR)
845+
cd $(CNI_TELEMETRY_SIDECAR_BUILD_DIR) && $(ARCHIVE_CMD) $(CNI_TELEMETRY_SIDECAR_ARCHIVE_NAME) azure-cni-telemetry-sidecar$(EXE_EXT)
846+
endif
847+
786848
##@ Utils
787849

788850
clean: ## Clean build artifacts.

cns/cni-telemetry-sidecar/Dockerfile

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Minimal Dockerfile using scratch (most reliable)
2+
FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS builder
3+
4+
# Install Go manually
5+
RUN apk add --no-cache go git ca-certificates make
6+
7+
ENV GOLANG_VERSION=1.21.0
8+
ENV GOPATH=/go
9+
ENV PATH=$GOPATH/bin:/usr/local/go/bin:$PATH
10+
11+
ARG VERSION=unknown
12+
ARG CNI_AI_PATH=""
13+
ARG CNI_AI_ID=""
14+
15+
WORKDIR /workspace
16+
17+
# Copy and build
18+
COPY go.mod go.sum ./
19+
RUN go mod download
20+
21+
COPY . .
22+
23+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
24+
-a -installsuffix cgo \
25+
-ldflags "-s -w -extldflags '-static' \
26+
-X main.version=${VERSION} \
27+
-X ${CNI_AI_PATH}=${CNI_AI_ID}" \
28+
-o azure-cni-telemetry-sidecar \
29+
./cns/cni-telemetry-sidecar/
30+
31+
# Use scratch for minimal image
32+
FROM scratch AS linux
33+
34+
# Copy CA certificates
35+
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
36+
37+
# Copy binary
38+
COPY --from=builder /workspace/azure-cni-telemetry-sidecar /azure-cni-telemetry-sidecar
39+
40+
# Labels
41+
LABEL org.opencontainers.image.title="Azure CNI Telemetry Sidecar"
42+
43+
ENTRYPOINT ["/azure-cni-telemetry-sidecar"]
44+
CMD ["--config", "/etc/cns/cns-config.json"]
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"time"
8+
9+
"github.com/Azure/azure-container-networking/cns/configuration"
10+
"github.com/Azure/azure-container-networking/cns/logger"
11+
)
12+
13+
// ConfigManager handles Azure CNS configuration loading and validation
14+
type ConfigManager struct {
15+
configPath string
16+
}
17+
18+
// NewConfigManager creates a new configuration manager for Azure CNS
19+
func NewConfigManager(configPath string) *ConfigManager {
20+
return &ConfigManager{
21+
configPath: configPath,
22+
}
23+
}
24+
25+
// LoadConfig loads and validates the Azure CNS configuration
26+
func (cm *ConfigManager) LoadConfig() (*configuration.CNSConfig, error) {
27+
logger.Printf("Loading Azure CNS configuration from: %s", cm.configPath)
28+
29+
// Wait for configuration file to become available (Kubernetes ConfigMap mount)
30+
if err := cm.waitForConfigFile(); err != nil {
31+
return nil, fmt.Errorf("Azure CNS configuration file not available: %w", err)
32+
}
33+
34+
// Read the configuration file from mounted volume
35+
data, err := os.ReadFile(cm.configPath)
36+
if err != nil {
37+
return nil, fmt.Errorf("failed to read Azure CNS configuration file: %w", err)
38+
}
39+
40+
// Parse JSON configuration into CNS config structure
41+
var config configuration.CNSConfig
42+
if err := json.Unmarshal(data, &config); err != nil {
43+
return nil, fmt.Errorf("failed to parse Azure CNS configuration: %w", err)
44+
}
45+
46+
// Validate configuration for Azure telemetry requirements
47+
if err := cm.validateConfig(&config); err != nil {
48+
return nil, fmt.Errorf("Azure CNS configuration validation failed: %w", err)
49+
}
50+
51+
logger.Printf("Azure CNS configuration loaded and validated successfully")
52+
return &config, nil
53+
}
54+
55+
// waitForConfigFile waits for the configuration file to become available
56+
// This is important in Kubernetes environments where ConfigMaps are mounted asynchronously
57+
func (cm *ConfigManager) waitForConfigFile() error {
58+
const maxRetries = 30
59+
const retryInterval = 2 * time.Second
60+
61+
for i := 0; i < maxRetries; i++ {
62+
if _, err := os.Stat(cm.configPath); err == nil {
63+
return nil
64+
}
65+
66+
if i == 0 {
67+
logger.Printf("Waiting for Azure CNS configuration file to become available...")
68+
}
69+
70+
time.Sleep(retryInterval)
71+
}
72+
73+
return fmt.Errorf("Azure CNS configuration file not available after %d attempts (%v total wait time)",
74+
maxRetries, time.Duration(maxRetries)*retryInterval)
75+
}
76+
77+
// validateConfig performs Azure-specific validation of the CNS configuration
78+
func (cm *ConfigManager) validateConfig(config *configuration.CNSConfig) error {
79+
// Validate that telemetry settings are properly configured
80+
if config.TelemetrySettings.AppInsightsInstrumentationKey == "" && !config.TelemetrySettings.DisableAll {
81+
logger.Printf("Warning: No Application Insights instrumentation key configured and telemetry not disabled")
82+
}
83+
84+
// Validate batch size settings for Azure Application Insights
85+
if config.TelemetrySettings.TelemetryBatchSizeBytes <= 0 {
86+
logger.Printf("Warning: Invalid telemetry batch size, using default")
87+
}
88+
89+
// Validate batch interval for optimal Azure ingestion
90+
if config.TelemetrySettings.TelemetryBatchIntervalInSecs <= 0 {
91+
logger.Printf("Warning: Invalid telemetry batch interval, using default")
92+
}
93+
94+
// Log configuration summary for Azure monitoring and debugging
95+
logger.Printf("Azure CNS Configuration Summary:")
96+
logger.Printf(" - Telemetry DisableAll: %t", config.TelemetrySettings.DisableAll)
97+
logger.Printf(" - Application Insights Key Present: %t",
98+
config.TelemetrySettings.AppInsightsInstrumentationKey != "")
99+
logger.Printf(" - Batch Size: %d bytes", config.TelemetrySettings.TelemetryBatchSizeBytes)
100+
logger.Printf(" - Batch Interval: %d seconds", config.TelemetrySettings.TelemetryBatchIntervalInSecs)
101+
logger.Printf(" - Disable Trace: %t", config.TelemetrySettings.DisableTrace)
102+
logger.Printf(" - Disable Metric: %t", config.TelemetrySettings.DisableMetric)
103+
logger.Printf(" - Disable Event: %t", config.TelemetrySettings.DisableEvent)
104+
logger.Printf(" - Debug Mode: %t", config.TelemetrySettings.DebugMode)
105+
106+
return nil
107+
}

cns/cni-telemetry-sidecar/main.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"os"
7+
"os/signal"
8+
"syscall"
9+
10+
"github.com/Azure/azure-container-networking/cns/logger"
11+
)
12+
13+
var (
14+
version = "unknown"
15+
configPath = flag.String("config", "/etc/cns/cns-config.json", "Path to CNS configuration file")
16+
)
17+
18+
func main() {
19+
flag.Parse()
20+
21+
// Initialize logging for the CNI telemetry sidecar
22+
logger.InitLogger("azure-cns-cni-telemetry-sidecar", 1, 1, "/var/log/azure-cns-telemetry")
23+
defer logger.Close()
24+
25+
logger.Printf("Starting Azure CNI Telemetry Sidecar v%s", version)
26+
27+
// Create telemetry sidecar service
28+
sidecar := NewTelemetrySidecar(*configPath)
29+
30+
// Setup graceful shutdown context
31+
ctx, cancel := context.WithCancel(context.Background())
32+
defer cancel()
33+
34+
// Handle OS signals for graceful shutdown
35+
sigCh := make(chan os.Signal, 1)
36+
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
37+
38+
go func() {
39+
sig := <-sigCh
40+
logger.Printf("Received shutdown signal %v, initiating graceful shutdown", sig)
41+
cancel()
42+
}()
43+
44+
// Run the telemetry sidecar
45+
if err := sidecar.Run(ctx); err != nil {
46+
logger.Errorf("Azure CNI Telemetry Sidecar failed: %v", err)
47+
os.Exit(1)
48+
}
49+
50+
logger.Printf("Azure CNI Telemetry Sidecar stopped gracefully")
51+
}

0 commit comments

Comments
 (0)