安裝與升級疑難排解

安裝程式中斷、升級沒生效,或服務起不來時該怎麼辦。直接搜尋錯誤訊息原文 —— 這裡的每一條都對應安裝程式裡真實存在的失敗路徑,或某個版本實際改變的行為。

從這裡開始

安裝程式中斷了,我該先看哪裡?

安裝程式會印出一行紅色的 ✗,指出失敗的原因。用那一行的文字在本頁搜尋。

如果安裝完成但服務不正常,下一個要看的是服務自己的記錄:

sudo journalctl -u jt-proxense -n 50 --no-pager

確認實際裝上的版本:

jt-proxense version

安裝失敗

This installer must run as root

一鍵安裝需要 sudo。它會建立系統帳號、寫入 systemd unit,並安裝到 /opt 底下。

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh | sudo bash

注意 sudo 要加在 bash 上,不是加在 curl 上。

No supported package manager (apt / dnf / yum / pacman / zypper) found

安裝程式透過這五種其中之一來裝系統套件。如果你的發行版不在其中,請先手動裝好 前置需求再重跑一次 —— 已經存在的它會略過:

git、python3 (3.10 以上)、python3-pip、python3-venv、systemd
Python 3.10+ required (found ...)

最常見於 RHEL/Rocky/AlmaLinux 8,它們的 python3 是 3.6:

sudo dnf install python3.11 python3.11-pip
curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh | sudo bash

安裝程式會挑它找到的最新直譯器,並讓 systemd unit 指向它,所以你不需要改系統預設值。

/opt/jt-proxense is non-empty but not a git checkout. Refusing to overwrite.

目標目錄裡已經有東西,而且不是安裝程式放的 —— 可能是舊的手動複本,或解壓縮出來的 檔案。它選擇拒絕,而不是刪掉你的檔案。

把它移開再重跑:

sudo mv /opt/jt-proxense /opt/jt-proxense.old
curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh | sudo bash

或是裝到別的位置:

curl -fsSL … | sudo JT_PROXENSE_INSTALL_DIR=/opt/jtp bash

你的資料不在那個目錄裡 —— 使用者、稽核記錄與設定放在 /var/lib/jt-proxense 與 /etc/jt-proxense,不會受影響。

Smoke test failed — a runtime module did not import

Python 相依套件裝好了,但其中一個載入不了。失敗訊息上一行會指出確切的模組名稱, 然後:

sudo python3 -m pip install --upgrade -r /opt/jt-proxense/requirements.txt
sudo systemctl restart jt-proxense

在 Debian 12 以上/Ubuntu 24.04,系統的 Python 環境被標記為 externally managed (PEP 668)。安裝程式會自動偵測並加上 --break-system-packages;如果你是 手動安裝,需要自己加同一個旗標。

git clone 或 curl 失敗/主機連不到 GitHub

安裝程式需要連到 github.com 與 raw.githubusercontent.com。在 proxy 後面的話,要把它匯出給整條指令 —— 只設在你自己 shell 裡的 proxy 傳不進 sudo 的環境:

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh \
  | sudo https_proxy=http://proxy:3128 bash

完全離線的主機,請在別處 clone 下來,複製到 /opt/jt-proxense, 然後在裡面執行 install.sh。

升級失敗

要怎麼升級?git pull 就夠了嗎?

重跑同一行安裝指令。它是冪等的,而且它就是升級路徑:

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh | sudo bash

單純 git pull 不夠。 它只抓程式碼,不會重新安裝 systemd unit、不會更新 Python 相依套件,也不會套用檔案擁有者與權限的修復 —— 結果是新程式跑在舊的 unit 底下。如果你已經這樣做了,事後補跑一次一鍵指令即可, 其餘它會修好。

安裝程式會裝哪一版?可以指定嗎?

從 v1.1.0 起,它安裝的是最新的發行標籤,而不是 main 的頂端 —— 所以同一行指令在不同兩天執行會得到相同的程式,而且有東西可以回滾。

指定特定版本,或刻意追開發分支:

# 指定某一個發行版
curl -fsSL … | sudo JT_PROXENSE_REF=v1.1.0 bash

# 追開發分支
curl -fsSL … | sudo JT_PROXENSE_REF=main bash
Your local changes would be overwritten/dubious ownership

安裝程式會把 checkout 重設到它要安裝的那個版本,所以在 /opt/jt-proxense 裡面的修改是刻意被丟棄的。要保留的設定請放在 config.yaml,那個檔案永遠不會被覆寫。

如果 git 回報 detected dubious ownership,那是因為 checkout 屬於 root (v1.1.0 起刻意如此 —— 服務不該能修改自己的程式碼)。用 root 執行 git,或交給安裝 程式處理:

sudo git -C /opt/jt-proxense status
它說升級完成了,但畫面還是舊版本

