1. 程式人生 > >解惑|你是否為容器監控操碎了心?

解惑|你是否為容器監控操碎了心?

導讀:容器對於物理機和虛擬機器,單從監控上看就不是一個數量級的,但監控又是至關重要的,沒有監控如同閉眼開車。

本次分享邀請數人云運維總監龐錚,本文將從以下幾個方面聊聊容器監控的相關思考:

  1. 容器監控面臨問題-容器設計及運營複雜性的挑戰;
  2. 容器的三種監控收集指標;
  3. 容器效能監控能力把控及報警調查。

容器監控的問題

為什麼要使用Docker

  • 需要一個可靠可擴充套件的基礎設施平臺
  • 大量的流量和使用者
  • 大量的內部服務
  • 不需要改造基礎設施:負載均衡、HTTP服務、日誌系統、資料庫、監控系統等
  • 抽象標準基礎設施服務,如 Haproxy\Mongodb\Es等
  • 提供快速的更新\部署能力

簡介

容器對於物理機和虛擬機器,單從監控上看就不是一個數量級的。但是監控又是至關重要的,如果沒有監控,如同閉著眼開車。先看下傳統監控解決的問題:

Markdown

  • 對於應用層:應用層的效能監控將找到程式碼的瓶頸和錯誤。
  • 對於基礎設施:收集基礎設施層的資源指標,如CPU\MEM。

而使用容器則在於資源層和應用層之間,應用監控和基礎設施監控無法起作用,造成了監控系統的盲點。

容器的設計

  • 原始初衷:安全

容器最開始設計就是為了提供執行時的安全隔離,且沒有虛擬化的資源開銷。容器提供了一種孤立執行軟體的方法,既不是程序也不是主機,而存在於兩者之間。

Markdown

  • 現在

現在使用容器主要有兩個重要原因:
- 提供了一個規模的標準

如果軟體是微服務架構,在 Kubernetes\Mesos 等容器平臺上進行無停機的擴縮和升級等系統操作。

  • 擺脫對於軟體系統的依賴

    一直以來使用 Lib直接編譯成二進位制可執行檔案是最好的,但 Lib 的增加,為了避免記憶體的過度消耗,導致執行時共享 Lib 的出現。為了解決軟體依賴的問題,建立了很多方法如:Apt、Yum、Rvm、V1irtualenv 等,但這會導致拖慢釋出週期,而容器直接解決了這個問題。

容器挑戰:運營的巨大複雜性

可以將每個容器看成一個迷你的主機,但它與主機的操作並不是很相同。

Markdown

上圖顯示了15年的系統演進過程。
- 15年前還是主機天下。
- 7年前引進虛擬化技術,而虛擬化技術帶來的是更好的資源利用率,但對於工程師來說沒有什麼變化。
- 而今天 Datadog 的資料顯示從收到了數十萬的主機資料中,越來越多的主機開始執行容器。
- 2016年開始使用 Docker 的使用者增長率為 40%。

Markdown

  • 執行容器例項主機佔總主機數量的 15%。

Markdown

  • 大型企業使用容器的使用者更多(超過500臺主機叢集)佔 60%,另一方面說明了容器對於規模性和擺脫軟體依賴的對於大型企業的用處更高,數人云的核心業務是幫客戶把已有的系統容器化,再將其應用搬到排程系統上,以及開發一些周邊系統,接觸的客戶也反映了這一點。

Markdown

  • 有 40% 的使用者將容器執行在類似 Mesos 和 Kubernetes 等容器叢集軟體中。

Markdown

  • 使用容器使用者中在第一個月到第十個月的九個月中,容器數量增長了 5 倍,並且資料非常線性。

Markdown

  • 執行應用統計比例。

Markdown

  • 在使用容器的公司中,每個主機執行容器例項為 7 個左右,而 25% 的公司每個主機執行容器例項為14個左右。

  • 容器的生命週期為 2.5 天,在自動化平臺的更短為不到 1 天,主要因為自動修復原因,而非自動平臺則 5.5 天。

Markdown

監控的爆炸性增長

在沒有容器以前,監控數量如:
Markdown

