歡迎光臨
每天分享高質量文章

聊聊Docker監控那點事兒

現在有很多的開源的Docker監控方案的實現,我們可以很容易的搭建一套監控系統出來;但是如果你有定製化的需求,則需要自己去實現;那麼我們該怎麼實現呢?需要監控哪些指標呢?這些指標又是什麼含義呢?應該怎樣去收集呢?本文我們來一起探討。
這裡我不會介紹整個監控系統的架構,也不會去分別介紹儲存、告警、展示、通知等等這些模組的實現,因為現在的開源的監控系統基本都包括這些,只會把重點放在Docker的指標上,所以內容會有些乾。接觸時間也不算太長,如果有錯誤的地方懇請指正。

監控範圍

取用一下下麵這張圖來說明:

我們把需要監控的物件分為三層,分別是應用層、系統層和虛擬那一層;這裡我們主要關註放在在系統層(CPU、memory、IO等等),以及虛擬層(可能包括容器的OOM,執行時間等等),所以我們主要探討一下對於這方面的監控。

怎樣監控

一般來說,對於Docker的監控,有三種最主要的方式去獲取效能指標,分別是:CGroup、Docke命令列以及Docker的API。
對於CGroup方式,就是透過CGroup的檔案來讀取這些指標,一般來說在/sys/fs/cgroup目錄下麵,例如CPU相關的指標/sys/fs/cgroup/cpuacct/docker/$CONTAINER_ID/cpuacct.stat;這裡的CONTAINER_ID就是容器的ID。
對於Docker命令列,其實就是透過docker stats來獲取:
$ docker stats $CONTAINER_ID
CONTAINER       CPU %     MEM USAGE/LIMIT     MEM %     NET I/O             BLOCK I/O
ecb37227ac84    0.12%     71.53 MiB/490 MiB   14.60%    900.2 MB/275.5 MB   266.8 MB/872.7 MB
和上面的命令列一樣,Docker API也能實時採集上面的這些指標,有兩種方式來開啟開啟Docker的API功能,分別是新增這樣的引數DOCKER_OPTS=”-H=unix:///var/run/docker.sock -H=0.0.0.0:6732″來分別開啟unix sock和http,其中unix sock方式是預設的。
$ echo -ne "GET /containers/$CONTAINER_ID/stats HTTP/1.1\r\n\r\n" | sudo nc -U /var/run/docker.sock
如果開放了介面,也可以直接透過介面訪問,傳回是一個很長的json,裡麵包含了CPU、memory等方面的指標。
那麼對這三種採集方式來說,哪一種是最合適的呢,從排除法來看,命令列的方式獲取的指標值比較有限,只能拿到基本的CPU、memory使用狀況,更詳細的沒有,所以這個方案只適合做一個粗略的監控,有對於有些時候的排障來說,可能並不夠用;再來看看Docker API的方式,這種方式需要每次傳送http請求,而且有多少container就發多少次,這個開銷也是不小的,所以這個方案最簡單但是我們仍然沒有考慮;顯而易見,最後我們選擇了從CGroup檔案的方式來獲取,下麵我們就來細細說一下需要監控哪些指標,以及怎麼來採集。

系統監控

CPU

相關的效能指標
  • user CPU:CPU使用者行程的時間百分比

  • system CPU:CPU執行系統呼叫的時間百分比

  • CPU util:總的CPU使用率

  • throttling (count):容器的CPU被限制的次數

  • throttling (time):容器的CPU使用率被限制的總時間


採集方式

