# 第15章 日志和監控
在虛擬化時代來臨時,很多企業迫切需要一種新手段來監控它們的新的虛擬環境。業內的監控工具也因此需要作出更新,以支持監控宿主機和虛擬機兩種不同的環境。如今,相同的問題在部署容器化的基礎設施時再次發生。Docker環境下的日志收集和監控乍聽起來可能會讓人頭疼不已,但是如果可以打破現有環境的束縛,并且使用新興的工具的話,便可以讓讀者從這一新的虛擬環境中真正受益。
運行在容器里的系統和應用與如今部署在虛擬服務器上的容器也不盡相同。對許多人來說,監控和記錄虛擬化系統上的另外的抽象層可能會是一件非常難以想象的事情。無論環境有多復雜、多抽象,我們始終需要建立一套監控和日志系統來了解應用的健康狀態及性能,并滿足各種審計的要求。事實上,對于企業來說似乎其最大的問題在于,針對這一抽象層現有的工具缺乏足夠的可視化能力。接下來,我們重點關注日志和監控這兩部分,只要做到這兩點用戶就可以讓Docker容器在自己的環境里成功地運行起來。
需要查看Docker容器里的日志時,有幾種不同的方案可供選擇。這些方案取決于你的容器所處的狀態,是正在運行還是已銷毀/已刪除。當考慮的是正在運行中的容器的日志方案時,有如下方案可供選擇:
- Docker原生提供的日志模塊支持;
- 通過連接正在運行的容器來提取日志;
- 將日志導出到Docker宿主機;
- 將日志發送到集中式的日志平臺;
- 將日志裝載到其他Docker容器里。
對于一個已經銷毀的/已刪除的容器來說,用戶的選擇非常有限。由于容器默認是臨時性的,因此用戶可能會在它們終止服務時丟失相應的數據。日志數據的丟失可能會不利于對已發生故障的應用的故障排查及定位。為了能夠在容器終止時將其日志保存下來,用戶可能需要將這些日志從容器導出到宿主機或者線上系統里。
Docker會自動捕獲運行在Docker容器里的進程的標準輸出和標準出錯信息,并且將它們重定向到容器的根文件系統上的一個指定的日志路徑里,宿主機上擁有`root`權限的用戶可以直接訪問這一容器日志。可以通過運行以下命令來獲取宿主機上容器默認的日志路徑:
`docker inspect -f '{{.LogPath}}' <container_id>`因此,運行在Docker容器里的進程若以前臺模式運行會讓用戶能夠很方便地利用Docker原生提供的日志功能來記錄其輸出信息。
Docker的1.6版本還引入了一些開箱即用的日志引擎。默認情況下,Docker采用的是`json-file`,它會將應用的日志以`json`格式存儲到前面提到的容器根文件系統的日志路徑下。這一點非常人性化,因為許多的集中式日志方案都需要一個`json`格式的數據流來方便建立搜索索引。
可以通過運行Docker客戶端內置的`logs`命令來查看容器日志的內容。請注意,運行`logs`命令將會輸出從容器啟動開始所有的日志,所以最好將它重定向到其他的類似于`more`或`less`這樣的查看工具:
`docker logs redis`如上所見,Docker命令行客戶端會自動將json編碼的文件解碼并且以文本的格式輸出到界面。
如果用戶正在檢查一個正在運行的容器發生的問題,那么一般便是運行上面這條命令。該命令會告訴Docker從宿主機記錄日志的地方找出一個名為`redis`的容器的日志。在默認情況下,如果沒有變動,容器的日志會存放到保存容器本身的系統文件的容器根文件系統里。切記在生產環境下運行容器時,如果應用日志的輸出量很大,一定要對日志配置輪換或將日志文件存放在一個可擴展的磁盤存儲里。如果運行下面這條命令,容器的日志會被源源不斷地寫到文件系統里,因此用戶將可以看到一個持續不斷的日志輸出:
`docker logs redis --follow (or -f for short)`Docker的`logs`命令還支持一個額外的參數以顯示文件的時間戳并限制logs命令可以查看的最多行數。當需要調試時間敏感的問題時可以使用--`timestamps`(或者-`t`)參數來顯示日志里每行的具體時間戳。也可以使用`--tail`加上一個數值,如`--tail 10,`來顯示容器標準輸出和標準錯誤輸出的最后10行內容。
Docker原生的`logs`命令目前所能提供的就是這些。例如,用戶正在運行`redis`數據庫服務器,它會將日志輸出到該容器的`/logs/redis.log`里。然而,由于Docker只會捕獲`stdout`和`stderr`的輸出,因此執行`docker logs redis`將無法顯示該文件的輸出內容。我們將在本節的后面部分解釋該如何處理這些日志。
Docker的第二個日志驅動的選擇便是`syslog`。一旦使用它,Docker會將所有的應用日志發送到運行在宿主機上的`syslog`。當然,用戶必須在啟動容器時明確指定該參數。在`redis`例子里,想要運行的命令應該是這樣的:
`docker run -d --log-driver=syslog redis`如果現在去查看宿主機上`syslog`的內容,用戶會看到我們剛啟動的`redis`容器生成的日志內容。由于許多開源的日志傳輸工具都是用來解析和傳輸`syslog`里的日志數據的,所以`syslog`是一個非常方便的日志方案。用戶可以輕松地以傳統的方式復用這些工具,通過將它們直接運行在宿主機上,然后指定宿主機的`syslog`作為它們的日志源,如此一來用戶會感覺自己好像回到了沒有Docker的時代。
最后,Docker還支持參數為`none`的日志選項,也就是說它會忽略應用程序生成的所有日志。同樣地,和`syslog`驅動類似,必須在啟動容器時明確指定該參數才能生效。雖然我們不建議在生產環境中運行的應用采取這樣的不捕獲任何日志的方式,但是它在某些場景下的確是一個非常方便的選擇,例如一些容器會生成大量的無用日志然后可能導致宿主機上的文件系統出現真實的I/O災難。
通常,我們不建議在容器或者宿主機操作系統里查看日志文件。然而,在排障時,用戶可能需要根據自己的排查順序查看一些正在運行的容器里的額外日志信息。接下來,為了繼續調查,用戶可能需要連接到一個正在運行的容器上。為了連接一個正在運行的容器,用戶需要做一些額外的、繁瑣的且重復的操作來實現安全訪問(`root`和`ssh`)。但是,如果的確需要這樣做,用戶可以使用Docker原生提供的`attach`命令。下面是一個使用該命令的實際用例:
`docker attach redis`連接容器即是連接到該容器里正在運行的進程的shell上。當連接到容器內部時,由于大多數的鏡像都是非常輕量地在運行單個進程,因此用戶可以在容器操作系統里執行的命令往往也會非常有限。
Docker在1.3版本推出了`docker exec`子命令,它允許用戶在正在運行的Docker容器里執行任何命令,包括用戶最喜歡的shell。這種方式比將`TTY`連接到正在運行的容器要優雅得多。用戶可以通過執行如下命令輕松來運行`bash`(如果容器的鏡像里裝了`bash`的話),隨后便可以在容器文件系統里查看日志或任何其他文件:
`docker exec -it redis /bin/bash`上述命令將會在運行`redis`服務的容器里再運行一個新進程。一旦進入到容器里,用戶便可以看到一些可能不會被記錄到`stderr`或者`stdout`的額外的日志文件。這可以用來幫助調試故障的應用或根據日志文件的內容來評估性能。然而,這并不是一個適用于生產系統的可擴展方案。生產系統一般需要的是一個集中式的日志平臺來查看匯總的日志流。
如今,企業通常運行或使用的都是集中式的日志系統。在傳統的服務器環境里,人們通常會使用各種客戶端軟件(如`syslog`)來讀取文件系統上存放日志的目錄或路徑,然后將它們發送到一個集中平臺上。為了利用集中式日志系統,用戶可能需要將容器里的日志文件轉移到宿主系統上。實際上,將Docker日志存放在宿主系統上并不是一件難事。將日志記錄到宿主系統里常用的有兩種方法。用戶可以在`Dockerfile`鏡像里使用`VOLUME`指令或使用`docker run -v`參數將容器里的一個文件路徑掛載到宿主文件系統上的一個位置上。
當需要將日志存放在宿主系統上時,Docker官方推薦的做法是采用`docker run -v volume`選項來掛載卷。使用`-v`參數便可以靈活地指定文件系統重定向輸出的位置。默認情況下,`Dockerfile`里的`VOLUME`指令使用的是容器里的路徑,其對應的一般是一個根文件系統的位置。如果用戶的根文件系統已經滿了,這就會導致用戶的宿主機出現一些大問題,而且它很難被清理干凈。例如,運行一個`redis`容器并且將它配置為日志存放到`/redislogs/redis.log`,然后將日志文件存放在宿主系統中。
如果`redis`配置文件被設置為將日志放到`/redislogs/redis.log`文件里,用戶可以很輕松地在`Dockerfile`里通過`VOLUME`命令(即`VOLUME /redislogs)`將日志歸檔。在鏡像里使用`VOLUME`指令會導致Docker運行時在容器里的`/redislogs`位置上創建一個新的掛載點,然后將內容復制到宿主系統上新創建的卷目錄。然而,宿主系統上的卷目錄并不是一個眾所周知的位置(一般地,它可能會是`/var/lib/docker/volumes/7d3b93bf75d687530a159ae77e61b035424dd0d52605224ff3e3c6479e603824/redislogs/redis.log`這樣的路徑)。在這種情況下便很難再在宿主機上去配置一個集中式的日志收集客戶端來收集數據卷里的文件內容。這也正是我們推薦使用`docker run -v`的方式來將日志持久化的原因。
通過在啟動容器時使用`docker run –v`選項,用戶可以從本質上將容器里的目錄或者文件路徑重定向到宿主文件系統上的某個位置上。一般將容器里的日志目錄“重定向”到宿主系統的日志位置的做法是使用如下指令:`docker run -v /logs:/apps/logs`。
當用戶把容器的日志重定向到宿主機上的某個集中的地方時,如`/logs/apps`,用戶便可以輕松地配置一個日志采集客戶端,從`/logs/apps`里收集所有的日志,然后將它們發送到集中的日志系統里。這也適用于在單臺宿主機上運行多個容器實例的場景。假設用戶在同一臺宿主機上運行了`nginx`、`redis`以及一個`worker`容器的實例。通過執行如下`docker`命令用戶將可以在`/apps`目錄里得到3個日志文件:
```
docker run -v /logs/apps/nginx/nginx.log nginx
docker run -v /logs/apps/redis/redis.log redis
docker run -v /logs/apps/worker/worker.log worker
```
運行在宿主機上的日志采集客戶端,如果它被配置為從`/logs/apps/**/*.log`讀取日志,便能夠收集所有的3個日志文件然后將它們發送出去。以上便是一個簡單的整合用戶的容器及日志采集客戶端的方法,這樣一來,開發/運維團隊便都能輕松記住該到哪去查找日志文件。
如果要將日志發送到一個集中式的日志系統里,除了將它導出到宿主機似乎別無他法。實際上,用戶無需把日志文件重定向到宿主系統。取而代之的是可以采取其他的辦法將日志導出,例如,用戶可以在容器里的應用進程一側運行一個日志采集客戶端,然后將收集到的日志通過網絡直接發送出去。如果運維團隊支持在一個容器里運行多個進程,用戶可以把容器簡單地看成是一臺服務器,然后在容器啟動的時候安裝和配置該客戶端。
這里以標準的`syslog`為例。運行在容器里的應用會把日志傳到`/logs/apps`里,然后安裝和配置一個`syslog`守護進程,從該目錄位置下讀取任何存在的日志信息。隨后,它被配置成可以將日志發送到一個集中式的日志服務器里。一旦該目錄下有新文件加進來,`syslog`守護進程將會如預期那樣讀取和傳輸對應的日志數據。
這一模式的確也很有價值,然而它存在一些明顯的短板。這些短板包括,若在一個容器里運行多個進程,這可能會導致容器在進程利用方面變得更加笨重,而且它在服務啟動腳本里增加了一些額外的復雜度,并使得應用隔離的便利性蕩然無存。在這個模式下,如果一個系統要運行10個容器,用戶就得在宿主機上運行10個`syslog`服務。如果這些`syslog`客戶端能夠被整合到一個單一的進程的話性能也許會更好。
將日志導出到在線系統的另一種辦法是直接從應用中獲取。假設在容器里運行一個Java應用,該Java代碼被配置成向代碼函數中寫入日志文件。該函數還可以設置成將日志發送到遠端服務器上的一個集中式隊列里(如[Kafka](http://kafka.apache.org)),之后這些日志數據會交由其他系統處理。這一方案使得開發人員能夠很輕松地配置一個可擴展到任意集中式系統的日志模型。然而,這一模式只適用于應用在遠程日志采集函數方面均采用同樣一段Java代碼的場景。如果另外有一個容器運行的是一段Python腳本,那么便意味著就得再寫一個Python實現的遠程日志函數。
這最后一個從容器里獲取日志的方案在業界正逐漸變得流行起來。通過在系統上的其他容器之間使用共享的數據卷,用戶便可以在其他正在運行的容器里提取自己所需的數據(在這個例子里即日志)。我們喜歡將這個從其他容器一側掛載日志的做法比作在一輛摩托車上安裝了一個側座。在一個系統上有多個容器的場景下采用這一方案可以節省一些不必要的處理時間。例如,如果系統上的每個容器都通過運行一個服務的方式來發送它的日志,那么由于每個容器里運行的是冗余的服務,因此會浪費一些不必要的資源。用戶可以把各個容器里所有的日志放到一個單獨的容器里面,然后將其集中起來并發送到一個集中式的日志系統。由于將所有的事務都整合到了一個統一的日志采集服務,因此用戶便可以節省一大筆的資源。
我們不妨用兩個容器來舉一個簡單的例子。一個容器運行一個`redis`服務,并且將日志事件發送到它的容器中的/logs/redis.log位置,對應的即是一個數據卷/logs。如果我們在`docker run`執行時運行標志`--volumes-from`來啟動另外一個容器,用戶就能在這個新容器里提取/logs數據卷的數據。當然,這樣做可能在概念上不容易理解,所以接下來我們會列舉一些實際的命令來幫助用戶更好地理解這一過程。
首先,我們會啟動一個新的名為`redis`的容器。隨后,我們會使用`-v`參數創建一個新的數據卷,由于我們之前通過`--name`參數指定了該容器的名稱,因此其他容器可以很方便地從它這里提取數據。
`docker run -d -v /logs --name redis registry/redis`然后,我們可以啟動一個日志收集容器,使用`--volumes-from`將第一個容器創建的/logs數據卷掛載到容器里面。注意別忘了帶上`redis`的名字。
`docker run -d --volumes-from redis --name log_collector registry/logcollector`這使得一個運行了大量Docker容器的系統具備了訪問共享資源的能力。這類案例在像Mesosphere這樣的網格式部署場景中尤為常見。
Docker提供了多種不同的方案來采集和查看日志。我們建議的做法是選擇一個最合適自己的基礎設施的、易于維護且可擴展的方案。一般公司最終選擇的也會是那套在他們的環境里工作最穩定的模式。那么,現在讓我們一起來看看該如何監控Docker容器吧。
對于Docker容器的監控歸根到底是用戶要怎樣監控運行在容器里的服務以及采集哪些指標數據。用戶所采取的Docker容器監控的手段實際上取決于現有的工具以及監控的形式。我們建議選擇那款團隊喜歡并且用的舒心的工具。大公司里一般可能采用的會是一些業界成熟的監控工具,如Nagios,而當需要監控一項像Docker這樣的新興技術時可能就需要發揮自己的創造力了。創業型公司則往往一開始使用的就是最新潮、最棒的技術,而它們一般都已經加入了對Docker的支持,如New Relic、Datadog或Sysdig。無論哪款工具,在絕大部分的場景下一般都能夠工作得很棒,因此這只關乎團隊究竟偏向于哪個以及它本身的成本收益比。
從某種意義上來講,監控和從容器里獲取日志的形式非常相似。用戶完全可以通過像使用一個init文件來管理運行在系統上的服務那樣去監控一個正在運行中的容器。另外一種選擇是去監控Docker的子系統(使用`docker ps`或`docker stats`),只要容器仍然在運行,它就可以被認為是健康的。有些公司甚至把監控客戶端像傳統服務器那樣放到了容器里面。用戶可以通過訪問應用層的如`http://<service>/health`這樣的端點服務或一些其他措施來監控容器的健康狀況。最后,如果用戶采用的是Etsy工程方案,用戶可能會對系統里的所有事情做些相應的權衡,最終可能會選擇通過在宿主機上、Docker服務以及容器本身的健康狀況方面采集相應的指標數據來完成監控。
許多已經開始使用Docker容器的企業就等于已經切換到了一個面向服務的架構(SOA),這可能就需要建立一套全新的監控標準。我們不打算深入探討如何監控SOA環境,但是讀者應該意識到這里面的區別。SOA里的服務通常都是以臨時模式運行的。這就意味著,如果服務運行在容器里的話可能會掛掉,但是,這并沒有什么太大問題。一個服務可以使用運行相同服務的多個容器以負載均衡的方式承載,然后如果它們停止服務的話會自動重啟,并自動去重新注冊自己的服務發現,隨后它們便可以恢復并且這中間沒有任何的宕機損失。一個負載均衡的臨時系統通常監控的重點會是應用服務本身的健康狀況而不是單臺服務器上的單個服務。絕大多數情況下這便意味著服務需要一個外部系統來監控其應用層的狀態或監控它服務端口的可用性,這可能就需要利用一些新的監控工具才能辦到。
監控服務器、服務及應用的方式多種多樣。下面我們通過一款名為Datadog的工具來監控一個簡單的環境,然后運行一個簡單的Python腳本來監控HTTP端點服務(如圖15-1所示)。Datadog提供了一個基于主機的客戶端來監控服務器,然后上面運行一個statsd的后端服務,用來完成應用級別的監控。在這個例子里,我們將會運行一臺單獨的服務器,然后在上面運行一個單個的Docker容器。我們會監控宿主機的CPU、內存以及磁盤IO,并且會從Docker守護進程里收集一些統計數據。緊接著,我們會使用一個運行在其他系統上的Python腳本來監控應用的HTTP端點服務,它會把一些指標數據發送到StatsD后端。最后,我們將端對端地監控一個在Docker容器里運行的系統。

圖15-1
大多數服務器所處的環境里都會部署一個監控系統,它會負責CPU、內存以及磁盤IO這些傳統的監控指標的處理和告警。當它們監控的是宿主機上的這些指標時,大部分監控系統也許還能正常運轉。但是如今應用服務是運行在Docker里的一個單獨進程,使用一些如`ps aux|grep nagios`或`service nagios status`之類的傳統命令來監控它們不太容易。這時候,用戶就需要查看自己的監控系統是否能監控Docker進程的狀態或者能否調用它提供的API來提取健康狀況的數據。如果它還沒有集成Docker的話,要是用戶覺得有必要,那么可以考慮自行采取一些措施來監控它。
在這個案例場景里,使用Datadog來監控宿主機會是一件非常簡單的事情。Datadog的客戶端和絕大多數傳統的監控系統客戶端一樣,它會安裝到本地然后將采集到的監控指標數據發送到一個集中式的系統里。這就跟Nagios、Zabbix或Hyperic監控客戶端做的事情沒什么兩樣。安裝Datadog客戶端很簡單,用戶只需要照著Datadog的安裝命令就能把它們的宿主機客戶端(在這里即Ubuntu)快速地配置起來。這里我們使用的命令是`DD_API_KEY=cdfadffdada9a... bash -c "$(curl -L https://raw. githubusercontent.com/DataDog/dd-agent/master/packaging/datadog-agent/source/install_agent.sh)"`。在客戶端安裝成功后,它便會立即開始將所收集到的數據發送到Datadog的基礎設施中。幾分鐘后,宿主機便能夠將用戶所有的CPU、內存和磁盤IO的具體指標收集到它們的監控數據瀏覽器和匯總看板里。一旦數據匯總到了系統里,我們便可以據此設置告警郵件,或如果某些員工需要還可以定制一些告警頁面。圖15-2所示是采集后的數據展示的一個簡單看板。

圖15-2
在這種情況下,用戶最多只能看到大多數基礎設施團隊針對應用環境想要監控內容的三分之一。在這個場景里,我們只能看到主機層面的監控指標,如CPU、內存和磁盤IO。我們無法知道系統里單個進程或服務的使用情況。最可能出現的場景便是當CPU占用率過高時,用戶想知道大部分資源具體是被哪個容器,進程或者服務占用的。問題的核心在于我們需要采集系統上運行的每個Docker容器的性能指標,然后據此制定相應的策略,以減少平均維護時間(MTTR)。就目前來說,我們對于系統上運行的Docker服務以及正在運行的容器的性能或者健康狀況幾乎一無所知。因此,接下來讓我們一起來看看如何監控Docker服務以及如何才能采集到相應的指標數據。
Docker提供了多種方式來監控容器以及從系統中采集數據。首先。我們將介紹Docker原生提供的幾個不同的命令。隨后,我們將介紹如何使用Datadog來監控Docker守護進程以及怎樣采集容器的監控指標。默認情況下,Datadog不會通過它們的API去采集Docker的監控指標。用戶需要到它們的網站上開啟集成支持。一旦開啟該集成,Datadog便能從Docker中采集相應的監控數據,這樣一來用戶便可以實現大規模的容器的監控。在我們講解例子之前不妨先使用`docker ps`看一下容器當前的狀態。
當在Docker容器里運行服務時,用戶將無法再依賴以前的一些眾所周知的工具來監控容器的進程。由于服務或進程是以容器的形式在宿主系統中運行,因此除了Docker服務本身,用戶將無法查看它們具體的健康狀況。以前像`ps`這樣用來檢查一個進程是否運行的工具,如今針對運行在Docker下的服務則被抽象需要運行`docker ps`或者`docker info`才能獲取到當前作為容器運行的服務的具體信息。
`docker ps`是一個簡單的、返回當前所運行容器的狀態的命令。要運行它也非常簡單。用戶只需要在其安裝了Docker的系統上輸入`docker ps`即可。用戶可能無法看到容器相關的所有信息,但是可以得到絕大部分的重要指標以及容器當前所處的狀態信息。有些企業甚至只需要運行`docker ps`便能滿足他們所有的需求。它允許用戶查看當前一個特定容器是否在運行,如果沒有的話可以將它重啟。下面這個簡單案例即是運行一段bash腳本來監控一個特定的Redis容器的運行狀態。如果該容器沒有處于運行狀態就會去重啟它:
```
#!/bin/bash
# Check for running Redis container, if its not running then
start it
STATE=$(docker inspect redis | jq ".[0].State.Running")
if [[ "$STATE" != "true" ]]; then
docker run -p 6379:6379 --name redis -v /logs/apps/redis/:/
logs -d hub.docker.com/redis
fi
```
這是一個非常簡單和初級的展示如何監控容器運行狀態的用例,而這也是許多企業目前的做法。絕大多數生產環境的編排組件采取類似方法來維護和自動化監控系統的健康狀況。當用戶所處的環境里Docker的部署已經達到了一定的規模,并且在任意給定的時間內均有大量的容器運行,那么用戶很可能需要一款更棒的工具和編排手段。如果想構建自己的容器監控組件(很多公司已經這么做了),Docker除了使用Bash腳本外還提供了其他方式來獲取容器的狀態,如使用[Docker Python API docker-py](https://github.com/docker/docker-py)或使用Docker的遠程[API](https://docs.docker.com/reference/api/docker_remote_api/)。用戶甚至可以使用Docker的集群管理系統,如[Shipyard](https://github.com/shipyard/shipyard),來查看多個系統里的不同容器的狀態。
現在,讀者應該了解了該如何獲取系統上容器的運行狀態。那么,接下來我們需要做的便是獲取容器的性能指標。就以之前系統出現過的高CPU占用率的情況為例。我們需要找出系統里是哪個容器占用了大部分的CPU資源。在這個場景下,用戶需要了解Docker提供的另外一個命令。Docker自1.5以上的版本提供了一個很棒的統計API及命令行工具,即`docker stats`,它可以獲取容器的實時性能指標,如CPU、內存、網絡IO、磁盤IO和塊IO。在該API開放之前,它實際上是cgroup使用的一些性能指標的原始導出數據。大部分監控公司采納了這個新的API,并嘗試將其用于從Docker系統中獲取更多的監控指標。用戶也可以借此命令推出自己的監控系統。`docker stats`命令為用戶提供了大量的容器運行時的信息,因此用戶可以據此構建整合了所需信息的更復雜的看板。
使用`docker ps`和`docker stats`命令已經讓用戶在監控容器的基礎狀態的道路上前進了一大步。然而,當用戶將容器從單個系統擴展到更大規模時,用戶還需要一個工具來把采集的監控指標聚合起來并擴展到一個易于使用的集中看板上。讓我們再來回顧下使用Datadog的這個案例,看看如何在大規模場景下用它來完成監控。
Datadog是通過使用Docker守護進程的套接字來訪問Docker API從而實現與Docker的集成。通過訪問該API,Datadog能夠監控當前宿主系統上運行了多少容器,Docker的CPU和內存占用情況,Docker容器狀態更改的事件流,cgroup一些不常見的指標數據,以及Docker鏡像的統計信息等。它會為用戶提供非常豐富的指標信息,展示當前在容器上運行的容器化服務的健康狀態和性能的具體情況。再次重申一遍,默認情況下,Datadog不會使用Docker API來監控,因此用戶需要到他們的官方網站上去手動開啟這一功能。在開啟該功能之后,用戶還需要將它加到系統上的Docker組才能讓Datadog客戶端有權限訪問Docker。一旦準備工作就緒,Datadog就會開始從系統里采集監控數據,并自動將這些數據發送到基礎設施里。如此一來,用戶便能設置自身團隊所需的特定Docker監控數據的看板和告警。依照[Datadog提供的具體例子](https://www.datadoghq.com/blog/monitor-docker-datadog/),用戶可以使用他們的Metrics Explorer查看對應的監控指標(如圖15-3所示)。

圖15-3
通過Datadog與Docker的集成,我們得以了解單個宿主機或多主機上有多少容器在運行,容器狀態變更的事件流,以及特定鏡像的一些指標信息。運維和開發團隊如今均可以使用這些信息來實現他們容器級的服務狀態及性能的監控。如果有大的變動,我們可以給運維團隊發出告警并通知他們這些問題。通過Datadog提供的Docker監控集成支持,用戶可以獲取一些普通主機級監控客戶端不能提供的抽象指標參數和信息。如今,我們能夠采集基于宿主機和Docker兩方面的監控數據,這樣一來整個系統具備了更高的可視化程度。這里面唯一漏掉的便是運行在容器里的應用程序本身的健康狀況。現在,讓我們一起來看看整個案例的最后一個部分吧。
用戶可以通過多種途徑來監控運行在容器里的應用的健康狀態。由于容器本身是一個完整打包的操作系統,因此可以在容器里面運行任意類型的應用程序。用戶可以把正在運行的容器簡單地看做是一臺普通的服務器,然后在鏡像創建過程中在容器里安裝一個基于主機層面的監控客戶端。但是,我們強烈建議將容器盡可能地保持在比較輕量的水準,最好只運行單個進程。雖然我們不建議一個容器里運行多個進程,但是有些公司的確成功地這樣做了,并且它的好處在于有時候可以很方便地與用戶現有的監控設施集成。在這個例子里,系統上的容器會運行一個簡單的HTTP服務并且對外開放一個/health的端點。為了確認運行在容器里的應用的健康狀態是好的,我們可以通過多種手段對它進行監控。用戶可以使用StatsD或其他自定義監控框架,如bash或者Python腳本,來監控它。我們先來看看StatsD的方案。
StatsD是一款輕量級的監控工具,它是由Etsy創建的用來簡化監控數據的統計和聚合,可以通過網上的一篇[博客](https://codeascraft.com/2011/02/15/measure-anything-measure-everything/)了解到更多細節。Statsd因它輕量和大規模場景下良好的擴展性的特點,已然成為一款非常受歡迎的應用層監控服務工具。針對一個運行在容器里的應用的健康狀況的監控,其中一種方案是調用Statsd的庫將應用的指標數據發送出去。例如,有個運行在容器里的Web應用,它會獲取POST請求,并據此處理一些信用卡數據。為了追蹤究竟收到了多少個POST請求,每當接收到一個POST請求時它會把對應的指標數據發送到StatsD服務器。該服務器隨后會通過連接到信用卡中心提供的API來處理相應的信用卡數據。用戶可以在連接該API的代碼里設置記錄開始訪問的時間。然后在調用返回且信用卡數據被處理之后,代碼部分再次記錄對應的結束時間。該代碼隨后計算兩個記錄時間之間的差值從而獲取該任務總共的耗時,然后將另外一個StatsD指標發送出去。而每當它成功響應一次客戶端提交的POST請求時,它會發送最后一個標志成功的StatsD指標以記錄總共處理的信用卡數量。在本例中,如果將所有采集到的StatsD監控指標繪制到一個看板上,用戶便能幾乎實時地觀察應用的健康狀況。用戶能看到該容器接收到了多少個POST請求,調用API所花費的時間,以及完成處理的信用卡數量。這是測量運行在容器里的應用健康狀態的一種方式。例如,若看板上已完成的信用卡處理數量突然下降,用戶可以給它發送一個包含其詳細信息的告警。
在本例的最開始我們曾經講過一個/health的端點服務,那么接下來我們將具體介紹一下這方面的內容。StatsD(和Datadog的具體實現)會將任意字符串的輸入看做是一個指標,然后將它們聚合匯總后展示給用戶。例如,如果我們想監控http://myservice.corp/health這個http端點服務,可以使用`echo"myservice.status- code:curl -sL -w "%{http_code}" "http://myservice.corp/ health"|c" | nc -u -w0 statsd.server.com 8125`。該命令會檢查服務器上這一http端點服務返回的狀態碼,然后借助netcat將數據發送給StatsD。Datadog還可以和StatsD集成在一起,專門針對用戶發送給statsd的監控數據做監控和告警。在本例中,如果該服務是健康的,它會返回一個200的狀態碼。Datadog將會收到`myservice.statuscode`的監控數據,而它的值即是200。如果該狀態碼返回的是一個500或者404錯誤,我們隨后便可以使用Datadog通過郵件或專門的呼叫系統去發送一個故障通知。下面的這個Python腳本即是一個采用Datadog結合StatsD實現的具體例子。
```
import requests # For URL monitoring
import statsd # We installed the Datadog statsd module
import sys
import time
sites = ['http://myservice.corp/health']
def check_web_response_code(url):
r = requests.get(url,allow_redirects=True,veri-
fy=False,stream=True)
return str(r.status_code)
def send_dogstatsd(options,site):
c = statsd.DogStatsd(options.statsd, options.statsport)
c.connect(host=options.statsd, port=options.statsport)
statname = 'httpmonitor'
tags = []
tags += ['site:'+site]
r = check_web_response_code(site)
c.gauge(statname, r, tags=tags)
def monitor_sites(options):
for site in sites:
send_dogstatsd(options,site)
def main():
while True:
monitor_sites(options)
time.sleep(30);
if __name__ == '__main__':
sys.exit(main())
```
用戶可以讓上面這個簡單的Python腳本在一個容器里運行,然后通過它來監控其他容器的健康狀況。這是一個非常簡單、輕松地獲取應用健康狀況的方法。讀者可以到StatsD的GitHub項目中了解更多詳細內容以及它所提供的額外的核心類庫。
總體來說,上述案例已經較為完整地介紹了如何端對端地監控一個系統上的Docker以及正在運行的容器。借助一個單獨的工具,我們便能監控服務器本身、Docker守護進程以及容器內的應用服務。在上述案例中,通過使用Datadog,應用服務的監控數據可以被整合到Datadog的復雜看板上,Datadog提供了宿主機、Docker服務及應用健康狀態的告警。這是一個非常基礎的例子,但是我們希望它可以展示該如何著手監控運行在Docker基礎設施里的應用服務。
而這一點也貫穿于整本書。我們希望讀者已經了解了在生產環境使用Docker所需要的工具和信息。
- 版權信息
- 版權聲明
- 內容提要
- 對本書的贊譽
- 譯者介紹
- 前言
- 本書面向的讀者
- 誰真的在生產環境中使用Docker
- 為什么使用Docker
- 開發環境與生產環境
- 我們所說的“生產環境”
- 功能內置與組合工具
- 哪些東西不要Docker化
- 技術審稿人
- 第1章 入門
- 1.1 術語
- 1.1.1 鏡像與容器
- 1.1.2 容器與虛擬機
- 1.1.3 持續集成/持續交付
- 1.1.4 宿主機管理
- 1.1.5 編排
- 1.1.6 調度
- 1.1.7 發現
- 1.1.8 配置管理
- 1.2 從開發環境到生產環境
- 1.3 使用Docker的多種方式
- 1.4 可預期的情況
- 為什么Docker在生產環境如此困難
- 第2章 技術棧
- 2.1 構建系統
- 2.2 鏡像倉庫
- 2.3 宿主機管理
- 2.4 配置管理
- 2.5 部署
- 2.6 編排
- 第3章 示例:極簡環境
- 3.1 保持各部分的簡單
- 3.2 保持流程的簡單
- 3.3 系統細節
- 利用systemd
- 3.4 集群范圍的配置、通用配置及本地配置
- 3.5 部署服務
- 3.6 支撐服務
- 3.7 討論
- 3.8 未來
- 3.9 小結
- 第4章 示例:Web環境
- 4.1 編排
- 4.1.1 讓服務器上的Docker進入準備運行容器的狀態
- 4.1.2 讓容器運行
- 4.2 連網
- 4.3 數據存儲
- 4.4 日志
- 4.5 監控
- 4.6 無須擔心新依賴
- 4.7 零停機時間
- 4.8 服務回滾
- 4.9 小結
- 第5章 示例:Beanstalk環境
- 5.1 構建容器的過程
- 部署/更新容器的過程
- 5.2 日志
- 5.3 監控
- 5.4 安全
- 5.5 小結
- 第6章 安全
- 6.1 威脅模型
- 6.2 容器與安全性
- 6.3 內核更新
- 6.4 容器更新
- 6.5 suid及guid二進制文件
- 6.6 容器內的root
- 6.7 權能
- 6.8 seccomp
- 6.9 內核安全框架
- 6.10 資源限制及cgroup
- 6.11 ulimit
- 6.12 用戶命名空間
- 6.13 鏡像驗證
- 6.14 安全地運行Docker守護進程
- 6.15 監控
- 6.16 設備
- 6.17 掛載點
- 6.18 ssh
- 6.19 私鑰分發
- 6.20 位置
- 第7章 構建鏡像
- 7.1 此鏡像非彼鏡像
- 7.1.1 寫時復制與高效的鏡像存儲與分發
- 7.1.2 Docker對寫時復制的使用
- 7.2 鏡像構建基本原理
- 7.2.1 分層的文件系統和空間控管
- 7.2.2 保持鏡像小巧
- 7.2.3 讓鏡像可重用
- 7.2.4 在進程無法被配置時,通過環境變量讓鏡像可配置
- 7.2.5 讓鏡像在Docker變化時對自身進行重新配置
- 7.2.6 信任與鏡像
- 7.2.7 讓鏡像不可變
- 7.3 小結
- 第8章 存儲Docker鏡像
- 8.1 啟動并運行存儲的Docker鏡像
- 8.2 自動化構建
- 8.3 私有倉庫
- 8.4 私有registry的擴展
- 8.4.1 S3
- 8.4.2 本地存儲
- 8.4.3 對registry進行負載均衡
- 8.5 維護
- 8.6 對私有倉庫進行加固
- 8.6.1 SSL
- 8.6.2 認證
- 8.7 保存/載入
- 8.8 最大限度地減小鏡像體積
- 8.9 其他鏡像倉庫方案
- 第9章 CI/CD
- 9.1 讓所有人都進行鏡像構建與推送
- 9.2 在一個構建系統中構建所有鏡像
- 9.3 不要使用或禁止使用非標準做法
- 9.4 使用標準基礎鏡像
- 9.5 使用Docker進行集成測試
- 9.6 小結
- 第10章 配置管理
- 10.1 配置管理與容器
- 10.2 面向容器的配置管理
- 10.2.1 Chef
- 10.2.2 Ansible
- 10.2.3 Salt Stack
- 10.2.4 Puppet
- 10.3 小結
- 第11章 Docker存儲引擎
- 11.1 AUFS
- 11.2 DeviceMapper
- 11.3 BTRFS
- 11.4 OverlayFS
- 11.5 VFS
- 11.6 小結
- 第12章 Docker 網絡實現
- 12.1 網絡基礎知識
- 12.2 IP地址的分配
- 端口的分配
- 12.3 域名解析
- 12.4 服務發現
- 12.5 Docker高級網絡
- 12.6 IPv6
- 12.7 小結
- 第13章 調度
- 13.1 什么是調度
- 13.2 調度策略
- 13.3 Mesos
- 13.4 Kubernetes
- 13.5 OpenShift
- Red Hat公司首席工程師Clayton Coleman的想法
- 第14章 服務發現
- 14.1 DNS服務發現
- DNS服務器的重新發明
- 14.2 Zookeeper
- 14.3 基于Zookeeper的服務發現
- 14.4 etcd
- 基于etcd的服務發現
- 14.5 consul
- 14.5.1 基于consul的服務發現
- 14.5.2 registrator
- 14.6 Eureka
- 基于Eureka的服務發現
- 14.7 Smartstack
- 14.7.1 基于Smartstack的服務發現
- 14.7.2 Nerve
- 14.7.3 Synapse
- 14.8 nsqlookupd
- 14.9 小結
- 第15章 日志和監控
- 15.1 日志
- 15.1.1 Docker原生的日志支持
- 15.1.2 連接到Docker容器
- 15.1.3 將日志導出到宿主機
- 15.1.4 發送日志到集中式的日志平臺
- 15.1.5 在其他容器一側收集日志
- 15.2 監控
- 15.2.1 基于宿主機的監控
- 15.2.2 基于Docker守護進程的監控
- 15.2.3 基于容器的監控
- 15.3 小結
- DockOne社區簡介
- 看完了