CentOS 7 → AlmaLinux 10 原地升級
這是一次真實生產遷移(華為雲跳板機,CentOS 7.7 → AlmaLinux 10.2,nginx/Docker/xxl-job/MySQL/NetBird/kubectl 全部保住)沉澱下來的流程。目標是每台機器都能安全、可回滾、可驗證地升上去,同時能批量推進。
核心思路:先摸清 → 快照兜底 → 每跳小步走(預檢→升級→重啟→對比基線→收尾)→ 最後現代化。坑幾乎都出在「非官方的東西」上(自編譯程式、pip 包、第三方源、自打包 openssh、容器重啟策略、VPN 存取路徑),所以勘察階段的價值最大。
什麼時候不要原地升級
先看 survey.sh 的 BLOCKER。以下情況建議新建 AlmaLinux 10 機器遷移業務,而不是原地升級:
- 是 Kubernetes 節點(kubelet 在跑)——要當成叢集級變更來做,按
references/kubernetes.md(已實測 kubeadm 1.21 + dockershim + flannel/ipvs 在 EL10 上可用,3m3w 叢集約 6 小時);也可以 drain 後換新節點 - 宿主機 rpm 裝的資料庫要跨多個大版本(MariaDB 5.5→10.11、PostgreSQL 9.2→16)且資料量大
- 非 x86_64、btrfs、系統碟極小且無法擴容
- CPU 不支援 x86-64-v3(AlmaLinux 10 標準版跑不了;可以只升到 9,或用 AlmaLinux 10 的 x86_64_v2 變體)
- 業務本身就是容器化的、配置都在 compose/k8s 裡——新建機器重新部署往往比原地升級更快
拿不準時把判斷依據告訴用戶,讓用戶選。
工具包
技能目錄 scripts/ 下(全部在本機經 SSH 上傳到目標機 /root/os-upgrade-kit/ 執行):
| 腳本 | 作用 |
|---|---|
remote.py | 本機運行:從資產表(xlsx/csv)取憑證,SSH 執行命令/上傳/下載/等待重啟;開了 keepalive;不列印密碼 |
survey.sh | 唯讀勘察,末尾輸出分級 RISK FLAGS(BLOCKER/HIGH/MEDIUM/INFO) |
backup.sh | 備份配置 + 生成業務基線 baseline.tsv(服務/連接埠/容器/各域名 HTTP 碼/kubectl) |
state.sh / verify.sh | 生成當前狀態 / 與基線對比,輸出 FAIL/WARN |
prep-el7.sh | EL7 準備:repos 修源、update 到 7.9、pipfix、inhibitors |
convert-el8.sh | 源系統是 CentOS 8 時:prep → run(官方 almalinux-deploy)→ post,轉成 AlmaLinux 8 |
swap-openssh.sh | 把自編譯 openssh 換回官方包(含自動兜底) |
hop.sh | 每一跳:prep→pre→report/answer→up→reboot→post |
nginx-static-build.sh | 自編譯 nginx 同版本靜態重編(過渡期一路可用) |
fleet_summary.py | 本機運行:彙總多台 survey 結果 → A/B/C/D 分類、VPN/EL8 標記、金絲雀與分批建議(--groups 讓同業務主備不同批) |
finalize.sh | EL10 收尾:compose 包裝、kubectl 對齊叢集、helm、postfix lmdb、nginx.org 源、cgroup v1 相容、SysV 包裝、legacy iptables 模組、redis→valkey、手工啟動行程轉 systemd 服務 |
k8s.sh | K8s 節點:backup(etcd 快照/配置/二進位/pod 列表,自動 pin 版本)、stop、nfs-off/nfs-on、nm、fix、check、pods(與升級前對比)、jvm-maxram(cgroup v2 下老 JDK 的 OOM) |
本機在 Windows Git Bash 下呼叫時,remote.py 已自動處理 MSYS 把 /root/... 改寫成 C:/Program Files/Git/root/... 的問題。
常用:
export FLEET_INVENTORY="/path/to/servers.xlsx" # 列: name, host, port, username, password(含密碼,切勿提交到倉庫)
S=<技能目錄>/scripts
python $S/remote.py HOST put $S /root/os-upgrade-kit
python $S/remote.py HOST run "bash /root/os-upgrade-kit/survey.sh > /root/os-upgrade-kit/survey.txt; sed -n '/RISK FLAGS/,\$p' /root/os-upgrade-kit/survey.txt"
python $S/remote.py HOST wait-ssh --timeout 3600
流程
階段 0:開工前必須和用戶確認的事(缺一不可)
這些是「用戶的決定」,不要替用戶假設:
- 雲端快照/整機備份已做(系統碟+資料碟)。這是唯一可靠的回滾手段,Claude 在機器內部做不了。
- 停機窗口:每跳一次重啟,全程約 2~4 小時(多數時間在下載包,鏡像源快慢決定);業務會中斷數次。
- 帶外控制台可用:阿里雲 VNC / 華為雲 VNC / AWS EC2 Serial Console(各自前置條件見
references/cloud-providers.md)。升級卡在 initramfs 時只能從這裡救。 - 存取路徑:你(本機)是怎麼連到這台機器的?如果走的 VPN/隧道就執行在這台機器上(survey 會報 BLOCKER),重啟和容器操作要格外小心——我們的真實事故就是
docker stop了全部容器導致 NetBird 路由容器開機不自啟,整個 VPC 失聯。 - 哪些東西已經沒用:勘察完把清單給用戶,讓用戶確認哪些元件/站點可以下線(例如 PHP、舊站點)。刪之前說清楚影響面(比如「刪 PHP 會導致 xxx 站點 404」),由用戶拍板。
階段 1:勘察 + 備份 + 基線
- 上傳工具包,跑
survey.sh,逐條看 RISK FLAGS,結合references/app-compat.md給每條定處理方案。 - 跑
backup.sh(預設放非系統碟如 /data),記下BACKUP_DIR。baseline.tsv是後面每跳對比的依據——升級前就已經異常的項目(如 502)要在報告裡註明「升級前即如此」,免得事後說不清。 - 把勘察結論、處理方案、需要用戶決定的事一次性告訴用戶(見階段 0)。
階段 2:EL7 準備
bash prep-el7.sh repos <MIRROR> # 國內推薦 https://mirrors.aliyun.com;阿里雲內網 http://mirrors.cloud.aliyuncs.com;海外 upstream
bash prep-el7.sh update # 到 7.9;出現 Failed 的包基本是 pip 衝突
bash prep-el7.sh pipfix
bash prep-el7.sh inhibitors
bash swap-openssh.sh # 僅當 survey 報 openssh 非官方包;完成後用【新連線】驗證
自編譯 nginx 等依賴 EL7 庫的程式:EL7 上就用 nginx-static-build.sh 靜態重編(或接受過渡期停用,最後裝官方包)。
宿主機資料庫:先邏輯備份。
源系統是 CentOS Linux 8 / CentOS Stream 8 時(survey 會報 BLOCKER):ELevate 不支援以它為源,跳過上面的 EL7 準備,先做同版本轉換,然後從階段 3 的 8→9 開始:
bash convert-el8.sh prep <MIRROR> # 修源(vault、EPEL8)、pip 衝突、多餘 kernel-devel
bash convert-el8.sh update # 升到 8.5(almalinux-deploy 要求 ≥8.4)
bash convert-el8.sh run <MIRROR> <almalinux-deploy.sh> # 官方 almalinux-deploy:-d 允許降級,-l 用鏡像
bash hop.sh reboot && python remote.py HOST wait-ssh # 重啟進入 AlmaLinux 核心
bash convert-el8.sh post # 刪除非 AlmaLinux 核心,dnf upgrade
國內伺服器通常存取不了 GitHub raw,需要先在本機下載 almalinux-deploy.sh 再上傳。
階段 3:每一跳(7→8、8→9、9→10 各做一遍)
bash hop.sh prep <MIRROR> # 裝本跳 ELevate、目標源和 docker-ce/epel 廠商源改鏡像、關掉 test/nightly 段、pip 清理、探測可達
bash hop.sh pre # 背景 preupgrade 並等待 → 自動 report
bash hop.sh answer # 已知安全問題自動回答;其餘阻斷項按 report 裡的修復命令處理,再 pre,直到 0 阻斷
bash hop.sh report # 重點看「將被刪除的包」和「關鍵包去向」——有業務依賴的要先想辦法
bash hop.sh up # 背景下載+生成升級環境
bash hop.sh reboot [額外要停的容器名] # 只停 restart=always 的有狀態容器;VPN 容器和 unless-stopped 容器不碰;業務方說「別動」就用 --no-stop
python remote.py HOST wait-ssh --timeout 3600 # 本機等;超時 → 控制台,見 references/recovery.md
bash hop.sh post <MIRROR> # 檢查失敗標記、清 leapp/舊核心/殘留源、dnf upgrade
bash verify.sh <BACKUP_DIR> # FAIL 必須處理;WARN 判斷是否預期
每跳之後修復業務問題再進下一跳,不要攢到最後。各跳特有的阻斷項和處理見 references/pitfalls.md 的「按跳分類」。
長任務一律背景跑:腳本已用 systemd-run 執行 leapp/dnf,SSH 斷了不影響;你只管輪詢。自己寫命令時也照此辦理,不要讓一個 SSH 會話掛幾十分鐘。
階段 4:EL10 收尾與現代化
按用戶需要選用 finalize.sh 子命令,並參考 references/app-compat.md:
- 自編譯 nginx → nginx.org 官方 stable 包(
ssl on;改listen 443 ssl;、TLS1.2/1.3、憑證放 /etc/nginx/ssl、systemd 自啟) docker-composev1 → 包裝到 compose v2;cgroup v1 老應用加 shim- kubectl → pkgs.k8s.io 源、與叢集同 minor;helm → 最新 v3
- postfix hash → lmdb;rc.local 裡的啟動項改成 systemd unit
dnf -y upgrade全量更新,確認dnf check-update為空- 最後再重啟一次做開機自啟測試,跑
verify.sh,全部服務自己起來才算完成
階段 5:交付報告
用 assets/report-template.md 的結構寫給用戶:版本對比、業務對比表(升級前/後)、做過的相容性改動、遺留與需要用戶處理的事(憑證到期、沒修好的雲廠商插件等)、備份位置。批量場景同時更新資產追蹤表(assets/fleet-tracker.csv)。
階段 6:沉澱經驗(回饋技能倉庫)
這個技能靠真實執行不斷進化,專案地址:https://github.com/seaworld008/centos7-lighthouse 。每次真實執行之後(成功、失敗、回滾都算):
- 按倉庫的
field-reports/TEMPLATE.md寫一份脫敏實戰報告(不寫真實 IP、域名、主機名、憑證)。 - 新問題寫進
references/pitfalls.md;能自動檢測的加進survey.sh;能自動處理的改進腳本。 - 如果當前工作目錄就是該倉庫,按其
AGENTS.md執行;否則把報告交給用戶,建議提交 issue 或 PR。
關鍵原則(每條都對應一次真實踩坑)
- 重啟前絕不
docker stop $(docker ps -q):unless-stopped的容器被手動停過就不會自啟;VPN 容器停了你便失聯。用hop.sh reboot。 - pip 裝進系統 site-packages 的東西必須在每跳前挪走:它們和 rpm 檔案衝突時,升級會在 initramfs 裡半途失敗,系統卡在緊急模式且處於「半升級」狀態。
- 不認識的、非官方的東西都要先處理:自編譯 openssh、自編譯 nginx、第三方 repo、
mysql-community-libs取代了mariadb-libs……leapp 不管它們,升級後它們就壞。 - 國內用國內鏡像,並關掉廠商源的 test/nightly 段:mirrorlist/metalink 常年慢或超時,docker nightly 源超時會直接讓 leapp 失敗。華為雲鏡像會 429 限流,阿里雲最穩。
pkill -f/pgrep -f的模式不能出現在你自己的命令列裡,否則把自己的 SSH 會話殺掉(我們中招兩次)。用 PID、用腳本檔案、或[l]eapp這種寫法。- 每跳都對比基線:業務問題當跳發現當跳修,比如 cgroup v2 讓 Yearning 崩、docker-compose v1 失效。
- 改變業務行為的決定交給用戶:刪元件、下線站點、關 TLS1.0/1.1、換大版本資料庫。
參考文件(按需讀取)
references/pitfalls.md— 全部坑:現象 → 原因 → 處理,按跳分類 + 通用。遇到報錯先查這裡。references/app-compat.md— 各類應用跨 EL7→EL10 的相容處理:nginx、Docker/compose/cgroup v2、Java、Python、PHP、資料庫、postfix、網路、SSH、kubectl/helm、SysV、TLS 加密策略。references/cloud-providers.md— 阿里雲/華為雲/AWS 差異:快照、帶外控制台前置條件、鏡像源、雲廠商 agent、cloud-init。references/recovery.md— 卡在 initramfs 緊急模式、開機失聯、半升級狀態時怎麼救(含 VNC 操作技巧)。references/fleet.md— 幾十上百台批量推進:資產表、並行勘察、分類、金絲雀、分批、記錄。references/kubernetes.md— K8s 節點原地升級:開工前必須弄清的事(存取路徑/控制面入口/etcd/儲存)、相容性結論、推薦順序、踩過的坑、恢復檢查清單。