$ cat /sys/fs/cgroup/cpuacct/docker/$CONTAINER_ID/cpuacct.stat
> user 2441  # docker開啟後執行使用者行程的時間
> system 985 # docker開啟後執行系統呼叫的時間
在x86系統中,上面的時間是按10毫秒增加,所以上面的CPU在使用者行程上消耗24.41秒,在系統呼叫上消耗9.85秒。
$ cat /sys/fs/cgroup/cpuacct/docker/$CONTAINER_ID/cpuacct.usage_percpu
> 44154016900  # docker開啟後單使用CPU的納秒(44.15s)
上面是單個CPU使用的時間,如果容器使用的是多核的CPU,那麼下麵可以獲取所有CPU的總的時間:
$ cat /sys/fs/cgroup/cpuacct/docker/$CONTAINER_ID/cpuacct.usage
> 44154016900 # 所有CPU使用的總納秒(44.15s)
對於Throttled,可以在cpu.stat中獲取:
$ cat /sys/fs/cgroup/cpu/docker/$CONTAINER_ID/cpu.stat
> nr_periods 565 # 已經過去的執行過的period數
> nr_throttled 559 # CPU被限制的次數
> throttled_time 11219582971 # CPU被限制的總時間,納秒為單位 (11.22 seconds)

指標解讀
我們知道在Docker中對CPU的限制方式有幾種,可以透過–cpu-shares,–cpu-period和–cpu-quota,–cpuset-cpus來配置,具體細節這裡不贅述。現在使用最多的方式是–cpu-period和–cpu-quota結合的方式,這時候CPU使用率的上限由兩者共同決定,比如說A容器配置的–cpu-period=100000 –cpu-quota=50000,那麼A容器就可以最多使用50%個CPU資源,如果配置的–cpu-quota=200000,那就可以使用200%個CPU資源。所有對採集到的CPU used的絕對值沒有意義,還需要參考上限。還是這個例子–cpu-period=100000 –cpu-quota=50000,如果容器試圖在0.1秒內使用超過0.05秒,則throttled就會觸發,所有throttled的count和time是衡量CPU是否達到瓶頸的最直觀指標。
另外,不像傳統的host,Docker不需要採集CPU的nice,idle,iowait和irq時間。
記憶體

相關效能指標
  • Memory:容器的記憶體使用

  • RSS:行程除了快取之外的記憶體消耗(包括棧和堆記憶體,等等)

  • Cache memory:記憶體中的磁碟資料快取

  • Swap:swap使用總量


採集方式

下麵的命令會打印出一大堆的關於記憶體的資訊,可能比你需要的多的多:
$ cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.stat
   cache
   rss
   mapped_file
   writeback
   swap
   pgpgin
   pgpgout
   pgfault
   pgmajfault
   inactive_anon
   active_anon
   inactive_file
   active_file
   unevictable
   hierarchical_memory_limit
   hierarchical_memsw_limit
   total_cache
   total_rss
   total_rss_huge
   total_mapped_file
   total_writeback
   total_swap
   total_pgpgin
   total_pgpgout
   total_pgfault
   total_pgmajfault
   total_inactive_anon
   total_active_anon
   total_inactive_file
   total_active_file
   total_unevictable
雖然上面得到的很多,但是通常我們更關心的核心指標在/sys/fs/cgroup/memory/docker/$CONTAINER_ID/的其他目錄中:
# 總的記憶體使用: cached + rss 
$ cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.usage_in_bytes
# 總的記憶體使用 + swap的使用
$ cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.memsw.usage_in_bytes
# 記憶體使用達到限制的次數
$ cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.failcnt
# 容器被限制使用的記憶體值
$ cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.limit_in_bytes

