iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
IT Operation

地端機房的三十天維運:開源服務封裝、GPU 節點守護與可稽核的變更管理系列 第 21 篇

Day 21|日誌集中與保存:節點不裝新代理,封存要能比對

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20261005/20141816Zx04euSnlI.png

告警說出事了,日誌說發生了什麼呢 ?

在 Day 20 設定的告警規則中,當指標 soak 到 180 秒、叢集法定人數少一票、或儲存池使用率突破 80% 時,維運團隊就會收到通知。然而告警響起後,值班工程師必須立即回答的問題往往是:「在那關鍵的十分鐘內,系統到底發生了什麼事?」

這個問題的線索散落在四個不同的角落:

  1. DGX 運算節點:journald 裡記錄著守護程式崩潰與核心(Kernel/NVRM)訊息。
  2. PVE 虛擬化節點:journald 記錄著 corosync 與 pve-ha-lrm 的狀態機轉移。
  3. Primary / Secondary NAS:QuLog Center 記錄著系統事件與儲存連線存取紀錄。

四個來源、兩種日誌格式、六台主機,卻沒有沒有單一介面能一目了然,加速掌握全局。

因此,今天的目標是:

  • 統一入口:將六台機器的日誌匯整至單一查詢介面。
  • 節點零代理:不替運算與虛擬化節點安裝額外的第三方程式(Daemonless/Agentless)。
  • 三層保存與不可竄改:定義來源端、熱資料層、長期封存層,並讓 WORM 封存具備「可對帳」與「密碼學防禦」特性,向資安稽核證明:寫進去的日誌一筆未漏,且封存後任何人(包含 root)都無法修改。

一、選型評估:為什麼不是 Graylog 或 Loki,也不是只用 NAS

在架構設計之初,必須先釐清現有的資源限制:

  • 收集端主機:位於 Day 19 建立的 PVE 輕量 VM,規格僅配置 2 vCPU、4 GB RAM、32 GB 磁碟,上面已常駐 Prometheus、Alertmanager、Grafana 與 Exporter。
  • 節點基線:節點作業系統為 DGX OS 與 Proxmox VE,原則上不希望引進肥大的 Agent,每多裝一個軟體套件,就增加一次版本相依與資安修補的負擔。
  • 封存目標:長期冷資料需存放在 NAS 的 WORM(Write Once, Read Many)共用資料夾,達到合規要求。
方案 收集端資源需求 節點端相依套件 原生接收 journald 保存與封存機制 查詢語言與生態
純 QuLog Center 位於 NAS,收集端零負擔 rsyslog 轉送 否,需先壓扁成單行 syslog 依 NAS 內部資料庫,冷備份需靠手動匯出 CSV 簡易關鍵字過濾
SpesLog 單一容器(亦支援 K8s、VM 或實體機 SDS 部署) 支援原生 Syslog、Windows Event、Linux Auditd 或專屬 Agent / API 否,需透過 Syslog 轉送、Auditd 或 Agent 收集 熱資料高速索引,冷資料高壓縮寫入 WORM 儲存池與 S3 物件儲存 關鍵字、布林邏輯與多維度過濾
Graylog Open 需 Graylog + MongoDB + OpenSearch 三套服務,JVM heap 需求高 Graylog Sidecar 或 rsyslog 否 索引依 Index Set 保留,合規封存多屬 Enterprise 功能 完整 Pipeline 規則與 RBAC
Grafana Loki 單一容器,但需規劃 Object Storage / Compactor Alloy 或 Promtail 否,需代理讀取 journal 依 Stream 保留,封存需額外腳本串接 LogQL,與 PromQL 語法相近
VictoriaLogs 單一二進位/容器,實測千萬筆常駐 160 MiB,磁碟佔用僅 236 MB 零額外安裝,直接使用 systemd 原生 systemd-journal-upload 是,/insert/journald 原生解析 Export 格式 支援 -retentionPeriod 與磁碟上限,匯出格式簡單 LogsQL,具 Grafana 專用 Datasource

淘汰與勝出理由

  1. 淘汰 Graylog:
    依據 Graylog 官方相容性矩陣,6.x~7.x 需要搭配 MongoDB 7+ 與 OpenSearch 1.1~2.19。三個服務各自需要 JVM Heap 或獨立記憶體空間,根本無法塞進 4 GB 的監控 VM。若單純為了記錄 6 台主機而額外開大型 VM,違背了輕量維運的初衷。
  2. VictoriaLogs 勝出關鍵:
    • 節點零額外 Agent:現代 Linux 的 systemd 內建 systemd-journal-upload(Debian/Ubuntu 位於 systemd-journal-remote 套件中),能以 native export 格式直接透過 HTTP POST 上傳。VictoriaLogs 原生支援該協定,_SYSTEMD_UNIT、PRIORITY、_TRANSPORT 等結構化欄位全數保留,完全不需在節點裝 Logstash、Fluentbit 或 Promtail。
    • 超高壓縮比與極低開銷:實測寫入 1,130 萬筆日誌後,記憶體僅佔 160 MiB,磁碟空間僅耗費 236 MB。

