stats_unix.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //go:build !windows
  2. package daemon // import "github.com/docker/docker/daemon"
  3. import (
  4. "context"
  5. "strings"
  6. statsV1 "github.com/containerd/cgroups/v3/cgroup1/stats"
  7. statsV2 "github.com/containerd/cgroups/v3/cgroup2/stats"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/container"
  10. "github.com/pkg/errors"
  11. )
  12. func copyBlkioEntry(entries []*statsV1.BlkIOEntry) []types.BlkioStatEntry {
  13. out := make([]types.BlkioStatEntry, len(entries))
  14. for i, re := range entries {
  15. out[i] = types.BlkioStatEntry{
  16. Major: re.Major,
  17. Minor: re.Minor,
  18. Op: re.Op,
  19. Value: re.Value,
  20. }
  21. }
  22. return out
  23. }
  24. func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
  25. c.Lock()
  26. task, err := c.GetRunningTask()
  27. c.Unlock()
  28. if err != nil {
  29. return nil, err
  30. }
  31. cs, err := task.Stats(context.Background())
  32. if err != nil {
  33. if strings.Contains(err.Error(), "container not found") {
  34. return nil, containerNotFound(c.ID)
  35. }
  36. return nil, err
  37. }
  38. s := &types.StatsJSON{}
  39. s.Read = cs.Read
  40. stats := cs.Metrics
  41. switch t := stats.(type) {
  42. case *statsV1.Metrics:
  43. return daemon.statsV1(s, t)
  44. case *statsV2.Metrics:
  45. return daemon.statsV2(s, t)
  46. default:
  47. return nil, errors.Errorf("unexpected type of metrics %+v", t)
  48. }
  49. }
  50. func (daemon *Daemon) statsV1(s *types.StatsJSON, stats *statsV1.Metrics) (*types.StatsJSON, error) {
  51. if stats.Blkio != nil {
  52. s.BlkioStats = types.BlkioStats{
  53. IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive),
  54. IoServicedRecursive: copyBlkioEntry(stats.Blkio.IoServicedRecursive),
  55. IoQueuedRecursive: copyBlkioEntry(stats.Blkio.IoQueuedRecursive),
  56. IoServiceTimeRecursive: copyBlkioEntry(stats.Blkio.IoServiceTimeRecursive),
  57. IoWaitTimeRecursive: copyBlkioEntry(stats.Blkio.IoWaitTimeRecursive),
  58. IoMergedRecursive: copyBlkioEntry(stats.Blkio.IoMergedRecursive),
  59. IoTimeRecursive: copyBlkioEntry(stats.Blkio.IoTimeRecursive),
  60. SectorsRecursive: copyBlkioEntry(stats.Blkio.SectorsRecursive),
  61. }
  62. }
  63. if stats.CPU != nil {
  64. s.CPUStats = types.CPUStats{
  65. CPUUsage: types.CPUUsage{
  66. TotalUsage: stats.CPU.Usage.Total,
  67. PercpuUsage: stats.CPU.Usage.PerCPU,
  68. UsageInKernelmode: stats.CPU.Usage.Kernel,
  69. UsageInUsermode: stats.CPU.Usage.User,
  70. },
  71. ThrottlingData: types.ThrottlingData{
  72. Periods: stats.CPU.Throttling.Periods,
  73. ThrottledPeriods: stats.CPU.Throttling.ThrottledPeriods,
  74. ThrottledTime: stats.CPU.Throttling.ThrottledTime,
  75. },
  76. }
  77. }
  78. if stats.Memory != nil {
  79. raw := map[string]uint64{
  80. "cache": stats.Memory.Cache,
  81. "rss": stats.Memory.RSS,
  82. "rss_huge": stats.Memory.RSSHuge,
  83. "mapped_file": stats.Memory.MappedFile,
  84. "dirty": stats.Memory.Dirty,
  85. "writeback": stats.Memory.Writeback,
  86. "pgpgin": stats.Memory.PgPgIn,
  87. "pgpgout": stats.Memory.PgPgOut,
  88. "pgfault": stats.Memory.PgFault,
  89. "pgmajfault": stats.Memory.PgMajFault,
  90. "inactive_anon": stats.Memory.InactiveAnon,
  91. "active_anon": stats.Memory.ActiveAnon,
  92. "inactive_file": stats.Memory.InactiveFile,
  93. "active_file": stats.Memory.ActiveFile,
  94. "unevictable": stats.Memory.Unevictable,
  95. "hierarchical_memory_limit": stats.Memory.HierarchicalMemoryLimit,
  96. "hierarchical_memsw_limit": stats.Memory.HierarchicalSwapLimit,
  97. "total_cache": stats.Memory.TotalCache,
  98. "total_rss": stats.Memory.TotalRSS,
  99. "total_rss_huge": stats.Memory.TotalRSSHuge,
  100. "total_mapped_file": stats.Memory.TotalMappedFile,
  101. "total_dirty": stats.Memory.TotalDirty,
  102. "total_writeback": stats.Memory.TotalWriteback,
  103. "total_pgpgin": stats.Memory.TotalPgPgIn,
  104. "total_pgpgout": stats.Memory.TotalPgPgOut,
  105. "total_pgfault": stats.Memory.TotalPgFault,
  106. "total_pgmajfault": stats.Memory.TotalPgMajFault,
  107. "total_inactive_anon": stats.Memory.TotalInactiveAnon,
  108. "total_active_anon": stats.Memory.TotalActiveAnon,
  109. "total_inactive_file": stats.Memory.TotalInactiveFile,
  110. "total_active_file": stats.Memory.TotalActiveFile,
  111. "total_unevictable": stats.Memory.TotalUnevictable,
  112. }
  113. if stats.Memory.Usage != nil {
  114. s.MemoryStats = types.MemoryStats{
  115. Stats: raw,
  116. Usage: stats.Memory.Usage.Usage,
  117. MaxUsage: stats.Memory.Usage.Max,
  118. Limit: stats.Memory.Usage.Limit,
  119. Failcnt: stats.Memory.Usage.Failcnt,
  120. }
  121. } else {
  122. s.MemoryStats = types.MemoryStats{
  123. Stats: raw,
  124. }
  125. }
  126. // if the container does not set memory limit, use the machineMemory
  127. if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 {
  128. s.MemoryStats.Limit = daemon.machineMemory
  129. }
  130. }
  131. if stats.Pids != nil {
  132. s.PidsStats = types.PidsStats{
  133. Current: stats.Pids.Current,
  134. Limit: stats.Pids.Limit,
  135. }
  136. }
  137. return s, nil
  138. }
  139. func (daemon *Daemon) statsV2(s *types.StatsJSON, stats *statsV2.Metrics) (*types.StatsJSON, error) {
  140. if stats.Io != nil {
  141. var isbr []types.BlkioStatEntry
  142. for _, re := range stats.Io.Usage {
  143. isbr = append(isbr,
  144. types.BlkioStatEntry{
  145. Major: re.Major,
  146. Minor: re.Minor,
  147. Op: "read",
  148. Value: re.Rbytes,
  149. },
  150. types.BlkioStatEntry{
  151. Major: re.Major,
  152. Minor: re.Minor,
  153. Op: "write",
  154. Value: re.Wbytes,
  155. },
  156. )
  157. }
  158. s.BlkioStats = types.BlkioStats{
  159. IoServiceBytesRecursive: isbr,
  160. // Other fields are unsupported
  161. }
  162. }
  163. if stats.CPU != nil {
  164. s.CPUStats = types.CPUStats{
  165. CPUUsage: types.CPUUsage{
  166. TotalUsage: stats.CPU.UsageUsec * 1000,
  167. // PercpuUsage is not supported
  168. UsageInKernelmode: stats.CPU.SystemUsec * 1000,
  169. UsageInUsermode: stats.CPU.UserUsec * 1000,
  170. },
  171. ThrottlingData: types.ThrottlingData{
  172. Periods: stats.CPU.NrPeriods,
  173. ThrottledPeriods: stats.CPU.NrThrottled,
  174. ThrottledTime: stats.CPU.ThrottledUsec * 1000,
  175. },
  176. }
  177. }
  178. if stats.Memory != nil {
  179. s.MemoryStats = types.MemoryStats{
  180. // Stats is not compatible with v1
  181. Stats: map[string]uint64{
  182. "anon": stats.Memory.Anon,
  183. "file": stats.Memory.File,
  184. "kernel_stack": stats.Memory.KernelStack,
  185. "slab": stats.Memory.Slab,
  186. "sock": stats.Memory.Sock,
  187. "shmem": stats.Memory.Shmem,
  188. "file_mapped": stats.Memory.FileMapped,
  189. "file_dirty": stats.Memory.FileDirty,
  190. "file_writeback": stats.Memory.FileWriteback,
  191. "anon_thp": stats.Memory.AnonThp,
  192. "inactive_anon": stats.Memory.InactiveAnon,
  193. "active_anon": stats.Memory.ActiveAnon,
  194. "inactive_file": stats.Memory.InactiveFile,
  195. "active_file": stats.Memory.ActiveFile,
  196. "unevictable": stats.Memory.Unevictable,
  197. "slab_reclaimable": stats.Memory.SlabReclaimable,
  198. "slab_unreclaimable": stats.Memory.SlabUnreclaimable,
  199. "pgfault": stats.Memory.Pgfault,
  200. "pgmajfault": stats.Memory.Pgmajfault,
  201. "workingset_refault": stats.Memory.WorkingsetRefault,
  202. "workingset_activate": stats.Memory.WorkingsetActivate,
  203. "workingset_nodereclaim": stats.Memory.WorkingsetNodereclaim,
  204. "pgrefill": stats.Memory.Pgrefill,
  205. "pgscan": stats.Memory.Pgscan,
  206. "pgsteal": stats.Memory.Pgsteal,
  207. "pgactivate": stats.Memory.Pgactivate,
  208. "pgdeactivate": stats.Memory.Pgdeactivate,
  209. "pglazyfree": stats.Memory.Pglazyfree,
  210. "pglazyfreed": stats.Memory.Pglazyfreed,
  211. "thp_fault_alloc": stats.Memory.ThpFaultAlloc,
  212. "thp_collapse_alloc": stats.Memory.ThpCollapseAlloc,
  213. },
  214. Usage: stats.Memory.Usage,
  215. // MaxUsage is not supported
  216. Limit: stats.Memory.UsageLimit,
  217. }
  218. // if the container does not set memory limit, use the machineMemory
  219. if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 {
  220. s.MemoryStats.Limit = daemon.machineMemory
  221. }
  222. if stats.MemoryEvents != nil {
  223. // Failcnt is set to the "oom" field of the "memory.events" file.
  224. // See https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
  225. s.MemoryStats.Failcnt = stats.MemoryEvents.Oom
  226. }
  227. }
  228. if stats.Pids != nil {
  229. s.PidsStats = types.PidsStats{
  230. Current: stats.Pids.Current,
  231. Limit: stats.Pids.Limit,
  232. }
  233. }
  234. return s, nil
  235. }
  236. // Resolve Network SandboxID in case the container reuse another container's network stack
  237. func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) {
  238. curr := c
  239. for curr.HostConfig.NetworkMode.IsContainer() {
  240. containerID := curr.HostConfig.NetworkMode.ConnectedContainer()
  241. connected, err := daemon.GetContainer(containerID)
  242. if err != nil {
  243. return "", errors.Wrapf(err, "Could not get container for %s", containerID)
  244. }
  245. curr = connected
  246. }
  247. return curr.NetworkSettings.SandboxID, nil
  248. }
  249. func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) {
  250. sandboxID, err := daemon.getNetworkSandboxID(c)
  251. if err != nil {
  252. return nil, err
  253. }
  254. sb, err := daemon.netController.SandboxByID(sandboxID)
  255. if err != nil {
  256. return nil, err
  257. }
  258. lnstats, err := sb.Statistics()
  259. if err != nil {
  260. return nil, err
  261. }
  262. stats := make(map[string]types.NetworkStats)
  263. // Convert libnetwork nw stats into api stats
  264. for ifName, ifStats := range lnstats {
  265. stats[ifName] = types.NetworkStats{
  266. RxBytes: ifStats.RxBytes,
  267. RxPackets: ifStats.RxPackets,
  268. RxErrors: ifStats.RxErrors,
  269. RxDropped: ifStats.RxDropped,
  270. TxBytes: ifStats.TxBytes,
  271. TxPackets: ifStats.TxPackets,
  272. TxErrors: ifStats.TxErrors,
  273. TxDropped: ifStats.TxDropped,
  274. }
  275. }
  276. return stats, nil
  277. }