Skip to main content

18 posts tagged with "Homelab"

View All Tags

Wei Ji

今天 (2026-01-12) 處理了一些 YaCy 跟 SearXNG 的故障排除,做個紀錄。

前情提要

故事得從我最近愛用的 LLM 工具 LDR (Local Deep Research) 說起,LDR 能夠根據使用者的輸入拆分成數個 LLM 任務:

其中一個任務長得像這樣:

將使用者原始輸入轉換成精簡的搜尋引擎關鍵字之後,呼叫 SearXNG 來取得搜尋結果。

SearXNG 本身不實做搜尋引擎,而是作為代理去呼叫多個搜尋引擎,諸如 Google, Bing, DuckDuckGo...

於是乎有時後會發生這種事情:

呼叫太頻繁被外部的搜尋引擎封鎖,造成有的時候 LDR 會「研究失敗」,因為沒有可靠的外部資訊來源。

我架設 LDR 時使用官方的預設組態,沒有特別設定 SearXNG,於是想說趁這個機會改善這個問題。以上圖為例, 我被 Brave 搜尋引擎擋了。

天下沒有白吃的午餐

老實說我不會特別怪罪搜尋引擎投放廣告、過濾內容...,畢竟:

當使用者沒有為服務付費,使用者本身就是產品,而不是消費者。

搜尋引擎自己有自己的營利模型,而 SearXNG 的使用者幾乎就是繞過這些措施白嫖搜尋引擎,即便搜尋引擎供應商本身有它的原罪,我也不認為應該正當化白嫖的行為。

找來找去,YaCy 似乎是比較合理的方案,它是一個去中心化的搜尋引擎,能夠透過 P2P (Peer-to-peer) 的方式分享索引資訊跟分散爬蟲任務。

網際網路巨大無比,只靠自己建立與維護索引資料根本癡心妄想,YaCy 在這方面就相對理想,讓一群人分散 Google 這種資訊巨獸才能建立的資料庫難度。

話雖如此,架設過程才發現要對外暴露埠才能對整個 YaCy 網路進行貢獻:

目前我的 Homelab 出於安全性考慮是沒有對外暴露的,不過我依然可以在本地建立自己的索引資料庫,至於「做出貢獻」這件事大概要緩緩以後再來處理了。

K8s 故障排除

架設的過程有遇到一點問題,在這邊紀錄一下問題跟解決辦法。