二、架構落地:兩種來源,一個入口

日誌架構主要由收集端的 Docker Compose 驅動,對外開放兩個接收埠:

  • 9428:接收四台 Linux 節點透過 HTTP POST 送來的 journald export 流。
  • 514 (TCP/UDP):接收兩台 NAS 送出的 syslog。
+-------------------------------------------------------+
|  [Nodes] DGX Spark & PVE Hosts                        |
|  systemd-journald ---> systemd-journal-upload (HTTP)  |
+------------------------------------+------------------+
                                     | Port 9428
+------------------------------------+                  |
|  [Storage] Primary & Secondary NAS |                  |
|  QuLog Center (Syslog Sender)      |                  |
+------------------+-----------------+                  |
                   | Port 514 (TCP)                     |
                   v                                    v
+-------------------------------------------------------+
|  [Collector VM]                                       |
|  VictoriaLogs (Port 9428 / 514)                       |
|   ├── Prometheus (up monitoring)                      |
|   ├── Grafana (Datasource Plugin)                     |
|   └── Daily Archiver Script ---> NFS (/mnt/worm)      |
+-------------------------------------------------------+

1. 節點端:原生 systemd-journal-upload 實戰

在 Linux 節點上啟用 journal 上傳相當簡單,但在實際佈署時有幾點需特別注意:

  1. 持久化儲存設定:
    若節點上已存在 /var/log/journal/,systemd 的預設值 Storage=auto 即會啟用持久化。建議透過 drop-in 設定檔明確宣告 Storage=persistent。注意:切勿貿然在線上環境寫入過小的 SystemMaxUse,否則套用瞬間會直接清掉節點上的歷史日誌。
  2. 動態權限管理:
    較新版 systemd(如 Ubuntu 24.04/Debian 12+)的 systemd-journal-upload.service 採用 DynamicUser=yes 與 StateDirectory= 機制。上傳的游標(cursor)預設會落在 /var/lib/private/systemd/journal-upload/state,由 systemd 自身處理權限,不需手動為其建立系統帳號。
  3. 歷史資料回填機制:
    首次啟動時,若不存在 cursor,上傳器會從 journal 起點開始傳送。實測中四台節點共送出 1,313 萬筆歷史紀錄,VictoriaLogs 會自動過濾丟棄早於保留期的資料,並記錄於 vl_rows_dropped_total{reason="too_small_timestamp"}。若不想傳送歷史資料,可在啟動前將游標指標指向 journal 結尾。

2. NAS 端:QuLog Center 傳送與時區陷阱

在 NAS 的 QuLog Center 設定「記錄傳送端」時,有兩大容易踩坑的細節:

  • 傳輸協定與格式限制:雖然管理介面標示支援 RFC-5424,但實測選用標準 TCP/UDP 傳輸時,封包會強制降為 RFC-3164;只有在啟用 TLS(TCP 6514)時才會輸出 RFC-5424。
  • 時區修正不可省略:RFC-3164 的時間戳記缺乏時區資訊,NAS 送出的是本地時間(如 UTC+8)。若 VictoriaLogs 執行於最小化容器(FROM scratch)且時區為 UTC,日誌時間會憑空偏移 8 小時。因此 VictoriaLogs 啟動參數必須明確加上 -syslog.timezone=Asia/Taipei。
  • 開關陷阱:在 QuLog Center 中,除了目的地連線需設為啟用外,最上方的「傳送記錄到遠端 Syslog 伺服器」全域總開關亦需確認開啟。

3. 收集端安全防護:Docker 與防火牆的穿透問題

在 Linux 系統中,若直接使用 UFW 限制連接埠,常會忽略 Docker 發佈的埠(Port Publishing)在 PREROUTING 階段做完 DNAT 後會直接走 FORWARD 鏈進容器,完全繞過 UFW 的 INPUT 規則。

為了確保日誌收集端不對外裸露,正確做法是利用 Docker 保留的 DOCKER-USER iptables 鏈:

  • Port 9428 只放行特定運算與虛擬化節點。
  • Port 514 只放行 NAS IP。
  • 容器之間(如 Grafana 存取 VictoriaLogs)走內部 Docker Bridge Network,不受限制。
  • 連接埠綁定指定 IPv4(0.0.0.0:9428),避免 docker-proxy 在 IPv6 位址產生非預期的監聽開口。

三、場域運作實測與降噪最佳化

當六個來源全部接通後,系統就逐步進入穩定狀態,接下來是近期觀察到的幾項關鍵維運細節:

1. 高串流數(Cardinality)的本質