先確認伺服器實際跑的是什麼:

jt-proxense version
sudo systemctl status jt-proxense --no-pager | head -5

如果那已經是新版,就是瀏覽器還留著舊的 index.html。頁面會偵測到過期的 bundle 並自己重新載入,但強制重新整理可以立刻解決: Ctrl+Shift+R (macOS 是 ⌘+Shift+R)。

要怎麼回滾到前一個版本?

安裝先前的標籤即可。你的資料庫與設定在安裝目錄之外,不會被動到:

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh \
  | sudo JT_PROXENSE_REF=v1.1.0 bash

資料庫 migration 是單向的。跨過有新增 migration 的版本回滾並不支援 —— 如果你想保留 退路,請在重大升級前先複製一份 /var/lib/jt-proxense/jt-proxense.db。

服務起不來

REFUSING TO START: config.yaml could not be parsed

這個檔案不是合法的 YAML。daemon 選擇拒絕啟動,而不是退回預設值,因為那些預設值會 把驗證關掉並綁定所有介面 —— 一個壞掉的檔案絕對不該安靜地把執行個體打開。

還原上一次變更前自動留下的副本:

ls -lt /opt/jt-proxense/config_backups/
sudo cp /opt/jt-proxense/config_backups/config_YYYYMMDD_HHMMSS.yaml \
        /opt/jt-proxense/config.yaml
sudo chown jt-proxense:jt-proxense /opt/jt-proxense/config.yaml
sudo chmod 600 /opt/jt-proxense/config.yaml
sudo systemctl restart jt-proxense

這一版不認識的設定欄位不算錯誤 —— 從 v1.1.1 起它會被忽略,並在記錄中具名警告, 周圍的設定都會保留。

systemd: Failed to start/unit 因 ReadWritePaths 失敗

從 v1.1.0 起,unit 會逐一列出可寫入的路徑,而 systemd 會拒絕啟動 ReadWritePaths 指向不存在路徑的 unit。重跑安裝程式就會建立它們:

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh | sudo bash

那些路徑是 /opt/jt-proxense/config.yaml、 /opt/jt-proxense/config_backups、 /opt/jt-proxense/.ssh、/var/lib/jt-proxense 與 /etc/jt-proxense。會發生這個狀況,通常是 unit 被手動更新而不是透過 安裝程式。

Address already in use/連接埠 8098 被占用

先找出是誰占住的,然後停掉它或改用別的連接埠:

sudo ss -tlnp | grep 8098

要改的話,編輯 /opt/jt-proxense/config.yaml 裡的 server.http_port 再重啟。全新安裝可以一開始就指定:

curl -fsSL … | sudo JT_PROXENSE_PORT=8099 bash
頁面載入了,但沒有樣式、版面整個擠成一直排

瀏覽器拿到了 JavaScript 卻沒拿到樣式表,通常是手動複製檔案之後,服務帳號讀不到 那些檔案:

sudo chmod 644 /opt/jt-proxense/dist/index.html \
                /opt/jt-proxense/dist/assets/index-*.js \
                /opt/jt-proxense/dist/assets/index-*.css
sudo systemctl restart jt-proxense

重跑安裝程式也會修好,而且更安全。

從介面新增叢集時存不起來

服務會寫入 config.yaml 並且先備份一份,所以兩者都必須是服務帳號可寫。 用 root shell 的重導向去改那個檔案會改變它的擁有者:

sudo chown jt-proxense:jt-proxense /opt/jt-proxense/config.yaml
sudo chmod 600 /opt/jt-proxense/config.yaml
sudo chown -R jt-proxense:jt-proxense /opt/jt-proxense/config_backups
sudo systemctl restart jt-proxense

無法登入

Too many attempts, please try again later

那個 IP 觸發了登入速率限制:五分鐘內失敗五次。冷卻時間過後會自己解除,或是立即解除:

sudo jt-proxense unlock            # 列出目前被鎖的
sudo jt-proxense unlock --all      # 解除全部鎖定

這只會清掉計數器,永遠不會更動任何密碼。

忘記 admin 密碼,或是驗證器裝置遺失

CLI 在服務沒有執行的情況下也能用 —— 它存在的目的就是這個:

sudo jt-proxense reset-password admin
sudo jt-proxense user reset-totp admin
驗證設定弄錯之後完全被關在外面

先把驗證關掉,修好設定,再打開:

sudo jt-proxense auth disable       # 同時只綁定 127.0.0.1
sudo systemctl restart jt-proxense

之後用 sudo jt-proxense auth set-local 重新啟用。

升級到 v1.1.0 之後

所有人在幾次登入失敗後一起被鎖住