使用容器後公式:假設每個容器收集 50 個度量,再加上應用收集 50 個度量。

系統監控   (容器數量*(容器監控 應用監控))= 每個主機監控數量100         (4 *(50 50))= 500/主機監控項

Markdown

以主機為中心的監控體系

容器作為主機,以主機為中心將有兩個問題無法解決:

  • 容器作為主機,因為容器生命週期非常短暫,所以監控系統會認為一半主機在頻發故障。

  • 如果不監控容器,那麼從主機到應用之間的監控是空白的,產生監控黑洞。

簡化監控體系

Markdown

如圖採用分層監控架構,更符合現有監控體系。主機層和應用層保持不變使用傳統的 Apm 和主機層監控,而容器層使用新的監控模式。

Markdown

如何監控容器

容器類似主機

它有一個迷你主機該有的一切,包含常駐程式、CPU、MEM、IO 和網路資源。但容器不能報告和主機完全相同的 Cgroup 指標。

容器監控資源

cpu

容器 CPU 會給出以下資料而不會有和主機一樣的全資料,如 Idle\Iowait\Irq。

Markdown

記憶體

Markdown

使用記憶體區別
- rss

屬於程序的資料,如 Stacks、Heaps 等。可以被進一步分解為
- 活動記憶體(active_anon)
- 非活動記憶體(inactive_anon)

必要時,非活動記憶體可以被交換到磁碟
- cache

快取儲存器儲存當前儲存在記憶體中的磁碟資料。可以進一步分解為
- 活動記憶體(active_file)
- 非活動記憶體(inactive_file)

必要時,首先回收非活動記憶體
- swap 使用量

io
Markdown

容器對於每個塊設別彙報4個指標,這種情況下,在主機監控層面跟蹤主機佇列和服務時間是個好辦法,如果同塊的佇列和服務時間增長,那麼因同塊 IO 是共享的,所以容器 IO 也受到影響。
- 讀取
- 寫入
- 同步
- 非同步

網路

和普通主機一樣,分為接收和傳送的多個度量資料。

Markdown

如何收集容器指標

容器有三種指標收集方法,但標準並不一樣:

Sysfs 中的 Pseudo-files

  • 預設情況下,通過Sysfs中的偽檔案可以得到容器的度量指標,且不需要 Root 許可權。這個方法是最快最清亮的方法。如果需要監控很多主機,速度可能是一個很重要的指標。但無法用這個方法收集到所有指標,如 IO 和網路指標會受到限制。

收集位置
- 假定偽檔案在作業系統目錄中的 /sys/fs/cgroup 中,某些系統可能在 /cgroup 中。訪問路徑包含容器ID。

CONTAINER_ID=$(docker run [OPTIONS] IMAGE [COMMAND] [ARG...] )

CPU 獲取方法

cd /sys/fs/cgroupu/docker/&& ll

-rw-r--r-- 1 root root 0 5月  31 10:17 cgroup.clone_children
  --w--w--w- 1 root root 0 5月  31 10:17 cgroup.event_control
  -rw-r--r-- 1 root root 0 5月  31 10:17 cgroup.procs
  -r--r--r-- 1 root root 0 5月  31 10:17 cpuacct.stat
  -rw-r--r-- 1 root root 0 5月  31 10:17 cpuacct.usage
  -r--r--r-- 1 root root 0 5月  31 10:17 cpuacct.usage_percpu
  -rw-r--r-- 1 root root 0 5月  31 10:17 cpu.cfs_period_us
  -rw-r--r-- 1 root root 0 5月  31 10:17 cpu.cfs_quota_us
  -rw-r--r-- 1 root root 0 5月  31 10:17 cpu.rt_period_us
  -rw-r--r-- 1 root root 0 5月  31 10:17 cpu.rt_runtime_us
  -rw-r--r-- 1 root root 0 5月  31 10:17 cpu.shares
  -r--r--r-- 1 root root 0 5月  31 10:17 cpu.stat
  -rw-r--r-- 1 root root 0 5月  31 10:17 notify_on_release
  -rw-r--r-- 1 root root 0 5月  31 10:17 tasks
  • CPU 使用(單位是10毫秒)