原先預期日誌串流維度 (_HOSTNAME, _SYSTEMD_UNIT) 會維持在數百個以內。但實測發現串流數達到了 50,000 個以上。深入排查發現:每次 SSH 登入都會動態產生一個 session-N.scope,自動化維運工具頻繁連線造成串流暴增。
所幸 VictoriaLogs 的架構對於高基數(High Cardinality)場景擁有良好的耐受度,1,130 萬筆日誌壓縮後僅 186 MB,索引僅 5 MB,系統未受效能衝擊。

2. 日誌降噪實戰:減少 75% 的無效輸出

分析收集到的日誌,通常我們去仔細檢視,你可以發現佔用量最大的往往不是重要錯誤,而是週期性雜訊:

  • PVE 節點 root 連線開銷:監控收集腳本每分鐘透過 SSH 連線 PVE 執行指令,由於 root 未開啟 Session 保留,每次登入都會建立與銷毀一組 user@0.service,單日產生近 10 萬行日誌。
    解法:在 PVE 節點執行 loginctl enable-linger root,大幅收斂 PAM 與 Session 建立紀錄。
  • Timer 產生的排程噪音:以高頻率(如每 10 秒)執行的 systemd service,每次觸發時 PID 1 都會產生啟動、完成、釋放的三行紀錄。
    解法:在 Service 單元加上 LogLevelMax=notice,平日抑制常態紀錄,僅在 exit code 非零時保留異常紀錄。

經過兩項最佳化,單台節點的日誌量從每天 22 萬筆降至 5 萬筆左右,整體日誌總量減少了約 75%。


四、三層保存架構與 WORM 防竄改對帳

日誌保存策略分為三層,兼顧「即時排查」、「中繼關聯」與「長期法規稽核」:

層級 存放位置 保存週期 核心目的
來源層 (Source) 節點本機 journal、NAS 內部空間 依本機磁碟容量滾動覆蓋 本機緊急救援與本機離線排查
熱資料層 (Hot) 收集端 VictoriaLogs 90 天(配置 12 GiB 空間上限) 即時告警關聯、Grafana LogsQL 交互查詢
封存層 (Cold) NAS WORM 共用資料夾 180 天(依企業法規程序調整) 符合資安稽核要求,防竄改與不可變更

https://ithelp.ithome.com.tw/upload/images/20261005/20141816z38SZCM1wH.jpg

1. 熱資料層容量與時間推算

在完成日誌降噪後,六台機器單日穩態日誌量約為 8.3 萬筆,磁碟每日增量僅約 2.75 MB。

  • 保存 90 天僅需耗費約 250 MB。
  • 設定好的 12 GiB 磁碟上限預計可容納數千天以上。
    這意味著在此規模下,系統永遠會先達到「90 天或 180 天保留期限」而非「容量上限」。12 GiB 的限制純粹作為保險絲,防止某服務因無窮迴圈噴日誌而塞爆收集端磁碟。

2. 封存排程與 WORM 自動鎖定陷阱

在 NAS 的 QuTS hero 系統建立 WORM 資料夾,設定為「企業模式」並啟用「10 分鐘自動鎖定」,保存期限設定為 180 天。

在自動化封存設計上,必須特別注意 WORM 的鎖定特性:

地雷設計:若封存腳本直接在掛載的 WORM 資料夾內建立 .tmp 檔案進行逐行寫入,一旦匯出時間或網路延遲超過 10 分鐘,暫存檔將被 WORM 引擎無情鎖定。後續的重試或更名操作都會被系統阻擋,且該檔案必須在儲存池躺滿 180 天才能刪除。

健全的封存流程:

  1. 本機 Staging:在收集端本地磁碟建立暫存區,依小時區間匯出 VictoriaLogs 的資料並壓縮成 .jsonl.gz。
  2. 完整性對帳:計算匯出檔案的行數(lines)是否嚴格等於 VictoriaLogs 回報的查詢命中筆數(hits)。
  3. 原子性寫入:比對成功後,產生 MANIFEST.tsv 與 SHA256SUMS,最後才以 cp 一次性複製至 /mnt/worm/。
  4. 檔案完整指標:以 SHA256SUMS 是否存在作為當日封存完成的判定基準。若傳輸中斷,絕不在 WORM 留下半成品。
# MANIFEST.tsv 範例
# source              lines   hits    bytes     sha256
journald-pve1         56291   56291   3418003   9ea92d71...
journald-spark02      10101   10101   682511    b067cc33...
syslog-nas-primary    1618    1618    112540    1a20025a...

3. WORM 實彈破壞性測試

在封存檔案鎖定延遲(10 分鐘)過後,進行七種蓄意破壞測試:

  1. 一般使用者 rm 刪除
  2. sudo rm -rf 強制刪除
  3. mv 移動更名
  4. tee -a 嘗試附加內容
  5. : > SHA256SUMS 截斷檔案
  6. touch 嘗試更新時間戳
  7. chmod 變更檔案權限

