獲取 Docker container 中的資源使用情況(轉)


我在使用docker時也發現了這個問題,看來在docker內執行top/free命令的確有問題

原文:https://zhuanlan.zhihu.com/p/35914450

作者:兩片

Docker 是一種對程序環境進行封裝並實現資源的隔離技術,可秒級啟動,可瞬間啟動千千萬萬個實例,這些優點讓它在出現之初就受到極大的關注,其火爆程度就如今天的深度學習一般。人們拿到了錘子就到處找釘子,什么都想套上 Docker,連生產數據庫都想用 Docker 來運行,少不了碰到很多釘子,磕磕碰碰才形成今天這個比較合理的各個使用場景。

 

在一個完善的系統中,我們總需要知道自己的服務使用了多少CPU,內存資源,想知道磁盤讀寫多少,網絡流量如何。如果在 Docker 容器中執行 top,free 等命令會發現我們能看到CPU所有核的使用情況以及宿主機的內存占用,這並不是我們需要的,我們需要的是這個容器被限制了多少 CPU,內存,當前容器內的進程使用了多少。

 

要明白為何 top,free 顯示的是宿主機的情況,以及如何獲得容器的資源限制需要先理解 Docker 的兩項基礎技術:Namespace 和 cgroup。兩者在 CoolShell 的博客里都已經講解得非常清楚,這里只簡單說明一下。Namespace 解決了環境隔離的問題,它將進程的PID,Network,IPC等和其它進程隔離開來,結合 chroot,讓進程仿佛運行在一個獨占的操作系統中。cgroup 則對進程能夠使用的資源作限制,如在一台48核256G的機器上只讓容器使用2核2G。

 

容器中CPU,內存,磁盤資源都是被 cgroup 限制和統計的,所有信息都放在 /sys/fs/cgroup 這個虛擬文件夾里,在容器里運行 mount 命令可以看到這些掛載記錄

...
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/cpu type cgroup (ro,nosuid,nodev,noexec,relatime,cpu)
cgroup on /sys/fs/cgroup/cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
...

CPU

獲取CPU信息需要用到 cpuset, cpu, cpuacct

  • cpuset 提供一個機制指定 cgroup 可以使用哪些CPU核哪些內存節點(an on-line node that contains memory)
  • cpu 設置CPU的使用配額/份額
  • cpuacct 提供 cgroup CPU 使用時長的統計信息

把上面三個文件夾的文件都看了一遍后,會發現 cgroup 里的提供的信息比物理機上的資源使用狀況信息少很多,這其實是非常合理的,因為兩者本質上就是不同的東西。比如物理機只有10%的時間在運行任務,那剩下90%時間物理機是真正稱得上空閑,而 cgroup 限制組內進程最多只能用50%的CPU,而組內進程只用了10%的CPU,但不能說還有40%或者80%是空閑的,在 cgroup 內根本沒有空閑這個概念,因此也就沒法從中得到常見的 idle 指標。

另外,cgroup 內沒有統計 iowait,中斷,這些也沒法拿到。不過沒關系,細化的指標可以在進程里找,系統信息足夠反映負載狀況即可。這里介紹一下我比較關心的3個指標的獲取方式。

LimitedCores: cgroup 限制可以使用的 CPU 核數

cgroup 有三種方式限制 CPU 的使用

  1. share,對應文件 cpu/cpu.shares,是系統內多個 cgroup 的進程同時運行時他們的CPU使用上限占比,比如只有兩個cgroup: [cgroup1.shares: 1024, cgroup2.shares: 512],那 cgroup1 可以用 2/3 的 CPU。
  2. quota,對應文件 cpu/cpu.cfs_quota_uscpu/cpu.cfs_period_us,表示在每個 period(時間間隔)內 cgroup 可以使用的 CPU 時間。文件里數字的單位是微秒,假設 {quota: 200000, period: 100000},意思是每 100ms 可以使用 200ms CPU 時間,相當於可以使用兩個核。quota 為 -1 的時候表示無限制。
  3. cpuset,對應文件 cpuset/cpuset.cpus, 表示 cgroup 可以用那幾個 CPU,比如 0,3-7,12 可以使用 7 個核。

對於 share,我們沒法知道有多少鄰居以及鄰居的值為多少,所以忽略這個限制。考慮到 cpuset 在生產環境極少使用,同時用 quota 和 cpuset 的就更少了,所以我們的策略是:先看 quota,如果 quota 有限制則返回,否則再看 cpuset。