指標解讀
使用的記憶體可以分解為:
RSS:RSS本身可以進一步分解為活動和非活動記憶體(active_anon和inactive_anon)。必要時,非活動的RSS記憶體被交換到磁碟。
Cache:反映快取在當前記憶體中的磁碟上的資料。快取可以進一步分解為活動和非活動記憶體(active_file,inactive_file)。 當系統需要記憶體時,可以首先回收非活動記憶體。
雖然cache這部分是可以多個容器共享的,但是在Docker中CGroup判斷memory.failcnt是否加一,是根據總的記憶體(RSS+Cache)是否達到memory.limit來決定。所以如果監控到容器的記憶體使用量一直上升,需要分清是RSS還是Cache導致的增加,如果是RSS的需要看下應用是否有記憶體洩露,如果是Cache部分,需要看最後是否能釋放。
mem failcnt發生不一定會導致容器OOM,因為有些記憶體被Cache用到了,OS清理掉一些Cache就沒問題了。作為開發者,需要調查下, 給應用劃分的Docker記憶體上限是否合理。因為Cache被清掉就意味著後續有檔案讀取操作的時候,需要將資料塊從磁碟page in到Cache裡,如果應用的服務效能比較依賴磁碟上的資料讀取效能,就需要關註下。
另外,在調查效能或穩定性問題時可能有價值的其他指標包括page faults,可以表示分段錯誤或從磁碟而不是記憶體中獲取資料(分別為pgfault和pgmajfault)。
I/O

相關效能指標
  • I/O serviced:I/O操作的次數

  • I/O service bytes:讀寫的byte數

採集方式
在目錄/sys/fs/cgroup/blkio/docker/$CONTAINER_ID/下有IO相關的指標檔案,由於系統的差異,下麵大部分檔案裡面的值都是0,在這種情況下,通常還有兩個檔案可以工作:blkio.throttle.io_service_bytes和blkio.throttle.io_serviced,它們分別記錄了總I/O位元組和操作。註意別被檔案名誤導,這裡並不是IO throttle的指標。
這些檔案裡前兩個數字是主要:次要裝置ID,例如blkio.throttle.io_service_bytes的輸出示例:
253:0 Read 13750272
253:0 Write 180224
253:0 Sync 180224
253:0 Async 13750272
253:0 Total 13930496

指標解讀
塊I/O是共享的,所以容器的I/O是沒有作限制的,也就沒有類似於throttle這樣的指標,那麼除了上面提到的容器特定的I/O指標之外,跟蹤主機的佇列和服務時間也是不錯的選擇。如果容器使用的塊裝置上的佇列長度或服務時間不斷增加,容器的I/O將受到影響。
網路

相關效能指標
  • Bytes:網路流量(包括接收和傳送)

  • Packets:網路包的個數(包括接收和傳送)

  • Error(receive):接收錯誤的資料包個數

  • Error(transmit):傳輸錯誤的資料包個數

  • Dropped:丟棄的包個數(包括接收和傳送)


採集方式
與上面不同的是,網路相關的指標不在CGroup的檔案夾下,而是採用平常行程的網路指標採集方式(畢竟Docker也是一個行程),在/proc/下獲取:
$ CONTAINER_PID=`docker inspect -f '{{ .State.Pid }}' $CONTAINER_ID`
$ cat /proc/$CONTAINER_PID/net/dev    
|   Receive                                                |  Transmit
face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
 eth0:     1296     16    0    0    0     0          0         0      816      10    0    0    0     0       0          0
   lo:        0      0    0    0    0     0          0         0        0       0    0    0    0     0       0          0

指標解讀
同樣,網路沒有throttle這樣的值,衡量時,需要結合網絡卡的兆數來看。
連線數

相關效能指標

  • established:建立的連線

  • close:close狀態的連線

  • close_wait:close_wait狀態的連線

  • time_wait:time_wait狀態的連線

  • ……

採集方式