# cat $CONTAINER_ID/cpuacct.stat     user 46409 #程序佔用  464.09s     system 22162 #系統呼叫佔用 221.62s

CPU 每核使用量
- 可以幫助識別每個核心的壓力

# cat $CONTAINER_ID/cpuacct.usage_percpu         362316789800  #自啟動以來佔用,單位納秒         360108180815
  • 如果想要得到對於伺服器彙總的cpu指標
# cat $CONTAINER_ID/cpuacct.usage
    722473378982
  • CPU 節流
  • 如果對 CPU 使用做了限制,可以從下面的方法中檢視
$ cat /sys/fs/cgroup/cpu/docker/$CONTAINER_ID/cpu.stat
    nr_periods 565 # 已經執行間隔數
    nr_throttled 559 # 被組抑制的次數
    throttled_time 12119585961 # 總使用時間,單位納秒(12.12s)

記憶體獲取方法

ll /sys/fs/cgroup/memory/docker/$CONTAINER_ID/

  # 沒有 total 標籤,不包含於子cgroup組
  cache 2015232
  rss 15654912
  rss_huge 0
  mapped_file 131072
  swap 0
  pgpgin 22623
  pgpgout 18309
  pgfault 27855
  pgmajfault 7
  inactive_anon 12148736
  active_anon 3506176
  inactive_file 2011136
  active_file 4096
  unevictable 0
  hierarchical_memory_limit 9223372036854775807
  hierarchical_memsw_limit 9223372036854775807

  # 有 total 標籤,包含於子cgroup組
  total_cache 2015232
  total_rss 15654912
  total_rss_huge 0
  total_mapped_file 131072
  total_swap 0
  total_pgpgin 22623
  total_pgpgout 18309
  total_pgfault 27855
  total_pgmajfault 7
  total_inactive_anon 12148736
  total_active_anon 3506176
  total_inactive_file 2011136
  total_active_file 4096
  total_unevictable 0

可以通過特定命令直接獲取一些指標:

# 總實體記憶體佔用 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

  # cgroup 記憶體限制,單位為位元組 
  $ cat /sys/fs/cgroup/memory/docker/$CONTAINER_ID/memory.limit_in_bytes
  注意如果最終返回的是一個很長的數值代表容器例項並沒有限制,如果想增加限制
  $ docker run -m 500M IMAGE [COMMAND] [ARG...]

IO

ll /sys/fs/cgroup/blkio/docker/$CONTAINER_ID

  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_merged
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_merged_recursive
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_queued
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_queued_recursive
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_service_bytes
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_service_bytes_recursive
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_serviced
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_serviced_recursive
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_service_time
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_service_time_recursive
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_wait_time
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.io_wait_time_recursive
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.leaf_weight
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.leaf_weight_device
  --w------- 1 root root 0 5月  31 10:17 blkio.reset_stats
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.sectors
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.sectors_recursive
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.throttle.io_service_bytes
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.throttle.io_serviced
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.throttle.read_bps_device
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.throttle.read_iops_device
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.throttle.write_bps_device
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.throttle.write_iops_device
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.time
  -r--r--r-- 1 root root 0 5月  31 10:17 blkio.time_recursive
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.weight
  -rw-r--r-- 1 root root 0 5月  31 10:17 blkio.weight_device
  -rw-r--r-- 1 root root 0 5月  31 10:17 cgroup.clone_children
  --w--w--w- 1 root root 0 5月  31 10:17 cgroup.event_control
  -rw-r--r-- 1 root root 0 5月  31 10:17 cgroup.procs
  -rw-r--r-- 1 root root 0 5月  31 10:17 notify_on_release
  -rw-r--r-- 1 root root 0 5月  31 10:17 tasks

根據系統不同可能會有更多的指標檔案,然而大部分的檔案返回值是零。這種情況下通常還有兩個可以工作的檔案。

  • blkio.throttle.io_service_bytes #io 操作位元組,實際操作而非限制,前面兩個用冒號分割的數字是-主裝置id:次要裝置Id。