$ kubectl logs yacy-0 -n search-stack
****************** YaCy Web Crawler/Indexer & Search Engine *******************
**** (C) by Michael Peter Christen, usage granted under the GPL Version 2 ****
**** USE AT YOUR OWN RISK! Project home and releases: https://yacy.net/ ****
** LOG of YaCy: DATA/LOG/yacy00.log (and yacy<xx>.log) **
** STOP YaCy: execute stopYACY.sh and wait some seconds **
** GET HELP for YaCy: join our community at https://community.searchlab.eu **
*******************************************************************************
[ YaCy v1.940 by Michael Christen / www.yacy.net ]
-------------------------------------------------------------------------------
Jan 12, 2026 11:49:04 AM net.yacy.cora.util.ConcurrentLog enQueueLog
WARNING: * could not create directories /opt/yacy_search_server/DATA/LOG
could not copy yacy.logging: /opt/yacy_search_server/DATA/LOG/yacy.logging (No such file or directory)
STARTUP: Trying to load logging configuration from file /opt/yacy_search_server/DATA/LOG/yacy.logging
could not find logging properties in homePath=/opt/yacy_search_server
Jan 12, 2026 11:49:04 AM net.yacy.cora.util.ConcurrentLog enQueueLog
WARNING: * java.io.FileNotFoundException: /opt/yacy_search_server/DATA/LOG/yacy.logging (No such file or directory)
java.io.FileNotFoundException: /opt/yacy_search_server/DATA/LOG/yacy.logging (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(Unknown Source)
at java.base/java.io.FileInputStream.<init>(Unknown Source)
at net.yacy.cora.util.ConcurrentLog.configureLogging(ConcurrentLog.java:385)
at net.yacy.yacy.startup(yacy.java:180)
at net.yacy.yacy.main(yacy.java:832)

Jan 12, 2026 11:49:05 AM net.yacy.cora.util.ConcurrentLog enQueueLog
SEVERE: * FATAL ERROR: Permission denied
java.io.IOException: Permission denied
at java.base/java.io.UnixFileSystem.createFileExclusively0(Native Method)
at java.base/java.io.UnixFileSystem.createFileExclusively(Unknown Source)
at java.base/java.io.File.createNewFile(Unknown Source)
at net.yacy.yacy.startup(yacy.java:194)
at net.yacy.yacy.main(yacy.java:832)

Jan 12, 2026 11:49:05 AM net.yacy.cora.util.ConcurrentLog shutdown
INFO: shutdown of ConcurrentLog.Worker void because it was not running.

典型的容器權限問題,在 K8s 可以透過 securityContext 處理:

apiVersion: apps/v1
kind: StatefulSet
spec:
template:
spec:
securityContext:
runAsUser: 100
runAsGroup: 1000
fsGroup: 1000

但是該如何找到正確的 UID 和 GID 呢?OCI image 調查神器:dive

SearXNG 也有類似的問題:

Wei Ji

前情提要

info

日期是根據 Git Commit 紀錄跟螢幕截圖時間推論的,但是不是真正精準的時間,因為 Git Commit 可能延後一段時間才推送,截圖的問題則是不是每一個當下都有截圖。

2025-10-04 左右: LiteLLM 和 Langfuse 上線。 2025-10-11 左右: Local Deep Research 上線。

2025-10-14 左右: 開始比較頻繁使用 Local Deep Research。

2025-10-22 左右:Homelab 開始出現當機異常。

  • 圖表的斷層就是伺服器完全當機讓資料採集的機能癱瘓造成的。
  • 可以觀察到 CPU 跟 RAM 異常使用是 Langfuse 服務簇的 ClickHouse 造成的。

2025-10-23 的紀錄:

2025-11-01 左右:連帶 Grafana 服務簇也失效,失效後就把 Langfuse 關掉然後沒有修復它了,直到最近 (2026-01) 佈署 K8s 才準備把服務遷移過去。

Grafana 失效前我有關查到 Docker 在重新拉取 image,推測是因為我忘記鎖定 image 版本造成的。


整個過程中出現過幾種不同的情況:

  • 電源鍵強制關機後恢復正常。
  • 電源鍵強制關機後 LiteLLM 異常。
    • Stack 刪掉後重新佈署後恢復正常。
  • 電源鍵強制關機後 Langfuse 異常。
    • 根據 clickhouse log 找到有問題的 block,刪除後重新佈署恢復正常(如下)。
2025.11.01 11:00:20.208590 [ 652 ] {c14fc20e-f2b0-45cb-939c-0fe47de9811c::202510_16681_17212_76} <Error> virtual bool DB::MergePlainMergeTreeTask::executeStep(): Exception is in merge_task.: Code: 40. DB::Exception: Checksum doesn't match: corrupted data. Reference: 52f594fc9d7f3ee0495f8f089d3882cf. Actual: c92bc03d0da25742da61b67ce5764aac. Size of compressed block: 3640. The mismatch is caused by single bit flip in data block at byte 2581, bit 2. This is most likely due to hardware failure. If you receive broken data over network and the error does not repeat every time, this can be caused by bad RAM on network interface controller or bad controller itself or bad RAM on network switches or bad CPU on network switches (look at the logs on related network switches; note that TCP checksums don't help) or bad RAM on host (look at dmesg or kern.log for enormous amount of EDAC errors, ECC-related reports, Machine Check Exceptions, mcelog; note that ECC memory can fail if the number of errors is huge) or bad CPU on host. If you read data from disk, this can be caused by disk bit rot. This exception protects ClickHouse from data corruption due to hardware failures: While reading or decompressing /var/lib/clickhouse/store/c14/c14fc20e-f2b0-45cb-939c-0fe47de9811c/202510_16681_17060_75/data.bin (position: 824782, typename: DB::ReadBufferFromFilePReadWithDescriptorsCache, compressed data header: <uninitialized>): (while reading column ProfileEvent_ReadBufferFromFileDescriptorRead): (while reading from part /var/lib/clickhouse/store/c14/c14fc20e-f2b0-45cb-939c-0fe47de9811c/202510_16681_17060_75/ in table system.metric_log (c14fc20e-f2b0-45cb-939c-0fe47de9811c) located on disk default of type local, from mark 2 with max_rows_to_read = 916, offset = 0): While executing MergeTreeSequentialSource. (CHECKSUM_DOESNT_MATCH), Stack trace (when copying this message, always include the lines below):
  • 電源鍵強制關機後 Langfuse 異常。

    • 根據 clickhouse log 找到有問題的 block,刪除後重新佈署依然無法恢復。
    • 把 clickhouse 建立全新的 Volume 後恢復正常。
  • 有幾次伺服器是完全沒螢幕反應,有反應的幾次可以在終端觀察到 CPU Lock:

watchdog: BUG: soft lockup - CPU#* stuck for **s!

仰賴鏈

  1. Biomes 重構的進展目前面臨大量的 Schema 型別修復工作,我需要借助 LLM 之力來解決。

  1. 我拒絕使用基於聊天或 IDE Agent 的 LLM 方案。
  2. 我想使用 ComfyUI 來構造專門用來處理 Schema 修復問題的 pipeline。
  3. 我在本地使用 LLM 工具我要求必須在有 LLM Observability 的前提下使用,所以我必須先修復我的 LLM Observability。
  4. 在有伺服器崩潰的前車之鑑,我要求必須在有 Observability 的前提下佈署 LLM Observability,所以我必須先修復我的 Observability。
  5. 但是我不想在現有的節點上修復 Observability,而是在新的節點使用 K8s 並佈署 Observability,因此我要先學習使用 K8s。

Observability 重建的當前狀態

到昨天 (2026-01-06) 為止,我已經:

  1. 設定新的節點,安裝 K3s。(2025-12-24)
  2. 整理之前跟 K8s 有關的知識點。(2025-12-28~2026-01-02)
  3. 釐清 Ingress/Load Balancer 的概念。(~2026-01-05)
  4. 釐清 PV/PVC 的概念。(~2026-01-05)
  5. 用 Helm 安裝 Longhorn。(2026-01-05)
  6. 用 Helm 安裝 Kube-Prometheus-Stack。(2026-01-06)

用 K8s 內的 Prometheus 和 Grafana 實例取代原本的,原本的兩張 Dashboard 則是本來就有 JSON 檔直接移過去。原本的伺服器指保留 Cadvisor 供新的 Prometheus 採集資料。

目前關於容器的 Dashboard 是處於兩個節點資料混在一起的狀態,暫時先不處理 Query 需要更新的問題:

Node Dashboard 則是直接接軌新的 Node Exporter 的資料,暫時也先不處理舊節點的資料:

info

Kube-Prometheus-Stack 其實有提供不少 Dashboard 用來觀察新節點,我就不在這邊一一列舉了。

Prometheus 安裝筆記

跟 Longhorn 一樣直接使用 Helm 安裝:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

helm repo update

helm show values prometheus-community/kube-prometheus-stack --version 51.6.0 >values.yaml

helm install prometheus-stack prometheus-community/kube-prometheus-stack \
-f values.yaml \
-n prometheus-stack \
--create-namespace \
--version 51.6.0

values.yaml 倒是有做一些調整:

  • 時區
  • 適應當前 K8s (K3s) 的 Ingress 為 Traefik
  • 對內網 DNS 暴露 Grafana
  • 適應當前 K8s 持久化實例為 Longhorn
  • 從舊 Node 的 Cadvisor 採集資料
grafana:
defaultDashboardsTimezone: Asia/Taipei
ingress:
ingressClassName: traefik
hosts:
- grafana.observable.gas.arachne