和上面的網路的採集差不多,我們也是透過Docker的pid的方式從/proc/下麵去取,具體的檔案為/proc/net/tcp和/proc/net/tcp6(如果沒有用tcp6可以忽略之)。
 $ cat net/tcp
 sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                    
  0: 0100007F:274C 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 10547 1 ffff880426cb0000 100 0 0 10 0                    
  1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 10616 1 ffff880426cb0780 100 0 0 10 0                    
  2: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 11239 1 ffff880426cb1680 100 0 0 10 0                    
  3: 00000000:2742 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 19286 1 ffff880427900780 100 0 0 10 0                    
  4: B42A020A:2742 D014020A:C683 06 00000000:00000000 03:00000CF2 00000000     0        0 0 3 ffff8804216c7d00                                      
  5: B42A020A:2742 D014020A:879D 06 00000000:00000000 03:00001259 00000000     0        0 0 3 ffff8804216c7e00                                      
  6: B42A020A:638C 3602030A:0885 01 00000000:00000000 00:00000000 00000000     0        0 64399466 1 ffff8804256fe180 20 4 30 10 -1                
  7: B42A020A:2742 D014020A:B04A 06 00000000:00000000 03:000016ED 00000000     0        0 0 3 ffff8804216c6500                                      
  8: B42A020A:2742 D014020A:CC0F 06 00000000:00000000 03:00000D61 00000000     0        0 0 3 ffff8804216c6800                                      
  9: B42A020A:688D 4C02030A:276A 01 00000000:00000000 00:00000000 00000000     0        0 64348606 1 ffff880426cb5280 20 4 30 10 12                
 10: B42A020A:2742 D014020A:AE80 06 00000000:00000000 03:00001688 00000000     0        0 0 3 ffff8804216c7000                                      
 11: B42A020A:DC39 3502030A:0885 01 00000000:00000000 00:00000000 00000000     0        0 63592428 2 ffff8804256fb480 20 4 30 10 -1                
 12: B42A020A:851F 4B02030A:276A 08 00000000:00000001 00:00000000 00000000     0        0 12895659 1 ffff8804220b4b00 20 4 28 10 7
其中關註第四列,st就是連線的狀態,用下麵的數字來表示,具體到和連線狀態的對映關係:
    "01": "established",
   "02": "syn_sent",
   "03": "syn_recv",
   "04": "fin_wait1",
   "05": "fin_wait2",
   "06": "time_wait",
   "07": "close",
   "08": "close_wait",
   "09": "last_ack",
   "0A": "listen",
   "0B": "closing",
   "0C": "unknown",
對照對映關係將他們加起來就可以得到對應狀態的連線數了。
磁碟

相關效能指標
  • used:磁碟使用量

  • used percent:磁碟使用率

採集方式
對於磁碟的採集,我們沒有找到一個簡便的方法,現在的做法是侵入到容器內部去採集,類似這樣的命令:
`docker exec -i $CONTAINER_PID "df"|grep -v "tmpfs"`
我們來看看直接df之後的結果:
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/mapper/docker-253:0-1049953-53ec5aa5f4a669b3a26b19cc2675f5537ba59014419e97324f703cb050152cc6
                     10475520     44496  10431024   0% /
tmpfs                 98894428         0  98894428   0% /dev
tmpfs                 98894428         0  98894428   0% /sys/fs/cgroup
/dev/mapper/VolGroup01-lv_root
                    279403540  38459756 226727800  15% /
/dev/mapper/VolGroup00-lv_root  279403540
                               38459756 226727800
                               17%
                               /
tmpfs                 98894428      2384 98892044
                     782 99% /
/dev/mapper/VolGroup00-lv_root
                    279403540  38459756 226727800  15% /
/dev/mapper/VolGroup00-lv_root
                    279403540  38459756 226727800  15% /etc/hostname
/dev/mapper/VolGroup00-lv_root
                    279403540  38459756 226727800  15% /etc/hosts
shm                      65536         0     65536   0% /dev/shm
tmpfs                 98894428         0  98894428   0% /proc/kcore
tmpfs                 98894428         0  98894428   0% /proc/timer_stats
tmpfs                 98894428         0  98894428   0% /proc/sched_debug
我們去掉tmpts之後,對剩下的解析,Mounted on在根目錄下(/)的device mapper就是我們要的那一行,然後就可以分別得到磁碟的used,available和used percent值了。

指標解讀
其實在正確的Docker使用中是不會需要採集磁碟容量的,因為我們對檔案的寫入應該持久化在宿主機的磁碟上。而且這種採集對資源的消耗很大,如果有需要也要酌情設定採集頻率。

指標解讀
對於這些不同狀態的連線,可以按需去採集。

事件監控

