Open denniszielke opened 2 years ago
Thanks for this. It is a consideration moving forward definitely
@denniszielke, have you considered OTEL as a side car option?
@JennyLawrance yes that is what we are doing today but on the long run this does not scale (we need to maintain the same config , we need to make sure every dev creates the right config for each microservice and this is not what a PaaS should enforce). Also the performance overhead is significant as long ACA is limited to 4GB per replica (app+sidecar)
Adding onto this (& tagging @agoncal as well) - If I wanted to run an OTel collector instance as its own containerapp its currently very difficult to inject the configuration, since ContainerApps does not expose a ConfigMap
that can be mounted as a volume.
For example, if I wanted to run the Otel collector on kubernetes, I could just deploy this:
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-collector-config
labels:
app: otel-collector
role: monitoring
data:
otel-collector-config.yml: |2
receivers:
otlp:
protocols:
grpc:
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
processors:
batch:
extensions:
health_check:
service:
extensions:
- health_check
pipelines:
traces:
receivers:
- otlp
processors:
- batch
exporters:
- jaeger
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: otel-collector
labels:
name: otel-collector
app: otel-collector
role: monitoring
app.kubernetes.io/part-of: monitoring
spec:
replicas: 1
selector:
matchLabels:
name: otel-collector
template:
metadata:
labels:
name: otel-collector
app: otel-collector
role: monitoring
spec:
containers:
- image: otel/opentelemetry-collector:0.53.0
name: otel-collector
args:
- "--config=/conf/otel-collector-config.yml"
ports:
- name: health-check
containerPort: 13133
- name: otlp-receiver
containerPort: 4317
volumeMounts:
- name: otel-collector-config
mountPath: /conf
volumes:
- name: otel-collector-config
configMap:
name: otel-collector-config
I haven't yet found a good way to do this in Azure ContainerApps. I found https://docs.microsoft.com/en-us/azure/container-apps/storage-mounts?pivots=aca-cli#azure-files but that means I have to maintain a separate yaml file specific to azure containerapps, which contains most of the same fields as a kubernetes yaml. That doesn't seem like a scalable solution, especially if all my CI/CD automation uses the az
cli.
This approach works https://www.honeycomb.io/blog/opentelemetry-collector-azure-container-apps
hey @alhardy I followed that guide, it works, but the effort to do it is huge and needs manual intervention ( downloading config and editing ). So we would hope the team can better the experience.
I even needed to do 'more' and detailed it here https://blog.depechie.com/posts/2022-10-13-opentelemetry-on-azure-container-apps/
Thank you @alhardy ! That still seems way more complicated than it should!
Agree.
I found out that if you do not use the az
cli but instead opt to deploy to azure using Bicep
the volume mount does work! Meaning I can now deploy OpenTelemetry Collector
fully automated from CI/CD without manually needing to download the config file after creation...
Example part of the Bicep
for the container app ( almost the same as the kubernetes one from @edeandrea
resource containerAppOtel 'Microsoft.App/containerApps@2022-03-01' = {
name: 'ca-${env}-${resourceLocationShort}-otel'
location: resourceLocationLong
properties: {
managedEnvironmentId: environment.id
configuration: {
ingress: {
external: true
targetPort: 4318
}
}
template: {
containers: [
{
image: 'otel/opentelemetry-collector-contrib:latest'
name: 'ca-${env}-${resourceLocationShort}-otel'
args: [
'--config=/etc/otel-config-test.yml'
]
resources: {
cpu: json('0.5')
memory: '1.0Gi'
}
volumeMounts: [
{
mountPath: '/etc'
volumeName: 'config'
}
]
}
]
scale: {
minReplicas: 1
maxReplicas: 1
}
volumes: [
{
name: 'config'
storageName: environment::azurefilestorage.name
storageType: 'AzureFile'
}
]
}
}
}
I'm looking for some assistance. I started with the Honeycomb article to build a collector in an Azure Container App and it wasn't starting correctly until I found this post, pointing to the second article and added the args section to point to my mounted config file. The collector is running and listening for otlp http traffic on port 4318 with the ingress set to allow external traffic. However, I cannot get it to receive any data from my .Net Core 7 api. I can get it all working on my laptop with the otel collector contrib running in docker desktop set for http on port 4318.
Here is the config file for the container:
receivers:
otlp:
protocols:
http:
exporters:
otlphttp/withauth:
auth:
authenticator: oauth2client
logs_endpoint: $CNAO_LOGS_ENDPOINT
metrics_endpoint: $CNAO_METRICS_ENDPOINT
traces_endpoint: $CNAO_TRACES_ENDPOINT
logging:
loglevel: debug
processors:
batch:
send_batch_size: 8192
timeout: 10s
attributes/collector_info:
actions:
- key: collector.hostname
value: $HOSTNAME
action: insert
- key: azure.container_app.revision
value: $CONTAINER_APP_REVISION
action: insert
- key: azure.container_app.name
value: $CONTAINER_APP_NAME
action: insert
- key: source.blog
value: "true"
action: insert
filter/healthcheck:
spans:
exclude:
match_type: strict
attributes:
- Key: http.target
Value: /health
extensions:
oauth2client:
client_id: $CNAO_CLIENT_ID
client_secret: $CNAO_CLIENT_SECRET
token_url: $CNAO_TOKEN_URL
service:
extensions: [oauth2client]
pipelines:
traces:
receivers: [otlp]
processors: [batch,filter/healthcheck,attributes/collector_info]
exporters: [logging, otlphttp/withauth]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/withauth]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/withauth]
telemetry:
logs:
level: "debug"
The log stream on the container shows that it has started successfully giving this output:
2023-10-16T10:41:15.15488 Connecting to the container 'collector'...
2023-10-16T10:41:15.17288 Successfully Connected to container: 'collector' [Revision: 'collector--1', Replica: 'collector--1']
2023-10-13T15:37:36.605516952Z 2023-10-13T15:37:36.605Z info service/pipelines.go:92 Processor is starting... {"kind": "processor", "name": "batch", "pipeline": "logs"}
2023-10-13T15:37:36.605528072Z 2023-10-13T15:37:36.605Z info service/pipelines.go:96 Processor started. {"kind": "processor", "name": "batch", "pipeline": "logs"}
2023-10-13T15:37:36.605566835Z 2023-10-13T15:37:36.605Z info service/pipelines.go:92 Processor is starting... {"kind": "processor", "name": "attributes/collector_info", "pipeline": "traces"}
2023-10-13T15:37:36.605573347Z 2023-10-13T15:37:36.605Z info service/pipelines.go:96 Processor started. {"kind": "processor", "name": "attributes/collector_info", "pipeline": "traces"}
2023-10-13T15:37:36.605622865Z 2023-10-13T15:37:36.605Z info service/pipelines.go:92 Processor is starting... {"kind": "processor", "name": "filter/healthcheck", "pipeline": "traces"}
2023-10-13T15:37:36.605652090Z 2023-10-13T15:37:36.605Z info service/pipelines.go:96 Processor started. {"kind": "processor", "name": "filter/healthcheck", "pipeline": "traces"}
2023-10-13T15:37:36.605657580Z 2023-10-13T15:37:36.605Z info service/pipelines.go:92 Processor is starting... {"kind": "processor", "name": "batch", "pipeline": "traces"}
2023-10-13T15:37:36.605698870Z 2023-10-13T15:37:36.605Z info service/pipelines.go:96 Processor started. {"kind": "processor", "name": "batch", "pipeline": "traces"}
2023-10-13T15:37:36.605707717Z 2023-10-13T15:37:36.605Z info service/pipelines.go:92 Processor is starting... {"kind": "processor", "name": "batch", "pipeline": "metrics"}
2023-10-13T15:37:36.605711765Z 2023-10-13T15:37:36.605Z info service/pipelines.go:96 Processor started. {"kind": "processor", "name": "batch", "pipeline": "metrics"}
2023-10-13T15:37:36.605715602Z 2023-10-13T15:37:36.605Z info service/pipelines.go:100 Starting receivers...
2023-10-13T15:37:36.605736781Z 2023-10-13T15:37:36.605Z info service/pipelines.go:104 Receiver is starting... {"kind": "receiver", "name": "otlp", "pipeline": "metrics"}
2023-10-13T15:37:36.605758522Z 2023-10-13T15:37:36.605Z warn internal/warning.go:51 Using the 0.0.0.0 address exposes this server to every network interface, which may facilitate Denial of Service attacks {"kind": "receiver", "name": "otlp", "pipeline": "metrics", "documentation": "https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/security-best-practices.md#safeguards-against-denial-of-service-attacks"}
2023-10-13T15:37:36.605821510Z 2023-10-13T15:37:36.605Z info otlpreceiver@v0.67.0/otlp.go:90 Starting HTTP server {"kind": "receiver", "name": "otlp", "pipeline": "metrics", "endpoint": "0.0.0.0:4318"}
2023-10-13T15:37:36.605855915Z 2023-10-13T15:37:36.605Z info service/pipelines.go:108 Receiver started. {"kind": "receiver", "name": "otlp", "pipeline": "metrics"}
2023-10-13T15:37:36.605861705Z 2023-10-13T15:37:36.605Z info service/pipelines.go:104 Receiver is starting... {"kind": "receiver", "name": "otlp", "pipeline": "logs"}
2023-10-13T15:37:36.605888225Z 2023-10-13T15:37:36.605Z info service/pipelines.go:108 Receiver started. {"kind": "receiver", "name": "otlp", "pipeline": "logs"}
2023-10-13T15:37:36.605897011Z 2023-10-13T15:37:36.605Z info service/pipelines.go:104 Receiver is starting... {"kind": "receiver", "name": "otlp", "pipeline": "traces"}
2023-10-13T15:37:36.605920645Z 2023-10-13T15:37:36.605Z info service/pipelines.go:108 Receiver started. {"kind": "receiver", "name": "otlp", "pipeline": "traces"}
2023-10-13T15:37:36.605927208Z 2023-10-13T15:37:36.605Z info service/service.go:105 Everything is ready. Begin running and processing data.
The code I am using for the otlp exporter in my .Net Core 7 project to hit the container app is as follows:
string endPoint = "https://collector.onazure.azurecontainerapps.io:4318/v1/traces";
Console.WriteLine($"AppD Endpoint: {endPoint}");
builder.AddOtlpExporter(options =>
{
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
options.Protocol = OtlpExportProtocol.HttpProtobuf;
options.ExportProcessorType = ExportProcessorType.Batch;
options.Endpoint = new Uri(endPoint);
})
.AddConsoleExporter(options => options.Targets = ConsoleExporterOutputTargets.Console);
I have tried running the project locally on my laptop and also have the api in it's own container with an APIM in place to test the endpoints. I can see in the console logs that the Writeline is output when starting up the container and that telemetry is being sent to the Console through the Console Exporter. However, it is not being received by the collector.
EDIT: I sorted the issue. At present Azure Container Apps only open one port, so you cannot map multiple ports like you can with Docker. Port 80 and 443 are open by default and I thought 4318 had successfully been opened as it says it's available in the Container overview. I used Telnet to confirm which ports were open and found 4318 was not available, so I tried sending the data to port 443 and it worked. My new endpoint is:
https://collector.onazure.azurecontainerapps.io:433/v1/traces
Is your feature request related to a problem? Please describe.
I want to use my own observability solution and not azure monitor. To connect I need the ability to configure my own otel processors and exporters.
Describe the solution you'd like.
I would like the ability to configure receivers, processors, (extensions?), the sampling rate and exporters.
Describe alternatives you've considered.
The alternative would be to run my own otel collector as ACA service and manually change all other ACA apps to use them. This does not seem like a scalable good idea.
Additional context.