Docker Linux 伺服器部署 DevOps 日誌管理

Docker 預設的 log 設定會在某天凌晨三點把你的磁碟吃滿

Docker 預設的 json-file log driver 沒有大小限制,一台忙碌的生產伺服器幾天內就能把磁碟塞滿。這篇文章從 daemon 層級設定、log driver 選擇,到輕量集中式 log 方案,講清楚生產環境該怎麼處理 container log。

Container 重啟之後 log 就消失了,這不是 bug,是設計如此。但真正危險的問題不是這個——是你的 container 還活著,log 卻默默在背景把整顆磁碟填滿,直到某天凌晨三點服務開始掛掉才發現。

Docker 預設的 log 行為在開發環境完全合理,到了生產環境卻是個定時炸彈。這篇文章就從這裡開始談起。

Docker 預設到底做了什麼

Docker 用 json-file 作為預設的 log driver。每個 container 的 stdout/stderr 都會被寫進 host 上的一個 JSON 檔案,路徑長這樣:

1
/var/lib/docker/containers/<container_id>/<container_id>-json.log

問題在於:預設沒有大小限制,也沒有自動 rotation

一個流量不算大的 web 服務,每天輸出幾百 MB log 是很正常的事。跑一個禮拜,幾 GB 就不見了。跑一個月,磁碟空間直接見底。更糟的是,這個檔案不會觸發任何警告,你不主動去看根本不知道它有多大。

用這個指令可以先查一下現況:

1
du -sh /var/lib/docker/containers/*/\*-json.log | sort -rh | head -20

如果你從來沒設定過 log rotation,跑一下這個指令大概會嚇到。

先從 daemon 層級設定全局預設值

最省力的做法是在 /etc/docker/daemon.json 設定全局預設值,這樣之後啟動的每個 container 都會自動套用:

1
2
3
4
5
6
7
8
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "5",
"compress": "true"
}
}

這三個參數的意思:

  • max-size: 每個 log 檔案超過 20MB 就 rotate
  • max-file: 最多保留 5 個舊檔案(加上當前的,總共 6 個)
  • compress: rotate 後壓縮舊檔案,節省磁碟空間

改完之後要重啟 Docker daemon:

1
sudo systemctl restart docker

**注意:這個設定只對之後新建的 container 有效。**已經在跑的 container 不會套用,必須重建才行。這是一個常見的誤區——改了 daemon.json 之後以為沒生效,其實是舊 container 繼承了舊設定。

json-file 以外的選擇

json-file 適合簡單的部署情境,但它有一個根本限制:用了某些 log driver 之後,docker logs 指令就沒辦法用了。這對除錯影響很大,所以換 driver 之前要想清楚。

幾個常見 driver 的實際取捨:

local driver:比 json-file 更緊湊的儲存格式,內建 rotation,也支援 docker logs。如果你只是想解決磁碟問題又不想引入額外元件,直接換成 local 是最省事的選擇。

1
2
3
4
5
6
7
{
"log-driver": "local",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}

journald driver:把 log 交給 systemd journal 管理。如果你的 host 本來就在用 systemd,這個整合很自然,journalctl 可以直接查。但跨主機查 log 就麻煩了。

fluentd / fluent-bit driver:把 log 串流到外部 aggregator,適合多台主機的集中式 log 架構。設定較複雜,但如果你已經在用 ELK 或類似的 stack,這是正確的接入點。

none driver:完全不記 log。偶爾用在那種輸出量超大、log 內容又完全沒有參考價值的 container 上(例如某些 metrics exporter)。正常的應用服務絕對不要用。

輕量集中式 log:Loki + Promtail

如果只有一兩台伺服器,跑完整的 ELK stack(Elasticsearch + Logstash + Kibana)資源成本太高,很多人乾脆放棄集中式 log。但其實有更輕量的選擇:Grafana Loki。

Loki 的設計思路跟 Prometheus 類似——不對 log 內容建立全文索引,只對 label 做索引。這讓它的資源需求遠低於 Elasticsearch,一台 2 核 2GB 的 VPS 就能跑起來。

用 Docker Compose 快速部署 Loki + Grafana 的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml

promtail:
image: grafana/promtail:latest
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock
command: -config.file=/etc/promtail/config.yml

grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana

volumes:
loki-data:
grafana-data:

Promtail 直接讀取 Docker 的 log 檔案並推送到 Loki,Grafana 負責查詢介面。這個組合適合中小規模的部署,比 ELK 簡單很多。

Loki 有個使用習慣要注意:它的查詢語言 LogQL 跟 SQL 不同,過濾條件要靠 label,而不是全文搜尋。如果你習慣 Elasticsearch 那種什麼都能搜的方式,Loki 一開始可能不太順手。但只要 label 設計合理,查詢速度其實很快。

Docker Compose 裡的 log 設定

如果你用 Docker Compose 管理服務,可以在 compose.yml 裡直接指定每個服務的 log 設定,覆蓋全局預設值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
services:
web:
image: nginx:alpine
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

worker:
image: myapp:latest
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "10"

不同服務的 log 量差異可能很大,web 服務和背景 worker 的需求通常不同,分開設定比較合理。

已經滿了怎麼辦

如果磁碟已經告急,緊急清空特定 container 的 log:

1
truncate -s 0 /var/lib/docker/containers/<container_id>/<container_id>-json.log

truncate 的好處是不需要重啟 container,檔案 inode 還在,Docker daemon 繼續正常寫入。直接刪掉檔案的話,Docker 可能繼續寫進一個已刪除的 fd,磁碟空間不會真的釋出,直到 container 重啟。

這只是應急手段,不是長期策略。清完之後馬上把 daemon.json 設定補上,才是正確的做法。

一個值得做的監控

在 log rotation 設定到位之後,還建議加一個磁碟使用率的監控。log 管理設定正確,但應用突然出現 bug 狂打 error log 的情況不是沒有發生過。預警機制遠比半夜被告警叫醒好處理。

一個簡單的 shell script 可以做到基本的磁碟監控,搭配前一篇文章提到的 Telegram Bot 推送通知,就是一套夠用的輕量監控方案。


如果你在找一個磁碟夠用、I/O 效能穩定、又不用擔心鄰居搶資源的 VPS 環境來跑這些服務,NCSE Network 的 VPS 方案採用 NVMe SSD 搭配臺灣是方電訊機房,log 寫入量大的工作負載也跑得住。詳細規格可以到 ncse.tw 查看。

需要穩定的雲端主機?

NCSE Network 提供企業級 VPS,7 天免費試用,臺灣是方電訊機房,99% SLA 保證。

查看 VPS 方案 →