除了上面系統層的監控,我們有時候還需要對容器的事件進行監控,這些事件包括:
attach
commit
copy
create
destroy
detach
die
exec_create
exec_detach
exec_start
export
health_status
kill
oom
pause
rename
resize
restart
start
stop
top
unpause
update
我們可以透過docker events來獲取到這些事件,該命令支援一個起始時間–since,也支援按不同的條件過濾,包括容器ID,事件型別等等。
$ docker events --filter 'event=stop'
2018-03-06T00:40:22.880175420+08:00 container stop 0fdb...ff37 (image=alpine:latest, name=test)
2018-03-06T00:41:17.888104182+08:00 container stop 2a8f...4e78 (image=alpine, name=kickass_brattain)
每一行就是一個事件,當然我們不會收集這些所有的事件,但一般會包括OOM,stop,destroy這些。

現成的監控方案

當然如果有現成的方案,我們也不需要去重覆地造輪子,簡單列舉幾個常用的開源收集吧:
  • cAdvisor:Google開發的容器監控指標採集,還支援聚合和一些資料處理;

  • Telegraf:Influxdata開發的收集Agent,這是一個通用的採集Agent,當然也支援Docker,另外該公司還提供了一整套監控方案叫做TICK,也歡迎大家去踩坑;

  • Prometheus:現在最火的Cloud方面的監控,而且是一整套的解決方案,包括告警、儲存等等;

除此之外還有一些收費的方案,例如Datadog、Sensu、Scout等等也提供了另外的選擇。

Q&A;

Q:既然當前已經存在很多指標監控方案,你們是基於什麼考慮要自己寫的?
A:因為現有的方案都是獨立的系統,我們的監控物件可不止容器,而且排查問題的時候可能還需要看宿主機的監控、網路裝置的監控等等,我們需要把容器的整合進來;另外用開源的方案不好做定製化。

Q:容器發生OOM時,計算的記憶體是包括Cache+RSS嗎?生產環境經常會發生業務容器OOM,可以從那幾個方面排查問題,並解決?
A:是的,排查問題當然要基於監控,看是否是使用記憶體一直不釋放,我們遇到的OOM一大部分都是應用本身有記憶體洩漏;這在使用虛擬機器的時候沒有暴露出來,在用Docker時候資源給得更少了就暴露出來了。

Q:是否可以將Pod下所有容器彙總的指標作為Pod的效能指標呢?
A:對於Kubernetes來說就更容易一些了,可以透過kubelet API server直接來獲取的。

Q:當遇到偶發的CPU throttled情況,是否意味著已經開始出現效能瓶頸?
A:不是,CPU throttled是在一個period裡面CPU的時間片到了限制觸發的,如果是job型別的應用,是會偶發cpu throttled,這時候可能不需要關心。

Q:現在針對容器的監控方案特別多,也基本上很完善。比如Telegraf採集、普羅米修斯採集等。想問下,你們那邊現在的告警是怎麼做的?
A:告警現在我們更多地配置在應用上,這樣反應地最直觀。如果要在容器層面做的話,建議對持續的CPU throttling和mem failcnt做告警。

Q:請問指標的儲存用的是什麼資料庫?
A:之前用的Elasticsearch,現在用的InfluxDB,自己包裝實現了一套叢集。

Q:對於無狀態的Java微服務容器,是否有必要進行監控?
A:這個最好在應用層去監控,但是在排查問題的時候還是需要容器層面的指標資料。
Kubernetes 實戰培訓

本次培訓內容包括:Docker容器的原理與基本操作;容器網路與儲存解析;Kubernetes的架構與設計理念詳解;Kubernetes的資源物件使用說明;Kubernetes 中的開放介面CRI、CNI、CSI解析;Kubernetes監控、網路、日誌管理;容器應用的開發流程詳解等,點選識別下方二維碼加微信好友瞭解具體培訓內容

3月23日開始上課,最後5個名額,點選閱讀原文連結即可報名。
贊(0)

分享創造快樂