看看程序員如何被 Docker 日志坑慘了
最近在讀《計(jì)算機(jī)程序的構(gòu)造和解釋》,里面有一句話:代碼必須能夠被人閱讀,只是機(jī)器恰巧可以執(zhí)行。
我也想到了一句話:BUG 一定能夠被人寫出,只是恰好我寫的多而已。
說多了都是淚,來看看我最近遇到的一個(gè)問題。
問題
普通的一天,打開普通的電腦,登錄一臺(tái)普通的服務(wù)器,敲下一條普通的命令。
在我使用命令補(bǔ)全時(shí),出現(xiàn)了一條不普通的提示:
- -bash: cannot create temp file for here-document: No space left on device ls -bash
怎么磁盤滿了?
使用 df -h 一看還真是。
什么原因呢?
解決
首先,查找一下系統(tǒng)里的大文件,看看是哪個(gè)小可愛搞的鬼。
- du -sh /* | grep G
很快就定位到了這個(gè)目錄:/var/lib/docker/containers。
原來是 Docker 這家伙,這個(gè)目錄下存放的都是容器運(yùn)行過程中產(chǎn)生的日志。
使用下面命令來給這些文件按大小排個(gè)序:
- du -d1 -h /var/lib/docker/containers | sort -h
- 32K /var/lib/docker/containers/d607c06e475191fff1abd0c2b4b672e7fe8a96cb197f4e8557b18600de2e60af
- 36K /var/lib/docker/containers/0d4321106721b9d26335fefef7b9e8e23629691684a4da2f953ac8223c8240c3
- 36K /var/lib/docker/containers/7525aab4aa917aa1016169114762261726ac7b9cc712bef35cdc7035b50d20ce
- 36K /var/lib/docker/containers/9252e1c373d59ef5613c2b6122eb6e43aa2bd822bd2c199aa67d6eb659c4adb7
- 142M /var/lib/docker/containers
- 142M /var/lib/docker/containers/15700ee92cd2831554b9a1e78127df0f07248c1498d35c17525407bc8a98bc1a
文件名稱就是容器 ID,每個(gè)文件對(duì)應(yīng)一個(gè)容器,也就可以定位到,具體是哪個(gè)容器產(chǎn)生了大量的日志。
使用這個(gè)命令可以將大文件快速清空:
- sh -c "cat /dev/null > ${log_file_name}"
但是清空了文件哪算解決問題,新的日志還在源源不斷往日志里打呢。看了看日志內(nèi)容,很熟悉。前兩天為了調(diào)試程序,剛加的一條 print。
編輯代碼,刪除 print,重啟容器。好了,日志不再瘋狂追加了。
為什么 print 語(yǔ)句將日志都輸出到文件里了呢?別著急,后面再來詳細(xì)介紹。
先處理一下眼前的問題,放任日志無限增長(zhǎng)是肯定不行的,需要有一個(gè)單個(gè)文件大小限制。否則,明天張三再加一條 print,磁盤又滿了。
這里有兩個(gè)方案:
- 單一容器配置
- 全局配置
單一容器配置
啟動(dòng)容器時(shí),通過參數(shù)來控制日志的文件個(gè)數(shù)和單個(gè)文件的大?。?/p>
- docker run -it --log-opt max-size=10m --log-opt max-file=3 redis
但這樣做是比較麻煩的,更多的采用的是全局配置的方式。
全局配置
編輯 /etc/docker/daemon.json:
- {
- "log-driver":"json-file",
- "log-opts":{
- "max-size" :"50m",
- "max-file":"3"
- }
- }
重啟 Docker 服務(wù):
- systemctl daemon-reload
- systemctl restart docker
注意: 已存在的容器不會(huì)生效,需要重建才可以。
接下來再說說上文提到的 print 問題。
Docker 日志
Docker 日志分為兩類:
- Docker 引擎日志(也就是 dockerd 運(yùn)行時(shí)的日志)
- 容器的日志,容器內(nèi)的服務(wù)產(chǎn)生的日志
引擎日志
Docker 引擎日志一般是交給了 Upstart(Ubuntu 14.04) 或者 systemd (CentOS 7, Ubuntu 16.04)。前者一般位于 /var/log/upstart/docker.log 下,后者一般通過 journalctl -u docker 進(jìn)行查看。
不同系統(tǒng)的位置都不一樣,網(wǎng)上有人總結(jié)了一份列表,我修正了一下,可以參考:
容器日志
使用下面命令可以顯示當(dāng)前運(yùn)行的容器的日志信息:
- docker logs CONTAINER
UNIX 和 Linux 命令有三種輸入輸出,分別是 STDIN、STDOUT 和 STDERR。docker logs 顯示的內(nèi)容包含 STDOUT 和 STDERR。
在生產(chǎn)環(huán)境下,如果我們的應(yīng)用輸出到日志文件里,那么我們?cè)谑褂?docker logs 時(shí)一般收集不到太多重要的信息。
這里來看一下 nginx 和 httpd 是怎么做的:
- nginx 官方鏡像,使用了一種方式,讓日志輸出到 STDOUT,也就是創(chuàng)建一個(gè)符號(hào)鏈接 /var/log/nginx/access.log 到 /dev/stdout。
- httpd 使用的是讓其輸出到指定文件,正常日志輸出到 /proc/self/fd/1 (STDOUT) ,錯(cuò)誤日志輸出到 /proc/self/fd/2 (STDERR)。
當(dāng)日志量比較大的時(shí)候,使用 docker logs 來查看日志,會(huì)對(duì) docker daemon 造成比較大的壓力,容易導(dǎo)致容器創(chuàng)建慢等一系列問題。
只有使用了 local 、json-file、journald 日志驅(qū)動(dòng)的容器才可以使用 docker logs 捕獲日志,使用其他日志驅(qū)動(dòng)無法使用 docker logs。
Docker 默認(rèn)使用 json-file 作為日志驅(qū)動(dòng)。
除此之外,Docker 還提供了很多其他日志驅(qū)動(dòng),這里就不過多介紹。還有日志管理方案,我也不是很有經(jīng)驗(yàn),大家如果感興趣的話自己搜搜看吧。