prometheus:
prometheusSpec:
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: longhorn
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
additionalScrapeConfigs: |
- job_name: beta-cadvisor
static_configs:
- targets:
- web.cadvisor.arachne

LLM Observability 重建的當前狀態

LiteLLM 雖然在 OpenRouter 排行榜蟬聯前幾名,但是實際使用下來給我的感覺不太穩定(經歷過加密 Bug、當機後無法正常復位)。

另一方面,Langfuse 給我的感覺則是相對肥大(服務簇需要同時配置 ClickHouse、MinIO、Redis、PostgresSQL),但是我又沒有使用到裡面的每一個功能。更別提 ClickHouse 就是表面上這次事故的主因。

因此我打算評估其他 LLM Gateway/Observability 方案,目前看上 Bifrost,試用了一下感覺不錯,就是它的資料庫配置稍微有點麻煩,這個部份可能之後再提。

Wei Ji

背景

我的 Homelab 是透過筆電和 USB 外接 DAS (Direct Attached Storage) 建構的,並且採過一些小坑。最近在建置新的節點時,發現我把相關的資訊分散在不同的筆記和 IaC (Infrastructure as Code) 裡面,於是想說趁這個機會整理一下資訊。

電池充電上限

Framework 的話可以在 BIOS 設定充電上限:

(從 Framework 的論壇借用螢幕截圖

另外一台 Lenovo 的筆電則是靠這個設定:

https://github.com/makifdb/lenopow

我會把把充電上限設在 80% 避免筆電作為伺服器長時間插著充電造成電池膨脹。

筆電螢幕問題

讓筆電不會因為螢幕蓋上而進入休眠模式,編輯 /etc/systemd/logind.conf

HandleLidSwitch=ignore

執行指令重啟服務:

systemctl restart systemd-logind.service

但是上述設定會造成另外一個問題:螢幕蓋著還是繼續發光,於是還需要一個步驟,編輯 /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT 插入 consoleblank=60

這會讓螢幕在 60 秒後熄滅。

完整 Ansible Playbook
- name: Setup laptop
hosts:
- arachne-node-beta
tasks:
- name: check if consoleblank is configured in the boot command
ansible.builtin.lineinfile:
backup: true
path: /etc/default/grub
regexp: '^GRUB_CMDLINE_LINUX_DEFAULT=".*consoleblank=60'
state: absent
check_mode: true
register: grub_cmdline_check
changed_when: false
- name: insert consoleblank if missing
ansible.builtin.lineinfile:
backrefs: true
path: /etc/default/grub
regexp: '^(GRUB_CMDLINE_LINUX_DEFAULT=".*)"$'
line: '\1 consoleblank=60"'
when: grub_cmdline_check.found == 0
notify: update grub
- name: Set HandleLidSwitch
ansible.builtin.lineinfile:
backrefs: true
path: /etc/systemd/logind.conf
regexp: "#?(HandleLidSwitch=)(?:.*)$"
line: '\1ignore'
notify: Restart logind
handlers:
- name: update grub
ansible.builtin.command: update-grub
- name: Restart logind
ansible.builtin.systemd_service:
name: systemd-logind
state: restarted

DAS 掛載

當服務透過 Docker 跑在容器內,持久化資料卻儲存在透過 USB 連線的外部儲存中,這個仰賴關係需要額外處理。

自動化載

編輯 /etc/fstab 加入以下內容:

# DAS
UUID=7f858b7e-f942-45b3-92dd-8c99b497b6a4 /mnt/das-storage ext4 defaults,nofail,x-systemd.automount 0 2

修改後執行:

systemctl daemon-reload
info

UUID 可以透過 lsblk -f 之類的指令獲得。

設定仰賴

新增檔案 /etc/systemd/system/docker.service.d/override.conf

[Unit]
After=mnt-das\\x2dstorage.automount
ConditionPathExists=/mnt/das-storage
systemctl restart docker.service

確保 Docker Daemon 在 DAS 掛載後才運行。

DAS SMART 檢查

因為電腦沒有直接和硬碟建立連線而是隔著一層 DAS,因此不能使用普通的 smartctl 指令確認,而必須使用額外的參數1

smartctl -a -d jmb39x-q,0 /dev/sdd
smartctl -a --device jmb39x-q,1 /dev/sdd
smartctl -a -d jmb39x-q,2 /dev/sdd
smartctl -a --device jmb39x-q,3 /dev/sdd

Footnotes

  1. QNAP External RAID Manager - Export SMART Data - QNAP NAS Community Forum. Retrieved 2026-01-03, from https://forum.qnap.com/viewtopic.php?p=873575&sid=573c6149a1276e4ab8d9f4785f1c6029#p873575

Wei Ji

最近在 Homelab 架了一個 PyPI 快取伺服器,選擇使用 devpi

Docker 映像檔則是使用:

然後遇到了一個怪問題,在這邊隨便紀錄一下:

INFO  [IDX] Committing 2500 new documents to search index.
[IDX] Error during indexing in /usr/local/lib/python3.13/site-packages/devpi_web/whoosh_index.py:401:handler whoosh.index.LockError

症狀就是服務長時間佔用 CPU 長達一、兩天,連進去容器下 top 看到一堆像是殭屍的 process(實際上暫用 CPU 的只有一個)。

最後只保留 /+files 這個資料夾、把其他快取的檔案刪掉再啟動伺服器就正常了。看起來像是 devpi 會下載整個 PyPI 的索引資料,但是同步完成前有什麼錯誤造成檔案損壞後無法從錯誤中恢復過來,所以一直保持「下載與處理索引」的狀態佔用 CPU。

Wei Ji

最近在清理我放在瀏覽器裡面的書籤,搭配最近架設的 Homelab ,以下是我的 SOP:

  1. 它是 GitHub 專案。
    1. 它超過兩個月沒更新:Clone 下來丟 Gitea
    2. 否則:餵給 Tiny Tiny RSS 訂閱1
  2. 它是 PDF:丟 Kavita
  3. 它是圖片:丟 Pinry
  4. 它是 YouTube 影片:丟 Jellyfin
  5. 它的主要價值是平台價值:暫時保留。
  6. 它是維基百科:暫時保留。
  7. 其他不宜直接備份原因:暫時保留。
  8. 否則:備份到 ArchiveBox

Footnotes

  1. RSS feeds for your Github releases, tags and activity. Retrieved 2025-04-28 from https://medium.com/@vilcins/cbda2c51373

Wei Ji

最近在自架 RSS 訂閱器,目前是使用 Tiny Tiny RSS,不過這裡還是把當時查資料時的其他候選方案簡單列一下。

參考

Wei Ji

最近在建置自己的 Homelab,入口網站或儀表板 (Dashboard) 的方案其實不少,我自己目前是使用 Dashy,不過這裡還是把當時查資料時的其他候選方案簡單列一下。

選擇 Dashy 沒有什麼特別的理由,就是星星數夠高、畫面簡單以及仍然保有擴充的潛力,這裡是它支援的資訊欄 (Widget)

其他參考資料

Wei Ji

最近在自架專案管理的工具,目前是使用 Vikunja,不過這裡還是把當時查資料時的其他候選方案簡單列一下。

Planka 明眼人都看得出來它是衝著 Trello 去的,因此作為一個「Kanban 本位主義」的專案管理軟體,使用案例著重在看板本身,對於卡片 (Ticket/Issue) 的管理就稍微薄弱,比如沒有人類友善的流水號可以辨別卡片。

OpenProject 則是側重卡片管理,可以堆疊巢狀結構也可以建立關聯,但是 UI 顯得比較老氣一點。

Wekan 和 Planka 一樣屬於「Kanban 本位主義」,可以從設定啟用人類友善流水號,不過對於卡片管理的功能比較缺乏,巢狀堆疊的設計不是很理想,而且沒辦法建立其他卡片關聯。