8:0 Read 2080768
  8:0 Write 0
  8:0 Sync 0
  8:0 Async 2080768
  8:0 Total 2080768
  253:0 Read 2080768
  253:0 Write 0
  253:0 Sync 0
  253:0 Async 2080768
  253:0 Total 2080768
  Total 4161536
  • blkio.throttle.io_serviced #io 操作次數,實際操作而非限制。
   8:0 Read 226
  8:0 Write 0
  8:0 Sync 0
  8:0 Async 226
  8:0 Total 226
  253:0 Read 226
  253:0 Write 0
  253:0 Sync 0
  253:0 Async 226
  253:0 Total 226
  Total 452

想檢視裝置之間的關係可以使用:

# lsblk
  NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
  sda               8:0    0   50G  0 disk
  ├─sda1            8:1    0  500M  0 part /boot
  ├─sda2            8:2    0 29.5G  0 part
  │ ├─centos-root 253:0    0 46.5G  0 lvm  /
  │ └─centos-swap 253:1    0    3G  0 lvm  [SWAP]
  └─sda3            8:3    0   20G  0 part
     └─centos-root 253:0    0 46.5G  0 lvm  /

網路

網路從 1.6.1版本以後才支援,和以上的路徑有所不同,獲取使用容器Pid獲取,注意Host模式獲取的是主機網路資料,所以 host 模式無法從容器資料統計網路資料。

$ CONTAINER_PID=`docker inspect -f '{{ .State.Pid }}' $CONTAINER_ID`
  $ cat /proc/$CONTAINER_PID/net/dev 

  Inter-|   Receive                                                |  Transmit
  face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
  eth0:    9655      90    0    0    0     0          0         0    31435      78    0    0    0     0       0          0
  lo:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0
  • cli 的 stats

使用 docker stats 會不斷的接收監控指標,1.9.0 以後指標包含磁碟io

  • cpu stats

cpu 佔用百分比,多個例項佔用cpu會根據分配進行佔用峰值,如果設定強制規約,那麼cpu只能佔設定的數值,比如20%
- 記憶體 stats

如果沒有明確記憶體限制,則限制為主機記憶體限制。如果主機上還有其他使用記憶體程序,那麼會在到達限制前耗盡記憶體。
- io stats

1.9.0 版本後支援,顯示總讀寫位元組
- 網路 stats

顯示總進/出流量位元組
- api

和 docker stats 命令一樣,但是提供更多的細節。守護程序監聽 unix:///var/run/docker.sock,只允許本地連線。使用 nc 呼叫方法:

echo "" | nc -U /var/run/docker.sock 例子 echo -ne "GET /containers/$CONTAINER_ID/stats HTTP/1.1\r\n\r\n" | sudo nc -U /var/run/docker.sock

Markdown

如何監控Docker的效能

監控都需要有什麼能力

  • 從每個 Docker 容器收集CPU、記憶體、IO、網路指標,並可以通過人和標籤或者標籤聚合做成指標,用來提供高解析度資源指標。
  • 微服務體系結構中,服務可以直接通訊或者使用佇列進行通訊,沒有中央負載均衡很難進行計量,通過標籤聚合能力可以很好的解決這個問題。
  • 需要通過圖形得之哪些服務超載,哪些服務導致其他服務失敗,哪些服務流量太多
  • 還可以監控其他非 Docker 服務,如 Haproxy、MongoDB、Es等等。

Markdown

Markdown

報警和調查

內部網路流量變化作為最重要的指標來觸發報警而不會引起報警洪水。因此聚合和分解服務級別流量可見性是至關重要的。此外,即使在測量交叉異常閥值前,報警系統也可以提醒網路流量變化。而其餘的資源指標是用來調查排錯的。

數人云容器監控實踐

Markdown

Markdown

Markdown

參考

QA

Q:有對Docker本身做什麼監控麼?

A:可以認為 Docker 監控是類主機監控,只不過是縮小版,基本上分為4部分:CPU、記憶體、磁碟、網路。