升級後最容易遇到的意外。X-Forwarded-For 現在只有來自 loopback 或你 明確指定的位址才會被採信。如果你的反向代理跑在另一台主機上,現在每個請求都帶著 代理的位址,所有使用者因此共用同一個速率限制計數 —— 任何人登入失敗五次,所有人一起被 鎖十五分鐘。稽核記錄也會把代理記成來源。

在 /opt/jt-proxense/config.yaml 裡指定你的代理:

auth:
  trusted_proxies:
    - 192.0.2.10        # 也可以用 CIDR:192.0.2.0/24

然後 sudo systemctl restart jt-proxense 並執行 sudo jt-proxense unlock --all。發生這個狀況時,記錄裡會對每個來源出現一次 ignoring X-Forwarded-For from …。

從其他來源提供的介面在升級後失效

從 v1.1.0 起,跨來源存取預設關閉。如果你的 SPA 與 API 在不同的主機或連接埠, 請指定該來源:

server:
  cors_origins:
    - https://ui.example.net

填 * 會被刻意忽略:萬用字元加上憑證,等於你瀏覽過的任何網站都能用你的 工作階段操作這個 API。

如果介面與 API 同源(預設就是如此,因為程式自己提供 SPA),你完全不需要設定這個。

記錄裡出現 ignoring unrecognised setting(s)

這是資訊性訊息。你的 config.yaml 裡有這一版不認識的欄位 —— 通常是打錯 字,或是來自其他版本的選項。該欄位會被忽略,其餘設定照常生效;請對照 config.example.yaml 檢查拼字。

在 v1.1.1 之前,這會讓服務起不來。如果你在 v1.1.0 而 daemon 因為某個關鍵字參數拒絕 啟動,請升級。

先前被拒絕的「單一叢集角色」使用者,現在可以用了

這是預期行為。在 v1.1.0 之前,角色檢查只讀全域 (*) 授權,所以 jt-proxense user grant bob cluster1 operator 在入口處被忽略了。現在它在 該叢集底下的端點會生效。只授予單一叢集的角色,仍然不適用於使用者管理與全域設定。

檢視目前的授權:

sudo jt-proxense user list

解除安裝

要怎麼完整移除?

一行。它會停止並停用服務,然後刪除安裝目錄、 /var/lib/jt-proxense(使用者、稽核記錄、叢集密鑰)、 /etc/jt-proxense(主金鑰)以及服務帳號:

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/uninstall.sh | sudo bash

它會先要你輸入 remove。在自動化流程裡加 --yes 跳過: … | sudo bash -s -- --yes。

這是不可逆的。 只要還有一點可能想把執行個體找回來,請先匯出 —— 見下一條。

想移除但保留設定和使用者

解除安裝前先匯出。這個封裝包含 config.yaml、SQLite 資料庫 (使用者、角色、稽核記錄、備註)以及能解密叢集密鑰的主金鑰,全部在一個以通關密語 加密的檔案裡:

sudo jt-proxense export-config /root/jt-proxense-backup.enc

把它複製到主機之外,然後再解除安裝。要在任何機器上還原:

curl -fsSL https://raw.githubusercontent.com/jasoncheng7115/jt-proxense/main/install.sh | sudo bash
sudo systemctl stop jt-proxense
sudo jt-proxense import-config /root/jt-proxense-backup.enc --force
sudo systemctl restart jt-proxense

這個封裝裡有主金鑰,請當成憑證看待,匯入完成後就刪掉它。

解除安裝程式回報:run as root 或 no terminal for confirmation

它需要 sudo,理由和安裝程式一樣。第二種訊息代表它是被管線呼叫的, 沒有終端機可以讀你的確認,這正是 --yes 的用途:

curl -fsSL … /uninstall.sh | sudo bash -s -- --yes
解除安裝後還有殘留

解除安裝程式容許缺件,所以不完整的狀態用同樣的指令手動執行即可清乾淨:

sudo systemctl stop jt-proxense; sudo systemctl disable jt-proxense
sudo rm -f /etc/systemd/system/jt-proxense.service /usr/local/bin/jt-proxense
sudo systemctl daemon-reload
sudo rm -rf /opt/jt-proxense /var/lib/jt-proxense /etc/jt-proxense
sudo userdel jt-proxense

如果你裝在別的位置,請代換該路徑 —— 解除安裝程式同樣會遵循 JT_PROXENSE_INSTALL_DIR。

還是卡住

這裡沒有符合的狀況,要怎麼回報?

開一個 issue,附上足以重現問題的資訊:

jt-proxense version
cat /etc/os-release | head -2
python3 -V
sudo journalctl -u jt-proxense -n 100 --no-pager

貼上之前請先遮蔽 PVE 主機名稱與 API token。絕對不要原封不動貼出 config.yaml —— 裡面有憑證。

到 GitHub 開 issue

沒有符合的條目。試試看少打幾個字,或是 開一個 issue.