Skip to content

Commit cca7085

Browse files
committed
cmd/dockerd: Add workaround for OTEL meter leak
OTEL meter implementation has a memory leak issue which causes each meter counter invocation to create a new instrument when the meter provider is not set. Also add a test, which will fail once a fixed OTEL is vendored. Signed-off-by: Paweł Gronowski <[email protected]>
1 parent 3900f9a commit cca7085

3 files changed

Lines changed: 38 additions & 1 deletion

File tree

cmd/dockerd/daemon_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package main
22

33
import (
4+
"runtime"
45
"testing"
56

67
"github.com/containerd/log"
78
"github.com/docker/docker/daemon/config"
89
"github.com/google/go-cmp/cmp/cmpopts"
910
"github.com/spf13/pflag"
11+
"go.opentelemetry.io/otel"
1012
"gotest.tools/v3/assert"
1113
is "gotest.tools/v3/assert/cmp"
1214
"gotest.tools/v3/fs"
@@ -281,3 +283,29 @@ func TestCDISpecDirs(t *testing.T) {
281283
})
282284
}
283285
}
286+
287+
// TestOtelMeterLeak tests for a memory leak in the OTEL meter implementation.
288+
// Once the fixed OTEL is vendored, this test will fail - the workaround
289+
// and this test should be removed then.
290+
func TestOtelMeterLeak(t *testing.T) {
291+
meter := otel.Meter("foo")
292+
293+
var before runtime.MemStats
294+
runtime.ReadMemStats(&before)
295+
296+
const counters = 10 * 1000 * 1000
297+
for i := 0; i < counters; i++ {
298+
_, _ = meter.Int64Counter("bar")
299+
}
300+
301+
var after runtime.MemStats
302+
runtime.ReadMemStats(&after)
303+
304+
allocs := after.Mallocs - before.Mallocs
305+
t.Log("Allocations:", allocs)
306+
307+
if allocs < 10 {
308+
// TODO: Remove Workaround OTEL memory leak in cmd/dockerd/daemon.go
309+
t.Fatal("Allocations count decreased. OTEL leak workaround is no longer needed!")
310+
}
311+
}

cmd/dockerd/docker.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import (
1515
"github.com/moby/buildkit/util/apicaps"
1616
"github.com/moby/term"
1717
"github.com/spf13/cobra"
18+
19+
"go.opentelemetry.io/otel"
20+
"go.opentelemetry.io/otel/metric/noop"
1821
)
1922

2023
var honorXDG bool
@@ -89,6 +92,12 @@ func main() {
8992
// Fixes https://github.com/docker/docker/issues/19728
9093
signal.Ignore(syscall.SIGPIPE)
9194

95+
// Workaround OTEL memory leak
96+
// See: https://github.com/open-telemetry/opentelemetry-go-contrib/issues/5190
97+
// The need for this workaround is checked by the TestOtelMeterLeak test
98+
// TODO: Remove this workaround after upgrading to v1.30.0
99+
otel.SetMeterProvider(noop.MeterProvider{})
100+
92101
// Set terminal emulation based on platform as required.
93102
_, stdout, stderr := term.StdStreams()
94103
onError := func(err error) {

vendor.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ require (
100100
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
101101
go.opentelemetry.io/otel v1.21.0
102102
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0
103+
go.opentelemetry.io/otel/metric v1.21.0
103104
go.opentelemetry.io/otel/sdk v1.21.0
104105
go.opentelemetry.io/otel/trace v1.21.0
105106
golang.org/x/mod v0.21.0
@@ -214,7 +215,6 @@ require (
214215
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
215216
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
216217
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
217-
go.opentelemetry.io/otel/metric v1.21.0 // indirect
218218
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
219219
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
220220
go.uber.org/atomic v1.9.0 // indirect

0 commit comments

Comments
 (0)