用 curl 存取 docker engine api

Docker 提供了 Docker Engine API,讓外部程式可以透過這組 API 跟 docker daemon 溝通或執行操作。

一般來說,dockerd 會開啟一個位於 /var/run/docker.sock 來接收 API 指令,通訊協定則是 HTTP。我們可以用 curl 直接存取。例如這是取得本地所有 image 資料:

$ curl -s --unix-socket /var/run/docker.sock  'http://docker/images/json' | jq .[]
{
  "Containers": -1,
  "Created": 1661938374,
  "Id": "sha256:798e70022408bec464b1c4af78d134f82b628adfb758310665158ba9ef2b60ce",
  "Labels": null,
  "ParentId": "",
  "RepoDigests": null,
  "RepoTags": [
    "my-little-image:latest"
  ],
  "SharedSize": -1,
  "Size": 370902888,
  "VirtualSize": 370902888
}
{
  "Containers": -1,
  "Created": 1661919273,
[下略]

有提供檢視 Container 目前狀態的介面,會透過 chunked encoding 的方式定時回傳 JSON 資料。

$ curl -s --unix-socket /var/run/docker.sock  'http://docker/containers/my_little_container/stats'
{"read":"2022-09-04T19:16:22.181573217Z","preread":"0001-01-01T00:00:00Z","pids_stats":{"current":2,"limit":18446744073709551615},"blkio_stats":{"io_service_bytes_recursive":[{"major":254,"minor":0,"op":"read","value":249856},{"major":254,"minor":0,"op":"write","value":12288}],"io_serviced_recursive":null,"io_queue_recursive":null,"io_service_time_recursive":null,"io_wait_time_recursive":null,"io_merged_recursive":null,"io_time_recursive":null,"sectors_recursive":null},"num_procs":0,"storage_stats":{},"cpu_stats":{"cpu_usage":{"total_usage":220360000,"usage_in_kernelmode":155989000,"usage_in_usermode":64371000},"system_cpu_usage":14033070000000,"online_cpus":4,"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"precpu_stats":{"cpu_usage":{"total_usage":0,"usage_in_kernelmode":0,"usage_in_usermode":0},"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"memory_stats":{"usage":790528,"stats":{"active_anon":0,"active_file":135168,"anon":270336,"anon_thp":0,"file":0,"file_dirty":0,"file_mapped":0,"file_writeback":0,"inactive_anon":270336,"inactive_file":135168,"kernel_stack":49152,"pgactivate":33,"pgdeactivate":0,"pgfault":6039,"pglazyfree":0,"pglazyfreed":0,"pgmajfault":0,"pgrefill":0,"pgscan":0,"pgsteal":0,"shmem":0,"slab":0,"slab_reclaimable":0,"slab_unreclaimable":0,"sock":0,"thp_collapse_alloc":0,"thp_fault_alloc":0,"unevictable":0,"workingset_activate":0,"workingset_nodereclaim":0,"workingset_refault":0},"limit":8217169920},"name":"/my_little_container","id":"a603950db63b00ec4269e115f242b339a56ed52e519bd0db70bbec95d5496b1f","networks":{"eth0":{"rx_bytes":1226,"rx_packets":15,"rx_errors":0,"rx_dropped":0,"tx_bytes":0,"tx_packets":0,"tx_errors":0,"tx_dropped":0}}}
{"read":"2022-09-04T19:16:23.189899509Z","preread":"2022-09-04T19:16:22.181573217Z","pids_stats":{"current":2,"limit":18446744073709551615},"blkio_stats":{"io_service_bytes_recursive":[{"major":254,"minor":0,"op":"read","value":249856},{"major":254,"minor":0,"op":"write","value":12288}],"io_serviced_recursive":null,"io_queue_recursive":null,"io_service_time_recursive":null,"io_wait_time_recursive":null,"io_merged_recursive":null,"io_time_recursive":null,"sectors_recursive":null},"num_procs":0,"storage_stats":{},"cpu_stats":{"cpu_usage":{"total_usage":221831000,"usage_in_kernelmode":157260000,"usage_in_usermode":64570000},"system_cpu_usage":14037110000000,"online_cpus":4,"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"precpu_stats":{"cpu_usage":{"total_usage":220360000,"usage_in_kernelmode":155989000,"usage_in_usermode":64371000},"system_cpu_usage":14033070000000,"online_cpus":4,"throttling_data":{"periods":0,"throttled_periods":0,"throttled_time":0}},"memory_stats":{"usage":790528,"stats":{"active_anon":0,"active_file":135168,"anon":270336,"anon_thp":0,"file":0,"file_dirty":0,"file_mapped":0,"file_writeback":0,"inactive_anon":270336,"inactive_file":135168,"kernel_stack":49152,"pgactivate":33,"pgdeactivate":0,"pgfault":6039,"pglazyfree":0,"pglazyfreed":0,"pgmajfault":0,"pgrefill":0,"pgscan":0,"pgsteal":0,"shmem":0,"slab":0,"slab_reclaimable":0,"slab_unreclaimable":0,"sock":0,"thp_collapse_alloc":0,"thp_fault_alloc":0,"unevictable":0,"workingset_activate":0,"workingset_nodereclaim":0,"workingset_refault":0},"limit":8217169920},"name":"/my_little_container","id":"a603950db63b00ec4269e115f242b339a56ed52e519bd0db70bbec95d5496b1f","networks":{"eth0":{"rx_bytes":1226,"rx_packets":15,"rx_errors":0,"rx_dropped":0,"tx_bytes":0,"tx_packets":0,"tx_errors":0,"tx_dropped":0}}}
[不斷持續]

如果要 pipe 給其他指令(例如 jq)做處理,記得要用 N 參數來關掉 buffering,不然資料不會被即時傳遞給 pipe 後面的指令。

$ curl -sN --unix-socket /var/run/docker.sock  'http://docker/containers/modest_bhaskara/stats'  | jq  -c .cpu_stats.cpu_usage
{"total_usage":264477000,"usage_in_kernelmode":187338000,"usage_in_usermode":77139000}
{"total_usage":264477000,"usage_in_kernelmode":187338000,"usage_in_usermode":77139000}
{"total_usage":265135000,"usage_in_kernelmode":187804000,"usage_in_usermode":77331000}
{"total_usage":265135000,"usage_in_kernelmode":187804000,"usage_in_usermode":77331000}
[不斷持續]