Skip to content

Commit 51254f5

Browse files
committed
OpenTelemetry Auto-Instrumentation
1 parent 3777436 commit 51254f5

File tree

6 files changed

+195
-67
lines changed

6 files changed

+195
-67
lines changed

Diff for: charts/naiserator/values.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ naiserator:
5959
observability:
6060
otel:
6161
enabled: false
62+
auto-instrumentation:
63+
enabled: false
64+
app-config: "nais-system/apps"
6265
collector:
6366
labels:
6467
- "app.kubernetes.io/name=opentelemetry-collector"

Diff for: pkg/naiserator/config/config.go

+73-63
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,14 @@ type Observability struct {
7171
}
7272

7373
type Otel struct {
74-
Enabled bool `json:"enabled"`
75-
Collector OtelCollector `json:"collector"`
74+
Enabled bool `json:"enabled"`
75+
Collector OtelCollector `json:"collector"`
76+
AutoInstrumentation AutoInstrumentation `json:"auto-instrumentation"`
77+
}
78+
79+
type AutoInstrumentation struct {
80+
Enabled bool `json:"enabled"`
81+
AppConfig string `json:"app-config"`
7682
}
7783

7884
type OtelCollector struct {
@@ -174,67 +180,69 @@ type Config struct {
174180
}
175181

176182
const (
177-
AivenProject = "aiven-project"
178-
AivenRange = "aiven-range"
179-
ApiServerIp = "api-server-ip"
180-
Bind = "bind"
181-
HealthProbeBindAddress = "health-probe-bind-address"
182-
ClusterName = "cluster-name"
183-
DryRun = "dry-run"
184-
NaisNamespace = "nais-namespace"
185-
FeaturesAccessPolicyNotAllowedCIDRs = "features.access-policy-not-allowed-cidrs"
186-
FeaturesAzurerator = "features.azurerator"
187-
FeaturesGCP = "features.gcp"
188-
FeaturesIDPorten = "features.idporten"
189-
FeaturesJwker = "features.jwker"
190-
FeaturesCNRM = "features.cnrm"
191-
FeaturesKafkarator = "features.kafkarator"
192-
FeaturesLinkerd = "features.linkerd"
193-
FeaturesMaskinporten = "features.maskinporten"
194-
FeaturesNetworkPolicy = "features.network-policy"
195-
FeaturesPrometheusOperator = "features.prometheus-operator"
196-
FeaturesVault = "features.vault"
197-
FeaturesWebhook = "features.webhook"
198-
FeaturesWonderwall = "features.wonderwall"
199-
FeaturesLegacyGCP = "features.legacy-gcp"
200-
FQDNPolicyEnabled = "fqdn-policy.enabled"
201-
GoogleCloudSQLProxyContainerImage = "google-cloud-sql-proxy-container-image"
202-
GoogleProjectId = "google-project-id"
203-
InformerFullSynchronizationInterval = "informer.full-sync-interval"
204-
KafkaBrokers = "kafka.brokers"
205-
KafkaEnabled = "kafka.enabled"
206-
KafkaLogVerbosity = "kafka.log-verbosity"
207-
KafkaTLSCAPath = "kafka.tls.ca-path"
208-
KafkaTLSCertificatePath = "kafka.tls.certificate-path"
209-
KafkaTLSEnabled = "kafka.tls.enabled"
210-
KafkaTLSInsecure = "kafka.tls.insecure"
211-
KafkaTLSPrivateKeyPath = "kafka.tls.private-key-path"
212-
KafkaTopic = "kafka.topic"
213-
KubeConfig = "kubeconfig"
214-
LeaderElectionImage = "leader-election.image"
215-
MaxConcurrentReconciles = "max-concurrent-reconciles"
216-
ObservabilityLoggingDestinations = "observability.logging.destinations"
217-
ObservabilityOtelCollectorLabels = "observability.otel.collector.labels"
218-
ObservabilityOtelCollectorNamespace = "observability.otel.collector.namespace"
219-
ObservabilityOtelCollectorPort = "observability.otel.collector.port"
220-
ObservabilityOtelCollectorProtocol = "observability.otel.collector.protocol"
221-
ObservabilityOtelCollectorService = "observability.otel.collector.service"
222-
ObservabilityOtelCollectorTLS = "observability.otel.collector.tls"
223-
ObservabilityOtelEnabled = "observability.otel.enabled"
224-
ProxyAddress = "proxy.address"
225-
ProxyExclude = "proxy.exclude"
226-
RateLimitBurst = "ratelimit.burst"
227-
RateLimitQPS = "ratelimit.qps"
228-
SecurelogsConfigMapReloadImage = "securelogs.configmap-reload-image"
229-
SecurelogsFluentdImage = "securelogs.fluentd-image"
230-
SynchronizerRolloutCheckInterval = "synchronizer.rollout-check-interval"
231-
SynchronizerRolloutTimeout = "synchronizer.rollout-timeout"
232-
SynchronizerSynchronizationTimeout = "synchronizer.synchronization-timeout"
233-
VaultAddress = "vault.address"
234-
VaultAuthPath = "vault.auth-path"
235-
VaultInitContainerImage = "vault.init-container-image"
236-
VaultKvPath = "vault.kv-path"
237-
WonderwallImage = "wonderwall.image"
183+
AivenProject = "aiven-project"
184+
AivenRange = "aiven-range"
185+
ApiServerIp = "api-server-ip"
186+
Bind = "bind"
187+
HealthProbeBindAddress = "health-probe-bind-address"
188+
ClusterName = "cluster-name"
189+
DryRun = "dry-run"
190+
NaisNamespace = "nais-namespace"
191+
FeaturesAccessPolicyNotAllowedCIDRs = "features.access-policy-not-allowed-cidrs"
192+
FeaturesAzurerator = "features.azurerator"
193+
FeaturesGCP = "features.gcp"
194+
FeaturesIDPorten = "features.idporten"
195+
FeaturesJwker = "features.jwker"
196+
FeaturesCNRM = "features.cnrm"
197+
FeaturesKafkarator = "features.kafkarator"
198+
FeaturesLinkerd = "features.linkerd"
199+
FeaturesMaskinporten = "features.maskinporten"
200+
FeaturesNetworkPolicy = "features.network-policy"
201+
FeaturesPrometheusOperator = "features.prometheus-operator"
202+
FeaturesVault = "features.vault"
203+
FeaturesWebhook = "features.webhook"
204+
FeaturesWonderwall = "features.wonderwall"
205+
FeaturesLegacyGCP = "features.legacy-gcp"
206+
FQDNPolicyEnabled = "fqdn-policy.enabled"
207+
GoogleCloudSQLProxyContainerImage = "google-cloud-sql-proxy-container-image"
208+
GoogleProjectId = "google-project-id"
209+
InformerFullSynchronizationInterval = "informer.full-sync-interval"
210+
KafkaBrokers = "kafka.brokers"
211+
KafkaEnabled = "kafka.enabled"
212+
KafkaLogVerbosity = "kafka.log-verbosity"
213+
KafkaTLSCAPath = "kafka.tls.ca-path"
214+
KafkaTLSCertificatePath = "kafka.tls.certificate-path"
215+
KafkaTLSEnabled = "kafka.tls.enabled"
216+
KafkaTLSInsecure = "kafka.tls.insecure"
217+
KafkaTLSPrivateKeyPath = "kafka.tls.private-key-path"
218+
KafkaTopic = "kafka.topic"
219+
KubeConfig = "kubeconfig"
220+
LeaderElectionImage = "leader-election.image"
221+
MaxConcurrentReconciles = "max-concurrent-reconciles"
222+
ObservabilityLoggingDestinations = "observability.logging.destinations"
223+
ObservabilityOtelCollectorLabels = "observability.otel.collector.labels"
224+
ObservabilityOtelCollectorNamespace = "observability.otel.collector.namespace"
225+
ObservabilityOtelCollectorPort = "observability.otel.collector.port"
226+
ObservabilityOtelCollectorProtocol = "observability.otel.collector.protocol"
227+
ObservabilityOtelCollectorService = "observability.otel.collector.service"
228+
ObservabilityOtelCollectorTLS = "observability.otel.collector.tls"
229+
ObservabilityOtelEnabled = "observability.otel.enabled"
230+
ObservabilityOtelAutoInstrumentationAppConfig = "observability.otel.auto-instrumentation.app-config"
231+
ObservabilityOtelAutoInstrumentationEnabled = "observability.otel.auto-instrumentation.enabled"
232+
ProxyAddress = "proxy.address"
233+
ProxyExclude = "proxy.exclude"
234+
RateLimitBurst = "ratelimit.burst"
235+
RateLimitQPS = "ratelimit.qps"
236+
SecurelogsConfigMapReloadImage = "securelogs.configmap-reload-image"
237+
SecurelogsFluentdImage = "securelogs.fluentd-image"
238+
SynchronizerRolloutCheckInterval = "synchronizer.rollout-check-interval"
239+
SynchronizerRolloutTimeout = "synchronizer.rollout-timeout"
240+
SynchronizerSynchronizationTimeout = "synchronizer.synchronization-timeout"
241+
VaultAddress = "vault.address"
242+
VaultAuthPath = "vault.auth-path"
243+
VaultInitContainerImage = "vault.init-container-image"
244+
VaultKvPath = "vault.kv-path"
245+
WonderwallImage = "wonderwall.image"
238246
)
239247

240248
func bindNAIS() {
@@ -303,6 +311,8 @@ func init() {
303311
flag.String(ObservabilityOtelCollectorNamespace, "nais-system", "namespace of the OpenTelemetry collector")
304312
flag.String(ObservabilityOtelCollectorService, "opentelmetry-collector", "service name of the OpenTelemetry collector")
305313
flag.String(ObservabilityOtelCollectorProtocol, "grpc", "protocol used by the OpenTelemetry collector")
314+
flag.Bool(ObservabilityOtelAutoInstrumentationEnabled, false, "enable OpenTelemetry auto-instrumentation")
315+
flag.String(ObservabilityOtelAutoInstrumentationAppConfig, "nais-system/apps", "path to OpenTelemetry auto-instrumentation config")
306316
flag.Int(ObservabilityOtelCollectorPort, 4317, "port used by the OpenTelemetry collector")
307317
flag.Bool(ObservabilityOtelCollectorTLS, false, "use TLS for the OpenTelemetry collector")
308318
flag.StringArray(ObservabilityOtelCollectorLabels, []string{}, "list of labels to be used by the OpenTelemetry collector")

Diff for: pkg/resourcecreator/observability/observability.go

+25-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ const otelExporterInsecure = "OTEL_EXPORTER_OTLP_INSECURE"
3737
const logLabelDefault = "logs.nais.io/flow-default"
3838
const logLabelPrefix = "logs.nais.io/flow-"
3939

40+
const autoInstrumentationInjectAnnotationPrefix = "instrumentation.opentelemetry.io/inject-"
41+
const autoInstrumentationContainerNamesAnnotation = "instrumentation.opentelemetry.io/container-names"
42+
4043
func otelEndpointFromConfig(collector config.OtelCollector) string {
4144
schema := "http"
4245
if collector.Tls {
@@ -73,6 +76,16 @@ func otelEnvVars(source Source, otel config.Otel) []corev1.EnvVar {
7376
}
7477
}
7578

79+
func otelAutoInstrumentAnnotations(source Source, otel config.Otel) map[string]string {
80+
runtime := source.GetObservability().AutoInstrumentation.Runtime
81+
autoInstrumentationInjectAnnotation := autoInstrumentationInjectAnnotationPrefix + runtime
82+
83+
return map[string]string{
84+
autoInstrumentationInjectAnnotation: otel.AutoInstrumentation.AppConfig,
85+
autoInstrumentationContainerNamesAnnotation: source.GetName(),
86+
}
87+
}
88+
7689
func labelsFromCollectorConfig(collector config.OtelCollector) map[string]string {
7790
labels := collector.Labels
7891
labelMap := make(map[string]string, len(labels))
@@ -147,16 +160,26 @@ func Create(source Source, ast *resource.Ast, config Config) error {
147160
return nil
148161
}
149162

150-
if obs.Tracing != nil && obs.Tracing.Enabled {
163+
if obs.AutoInstrumentation != nil && obs.AutoInstrumentation.Enabled && obs.Tracing != nil && obs.Tracing.Enabled {
164+
return fmt.Errorf("auto-instrumentation and tracing cannot be enabled at the same time")
165+
}
166+
167+
if (obs.Tracing != nil && obs.Tracing.Enabled) || (obs.AutoInstrumentation != nil && obs.AutoInstrumentation.Enabled) {
151168
if !cfg.Otel.Enabled {
152-
return fmt.Errorf("tracing is not supported in this cluster configuration")
169+
return fmt.Errorf("tracing and auto-instrumentation are not supported in this cluster")
153170
}
154171

155172
netpol, err := tracingNetpol(source, cfg.Otel)
156173
if err != nil {
157174
return err
158175
}
159176

177+
if obs.AutoInstrumentation != nil && obs.AutoInstrumentation.Enabled {
178+
for k, v := range otelAutoInstrumentAnnotations(source, cfg.Otel) {
179+
ast.Annotations[k] = v
180+
}
181+
}
182+
160183
ast.Env = append(ast.Env, otelEnvVars(source, cfg.Otel)...)
161184
ast.AppendOperation(resource.OperationCreateOrUpdate, netpol)
162185
}

Diff for: pkg/resourcecreator/pod/pod.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ func CreateAppObjectMeta(app Source, ast *resource.Ast, cfg Config) metav1.Objec
373373
port = strconv.Itoa(app.GetPort())
374374
}
375375

376-
objectMeta.Annotations = map[string]string{}
376+
//objectMeta.Annotations = map[string]string{}
377377

378378
objectMeta.Annotations["kubectl.kubernetes.io/default-container"] = app.GetName()
379379

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
testconfig:
2+
description: enable opentelemetry auto-instrumentation
3+
4+
config:
5+
observability:
6+
otel:
7+
enabled: true
8+
auto-instrumentation:
9+
enabled: true
10+
app-config: "system-namespace/app-config"
11+
collector:
12+
labels:
13+
- key1=value1
14+
- key2=value2
15+
- key3=value3
16+
namespace: "system-namespace"
17+
port: 4317
18+
service: "my-collector"
19+
tls: false
20+
protocol: "grpc"
21+
22+
input:
23+
kind: Application
24+
apiVersion: nais.io/v1alpha1
25+
metadata:
26+
name: myapplication
27+
namespace: mynamespace
28+
uid: "123456"
29+
labels:
30+
team: myteam
31+
spec:
32+
image: navikt/myapplication:1.2.3
33+
observability:
34+
autoInstrumentation:
35+
enabled: true
36+
runtime: "java"
37+
38+
tests:
39+
- apiVersion: apps/v1
40+
kind: Deployment
41+
name: myapplication
42+
operation: CreateOrUpdate
43+
match:
44+
- type: subset
45+
name: "otel annotation config set in pod"
46+
resource:
47+
spec:
48+
template:
49+
metadata:
50+
annotations:
51+
"instrumentation.opentelemetry.io/inject-java": "system-namespace/app-config"
52+
"instrumentation.opentelemetry.io/container-names": "myapplication"
53+
- type: subset
54+
name: "otel environment variables set in pod"
55+
resource:
56+
spec:
57+
template:
58+
spec:
59+
containers:
60+
- image: navikt/myapplication:1.2.3
61+
name: myapplication
62+
env:
63+
- name: OTEL_SERVICE_NAME
64+
value: myapplication
65+
- name: OTEL_RESOURCE_ATTRIBUTES
66+
value: service.name=myapplication,service.namespace=mynamespace
67+
- name: OTEL_EXPORTER_OTLP_ENDPOINT
68+
value: http://my-collector.system-namespace:4317
69+
- name: OTEL_EXPORTER_OTLP_PROTOCOL
70+
value: grpc
71+
- name: OTEL_EXPORTER_OTLP_INSECURE
72+
value: "true"
73+
74+
- apiVersion: networking.k8s.io/v1
75+
kind: NetworkPolicy
76+
name: myapplication-tracing-438f0c6d
77+
operation: CreateOrUpdate
78+
match:
79+
- type: subset
80+
name: "network policy allows traffic to data collector"
81+
resource:
82+
spec:
83+
egress:
84+
- to:
85+
- namespaceSelector:
86+
matchLabels:
87+
kubernetes.io/metadata.name: system-namespace
88+
podSelector:
89+
matchLabels:
90+
key1: value1
91+
key2: value2
92+
key3: value3

Diff for: pkg/resourcecreator/testdata/observability_tracing_enabled_error.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ input:
2121
tracing:
2222
enabled: true
2323

24-
error: "tracing is not supported in this cluster configuration"
24+
error: "tracing and auto-instrumentation are not supported in this cluster"

0 commit comments

Comments
 (0)