daemon: trace containerCreate

This commit adds a new OTel span to `(*Daemon).containerCreate()` and
puts filtered container labels in the span attributes.

The filter is based on a comma-separated list of labels provided through
the `DOCKER_OTEL_INCLUDE_CONTAINER_LABEL_ATTRS` environment variable.
This label might be removed at any point in time if we figure out a
better way to filter labels, or if that span becomes unecessary.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
Albin Kerouanton
2025-04-08 21:46:52 +02:00
parent 3fc5bfd565
commit 0c5e816638

View File

@@ -1,3 +1,6 @@
// TODO(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.22
package daemon // import "github.com/docker/docker/daemon"
import (
@@ -5,6 +8,7 @@ import (
"fmt"
"os"
"strings"
"sync"
"time"
"github.com/containerd/log"
@@ -20,11 +24,15 @@ import (
"github.com/docker/docker/image"
"github.com/docker/docker/internal/metrics"
"github.com/docker/docker/internal/multierror"
"github.com/docker/docker/internal/otelutil"
"github.com/docker/docker/runconfig"
"github.com/moby/sys/user"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/selinux/go-selinux"
"github.com/tonistiigi/go-archvariant"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
type createOpts struct {
@@ -57,7 +65,15 @@ func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(ctx context.Context
})
}
func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStore, opts createOpts) (containertypes.CreateResponse, error) {
func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStore, opts createOpts) (_ containertypes.CreateResponse, retErr error) {
ctx, span := otel.Tracer("").Start(ctx, "daemon.containerCreate", trace.WithAttributes(
labelsAsOTelAttributes(opts.params.Config.Labels)...,
))
defer func() {
otelutil.RecordStatus(span, retErr)
span.End()
}()
start := time.Now()
if opts.params.Config == nil {
return containertypes.CreateResponse{}, errdefs.InvalidParameter(runconfig.ErrEmptyConfig)
@@ -123,6 +139,31 @@ func (daemon *Daemon) containerCreate(ctx context.Context, daemonCfg *configStor
return containertypes.CreateResponse{ID: ctr.ID, Warnings: warnings}, nil
}
var (
containerLabelsFilter []string
containerLabelsFilterOnce sync.Once
)
func labelsAsOTelAttributes(labels map[string]string) []attribute.KeyValue {
containerLabelsFilterOnce.Do(func() {
containerLabelsFilter = strings.Split(os.Getenv("DOCKER_CONTAINER_LABELS_FILTER"), ",")
})
// This env var is a comma-separated list of labels to be included in the
// OTel span attributes. The labels are prefixed with "label." to avoid
// collision with other attributes.
//
// Note that, this is an experimental env var that might be removed
// unceremoniously at any point in time.
attrs := make([]attribute.KeyValue, 0, len(containerLabelsFilter))
for _, k := range containerLabelsFilter {
if v, ok := labels[k]; ok {
attrs = append(attrs, attribute.String("label."+k, v))
}
}
return attrs
}
// Create creates a new container from the given configuration with a given name.
func (daemon *Daemon) create(ctx context.Context, daemonCfg *config.Config, opts createOpts) (retC *container.Container, retErr error) {
var (