diff --git a/extension/cgroupruntimeextension/README.md b/extension/cgroupruntimeextension/README.md index f7d79099d6c1..995be9cf314c 100644 --- a/extension/cgroupruntimeextension/README.md +++ b/extension/cgroupruntimeextension/README.md @@ -15,7 +15,7 @@ ## Overview -The OpenTelemetry Cgroup Auto-Config Extension is designed to optimize Go runtime performance in containerized environments by automatically configuring GOMAXPROCS and GOMEMLIMIT based on the Linux cgroup filesystem. This extension leverages [automaxprocs](https://github.com/uber-go/automaxprocs) and [automemlimit](https://github.com/KimMachineGun/automemlimit) packages to dynamically adjust Go runtime variables, ensuring efficient resource usage aligned with container limits. +The OpenTelemetry Cgroup Auto-Config Extension is designed to optimize Go runtime performance in containerized environments by automatically configuring GOMAXPROCS and GOMEMLIMIT based on the Linux cgroup filesystem. This extension leverages [automaxprocs](https://github.com/uber-go/automaxprocs) or [gomaxecs](https://github.com/rdforte/gomaxecs) for AWS ECS Tasks and [automemlimit](https://github.com/KimMachineGun/automemlimit) packages to dynamically adjust Go runtime variables, ensuring efficient resource usage aligned with container limits. ## Configuration diff --git a/extension/cgroupruntimeextension/factory.go b/extension/cgroupruntimeextension/factory.go index 1905811dc3b8..4079c89c4021 100644 --- a/extension/cgroupruntimeextension/factory.go +++ b/extension/cgroupruntimeextension/factory.go @@ -9,6 +9,7 @@ import ( "runtime/debug" "github.com/KimMachineGun/automemlimit/memlimit" + gomaxecs "github.com/rdforte/gomaxecs/maxprocs" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/extension" "go.uber.org/automaxprocs/maxprocs" @@ -42,9 +43,17 @@ func createExtension(_ context.Context, set extension.Settings, cfg component.Co cgroupConfig := cfg.(*Config) return newCgroupRuntime(cgroupConfig, set.Logger, func() (undoFunc, error) { - undo, err := maxprocs.Set(maxprocs.Logger(func(str string, params ...any) { - set.Logger.Debug(fmt.Sprintf(str, params)) - })) + var undo func() + var err error + if gomaxecs.IsECS() { + undo, err = gomaxecs.Set(gomaxecs.WithLogger(func(str string, params ...any) { + set.Logger.Debug(fmt.Sprintf(str, params)) + })) + } else { + undo, err = maxprocs.Set(maxprocs.Logger(func(str string, params ...any) { + set.Logger.Debug(fmt.Sprintf(str, params)) + })) + } return undoFunc(undo), err }, func(ratio float64) (undoFunc, error) { diff --git a/extension/cgroupruntimeextension/go.mod b/extension/cgroupruntimeextension/go.mod index fa1361e14d55..65ff877c1af9 100644 --- a/extension/cgroupruntimeextension/go.mod +++ b/extension/cgroupruntimeextension/go.mod @@ -1,10 +1,11 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/extension/cgroupruntimeextension -go 1.22.0 +go 1.22.4 require ( github.com/KimMachineGun/automemlimit v0.6.1 github.com/containerd/cgroups/v3 v3.0.2 + github.com/rdforte/gomaxecs v1.1.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/component v0.116.0 go.opentelemetry.io/collector/component/componenttest v0.116.0 diff --git a/extension/cgroupruntimeextension/go.sum b/extension/cgroupruntimeextension/go.sum index 50f31892465d..57c500b875f6 100644 --- a/extension/cgroupruntimeextension/go.sum +++ b/extension/cgroupruntimeextension/go.sum @@ -55,6 +55,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/rdforte/gomaxecs v1.1.0 h1:fpDkJtuBRtRQjcxAKdARjwjYzxlmmGkSmcqzF0UKuOg= +github.com/rdforte/gomaxecs v1.1.0/go.mod h1:8agrawOmcvb+oBa6EnV2oADDtnDtkVx1Q0H/Ht7GiFc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= diff --git a/extension/cgroupruntimeextension/integration_test.go b/extension/cgroupruntimeextension/integration_test.go index 64d79c63d47c..a9084821b71e 100644 --- a/extension/cgroupruntimeextension/integration_test.go +++ b/extension/cgroupruntimeextension/integration_test.go @@ -9,8 +9,11 @@ package cgroupruntimeextension // import "github.com/open-telemetry/opentelemetr import ( "context" + "encoding/json" "fmt" "math" + "net/http" + "net/http/httptest" "os" "path" "path/filepath" @@ -63,6 +66,21 @@ func cgroupMaxCpu(filename string) (quota int64, period uint64, err error) { return quota, period, err } +func startMockECSServer() *httptest.Server { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := map[string]interface{}{ + "DockerID": "container-id", + "Limits": map[string]interface{}{ + "CPU": 2.0, + }, + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) + }) + + return httptest.NewServer(handler) +} + func TestCgroupV2SudoIntegration(t *testing.T) { checkCgroupSystem(t) pointerInt64 := func(val int64) *int64 { @@ -81,6 +99,7 @@ func TestCgroupV2SudoIntegration(t *testing.T) { config *Config expectedGoMaxProcs int expectedGoMemLimit int64 + setECSMetadataURI bool }{ { name: "90% the max cgroup memory and 12 GOMAXPROCS", @@ -144,6 +163,24 @@ func TestCgroupV2SudoIntegration(t *testing.T) { // 134217728 * 0.1 expectedGoMemLimit: 13421772, }, + { + name: "running on AWS ECS with 90% of max cgroup memory and 2 GOMAXPROCS", + cgroupCpuQuota: pointerInt64(-1), + cgroupCpuPeriod: 8000, + cgroupMaxMemory: 134217728, // 128 MB + config: &Config{ + GoMaxProcs: GoMaxProcsConfig{ + Enabled: true, + }, + GoMemLimit: GoMemLimitConfig{ + Enabled: true, + Ratio: 0.9, + }, + }, + expectedGoMaxProcs: 22, + expectedGoMemLimit: 120795955, // 134217728 * 0.9 + setECSMetadataURI: true, + }, } cgroupPath, err := cgroup2.PidGroupPath(os.Getpid()) @@ -220,6 +257,13 @@ func TestCgroupV2SudoIntegration(t *testing.T) { }) require.NoError(t, err) + if test.setECSMetadataURI { + server := startMockECSServer() + defer server.Close() + os.Setenv("ECS_CONTAINER_METADATA_URI_V4", server.URL) + defer os.Unsetenv("ECS_CONTAINER_METADATA_URI_V4") + } + factory := NewFactory() ctx := context.Background() extension, err := factory.Create(ctx, extensiontest.NewNopSettings(), test.config)