Usage:cgroup 的 CPU 占用率,占了物理機的多少CPU

上面我們拿到了能用多少個核,自然知道 cgroup 的占用上限,只要知道 cgroup 用了物理機的多少 CPU 就可以知道飽和程度了。要獲取這個首先需要知道一段時間內物理機用了多少CPU時間,然后獲得 cgroup 用了多少CPU時間,最后相除。

  1. 物理機使用的CPU時間從 /proc/stat 里獲取,將里面 cpu 那一行的數字相加並且乘以物理機CPU核數即可得到從開機到現在用的CPU總時間。可以設置一秒的時間間隔,求差即可得到這一秒內用的CPU時間。
  2. cgroup 使用的CPU時間可以從 cpuacct/cpuacct.usage 中獲得,也是求一段時間的差即可。

特別需要注意的是 /proc/stat 里單位是納秒,而 cpuacct.usage 里的是 Clock Tick,一般是 100納秒/tick,准確數字可以通過 getconf CLK_TCK 命令獲得。

Throttled:cgroup 的 CPU 使用被限制的次數

如果被頻繁限制的話,說明很可能分配的CPU不夠用了。可以從 cpu/cpu.stat 的字段獲得。

內存

cgroup 對內存的使用做了比較多的統計,但是內存使用率本身就是一個糊塗賬,因為很多內存是為了提高性能從磁盤映射過來的需要時可以清理掉。cgroup 里的內存信息和定義推薦看 Kernal Doc cgroup-v1 memory

需要額外提一下的是 cache 和 mapped_file,cache 是 page cache memory 也成為 disk cache 是磁盤映射到內存的內存,而 mapped_file 也是一樣意思,但實際中常常會發現 cache 比 mapped_file 大很多,這其實是 mapped_file 是還被進程引用着的,而 cache 則包括曾經被進程用過但現在已經沒有任何進程使用的映射,也就是 unmapped_file。

對於內存,可以重點關注以下幾個指標(沒有特別注明,所有指標都從 memory/memory.stat 中取:

  1. Total: cgroup 被限制可以使用多少內存,可以從文件里的 hierarchical_memory_limit 獲得,但不是所有 cgroup 都限制內存,沒有限制的話會獲得 2^64-1 這樣的值,我們還需要從 /proc/meminfo 中獲得 MemTotal,取兩者最小。
  2. RSS: Resident Set Size 實際物理內存使用量,在 memory/memory.stat 的 rss 只是 anonymous and swap cache memory,文檔里也說了如果要獲得真正的 RSS 還需要加上 mapped_file。
  3. Cached: memory/memory.stat 中的 cache
  4. MappedFile: memory/memory.stat 中的 mapped_file
  5. SwapTotal: 限制的 swap 大小,(hierarchical_memsw_limit - hierarchical_memory_limit) 同樣會遇到內存沒有限制的情況。
  6. SwapUsed: memory/memory.stat 中的 total_swap

磁盤

cgroup 通過 blkio 子系統實現對磁盤讀寫控制。當前有兩種限制磁盤的策略,CFQ(就是各個cgroup平分磁盤使用時間)和 Throttling。

如果是 CFQ,可從 blkio/blkio.io_service_bytes_recursive 獲得各個磁盤的 IO 統計

如果是 Throttling,可從 blkio/blkio.throttle.io_service_bytes 獲得各個磁盤的 IO 統計

文件里會將 IO 分為 Read/Write, Sync/Async,將所有磁盤的 Read/Write相加即可得到磁盤讀寫量。

網絡

網絡統計信息放在 /sys/class/net/<ethX>/statistics 中,在容器中只能看到自己用到的網絡接口,但網絡接口的名字常常不確定,可以先通過命令 ip -o -4 route show to default | awk '{print $5}' 獲得默認網絡接口的名字。

然后就可以從 rx_bytes 獲得接收字節數,從 tx_bytes 獲得發出字節數了。

至此,比較重要的信息都已經拿到,有時還需要提防一下宿主機超賣的情況,有時出了問題並不是自己容器用資源太多,而是資源都被同主機的其它主機占用了,這時可以從 /proc 里拿額外的信息來判斷。

package

發現目前沒有專門針對 container 的指標獲取,於是寫了個 Go 的,放在 github 上,地址:container-metrics

參考鏈接

    1. CoolShell Docker
    2. Kernal Doc cgroup-v1
    3. Docker runmetrics
    4. docker stats命令源碼分析結果
    5. Linux Cgroup系列(05):限制cgroup的CPU使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM