Testcontainers for Go is a Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done.
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.
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.
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 theContainer
interface asContainer.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 measuringCPU
time, while Windows uses100s
ofnanoseconds
.container's stats in JSON format. Click to visualize
```json "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):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