摘要:寫在前面最近在研究集群的監控,為了徹底弄清楚,簡單看了一點源碼。如何調用上述的監控功能的監控采用了組件。隨后,會在監控機器的性能時,分為磁盤使用情況的監控和磁盤讀寫情況的監控。
寫在前面
最近在研究docker集群(kubernetes)的監控,為了徹底弄清楚,簡單看了一點源碼。這里分享一下我學到的東西。
docker api: stats首先是docker的api,stats的具體使用場景如:
http://$dockerip:2375/containers/$containerid/stats
可以獲取docker機器上某一個容器的狀態,該請求的response會持續的寫響應報文回來(每秒一次)
http://$dockerip:2375/containers/$containerid/stats?stream=false
參數stream默認是true,設為false后,response只寫一次。
docker中該api對應的操作,就相當于docker stats $CID 這條命令,它調用到了ContainerStats()方法(位于docker項目目錄的/daemon/stats.go 第19行),函數中啟動了一個收集器:
daemon.SubscribeToContainerStats(name)
收集器定時寫數據到update變量中。
然后啟動一個協程讀數據,獲取數據的途徑包括:
update := v.(*execdriver.ResourceStats)
和
nwStats, err := daemon.getNetworkStats(name)
可以看到網絡狀態是另外讀的,而cpu,內存狀態在哪讀呢?我們一步步跳轉,看到在這里:
/daemon/execdriver/driver_linux.go 112行:
func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error)
在這個函數里我們看得到,其實所謂的獲取容器狀態,就是讀文件而已。我們舉一個已經在docker運行的容器來說:
cat /run/docker/execdriver/native/$containerID/state.json
這里面記錄了一個cgroup_paths字段,他的值是一個路徑,通過cstats, err := mgr.GetStats()程序才真正拿到了監控數據,如cpu的狀態信息,存儲在一個如下的路徑中:
cd /sys/fs/cgroup/cpu,cpuacct/system.slice/docker-9b479ceaf3bef1caee2c37dfdc982a968144700a2c42991b238b938788a8a91f.scope
又比如內存的大致信息存在:
cat /sys/fs/cgroup/memory/system.slice/docker-9b479ceaf3bef1caee2c37dfdc982a968144700a2c42991b238b938788a8a91f.scope/memory.stat
具體里面放了什么數據,大家就自己看咯。
還剩下一個數據,也是我們討論的重點:網絡IO。
我們回到/daemon/stats.go:
看看函數getNetworkStats(name string):
每個docker容器創建后都會又docker創建一個網卡,網卡名以veth開頭,后面是一串隨機生成的十六進制碼。
我們只要找到該網卡,然后,像上文的cpu,內存狀態獲取的方法一樣,去找文件就行了。
然而這個網卡名似乎是存在內存中,至少我沒有看到docker將這個網卡名存到別的地方。
代碼中:
nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID)
可以看出獲取了網卡(network),爾后有一個Statistics()方法,我們繼續跟蹤會發現,docker調用了一個組件包:
github.com/docker/libcontainer
其中有statistics方法,并執行了一個cat的命令(該組件包的/sandbox/interface_linux.go 第174行):
data, err := exec.Command("cat", netStatsFile).Output()
是的,又是讀文件。
這次讀的文件在:/sys/class/net/目錄下,
我們進入該目錄可以看到有許多子目錄,其中就包括了docker啟動容器時生成的網卡。
個人猜測:docker在內存中保存了容器到網卡的映射關系。通過這個映射關系可以找到網卡的統計數據(數據由內核生成,記錄在機器的文件中)
我們可以看到如下的數據:
將這些數據統一起來,就是docker stats 這條命令干的事。
kubernetes如何調用上述的監控功能kubernetes的監控采用了cAdvisor組件。因為kubernetes中記錄了容器的信息(但是沒有記錄容器-網卡的映射關系),所以運行在節點上的cAdvisor不需要通過docker stats去獲取容器的cpu和內存使用數據。
而網絡IO數據呢?
我們知道k8s部署運行一個容器是會先生成一個pause容器。
是的,網絡IO都記錄在pause容器中。這里大家可以在自己的k8s環境中驗證。
所以只要我們獲取某容器對應的pause容器的containerID,我們就可以用如上的方式去抓到網絡IO。
因為cAdvisor并不是為k8s專門設計的,不會特地在獲取網絡IO時去遍歷查找容器對應的pause容器。所以當前的cAdvisor沒有辦法獲取容器的網絡IO。
所以如果在使用k8s集群,想要加入網絡IO監控功能的時候,可以從k8s自帶的cAdvisor入手。
cAdvisor上面講到cAdvisor,那么cAdvisor是如何獲取網絡的IO的呢?
首先,在k8s(1.0.6版本)的/pkg/kubelet/cadvisor/cadvisor_linux.go中51行,New(port int)方法,這是kubelet調用cAdvisor的入口。實例化一個cAdvisorClient,并執行他的Start()方法,我們可以進入cAdvisor項目中找到該方法(github.com/google/cadvisor/manager/manager.go 195行)。
我們看到有一句
err := docker.Register(self, self.fsInfo)
先mark之。繼續看:
glog.Infof("Starting recovery of all containers") err = self.detectSubcontainers("/")
這里程序程序檢查所有在運行的容器,同內存中記錄的容器作比較,有新的容器就新建一個相關的處理機制:一個Container的結構(有減少的,就刪除),然后執行cont.Start() (github.com/google/cadvisor/manager/manager.go 766行)
持續追蹤我們就可以知道每秒它收集一次監控信息,收集方法即
stats, statsErr := c.handler.GetStats()
(/cadvisor/manager/container.go 401行)
handler是一個接口。我們得知道當時是如何給他賦值的。這里不贅述了,我們直接找關鍵的方法:
func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error)
(cadvisor/container/docker/handler.go 第305行)
它又調用了:
func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info.ContainerStats, error) (cadvisor/container/libcontainer/helpers.go 第77行)
這里就是真正獲取監控信息的地方,它引入了libcontainer包中的同名方法,
最終是導入了"github.com/docker/libcontainer/cgroups"這個第三方包中的方法。是不是很熟悉?對就是上文中提到的docker stats獲取網絡IO用到的包。我們到這個包里找到函數:
(/libcontainer/cgroups/fs/apply_raw.go 第148行)
func (m *Manager) GetStats() (*cgroups.Stats, error) { m.mu.Lock() defer m.mu.Unlock() stats := cgroups.NewStats() for name, path := range m.Paths { //m.Paths中包括了cpu,memory,network等字段,根據這些字段找到相應的目錄 sys, ok := subsystems[name] if !ok || !cgroups.PathExists(path) { continue } //讀目錄里的文件獲取監控信息 if err := sys.GetStats(path, stats); err != nil { return nil, err } } return stats, nil }
我們可以看到libcontainer/cgroups/fs目錄下有cpu.go,memory.go等文件,每一個文件中都有一個集成了GetStats()的結構,我們可以獲取到任何數據。
我們再回到cadvisor項目中cadvisor/container/libcontainer/helpers.go 第77行,
往下看:
// TODO(rjnagal): Use networking stats directly from libcontainer. stats.Network.Interfaces = make([]info.InterfaceStats, len(networkInterfaces)) for i := range networkInterfaces { interfaceStats, err := sysinfo.GetNetworkStats(networkInterfaces[i]) if err != nil { return stats, err } stats.Network.Interfaces[i] = interfaceStats }
這里官方還用了別的手段再去找network的數據,雖然不知道是處于什么理由,也不管這里是去找哪個系統文件看狀態,但是這樣依然是拿不到數據的。因為根本沒有找到容器對應的網卡。這里找網卡的方法在cadvisor/container/docker/handler.go 第311行:
var networkInterfaces []string if len(config.Networks) > 0 { // ContainerStats only reports stat for one network device. // TODO(vmarmol): Handle multiple physical network devices. for _, n := range config.Networks { // Take the first non-loopback. if n.Type != "loopback" { networkInterfaces = []string{n.HostInterfaceName} break } } } stats, err := containerLibcontainer.GetStats(self.cgroupManager, networkInterfaces)
很顯然這里是要改進的(在k8s的環境下)。
注:隨著版本更新,新版本的cAdvisor采用github.com/opencontainers包。詳見:github.com/opencontainers/runc/libcontainer/container_linux.go line 151:
另外,cadvisor獲取網絡監控數據的正確途徑應該是:
監控pause容器,在其Pid對應的/proc/$pid/net/dev文件中即可得到容器內部網絡監控數據
cadvisor在主函數初始化時,通過一些init方法在內存中構建了機器上的文件系統的結構(有哪些磁盤、分別掛載目錄是啥、maj和min號是啥)。隨后,cadvisor會在監控機器的性能時,分為磁盤使用情況的監控和磁盤讀寫情況的監控。
磁盤使用情況的監控cadvisor會執行syscall.Statfs方法,通過linux系統調用,獲得磁盤的總大小、可用空間、inodes總數和使用數等信息
磁盤讀寫情況的監控通過讀取機器的/proc/diskstats文件內容,形如:
cat /proc/diskstats 254 0 vda 48560 0 994278 64304 2275295 70286 18962312 6814364 0 1205480 6877464 254 1 vda1 48177 0 991034 64008 1865714 70286 18962304 6777592 0 1170880 6840740 254 16 vdb 700 0 4955 284 0 0 0 0 0 284 284
通過這些數據計算出各個存儲設備的讀寫次數、讀寫速度。(這里文件內容釋義和計算方式可以參見http://www.udpwork.com/item/1...)
暴露接口當我們調用cadvisor的接口:
/api/v2.1/machinestats 時,就能從返回的json結構中找到filesystem字段,包含了磁盤讀寫性能和磁盤使用情況的監控數據
上文提到,cadvisor在初始化階段就生成了機器文件系統的抽象結構,但是這個結構不會動態檢查和更新,當機器上動態掛載了一個數據盤(比如使用ceph rbd做pod的pv),cadvisor不會感知,這個新掛的盤的監控數據也無法被感知。
目前社區暫時沒有對這個功能進行優化
應用說了這么多,其實給大家在實現k8s容器網絡IO監控這個需求時提供一點想法:
1.從cadvisor入手,找到對應pause容器的containerID,那么就可以用libcontainer包去獲取到數據(也即用docker stats的方式)。
2.從docker入手,創建容器時記錄網卡信息,能否提供一個接口,根據容器名找到對應網卡名,那么cadvisor只要通過這個docker API得到網卡名,就可以自己讀文件獲取網絡IO。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/26484.html
摘要:寫在前面最近在研究集群的監控,為了徹底弄清楚,簡單看了一點源碼。如何調用上述的監控功能的監控采用了組件。隨后,會在監控機器的性能時,分為磁盤使用情況的監控和磁盤讀寫情況的監控。 寫在前面 最近在研究docker集群(kubernetes)的監控,為了徹底弄清楚,簡單看了一點源碼。這里分享一下我學到的東西。 docker api: stats 首先是docker的api,stats的具體...
摘要:馬拉松會匹配每個和提供的資源,然后通過將任務下發下去。對外暴露的就是負載均衡的某個服務,后面自動將流量轉發到某個容器的端口上。還有一直辦法是用內網的,這個會維護現有的容器列表端口,并且返回任意一個的端口,頁實現了負載均衡和服務發現功能。 演講嘉賓 數人云COO 謝樂冰 在德國工作十年,回國后加入惠普電信運營商部門,擁有多年項目經驗和創業公司工作經驗。在數人云負責產品售前和運營,專注行...
摘要:基于年底或年初沒有推廣的現狀,唯品會部門目前已經做了兩年的時間。唯品會現狀唯品會目前線上有一千多個域,每個域之間相互的依賴比較復雜,每次的部署發布困難。這是唯品會的架構,主要包含持續集成和持續部署。 數人云上海&深圳兩地容器之Mesos/K8S/Swarm三國演義的嘉賓精彩實錄第三更來啦。唯品會是數人云Meetup的老朋友,去年曾做過RPC服務框架和Mesos容器化的分享。本次分享中,...
摘要:去年換工作后,開始真正在生產環境中接觸容器與。今天想先談談,我理解的容器是什么,以及為什么它們能火起來。一個容器鏡像的實質就是程序進程加所有運行時環境及配置依賴的集合。這里再談談我理解的。而,就是目前的容器編排的平臺的事實標準了。 去年換工作后,開始真正在生產環境中接觸容器與Kubernetes。邊惡補相關知識的同時,也想把學到的內容和自己的理解整理出來。學習的途徑包括k8s官方文檔...
摘要:去年換工作后,開始真正在生產環境中接觸容器與。今天想先談談,我理解的容器是什么,以及為什么它們能火起來。一個容器鏡像的實質就是程序進程加所有運行時環境及配置依賴的集合。這里再談談我理解的。而,就是目前的容器編排的平臺的事實標準了。 去年換工作后,開始真正在生產環境中接觸容器與Kubernetes。邊惡補相關知識的同時,也想把學到的內容和自己的理解整理出來。學習的途徑包括k8s官方文檔...
閱讀 1660·2021-11-12 10:35
閱讀 1611·2021-08-03 14:02
閱讀 2655·2019-08-30 15:55
閱讀 2024·2019-08-30 15:54
閱讀 735·2019-08-30 14:01
閱讀 2421·2019-08-29 17:07
閱讀 2246·2019-08-26 18:37
閱讀 3028·2019-08-26 16:51