問題
在本機架 ELK 日誌平台(Elasticsearch/Kibana/Filebeat 9.5.4,安全機制打開、只收一般應用日誌),收一個 kotlin-ktor 服務的單行 JSON log。過程卡了六次,每一次的錯誤訊息都指向錯的方向。
解法
| # | 症狀 | 真正原因 | 解法 |
|---|---|---|---|
| 1 | 拉 9.5.5 的 image 失敗 | 版本清單(artifacts API)列出的版本,image 還沒發 | 用 docker manifest inspect 逐一確認三個 image 都在,挑都在的最新版(9.5.4) |
| 2 | 9.5.4、9.5.3 也「拉不到」 | 指令包了 60 秒 timeout,1–2GB 的 image 拉不完,逾時被當成不存在 | 存在與否用 manifest 查;真的拉放背景、不設短逾時 |
| 3 | 怕收到同一台機器上別的開發棧 | 預設 autodiscover 會收所有容器 | hints.default_config.enabled: false,只收貼了 label 的容器(opt-in)。label 放在 ELK 自己的 override 檔,不改來源服務的 repo |
| 4 | 驗收突然出現 53 筆 stack 碎片 | 來源服務一啟動就當掉,JVM 在 logger 之外印出多行純文字 stack | 這不是管線的錯:「應用程式 log 一律是單行 JSON」這條規則,只管得到經過 logger 的輸出,管不到程序在 logger 之外直接印的東西。啟動失敗靠容器狀態與告警抓;碎片檢查只看這一輪之後的文件 |
| 5 | 來源服務第二次啟動就當 | 祕密每次隨機產生,舊 MySQL volume 還是舊密碼 → Access denied | 拋棄式來源每次 down -v 再起 |
| 6 | 「錯誤行都要有 stack」驗不過 | 一個失敗請求有兩筆 ERROR:APP channel 帶完整 stack,ACCESS channel 只用 stackTraceId 指過去 | 驗「至少一筆完整多行 stack、沒有碎片」,不是每筆都要有 |
第 3 點的設定,官方文件的說法是:把 hints.default_config.enabled 設成 false,就只會收貼了啟用 hint(co.elastic.logs/enabled)的容器(Filebeat autodiscover hints)。我在驗收裡實測,同一台機器上另一個沒貼 label 的開發棧是 0 筆。
做對了、也驗過的設計:
- 收集端重啟不掉資料:Filebeat 停掉期間的 log,重啟後從 registry 記的位置補回。官方文件寫的是 Filebeat 會把每個檔案的讀取位置定期寫進 registry,重啟後從最後已知位置繼續(How Filebeat works)。我的驗收是反向做的:停掉 Filebeat 再送請求,驗收必紅;重新啟動、不重送請求,同一個 requestId 的 3 筆與錯誤行自己補回來,驗收轉綠。
- 最小權限要反向驗:收集帳號讀資料、刪資料流、看帳號清單都必須回 403,不是只驗「寫得進去」。
為什麼這樣解
- 第 1、2 點是同一種錯:「查不到」有好幾種原因(不存在、沒權限、逾時),工具常常回一樣的失敗。要用能區分原因的方法查,而不是重試同一個指令——這跟比錯基準是同一族:檢查跑了、有輸出,但量的不是你以為的東西。
- 第 3 點:不收不該收的,比收了再過濾可靠,也不會把別人的資料拉進這個平台的稽核範圍。
- 第 4、6 點:驗收條件要跟資料的真實形狀對齊。寫得太鬆會假綠,寫得太嚴會把正常設計判成錯。
- 第 5 點:「每次都產生新祕密」和「資料保留」不能同時成立,要選一個。拋棄式環境選前者。
怎麼預防再次發生
- 選版本:版本清單只是候選。真正能用的版本,以 registry 上所有用到的 image 都存在為準。
- 「失敗」先分清楚原因:逾時、不存在、沒權限,用不同的指令確認,不要拿同一個失敗結果下結論。
- 日誌收集預設 opt-in,並把「沒貼 label 的容器 0 筆」寫進驗收。
- 驗收要附反向驗證:停掉收集端必紅;權限檢查驗「被拒」。
- 帶隨機祕密的拋棄式環境,啟動前一律清乾淨。
相關
- 下游逾時跟請求上限設成一樣長:同一輪驗收時另外抓到的逾時賽跑
- Logstash 從 MySQL 同步到 Elasticsearch 的七個坑:搜尋段的同步坑
- gitleaks 本機綠、CI 紅:同樣是「工具回成功/失敗,但原因不是你以為的那個」
- 檢查跑了、有輸出、結論還是錯的:查不到 ≠ 不存在