CentOS Stream 8 舊主機搬到 Ubuntu 26.04 新主機:Cursor agent 在一整晚遷移裡做了什麼

一台跑了多年的 CentOS Stream 8 雲端虛擬機,要搬到同一家雲端上的新機:Ubuntu 26.04。一個晚上把問題追蹤、內容網站、監控、Git 代管、網頁代理和一個很小的 PHP 測試站依序遷過去;無線網路控制器確認設備退役後直接關掉。DNS 留在舊主機。

這篇記的不是清單,而是這次協作實際怎麼分工。人決定順序與切換時機;Cursor 裡的 AI agent 負責把限制記住、把步驟做完、把結果核對到可以復查。

說明:本文已去識別化。主機名稱、IP、網域、帳號與內部系統的對外名稱都不寫。軟體名稱保留,因為那是技術內容。

一、先講好什麼不能動

遷移最容易出事的,不是某一條指令打錯,而是做到第三個服務時,把第一個服務的限制忘了。這次把限制先講清楚,讓 agent 整晚都用同一組規則:

  • 不停舊主機的網頁伺服器與 SSH。舊站要停,就改成靜態的停用頁,不是把整台網頁伺服器關掉。
  • 密碼、私鑰、帳號雜湊不進對話,也不進終端機紀錄。
  • 監控只能有一台在收資料。舊的收集器停掉之後,不可以被下一次重開機叫醒。
  • DNS 有兩套權威。agent 只改其中一套;另一套由人改。沒改完之前,不把舊站改成停用頁。
  • 已經遷完、確認正常的站,後面的工作不再動它。

這些規則不是寫在第一則提示詞就算數。對話中途被壓縮之後,agent 仍然按同一組限制做下一個服務。這是這次它比「再開一個會談、把步驟貼上去」有用的地方。

二、一個晚上實際搬了什麼

問題追蹤

先在新主機排跑,備份對過雜湊再切。舊站改成靜態維護頁,網頁伺服器繼續跑,其他還指向舊主機的名稱不會一起死掉。資料庫備份放到另一台儲存設備,雜湊相同之後才刪除主機上的那份。

內容網站

用新主機已有的 PHP-FPM,不裝進 Apache 的 mod_php。這跟前一篇 PHP2Shell 應對實錄 是同一個判斷:mod_php 的暴露面比 FPM 大。憑證從舊主機複製過來,但複製時不能讓私鑰跟命令的標準輸入混在同一條管線裡,否則私鑰會落進終端機紀錄。

監控

Zabbix 先停在長期支援版做小版本更新,再由人決定要不要跳到下一個功能版。agent 的評估是:技術上可以直接升,但這個功能版不是長期支援,而下一個長期支援版已經在釋出候選。人仍決定升。升級前先備份;資料庫結構在服務啟動時自動跑完;網頁目錄換了,站台根目錄要跟著改,否則登入頁會指到一個只會印出更新訊息的檔案。

六個監控套件之後一直鎖定。鎖定擋住的不是自動安全更新(那個來源根本沒被列入),而是下一次有人手動升級時,不要順便把監控升上去、重啟服務、再改一次資料庫。

Git 代管

裸倉庫用參照雜湊比對,不靠「複製看起來完成」。舊的推送入口改成一句拒絕訊息,這樣還指向舊位址的人會馬上知道服務已經搬走,而不是把資料推進一個沒人在收的倉庫。新主機上的網頁瀏覽不能用全站別名,否則每一個網站都會多出同一條路徑。

網頁代理

Squid 沒有 DNS 名稱,使用者直接把代理指到 IP。新舊兩邊可以同時聽,所以先讓新的上線,舊的繼續收連線,等使用者改設定。這裡踩到一個典型的防火牆分層:主機自己的防火牆開了,雲端的防火牆仍把那個不常見的埠丟掉。從主機自己連自己會成功,從外面連會逾時。沒有從外面測過之前,不能說通了。

測試站與已退役的控制器