測試結果:全部操作均由底層檔案系統直接回傳 Operation not permitted。由於 NFS 掛載設定了 All Squash,即使是收集端的 root 帳號亦無法跨越 WORM 的核心保護,證明日誌在寫入後具備高度的法律與稽核效力。


五、告警與日誌關聯:Grafana 與 LogsQL

有了統一日誌平台後,Day 20 的 Prometheus 告警便能直接在 Grafana Explore 進行關聯查詢。
https://ithelp.ithome.com.tw/upload/images/20261005/20141816XAzZplL8Gv.jpg

常用 LogsQL 實戰範例

  1. 守護程式與 OOM 崩潰調查:
    _transport:journal AND (trigger OR would_kill OR sigterm OR sigkill)
    
  2. NVIDIA GPU 驅動與核心異常:
    _TRANSPORT:=kernel AND (NV_ERR_NO_MEMORY OR NVRM OR Xid)
    
  3. NAS 連線拒絕事件(Syslog):
    app_name:qulog AND "conn log:" AND (deny OR failed)
    

Grafana 離線映像建置要點

為了確保系統完全處於可重現與安全受控狀態(Air-gapped 概念),不建議在 Grafana 容器啟動時使用 GF_INSTALL_PLUGINS 動態連網下載外掛。
建議透過自建 Dockerfile,將預先驗證過 SHA256 雜湊值的 victorialogs-datasource 解壓縮置入 /opt/grafana-plugins,並配置 GF_PLUGINS_PREINSTALL_DISABLED=true 關閉未受監管的自動更新,維持環境版本絕對鎖定。


六、部署步驟指南

1. 收集端配置

# 取得設定檔與專案
git clone https://github.com/ivanusto/onprem-logs && cd onprem-logs

# 啟動日誌核心
docker compose up -d

# 設定白名單防火牆規則(僅允許授權節點與 NAS 進入)
sudo install -m 0644 collector/onprem-logs-allowlist.default /etc/default/onprem-logs-allowlist
sudo install -m 0644 collector/onprem-logs-allowlist.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now onprem-logs-allowlist

# 掛載 NAS WORM 共用目錄
sudo install -m 0644 collector/mnt-worm.mount /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now mnt-worm.mount

# 設定排程封存與驗證工作
sudo install -d -o metrics /var/log/onprem-logs /srv/drills/onprem-logs /var/tmp/onprem-logs-stage
sudo install -m 0644 collector.cron /etc/cron.d/onprem-logs

2. 運算與虛擬化節點配置

在每台 DGX Spark 與 Proxmox VE 節點上執行上傳設定腳本,指向收集端位址:

# 指向收集端 IP
sudo ./node/install-journal-upload.sh http://<collector-ip>:9428

3. NAS 端配置

  1. 開啟 QuLog Center -> 記錄傳送端 -> 傳送到 Syslog 伺服器。
  2. 目的地填入收集端 IP,通訊協定選擇 TCP,連接埠設定為 514。
  3. 確認頁面上方的「傳送記錄到遠端 Syslog 伺服器」總開關已處於啟用狀態。

4. 驗證日誌流

回到收集端,直接透過 HTTP API 檢視最近 5 分鐘各主機回報的日誌統計:

curl -s http://127.0.0.1:9428/select/logsql/query \
  -d 'query=_time:5m | stats by (_HOSTNAME, hostname) count()'

結語與成果驗證

透過本篇的架構建置,我們在零外掛代理的前提下,達成了以下效益:

  1. 極致輕量:單一二進位容器統整六台機器日誌,穩態記憶體佔用小於 200 MiB。
  2. 雜訊抑制:透過 systemd 與 PAM 層級的設定修正,成功去除 75% 的背景無效日誌。
  3. 稽核合規:每日自動封存至硬體級 WORM 空間,搭配 lines == hits 行數檢核與 SHA256 簽署,確保所有事件皆可對帳且無篡改之虞。

在解決了「系統與服務發生了什麼事」之後,下一個維運關鍵是網路邊界防禦。Day 22 將進入防火牆日誌分析:我們將利用 LogsQL 剖析節點上的連線拒絕紀錄與 NAS 存取行為,並將具備攻擊特徵的行為轉化為即時告警規則。


系列文章與程式碼索引:onprem-ops-30days

本日程式碼:onprem-logs、onprem-metrics v0.2.1

參考資料


上一篇
Day 20|儀表板與告警,打造一個簡潔的單一面板
下一篇
Day 22|被擋下的封包才是便宜情報:從防火牆靜默盲點到日誌降噪與自動化告警
系列文
地端機房的三十天維運:開源服務封裝、GPU 節點守護與可稽核的變更管理 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言