diff --git a/.chloggen/service-instanc-id-mapping.yaml b/.chloggen/service-instanc-id-mapping.yaml
new file mode 100755
index 0000000000..46e33b03cb
--- /dev/null
+++ b/.chloggen/service-instanc-id-mapping.yaml
@@ -0,0 +1,27 @@
+# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
+change_type: bug_fix
+
+# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
+component: auto-instrumentation
+
+# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: Remove the mapping of `app.kubernetes.io/instance` to `service.instance.id`
+
+# One or more tracking issues related to the change
+issues: [3495]
+
+# (Optional) One or more lines of additional information to render under the primary note.
+# These lines will be padded with 2 spaces and then inserted directly into the document.
+# Use pipe (|) for multiline entries.
+subtext: |
+ Technically, this is a breaking change, but we regard it as a bug fix because the previous behavior was incorrect.
+
+ if you did have multiple container instrumentation and use `app.kubernetes.io/instance` to set the `service.instance.id`,
+ you will now see multiple instances in the UI - which is the correct behavior.
+
+ You can still use the attribute `resource.opentelemetry.io/service.instance.id` to set the `service.instance.id`,
+ which will be shared across all containers in the pod - but this is not recommended for multiple container instrumentation instances.
+
+ Refer to the [semantic conventions](https://opentelemetry.io/docs/specs/semconv/resource/#service-experimental)
+ for more information.
+
diff --git a/README.md b/README.md
index 6244ab90cf..e027144d4b 100644
--- a/README.md
+++ b/README.md
@@ -725,7 +725,8 @@ EOF
### Configure resource attributes with annotations
-This example shows a pod configuration with OpenTelemetry annotations using the `resource.opentelemetry.io/` prefix. These annotations can be used to add resource attributes to data produced by OpenTelemetry instrumentation.
+This example shows a pod configuration with OpenTelemetry annotations using the `resource.opentelemetry.io/` prefix.
+These annotations can be used to add resource attributes to data produced by OpenTelemetry instrumentation.
```yaml
apiVersion: v1
@@ -733,6 +734,7 @@ kind: Pod
metadata:
name: example-pod
annotations:
+ # this is just an example, you can create any resource attributes you need
resource.opentelemetry.io/service.name: "my-service"
resource.opentelemetry.io/service.version: "1.0.0"
resource.opentelemetry.io/environment: "production"
@@ -750,7 +752,6 @@ The following labels are supported:
- `app.kubernetes.io/name` becomes `service.name`
- `app.kubernetes.io/version` becomes `service.version`
- `app.kubernetes.io/part-of` becomes `service.namespace`
-- `app.kubernetes.io/instance` becomes `service.instance.id`
```yaml
apiVersion: v1
@@ -761,7 +762,6 @@ metadata:
app.kubernetes.io/name: "my-service"
app.kubernetes.io/version: "1.0.0"
app.kubernetes.io/part-of: "shop"
- app.kubernetes.io/instance: "my-service-123"
spec:
containers:
- name: main-container
@@ -794,6 +794,38 @@ The priority for setting resource attributes is as follows (first found wins):
This priority is applied for each resource attribute separately, so it is possible to set some attributes via
annotations and others via labels.
+### How resource attributes are calculated from the pod's metadata
+
+The following resource attributes are calculated from the pod's metadata.
+
+#### How `service.name` is calculated
+
+Choose the first value found:
+
+- `pod.annotation[resource.opentelemetry.io/service.name]`
+- `if (config[useLabelsForResourceAttributes]) pod.label[app.kubernetes.io/name]`
+- `k8s.depleyment.name`
+- `k8s.replicaset.name`
+- `k8s.statefulset.name`
+- `k8s.daemonset.name`
+- `k8s.cronjob.name`
+- `k8s.job.name`
+- `k8s.pod.name`
+- `k8s.container.name`
+
+#### How `service.version` is calculated
+
+- `pod.annotation[resource.opentelemetry.io/service.version]`
+- `if (cfg[useLabelsForResourceAttributes]) pod.label[app.kubernetes.io/version]`
+- `if (contains(container docker image tag, '/') == false) container docker image tag`
+
+#### How `service.instance.id` is calculated
+
+
+- `pod.annotation[resource.opentelemetry.io/service.instance.id]`
+- `if (config[useLabelsForResourceAttributes]) pod.label[app.kubernetes.io/instance]`
+- `concat([k8s.namespace.name, k8s.pod.name, k8s.container.name], '.')`
+
## Contributing and Developing
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
diff --git a/apis/v1alpha1/instrumentation_types.go b/apis/v1alpha1/instrumentation_types.go
index e290f4033b..4f2e6f986e 100644
--- a/apis/v1alpha1/instrumentation_types.go
+++ b/apis/v1alpha1/instrumentation_types.go
@@ -152,7 +152,6 @@ type Defaults struct {
// - `app.kubernetes.io/name` becomes `service.name`
// - `app.kubernetes.io/version` becomes `service.version`
// - `app.kubernetes.io/part-of` becomes `service.namespace`
- // - `app.kubernetes.io/instance` becomes `service.instance.id`
UseLabelsForResourceAttributes bool `json:"useLabelsForResourceAttributes,omitempty"`
}
diff --git a/docs/api.md b/docs/api.md
index 5a15a4c945..e0c219d54d 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1389,8 +1389,7 @@ Defaults defines default values for the instrumentation.
UseLabelsForResourceAttributes defines whether to use common labels for resource attributes:
- `app.kubernetes.io/name` becomes `service.name`
- `app.kubernetes.io/version` becomes `service.version`
- - `app.kubernetes.io/part-of` becomes `service.namespace`
- - `app.kubernetes.io/instance` becomes `service.instance.id`
+ - `app.kubernetes.io/part-of` becomes `service.namespace`
false |
diff --git a/pkg/constants/env.go b/pkg/constants/env.go
index 27963fb900..eebf26e2f4 100644
--- a/pkg/constants/env.go
+++ b/pkg/constants/env.go
@@ -35,10 +35,9 @@ const (
AnnotationDefaultAutoInstrumentationApacheHttpd = InstrumentationPrefix + "default-auto-instrumentation-apache-httpd-image"
AnnotationDefaultAutoInstrumentationNginx = InstrumentationPrefix + "default-auto-instrumentation-nginx-image"
- LabelAppName = "app.kubernetes.io/name"
- LabelAppInstance = "app.kubernetes.io/instance"
- LabelAppVersion = "app.kubernetes.io/version"
- LabelAppPartOf = "app.kubernetes.io/part-of"
+ LabelAppName = "app.kubernetes.io/name"
+ LabelAppVersion = "app.kubernetes.io/version"
+ LabelAppPartOf = "app.kubernetes.io/part-of"
LabelTargetAllocator = "opentelemetry.io/target-allocator"
ResourceAttributeAnnotationPrefix = "resource.opentelemetry.io/"
diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go
index 87141a1cf8..c23106bea2 100644
--- a/pkg/instrumentation/sdk.go
+++ b/pkg/instrumentation/sdk.go
@@ -474,11 +474,16 @@ func chooseServiceVersion(pod corev1.Pod, useLabelsForResourceAttributes bool, i
// chooseServiceInstanceId returns the service.instance.id to be used in the instrumentation.
// The precedence is as follows:
-// 1. annotation with key "service.instance.id" or "app.kubernetes.io/instance"
+// 1. annotation with key "service.instance.id"
// 2. namespace name + pod name + container name
// (as defined by https://opentelemetry.io/docs/specs/semconv/resource/#service-experimental)
-func createServiceInstanceId(pod corev1.Pod, useLabelsForResourceAttributes bool, namespaceName, podName, containerName string) string {
- serviceInstanceId := chooseLabelOrAnnotation(pod, useLabelsForResourceAttributes, semconv.ServiceInstanceIDKey, constants.LabelAppInstance)
+func createServiceInstanceId(pod corev1.Pod, namespaceName, podName, containerName string) string {
+ // Do not use labels for service instance id,
+ // because multiple containers in the same pod would get the same service instance id,
+ // which violates the uniqueness requirement of service instance id -
+ // see https://opentelemetry.io/docs/specs/semconv/resource/#service-experimental.
+ // We still allow the user to set the service instance id via annotation, because this is explicitly set by the user.
+ serviceInstanceId := chooseLabelOrAnnotation(pod, false, semconv.ServiceInstanceIDKey, "")
if serviceInstanceId != "" {
return serviceInstanceId
}
@@ -527,7 +532,7 @@ func (i *sdkInjector) createResourceMap(ctx context.Context, otelinst v1alpha1.I
k8sResources[semconv.K8SPodNameKey] = pod.Name
k8sResources[semconv.K8SPodUIDKey] = string(pod.UID)
k8sResources[semconv.K8SNodeNameKey] = pod.Spec.NodeName
- k8sResources[semconv.ServiceInstanceIDKey] = createServiceInstanceId(pod, useLabelsForResourceAttributes, ns.Name, fmt.Sprintf("$(%s)", constants.EnvPodName), pod.Spec.Containers[index].Name)
+ k8sResources[semconv.ServiceInstanceIDKey] = createServiceInstanceId(pod, ns.Name, fmt.Sprintf("$(%s)", constants.EnvPodName), pod.Spec.Containers[index].Name)
i.addParentResourceLabels(ctx, otelinst.Spec.Resource.AddK8sUIDAttributes, ns, pod.ObjectMeta, k8sResources)
for k, v := range k8sResources {
diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go
index 04f9826807..09e7ee427b 100644
--- a/pkg/instrumentation/sdk_test.go
+++ b/pkg/instrumentation/sdk_test.go
@@ -156,10 +156,9 @@ func TestSDKInjection(t *testing.T) {
},
},
Labels: map[string]string{
- "app.kubernetes.io/name": "app-name",
- "app.kubernetes.io/instance": "app-id",
- "app.kubernetes.io/version": "v1",
- "app.kubernetes.io/part-of": "shop",
+ "app.kubernetes.io/name": "app-name",
+ "app.kubernetes.io/version": "v1",
+ "app.kubernetes.io/part-of": "shop",
},
Annotations: map[string]string{
"resource.opentelemetry.io/foo": "bar",
@@ -180,10 +179,9 @@ func TestSDKInjection(t *testing.T) {
Name: "app",
UID: "pod-uid",
Labels: map[string]string{
- "app.kubernetes.io/name": "app-name",
- "app.kubernetes.io/instance": "app-id",
- "app.kubernetes.io/version": "v1",
- "app.kubernetes.io/part-of": "shop",
+ "app.kubernetes.io/name": "app-name",
+ "app.kubernetes.io/version": "v1",
+ "app.kubernetes.io/part-of": "shop",
},
Annotations: map[string]string{
"resource.opentelemetry.io/foo": "bar",
@@ -396,10 +394,9 @@ func TestSDKInjection(t *testing.T) {
},
},
Labels: map[string]string{
- "app.kubernetes.io/name": "app-name",
- "app.kubernetes.io/instance": "app-id",
- "app.kubernetes.io/version": "v1",
- "app.kubernetes.io/part-of": "shop",
+ "app.kubernetes.io/name": "app-name",
+ "app.kubernetes.io/version": "v1",
+ "app.kubernetes.io/part-of": "shop",
},
Annotations: map[string]string{
"resource.opentelemetry.io/foo": "bar",
@@ -420,10 +417,9 @@ func TestSDKInjection(t *testing.T) {
Name: "app",
UID: "pod-uid",
Labels: map[string]string{
- "app.kubernetes.io/name": "app-name",
- "app.kubernetes.io/instance": "app-id",
- "app.kubernetes.io/version": "v1",
- "app.kubernetes.io/part-of": "shop",
+ "app.kubernetes.io/name": "app-name",
+ "app.kubernetes.io/version": "v1",
+ "app.kubernetes.io/part-of": "shop",
},
Annotations: map[string]string{
"resource.opentelemetry.io/foo": "bar",
@@ -481,7 +477,7 @@ func TestSDKInjection(t *testing.T) {
},
{
Name: "OTEL_RESOURCE_ATTRIBUTES",
- Value: "foo=bar,k8s.container.name=application-name,k8s.deployment.name=my-deployment,k8s.deployment.uid=depuid,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),k8s.pod.uid=pod-uid,k8s.replicaset.name=my-replicaset,k8s.replicaset.uid=rsuid,service.instance.id=app-id,service.namespace=shop,service.version=v1",
+ Value: "foo=bar,k8s.container.name=application-name,k8s.deployment.name=my-deployment,k8s.deployment.uid=depuid,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),k8s.pod.uid=pod-uid,k8s.replicaset.name=my-replicaset,k8s.replicaset.uid=rsuid,service.instance.id=project1.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).application-name,service.namespace=shop,service.version=v1",
},
},
},
@@ -516,10 +512,9 @@ func TestSDKInjection(t *testing.T) {
Namespace: "project1",
Name: "app",
Labels: map[string]string{
- "app.kubernetes.io/name": "not-used",
- "app.kubernetes.io/instance": "not-used",
- "app.kubernetes.io/version": "not-used",
- "app.kubernetes.io/part-of": "not-used",
+ "app.kubernetes.io/name": "not-used",
+ "app.kubernetes.io/version": "not-used",
+ "app.kubernetes.io/part-of": "not-used",
},
},
Spec: corev1.PodSpec{
@@ -557,10 +552,9 @@ func TestSDKInjection(t *testing.T) {
Namespace: "project1",
Name: "app",
Labels: map[string]string{
- "app.kubernetes.io/name": "not-used",
- "app.kubernetes.io/instance": "not-used",
- "app.kubernetes.io/version": "not-used",
- "app.kubernetes.io/part-of": "not-used",
+ "app.kubernetes.io/name": "not-used",
+ "app.kubernetes.io/version": "not-used",
+ "app.kubernetes.io/part-of": "not-used",
},
},
Spec: corev1.PodSpec{