測試站只有四個檔案,但裡面有 PHP。兩套 DNS 都指到新主機之後,才把舊站改成靜態的服務不可用,並把 PHP 檔移出網站目錄。舊主機的 PHP 還停在沒有修補的版本上,網站還能執行它,就等於把 PHP2Shell 的入口留著。

無線網路控制器的服務早就失敗了,網站卻還把請求轉進去。設備退役之後,把網站改成靜態停用頁,關掉只給它用的埠,並讓開機不再嘗試啟動它。這不是遷移,是把宣告退役做成真的關掉。

三、Cursor agent 實際承擔了哪一段

若把這次工作想成「問 AI 該怎麼搬」,會看走它的作用。它做的是下面這幾件事。

  • 把限制當成執行條件,不是當成備註。每做下一個服務,先確認上一個服務還在、不該動的 DNS 沒被改、舊的收集器沒有被叫起來。
  • 核對比「指令回傳 0」更嚴。備份要雜湊相同,登入頁要是新版本的文件連結,兩套權威 DNS 要分別查,旁邊已經上線的站要再要求一次。有一次升級後服務已經跑起來,但登入頁還沒指到新的網頁目錄,這件事是核對時才抓到的。
  • 把「看起來已經好了」拆開。憑證檔複製到新主機,不等於會自動續期。套件列表裡看得到更新,不等於這台機器的自動更新會裝上去。這兩件事都是排檢時才分開的。
  • 每做完一件就寫內部紀錄。記錄裡有時間、做了什麼、哪裡還沒切完。隔天不用靠聊天記錄回憶。
  • 評估與決定分開。監控要不要離開長期支援版,agent 寫出支援期限與這台主機的條件;要不要升,是人說了算。

Cursor 這裡的 agent 不是另開一個聊天視窗。它在同一個工作區裡連主機、改設定、查 DNS、寫紀錄,而且下一步看得到上一步的結果。對這種跨好幾個小時、每一步都會影響下一步的工作,這比把步驟貼進聊天室再自己照做穩。

四、哪些事不該交給它自己決定

  • 沒有核准,不切正式流量。新服務可以先排跑、先驗證;把名稱指過去,要等人說可以。
  • 不為了「看起來比較整齊」而改已經在用的設定。Git 倉庫裡有一條沒有上游的分支,就讓它繼續沒有上游。
  • 不開全站的別名或管理頁。安裝程序預設會把這些路徑掛到每一個站,要馬上關掉。
  • 不把「退役」留成一個還在轉送的網站,也不把「服務沒在跑」留成「重開機就會再跑起來」。

五、搬完之後還剩什麼

新主機的代理已經對外服務,舊代理仍然有人在用。這一項要等使用者改完才能關。其餘還開著的,多數不是要搬的應用:主機管理介面仍對外開放、資料庫裡是已遷走服務的舊副本、快取與 PHP 處理程序還在,但網站已經不把請求送過去。監控程序沒在跑,開機卻仍會啟動。舊主機若重開,會再多出一台收集器。

這也是 agent 適合做的最後一步:不是再搬一個服務,而是把「還開著什麼、哪些不用搬、哪些重開機會回來」列成一張可以交接的清單。

六、這次學到的事

  1. 先寫限制,再讓 agent 動手。限制越具體,它越能在長工作裡替你擋住下一步。
  2. 切換前先讓新舊並存。監控這類不能兩台同時收資料的,例外;代理這類可以並存的,就別急著關舊的。
  3. 驗證要從外面做。主機自己連得通,只代表本機沒壞。雲端防火牆、DNS 快取、另一套權威,都要單獨看。
  4. 停用頁比關掉服務安全。還有別的名稱指著這台主機時,把網頁伺服器停掉,傷害比留一頁靜態說明更大。
  5. 「已經搬走」要再清點一次。程序沒在跑,不代表開機不會再跑;憑證在磁碟上,不代表會自動續期。

人決定要搬什麼、何時切、哪一版可以上。Cursor agent 把這些決定做成一連串能複查的步驟,並在下一步開始前先確認上一步還成立。

本文由作者與 Cursor agent 協作完成。

發佈留言