Workload Configuration
In order to instrument Azure Container App workloads the Upwind Tracer will need to be utilized.
Configuring a workload to be instrumented with the tracer requires embedding the tracer as part of the container image using a sidecar pattern or by building the tracer directly into your application image.
Prerequisites
Before configuring workloads, you need the following information from your cluster manager deployment:
| Value | Description | How to Get |
|---|---|---|
container_app_environment_id | The Container App Environment ID | Azure Portal → Container App Environment → Properties → Resource ID |
container_app_environment_name | The Container App Environment name | Azure Portal → Container App Environment → Overview → Name |
cluster_manager_api_host | Cluster manager hostname and port | Azure Portal → Container App → Ingress → FQDN, append :443 for external or :80 for internal |
acr_login_server | ACR login server | Azure Portal → Container Registry → Login server |
acr_pull_identity_id | Managed identity for ACR pull | Azure Portal → Managed Identities → {name}-acr-pull-identity → Resource ID |
You can also retrieve these values using the Azure CLI:
# Get Container App Environment ID
az containerapp env show --name {ENV_NAME} --resource-group {RESOURCE_GROUP} --query id -o tsv
# Get Cluster Manager FQDN
az containerapp show --name {CLUSTER_MANAGER_NAME} --resource-group {RESOURCE_GROUP} --query "properties.configuration.ingress.fqdn" -o tsv
# Get ACR Login Server
az acr show --name {ACR_NAME} --resource-group {RESOURCE_GROUP} --query loginServer -o tsv
# Get ACR Pull Identity ID
az identity show --name {NAME}-acr-pull-identity --resource-group {RESOURCE_GROUP} --query id -o tsv
ACR Pull-Through Cache
By default, the cluster manager module creates an Azure Container Registry with a pull-through cache for ECR public images. This helps avoid rate limiting when pulling the tracer image.
Use the cached tracer image:
{ACR_LOGIN_SERVER}/upwindsecurity/images/tracer:0.7.16
Option 1: Init Container Pattern (Recommended)
You can deploy applications with the Upwind Tracer using an init container pattern. This approach keeps your application image unchanged and ensures the tracer binary is ready before your application starts.
Configure your workload with the managed identity for ACR authentication:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
variable "subscription_id" {
description = "Azure subscription ID"
type = string
}
variable "resource_group_name" {
description = "Resource group name (same as cluster manager)"
type = string
}
variable "location" {
description = "Azure region"
type = string
}
variable "container_app_environment_id" {
description = "Container App Environment ID from cluster manager"
type = string
}
variable "container_app_environment_name" {
description = "Container App Environment name"
type = string
}
variable "cluster_manager_api_host" {
description = "Cluster Manager API host (e.g., my-cm.internal.env.azurecontainerapps.io:80)"
type = string
}
variable "acr_login_server" {
description = "ACR login server URL"
type = string
}
variable "acr_pull_identity_id" {
description = "Managed identity ID for ACR pull"
type = string
}
resource "azurerm_container_app" "my_app" {
name = "my-traced-app"
resource_group_name = var.resource_group_name
container_app_environment_id = var.container_app_environment_id
revision_mode = "Single"
# User-assigned identity for ACR pull
identity {
type = "UserAssigned"
identity_ids = [var.acr_pull_identity_id]
}
# ACR registry configuration
registry {
server = var.acr_login_server
identity = var.acr_pull_identity_id
}
template {
# Shared volume for the tracer binary
volume {
name = "tracer-shared"
storage_type = "EmptyDir"
}
# Init container - copies tracer binary to shared volume before app starts
init_container {
name = "tracer-init"
image = "${var.acr_login_server}/upwindsecurity/images/tracer:0.7.16"
cpu = 0.25
memory = "0.5Gi"
command = ["/var/lib/upwind/upwind-tracer", "--self-copy-path", "/shared/upwind-tracer"]
volume_mounts {
name = "tracer-shared"
path = "/shared"
}
}
# Your application container wrapped by tracer
container {
name = "application"
image = "docker.io/your-org/your-app:latest"
cpu = 0.5
memory = "1Gi"
command = ["/shared/upwind-tracer", "--", "/path/to/your/app"]
volume_mounts {
name = "tracer-shared"
path = "/shared"
}
env {
name = "UPWIND_TRACER_API_HOST"
value = var.cluster_manager_api_host
}
env {
name = "UPWIND_CLOUD_PROVIDER"
value = "azure"
}
env {
name = "UPWIND_CLOUD_ACCOUNT_ID"
value = var.subscription_id
}
env {
name = "UPWIND_TRACER_ZONE"
value = var.location
}
env {
name = "CONTAINER_APP_RESOURCE_GROUP"
value = var.resource_group_name
}
env {
name = "CONTAINER_APP_ENVIRONMENT_NAME"
value = var.container_app_environment_name
}
}
}
}
How It Works
The init container pattern works as follows:
- Init Container: Runs before the application container starts, using
--self-copy-pathto copy the tracer binary to the shared volume, then exits - Application Container: Starts after init container completes, with the tracer binary already available at
/shared/upwind-tracer - Shared Volume: An EmptyDir volume named
tracer-sharedis mounted at/sharedin both containers
The tracer automatically intercepts system calls and sends telemetry to the cluster manager.
Option 2: Build Tracer into Application Image
Alternatively, you can build the tracer directly into your application image.
Add the Upwind Tracer to your Dockerfile
This approach involves:
- Adding a build stage to copy the Upwind Tracer binary
- Setting up the entrypoint to run the tracer and then execute your application
Below is an example Dockerfile with these additions:
# syntax=docker/dockerfile:1
# (1) Add the Upwind Tracer image as a build stage.
# Use ACR cache URL: {ACR_LOGIN_SERVER}/upwindsecurity/images/tracer:0.7.16
FROM {ACR_LOGIN_SERVER}/upwindsecurity/images/tracer:0.7.16 AS upwind-tracer
# (2) Copy the Upwind Tracer binary from the build stage into your workload image.
FROM your-workload-image
COPY /var/lib/upwind /var/lib/upwind
# (3) Set the default entrypoint to the Upwind Tracer, and pass the path
# to your application so the tracer can invoke it.
ENTRYPOINT ["/var/lib/upwind/upwind-tracer", "--", "/path/to/your/app"]
Build and Push Image
After defining and creating your image, you need to push it to Azure Container Registry or another supported registry and deploy it as a Container App.
Configure Tracer to Cluster Manager Communication
The tracer pushes telemetry to the Upwind Cluster Manager via a gRPC connection. You need to set environment variables on your container app to configure this communication.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
resource "azurerm_container_app" "workload" {
name = "my-traced-app"
resource_group_name = var.resource_group_name
container_app_environment_id = var.container_app_environment_id
revision_mode = "Single"
template {
container {
name = "application"
image = "your-registry/your-app:latest"
cpu = 0.5
memory = "1Gi"
env {
name = "UPWIND_TRACER_API_HOST"
value = var.cluster_manager_api_host
}
env {
name = "UPWIND_CLOUD_PROVIDER"
value = "azure"
}
env {
name = "UPWIND_CLOUD_ACCOUNT_ID"
value = var.subscription_id
}
env {
name = "UPWIND_TRACER_ZONE"
value = var.location
}
env {
name = "CONTAINER_APP_RESOURCE_GROUP"
value = var.resource_group_name
}
env {
name = "CONTAINER_APP_ENVIRONMENT_NAME"
value = var.container_app_environment_name
}
}
}
}
Environment Variable Details
| Variable | Description | Example |
|---|---|---|
UPWIND_TRACER_API_HOST | The hostname and port of the cluster manager | my-cm.internal.env.azurecontainerapps.io:80 |
UPWIND_CLOUD_PROVIDER | Cloud provider | azure |
UPWIND_CLOUD_ACCOUNT_ID | Azure subscription ID | Your subscription ID |
UPWIND_TRACER_ZONE | Azure region | eastus |
CONTAINER_APP_RESOURCE_GROUP | Resource group name | Your resource group name |
CONTAINER_APP_ENVIRONMENT_NAME | Container App Environment name | Your environment name |
App Service Workloads
If you have Azure App Service workloads and want them to send traces to the cluster manager, follow these steps. This requires the cluster manager to be installed with VNet integration.
Prerequisites
- Cluster manager deployed with VNet integration (
infrastructure_subnet_id,internal_load_balancer_enabled, andcreate_private_dns_zoneset) - App Service with VNet integration to the same VNet
- App Service subnet delegated to
Microsoft.Web/serverFarms
Option A: Embedded Tracer
Build the tracer into your App Service container image (see Option 2: Build Tracer into Application Image) and configure environment variables:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
resource "azurerm_linux_web_app" "app" {
name = "my-traced-app"
resource_group_name = var.resource_group_name
location = var.location
service_plan_id = azurerm_service_plan.this.id
site_config {
application_stack {
docker_image_name = "your-traced-image:latest"
docker_registry_url = "https://your-registry.azurecr.io"
}
}
app_settings = {
UPWIND_TRACER_API_HOST = var.cluster_manager_api_host
UPWIND_CLOUD_PROVIDER = "azure"
UPWIND_CLOUD_ACCOUNT_ID = var.subscription_id
UPWIND_TRACER_ZONE = var.location
}
# VNet integration to reach the internal cluster manager
virtual_network_subnet_id = azurerm_subnet.app_service.id
}
Option B: Sidecar Pattern (Premium Plan)
If you have a Premium plan (P1v2 or higher), you can use the sidecar pattern instead of embedding the tracer. This uses the AzAPI provider to configure sidecar containers.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
azapi = {
source = "azure/azapi"
version = "~> 2.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
provider "azapi" {}
resource "azurerm_service_plan" "this" {
name = "my-app-service-plan"
resource_group_name = var.resource_group_name
location = var.location
os_type = "Linux"
sku_name = "P1v2" # Premium plan required for sidecars
}
resource "azurerm_linux_web_app" "app" {
name = "my-traced-app"
resource_group_name = var.resource_group_name
location = var.location
service_plan_id = azurerm_service_plan.this.id
site_config {}
app_settings = {
UPWIND_TRACER_API_HOST = var.cluster_manager_api_host
UPWIND_CLOUD_PROVIDER = "azure"
UPWIND_CLOUD_ACCOUNT_ID = var.subscription_id
UPWIND_TRACER_ZONE = var.location
WEBSITES_ENABLE_APP_SERVICE_STORAGE = "true"
}
virtual_network_subnet_id = azurerm_subnet.app_service.id
}
# Enable sidecar mode
resource "azapi_update_resource" "sidecar_mode" {
resource_id = azurerm_linux_web_app.app.id
type = "Microsoft.Web/sites@2024-04-01"
body = {
properties = {
siteConfig = {
linuxFxVersion = "SITECONTAINERS"
}
}
}
}
# Main application container - uses tracer from /home
resource "azapi_resource" "main_container" {
type = "Microsoft.Web/sites/sitecontainers@2024-04-01"
name = "main"
parent_id = azurerm_linux_web_app.app.id
body = {
properties = {
image = "nginx:latest"
isMain = true
targetPort = "80"
authType = "Anonymous"
startUpCommand = "/home/upwind-tracer -- /docker-entrypoint.sh nginx"
}
}
depends_on = [azapi_update_resource.sidecar_mode]
}
# Tracer sidecar - copies binary to /home
resource "azapi_resource" "tracer_sidecar" {
type = "Microsoft.Web/sites/sitecontainers@2024-04-01"
name = "upwind-tracer"
parent_id = azurerm_linux_web_app.app.id
body = {
properties = {
image = "{ACR_LOGIN_SERVER}/upwindsecurity/images/tracer:0.7.16"
isMain = false
authType = "Anonymous"
startUpCommand = "/var/lib/upwind/upwind-tracer --self-copy-path /home/upwind-tracer --self-copy-keep-running"
}
}
depends_on = [azapi_resource.main_container]
}
The sidecar pattern uses the shared /home directory (instead of EmptyDir) to share the tracer binary between containers.
Option C: Instrument an Existing Container App
To instrument an existing Container app, run the following command:
upwindctl azure instrument-appservice \
--subscription-id=<subscription-id> \
--resource-group=<resource-group> \
--app-service-plan=<app-service-plan> \
--client-id=<client-id> \
--client-secret=<client-secret>
Verifying the Installation
After deploying your traced application:
- Navigate to the Azure Portal
- Go to your Container App
- Check the logs to verify the tracer is running:
az containerapp logs show --name <app-name> --resource-group <resource-group> --follow - Look for log entries indicating the tracer has started and is sending data to the cluster manager
Troubleshooting
Issue: Tracer fails to connect to cluster manager
- Verify the
UPWIND_TRACER_API_HOSTis set correctly - Ensure both the application and cluster manager are in the same Container App Environment
- Check that the cluster manager is running and accessible
Issue: Application fails to start with tracer
- Verify the tracer binary is executable
- Check that the application entrypoint path is correct
- Review container logs for specific error messages
Issue: No telemetry data visible in Upwind console
- Ensure the cluster manager has valid Upwind credentials
- Verify network connectivity between the application and cluster manager
- Check that the tracer environment variables are set correctly