From 3d350aa513917093091abaad41588fab8f0b5a2f Mon Sep 17 00:00:00 2001 From: fallwith Date: Wed, 20 Sep 2023 17:14:44 -0700 Subject: [PATCH 1/4] Support cgroups v1 and v2 for Docker containers Previously the agent was only capable of gleaning a Docker container id by leveraging a cgroups v1 approach. Now, it will first attempt to leverage a cgroups v2 approach and fall back to the v1 approach if that doesn't work. All other Docker related behavior such as that seen with containers that don't have cgroups functionality enabled is not impacted. --- lib/new_relic/agent/system_info.rb | 24 ++++++++++++++++++++++++ test/new_relic/agent/system_info_test.rb | 17 +++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/lib/new_relic/agent/system_info.rb b/lib/new_relic/agent/system_info.rb index 5d72674445..062157e6d7 100644 --- a/lib/new_relic/agent/system_info.rb +++ b/lib/new_relic/agent/system_info.rb @@ -172,15 +172,39 @@ def self.os_version proc_try_read('/proc/version') end + # When operating within a Docker container, attempt to obtain the + # container id. + # + # First look for `/proc/self/mountinfo` to exist on disk to signify + # cgroups v2. If that file exists, read it and expect it to contain one + # or more "/docker/containers//" lines from which the + # container id can be gleaned. + # + # Next look for `/proc/self/cgroup` to exist on disk to signify cgroup v1. + # If that file exists, read it and parse the "cpu" group info in the hope + # of finding a 64 character container id value. + # + # For non-cgroups based containers, use a `nil` value for the container + # id without generating any warnings or errors. def self.docker_container_id return unless ruby_os_identifier.include?('linux') + cgroupsv2_based_id = docker_container_id_for_cgroupsv2 + return cgroupsv2_based_id if cgroupsv2_based_id + cgroup_info = proc_try_read('/proc/self/cgroup') return unless cgroup_info parse_docker_container_id(cgroup_info) end + def self.docker_container_id_for_cgroupsv2 + mountinfo = proc_try_read('/proc/self/mountinfo') + return unless mountinfo + + Regexp.last_match(1) if mountinfo =~ %r{/docker/containers/([^/]+)/} + end + def self.parse_docker_container_id(cgroup_info) cpu_cgroup = parse_cgroup_ids(cgroup_info)['cpu'] return unless cpu_cgroup diff --git a/test/new_relic/agent/system_info_test.rb b/test/new_relic/agent/system_info_test.rb index a44e8b5f30..33fc104493 100644 --- a/test/new_relic/agent/system_info_test.rb +++ b/test/new_relic/agent/system_info_test.rb @@ -60,6 +60,7 @@ def setup end end + # BEGIN cgroups v1 container_id_test_dir = File.join(cross_agent_tests_dir, 'docker_container_id') container_id_test_cases = load_cross_agent_test(File.join('docker_container_id', 'cases')) @@ -86,6 +87,21 @@ def setup end end end + # END cgroups v1 + + # BEGIN cgroups v2 + def test_docker_container_id_is_gleaned_from_mountinfo_for_cgroups_v2 + skip_unless_minitest5_or_above + + container_id = "And Autumn leaves lie thick and still o'er land that is lost now" + mountinfo = "line1\nline2\n/docker/containers/#{container_id}/other/content\nline4\nline5" + NewRelic::Agent::SystemInfo.stub :ruby_os_identifier, 'linux' do + NewRelic::Agent::SystemInfo.stub :proc_try_read, mountinfo, %w[/proc/self/mountinfo] do + assert_equal container_id, NewRelic::Agent::SystemInfo.docker_container_id + end + end + end + # END cgroups v2 each_cross_agent_test :dir => 'proc_meminfo', :pattern => '*.txt' do |file| if File.basename(file) =~ /^meminfo_(\d+)MB.txt$/ @@ -297,6 +313,7 @@ def test_system_info_bsd_predicate def test_supportability_metric_recorded_when_docker_id_unavailable NewRelic::Agent::SystemInfo.stubs(:ruby_os_identifier).returns('linux') cgroup_info = File.read(File.join(cross_agent_tests_dir, 'docker_container_id', 'invalid-length.txt')) + NewRelic::Agent::SystemInfo.expects(:proc_try_read).with('/proc/self/mountinfo').returns(cgroup_info) NewRelic::Agent::SystemInfo.expects(:proc_try_read).with('/proc/self/cgroup').returns(cgroup_info) in_transaction('txn') do From 6ec042d577b7b753c4ad9edcac7f5d1b9891011e Mon Sep 17 00:00:00 2001 From: fallwith Date: Wed, 20 Sep 2023 17:45:29 -0700 Subject: [PATCH 2/4] CHANGELOG: PR 2026 Update CHANGELOG for the Docker container id support for cgroups v2 / PR 2026 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f65e4c06..8d68de617a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # New Relic Ruby Agent Release Notes +## dev + +Version brings support for gleaning a Docker container id from cgroups v2 based containers. + +- **Feature: Enhance Docker container id reporting** + + Previously the agent was only capable of determining a host Docker container's id if the container was based on cgroups v1. Now containers based on cgroups v2 will also have their container ids reported to New Relic. [PR#2026](https://github.com/newrelic/newrelic-ruby-agent/issues/2026). + ## v9.5.0 Version 9.5.0 introduces Stripe instrumentation, allows the agent to record additional response information on a transaction when middleware instrumentation is disabled, introduces new `:'sidekiq.args.include'` and `:'sidekiq.args.exclude:` configuration options to permit capturing only certain Sidekiq job arguments, updates Elasticsearch datastore instance metrics, and fixes a bug in `NewRelic::Rack::AgentHooks.needed?`. From eec7037e06c04312e552e9141f9a24d4c8194322 Mon Sep 17 00:00:00 2001 From: fallwith Date: Wed, 20 Sep 2023 17:46:21 -0700 Subject: [PATCH 3/4] CHANGELOG: reference PR, not Issue For the Docker container id support for cgroups v2, reference the PR, not the Issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d68de617a..f3a7b46a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Version brings support for gleaning a Docker container id from cgroups v2 - **Feature: Enhance Docker container id reporting** - Previously the agent was only capable of determining a host Docker container's id if the container was based on cgroups v1. Now containers based on cgroups v2 will also have their container ids reported to New Relic. [PR#2026](https://github.com/newrelic/newrelic-ruby-agent/issues/2026). + Previously the agent was only capable of determining a host Docker container's id if the container was based on cgroups v1. Now containers based on cgroups v2 will also have their container ids reported to New Relic. [PR#2229](https://github.com/newrelic/newrelic-ruby-agent/issues/2229). ## v9.5.0 From fcb55e08381147703d63474a9bf5b9a5ca1179cd Mon Sep 17 00:00:00 2001 From: James Bunch Date: Wed, 20 Sep 2023 17:55:40 -0700 Subject: [PATCH 4/4] Update CHANGELOG.md Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a7b46a80..d1f4fee461 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Version brings support for gleaning a Docker container id from cgroups v2 - **Feature: Enhance Docker container id reporting** - Previously the agent was only capable of determining a host Docker container's id if the container was based on cgroups v1. Now containers based on cgroups v2 will also have their container ids reported to New Relic. [PR#2229](https://github.com/newrelic/newrelic-ruby-agent/issues/2229). + Previously, the agent was only capable of determining a host Docker container's ID if the container was based on cgroups v1. Now, containers based on cgroups v2 will also have their container IDs reported to New Relic. [PR#2229](https://github.com/newrelic/newrelic-ruby-agent/issues/2229). ## v9.5.0