Q:使用的整套監控工具是哪些?容器CPU記憶體網路 如何監控?容器事件比如起停如何監控。

A:整套工具數人云使用的是Cadvisor + Prometheus + Grafana ,當然中間的元件是可以替換的,但基本上圍繞著採集、儲存計算、展現來做。採集也可以使用自己的,比如文章說的自己寫代理去拿。容器的監控資料當然靠採集程式了。起停這個一般通過監控Docker的事件來實現,採集工具也能收。

Q:分享的監控圖片,有資料的,是使用什麼監控工具達成的?

A:這個分兩種,一種是靠底層的繪圖引擎,將資料從儲存裡讀出來自己繪製,一種就是用類Grafana的程式。

Q:如果用Zabbix監控,是否需要定義容器的的歷史資料保留時間和趨勢資料儲存週期,我設定的時歷史資料保留7天,趨勢資料14天,這樣是否合理?

A:我認為Zabbix 是上一代監控體系,或者以主機為中心的監控體系,如果是容器監控,建議還是考慮時序類的監控體系,比如Influxdb\Prometheus等,Zabbix還可以沿用作為主機的,只是Docker單獨分離出來,這樣基礎建設可以複用。

Q:建不建議通過Pod中放一個監控容器來監控應用容器,比如Zabbix客戶端的監控容器在Pod中,如果這麼做 優缺點哪些?

A:Pod應該是Kubernetes的概念,和容器其實關係不大,這個Kubernetes自己應該會提供資料,具體不是很清楚。但是Abbix還是建議保留在主機層面使用,除非大改,否則即使靠拆分資料庫什麼的解決,未來維護和效能也是運維大坑。

Q:Cadvisor Heapster 和 Prometheus 哪種好用一些,各自優缺點有哪些。

A: Heapster不熟悉, Prometheus很好,Google個人的開源專案,都是Google套路,唯獨儲存是個問題,這塊還需要看他們未來如何處理,現在單機儲存雖然效能上還可以,但是擴充套件能力比較差。

Q:監控工具推薦哪個?對於容器生命週期短,有何策略應對?如何實現快速監控策略?

A:監控工具推薦剛才已經說了,可以參考數人云的方案然後自己摸索出適合自己的。至於容器生命週期短的問題,這個不就是容器設計嘛,很正常,多起幾個相同的服務頂上。

Q:容器的一大特點是IP或者ID資訊變化頻繁,這就會導致時間序列資料庫儲存的監控資料量增長和vm相比大上不少,這塊有什麼應對方案嗎?嘗試過固定ID的,但是效果不佳。

A:這塊確實沒有什麼好辦法,不過可以換個角度,可以將底層的例項抽象一個維度,比如起了1個服務10個容器,把容器編號0-9,對應掛掉的容器,新啟動繼承這個編號。從時序上用這個作為標記,就能看比較直觀的顯示了。此功能數人云Swan (歡迎Star&Fork)實現了,可以考慮。

Q:容器的安全如何做監控?

A:這個問題問的好,現在比較通用的監控基本上走的是兩條路,一個是監控效能,一個是監控業務,安全層面監控,到現在我覺得還是要靠網路層來監控比較靠譜。

Q:Docker啟動Kafka叢集的問題,有沒有控制記憶體方面的經驗呢?

A:Kafka叢集,效能監控的話,可以沿用原來的Kafka叢集監控軟體,當然如果想做資料匯聚,也可以使用開源軟體將資料匯聚到一個數據儲存,然後在匯聚出來。關於Docker記憶體的超出被殺問題,這個主要是看自身對於容器記憶體設定的容忍度問題,這裡可以把容器當成一個機器,看到底給這個機器插多少記憶體合適。

Q:Promethues有沒有做高可用?

A:如果儲存高可用的話,可以考慮使用兩臺Prometheus同時抓,這樣資料完全一樣,也沒啥壓力。

分享人龐錚,數人云運維總監。15 年以上運維工作經驗。就職過巨集碁戲谷、第三波、SQUARE ENIX CO, LTD 等。2015年加入數人云,從事數人云平臺運維管理,在容器技術及SRE實踐方面有深入研究。