testcontainers-go icon indicating copy to clipboard operation
testcontainers-go copied to clipboard

[Feature]: Expose containers stats

Open pablochacin opened this issue 1 year ago • 1 comments

Problem

In some use cases, it would be convenient to have access to the container's stats to measure the resource (memory, CPU) consumption to detect regressions or establish a comparison between alternative implementations or configurations.

Solution

Docker provides this information in the stats command. The proposed solution is to expose this information from the Container interface as Container.Stats()

The raw information obtained from docker is quite extensive as seen in the example below, with many elements only available in Linux systems but not in Windows. Moreover, the units used for the reported metrics can vary between platforms. For example, Linux uses nanoseconds for measuring CPU time, while Windows uses 100s of nanoseconds.

container's stats in JSON format. Click to visualize
  "stats": {
    "read": "2023-10-27T12:25:06.019724365Z",
    "pre_read": "0001-01-01T00:00:00Z",
    "pids_stats": {
      "current": 1059,
      "limit": 76608
    },
    "blkio_stats": {
      "io_service_bytes_recursive": [
        {
          "major": 253,
          "minor": 2,
          "op": "read",
          "value": 0
        },
        {
          "major": 253,
          "minor": 2,
          "op": "write",
          "value": 4096
        },
        {
          "major": 259,
          "minor": 0,
          "op": "read",
          "value": 1744896
        },
        {
          "major": 259,
          "minor": 0,
          "op": "write",
          "value": 0
        },
        {
          "major": 253,
          "minor": 0,
          "op": "read",
          "value": 1744896
        },
        {
          "major": 253,
          "minor": 0,
          "op": "write",
          "value": 12197888
        },
        {
          "major": 253,
          "minor": 1,
          "op": "read",
          "value": 1744896
        },
        {
          "major": 253,
          "minor": 1,
          "op": "write",
          "value": 12505088
        }
      ],
      "io_serviced_recursive": [],
      "io_queued_recursive": [],
      "io_service_time_recursive": [],
      "io_wait_time_recursive": [],
      "io_merged_recursive": [],
      "io_time_recursive": [],
      "sectors_recursive": []
    },
    "num_procs": 0,
    "storage_stats": {
      "read_count_normalized": 0,
      "read_size_bytes": 0,
      "write_count_normalized": 0,
      "write_size_bytes": 0
    },
    "cpu_stats": {
      "cpu_usage": {
        "total_usage": 25415300269000,
        "percpu_usage": [],
        "usage_in_kernelmode": 7710788995000,
        "usage_in_usermode": 17704511274000
      },
      "system_usage": 14395911570000000,
      "online_cpu_s": 20,
      "throttling_data": {
        "periods": 0,
        "throttled_periods": 0,
        "throttled_time": 0
      }
    },
    "pre_cpu_stats": {
      "cpu_usage": {
        "total_usage": 0,
        "percpu_usage": [],
        "usage_in_kernelmode": 0,
        "usage_in_usermode": 0
      },
      "system_usage": 0,
      "online_cpu_s": 0,
      "throttling_data": {
        "periods": 0,
        "throttled_periods": 0,
        "throttled_time": 0
      }
    },
    "memory_stats": {
      "usage": 3990077440,
      "max_usage": 0,
      "stats": {
        "pglazyfreed": 0,
        "pgmajfault": 341,
        "shmem": 275202048,
        "slab": 170641456,
        "anon_thp": 29360128,
        "file": 2074447872,
        "file_dirty": 4096,
        "sock": 12288,
        "file_mapped": 401707008,
        "file_writeback": 0,
        "pgactivate": 87325,
        "pgfault": 17291097,
        "pgrefill": 2955,
        "pgscan": 170011,
        "workingset_refault": 0,
        "active_file": 194707456,
        "anon": 1692143616,
        "workingset_nodereclaim": 0,
        "inactive_anon": 1813409792,
        "kernel_stack": 17350656,
        "unevictable": 0,
        "active_anon": 155107328,
        "inactive_file": 1606324224,
        "pgdeactivate": 2646,
        "slab_reclaimable": 144779624,
        "thp_collapse_alloc": 2,
        "thp_fault_alloc": 24,
        "pglazyfree": 436,
        "slab_unreclaimable": 25861832,
        "workingset_activate": 0,
        "pgsteal": 113971
      },
      "failcnt": 0,
      "limit": 67073630208,
      "commit": 0,
      "commit_peak": 0,
      "private_working_set": 0
    }
  },
  "read": "2023-10-27T12:25:06.019724365Z",
  "pre_read": "0001-01-01T00:00:00Z",
  "pids_stats": {
    "current": 1059,
    "limit": 76608
  },
  "blkio_stats": {
    "io_service_bytes_recursive": [
      {
        "major": 253,
        "minor": 2,
        "op": "read",
        "value": 0
      },
      {
        "major": 253,
        "minor": 2,
        "op": "write",
        "value": 4096
      },
      {
        "major": 259,
        "minor": 0,
        "op": "read",
        "value": 1744896
      },
      {
        "major": 259,
        "minor": 0,
        "op": "write",
        "value": 0
      },
      {
        "major": 253,
        "minor": 0,
        "op": "read",
        "value": 1744896
      },
      {
        "major": 253,
        "minor": 0,
        "op": "write",
        "value": 12197888
      },
      {
        "major": 253,
        "minor": 1,
        "op": "read",
        "value": 1744896
      },
      {
        "major": 253,
        "minor": 1,
        "op": "write",
        "value": 12505088
      }
    ],
    "io_serviced_recursive": [],
    "io_queued_recursive": [],
    "io_service_time_recursive": [],
    "io_wait_time_recursive": [],
    "io_merged_recursive": [],
    "io_time_recursive": [],
    "sectors_recursive": []
  },
  "num_procs": 0,
  "storage_stats": {
    "read_count_normalized": 0,
    "read_size_bytes": 0,
    "write_count_normalized": 0,
    "write_size_bytes": 0
  },
  "cpu_stats": {
    "cpu_usage": {
      "total_usage": 25415300269000,
      "percpu_usage": [],
      "usage_in_kernelmode": 7710788995000,
      "usage_in_usermode": 17704511274000
    },
    "system_usage": 14395911570000000,
    "online_cpu_s": 20,
    "throttling_data": {
      "periods": 0,
      "throttled_periods": 0,
      "throttled_time": 0
    }
  },
  "pre_cpu_stats": {
    "cpu_usage": {
      "total_usage": 0,
      "percpu_usage": [],
      "usage_in_kernelmode": 0,
      "usage_in_usermode": 0
    },
    "system_usage": 0,
    "online_cpu_s": 0,
    "throttling_data": {
      "periods": 0,
      "throttled_periods": 0,
      "throttled_time": 0
    }
  },
  "memory_stats": {
    "usage": 3990077440,
    "max_usage": 0,
    "stats": {
      "inactive_anon": 1813409792,
      "kernel_stack": 17350656,
      "unevictable": 0,
      "active_anon": 155107328,
      "inactive_file": 1606324224,
      "pgdeactivate": 2646,
      "slab_reclaimable": 144779624,
      "thp_collapse_alloc": 2,
      "thp_fault_alloc": 24,
      "pglazyfree": 436,
      "slab_unreclaimable": 25861832,
      "workingset_activate": 0,
      "pgsteal": 113971,
      "pglazyfreed": 0,
      "pgmajfault": 341,
      "shmem": 275202048,
      "slab": 170641456,
      "anon_thp": 29360128,
      "file": 2074447872,
      "file_dirty": 4096,
      "sock": 12288,
      "file_mapped": 401707008,
      "file_writeback": 0,
      "pgactivate": 87325,
      "pgfault": 17291097,
      "pgrefill": 2955,
      "pgscan": 170011,
      "workingset_refault": 0,
      "active_file": 194707456,
      "anon": 1692143616,
      "workingset_nodereclaim": 0
    },
    "failcnt": 0,
    "limit": 67073630208,
    "commit": 0,
    "commit_peak": 0,
    "private_working_set": 0
  },
  "name": "/test-e2e-control-plane",
  "id": "6271ed069235600bcbdbe70505db90f299197c69a49b097dfa5b6e941b917172",
  "networks": {
    "eth0": {
      "rx_bytes": 80197969,
      "rx_packets": 19779,
      "rx_errors": 0,
      "rx_dropped": 0,
      "tx_bytes": 2127699,
      "tx_packets": 5507,
      "tx_errors": 0,
      "tx_dropped": 0,
      "endpoint_id": "",
      "instance_id": ""
    }
  }
}

Therefore, we suggest returning a custom type with the information provided by the stats command and containing only fields that are available in both Linux and Windows platforms and normalized to one unit (for example, nanoseconds):

type ContainerStats struct {
            Timestamp               time.Time
            CPUUsageTotal       uint64
            CPUUsageInKernel uint64
            CPUUsageUser       uint64
            CPUPercentage      float64
            MemoryUsage         uint64               // in Windows, it corresponds to Commit memory
            MemoryMaxUsage   uint64               // in Windows, it corresponds to PeakCommit memory
            MemoryPercentage float64
            NetworkRxBytes      uint64
            NetworkTxBytes      uint64
            BlockIOReadBytes  uint64               // BlockIO metrics must be calculated from different sources in Windows and Linux
            BlockIOWriteBytes  uint64                
}

Benefit

Offers the ability to monitor a container's resource usage.

Alternatives

We don't see any alternative that is convenient for the test developer.

Would you like to help contributing this feature?

Yes

pablochacin avatar Oct 27 '23 10:10 pablochacin

hi @pablochacin! I started using testcontainers-go and I would be very interested in using this feature

ervitis avatar May 01 '24 01:05 ervitis