外掛存儲(chǔ)玩轉(zhuǎn)opkg:tmpfs內(nèi)存安裝與斷電恢復(fù)指南)
1. 為什么要在無(wú)外掛存儲(chǔ)的Padavan上折騰opkg手里有一臺(tái)刷了Padavan的老路由比如K2P、新路由3、小娛C1這類機(jī)器USB口要么沒(méi)有要么被占著要么插了U盤之后供電不穩(wěn)老掉盤。這種場(chǎng)景下想裝個(gè)插件第一反應(yīng)通常是“沒(méi)硬盤裝不了opkg”。其實(shí)Padavan本身就是一個(gè)精簡(jiǎn)的嵌入式Linux系統(tǒng)內(nèi)核支持tmpfs內(nèi)存就是現(xiàn)成的“硬盤”。把opkg的安裝目錄掛到tmpfs上插件照樣跑重啟前把關(guān)鍵文件備份到閃存里斷電恢復(fù)也不是沒(méi)辦法。這個(gè)思路解決的核心問(wèn)題是在物理存儲(chǔ)受限的嵌入式設(shè)備上利用內(nèi)存文件系統(tǒng)擴(kuò)展軟件安裝能力。適合手里有Padavan設(shè)備、想裝Entware插件但不想外掛U盤的人也適合想理解嵌入式Linux存儲(chǔ)分層邏輯的玩家。前提是你得接受一個(gè)事實(shí)——tmpfs里的東西斷電就沒(méi)了所以恢復(fù)方案是必選項(xiàng)不是可選項(xiàng)。我自己的主力測(cè)試機(jī)是一臺(tái)改了128MB內(nèi)存的K2P閃存只有16MBUSB口焊盤還在但沒(méi)引出來(lái)。之前一直用U盤跑Entware后來(lái)U盤接觸不良頻繁掉盤才轉(zhuǎn)向tmpfs方案。實(shí)測(cè)下來(lái)裝幾個(gè)輕量級(jí)插件完全夠用內(nèi)存占用可控。2. tmpfs方案的整體設(shè)計(jì)與選型考量2.1 為什么是tmpfs而不是其他內(nèi)存文件系統(tǒng)Linux下常見的內(nèi)存文件系統(tǒng)有ramfs和tmpfs兩種。ramfs不限制大小寫滿內(nèi)存直接死機(jī)tmpfs有大小上限可以掛載時(shí)指定超了會(huì)報(bào)“No space left on device”系統(tǒng)不會(huì)崩。Padavan內(nèi)核默認(rèn)編譯了tmpfs支持mount -t tmpfs直接可用不需要額外裝模塊。選tmpfs的另一個(gè)原因是它支持動(dòng)態(tài)伸縮。你掛載時(shí)給個(gè)上限比如32MB實(shí)際用多少占多少內(nèi)存不是一次性吃掉32MB。這對(duì)內(nèi)存緊張的路由器很關(guān)鍵。ramfs雖然也能用但風(fēng)險(xiǎn)太高不推薦。還有一個(gè)隱藏優(yōu)勢(shì)tmpfs支持mode和uid/gid掛載參數(shù)可以直接把目錄權(quán)限設(shè)好省去后面chmod的麻煩。比如mount -t tmpfs -o size32M,mode0755 tmpfs /opt掛完就是755權(quán)限opkg安裝時(shí)不會(huì)因?yàn)闄?quán)限問(wèn)題報(bào)錯(cuò)。2.2 目錄結(jié)構(gòu)怎么規(guī)劃才不亂Padavan的根文件系統(tǒng)是只讀的squashfs可寫分區(qū)通常只有/etc/storage和/tmp。/tmp本身就是tmpfs但它是系統(tǒng)臨時(shí)目錄重啟會(huì)清空而且很多進(jìn)程往里寫東西不適合直接拿來(lái)裝opkg。我的做法是新建一個(gè)獨(dú)立掛載點(diǎn)比如/opt。Padavan默認(rèn)沒(méi)有/opt目錄需要先創(chuàng)建。但根目錄只讀不能直接mkdir /opt。解決辦法是在啟動(dòng)腳本里先mkdir -p /tmp/opt然后mount --bind或者直接掛tmpfs到/tmp/opt再用軟鏈接指向/opt。不過(guò)軟鏈接在opkg安裝時(shí)可能出問(wèn)題更穩(wěn)妥的方式是直接掛載到/opt但需要先解決根目錄只讀的問(wèn)題。實(shí)測(cè)可行的方案是在/etc/storage下建一個(gè)opt目錄然后啟動(dòng)時(shí)把它掛載為tmpfs。/etc/storage是可寫的JFFS2分區(qū)重啟后文件還在但掛載tmpfs后原內(nèi)容被覆蓋相當(dāng)于一個(gè)“可寫掛載點(diǎn)”。具體命令mkdir -p /etc/storage/opt mount -t tmpfs -o size32M,mode0755 tmpfs /etc/storage/opt ln -sf /etc/storage/opt /opt這樣/opt就指向了tmpfs空間。軟鏈接對(duì)opkg來(lái)說(shuō)透明安裝路徑寫/opt即可。2.3 內(nèi)存容量與插件體積的平衡K2P改128MB內(nèi)存后系統(tǒng)跑起來(lái)大概占40-50MB剩余70MB左右可用。tmpfs給32MB裝Entware基礎(chǔ)環(huán)境加幾個(gè)插件夠用。如果內(nèi)存只有64MB建議tmpfs不超過(guò)16MB只裝最核心的插件。Entware基礎(chǔ)安裝包opkg 核心工具大概占8-12MB具體看版本。裝完之后剩余空間裝插件單個(gè)插件通常幾百KB到幾MB。比如curl約1MBwget約500KBopenssl-util約2MB。32MB空間裝5-8個(gè)輕量插件沒(méi)問(wèn)題。需要警惕的是日志文件。有些插件默認(rèn)往/opt/var/log寫日志tmpfs寫滿會(huì)導(dǎo)致插件異常。建議把日志目錄軟鏈接到/tmp或者定期清理。3. 手把手實(shí)操?gòu)牧愦罱╰mpfs版opkg環(huán)境3.1 確認(rèn)固件版本與內(nèi)核支持先SSH登錄路由器執(zhí)行uname -a cat /proc/filesystems | grep tmpfs如果輸出里有tmpfs說(shuō)明內(nèi)核支持。Padavan大部分版本都支持但個(gè)別精簡(jiǎn)版可能裁掉了。如果沒(méi)有這個(gè)方案直接放棄換固件或者外掛存儲(chǔ)。還要確認(rèn)/etc/storage可寫touch /etc/storage/test rm /etc/storage/test沒(méi)報(bào)錯(cuò)就說(shuō)明可寫。如果報(bào)“Read-only file system”需要先在Padavan管理頁(yè)面“系統(tǒng)管理-配置管理”里點(diǎn)擊“保存內(nèi)部存儲(chǔ)到閃存”來(lái)激活可寫分區(qū)。3.2 創(chuàng)建掛載點(diǎn)與自動(dòng)掛載腳本Padavan的啟動(dòng)腳本放在/etc/storage/started_script.sh這個(gè)文件在重啟后會(huì)執(zhí)行。如果不存在就新建cat /etc/storage/started_script.sh EOF #!/bin/sh # tmpfs opkg 掛載腳本 mkdir -p /etc/storage/opt mount -t tmpfs -o size32M,mode0755 tmpfs /etc/storage/opt ln -sf /etc/storage/opt /opt # 恢復(fù)備份的opkg數(shù)據(jù) if [ -f /etc/storage/opt_backup.tar.gz ]; then tar -xzf /etc/storage/opt_backup.tar.gz -C /etc/storage/opt fi EOF chmod x /etc/storage/started_script.sh這段腳本做了三件事建目錄、掛tmpfs、從備份恢復(fù)。備份文件放在/etc/storage下重啟后還在解壓到tmpfs里就恢復(fù)了之前的安裝狀態(tài)。注意started_script.sh在Padavan啟動(dòng)流程中的執(zhí)行時(shí)機(jī)比較早如果此時(shí)/etc/storage還沒(méi)掛載好腳本會(huì)失敗。實(shí)測(cè)在K2P上沒(méi)問(wèn)題但不同機(jī)型可能有差異。保險(xiǎn)做法是在腳本開頭加sleep 5等系統(tǒng)穩(wěn)定后再執(zhí)行。3.3 安裝Entware基礎(chǔ)環(huán)境Entware官方提供了一鍵安裝腳本但默認(rèn)裝到/opt正好符合我們的掛載點(diǎn)。執(zhí)行opkg_install.sh如果提示找不到命令手動(dòng)下載cd /tmp wget http://bin.entware.net/mipselsf-k3.4/installer/opkg chmod x opkg ./opkg -VPadavan的CPU架構(gòu)通常是mipsel或arm需要選對(duì)源。K2P是MT7621屬于mipsel用mipselsf-k3.4源。新路由3是MT7621同樣。如果是ARM架構(gòu)的機(jī)器比如某些高通方案用armv7sf-k3.2源。安裝完成后/opt/bin/opkg應(yīng)該存在。把它加入PATHexport PATH/opt/bin:/opt/sbin:$PATH echo export PATH/opt/bin:/opt/sbin:$PATH /etc/profile然后opkg update更新軟件列表。如果報(bào)SSL錯(cuò)誤先裝openssl-util和ca-certificatesopkg install openssl-util ca-certificates3.4 安裝常用插件與空間監(jiān)控基礎(chǔ)環(huán)境好了之后裝幾個(gè)常用插件測(cè)試opkg install curl wget-ssl htop nano裝完用df -h看tmpfs占用df -h | grep opt輸出類似tmpfs 32.0M 12.3M 19.7M 38% /etc/storage/opt12.3MB是基礎(chǔ)環(huán)境加幾個(gè)插件的體積剩余19.7MB還能裝不少東西。監(jiān)控內(nèi)存使用free -m如果可用內(nèi)存低于10MB就要考慮減少tmpfs大小或者卸載不用的插件。4. 斷電恢復(fù)方案讓tmpfs數(shù)據(jù)“活”過(guò)重啟4.1 備份策略與自動(dòng)化腳本tmpfs斷電即失所以必須在重啟前把/opt下的關(guān)鍵數(shù)據(jù)打包到/etc/storage。手動(dòng)備份命令tar -czf /etc/storage/opt_backup.tar.gz -C /etc/storage/opt .但手動(dòng)備份容易忘最好做成定時(shí)任務(wù)。Padavan自帶crontab加一條每小時(shí)備份echo 0 * * * * tar -czf /etc/storage/opt_backup.tar.gz -C /etc/storage/opt . /etc/storage/cron/crontabs/admin不過(guò)每小時(shí)備份會(huì)頻繁寫閃存JFFS2分區(qū)有擦寫壽命限制。更合理的做法是只在關(guān)機(jī)或重啟前備份。Padavan的關(guān)機(jī)腳本放在/etc/storage/shutdown_script.sh可以在這里觸發(fā)備份cat /etc/storage/shutdown_script.sh EOF #!/bin/sh tar -czf /etc/storage/opt_backup.tar.gz -C /etc/storage/opt . sync EOF chmod x /etc/storage/shutdown_script.sh但斷電是突發(fā)的關(guān)機(jī)腳本不一定執(zhí)行。所以還需要一個(gè)“定期備份手動(dòng)備份”的組合策略。我的做法是每天凌晨3點(diǎn)備份一次平時(shí)裝完新插件手動(dòng)執(zhí)行一次備份。這樣即使斷電最多丟失一天內(nèi)的變更。4.2 恢復(fù)流程與驗(yàn)證方法重啟后started_script.sh會(huì)自動(dòng)解壓備份到tmpfs。驗(yàn)證恢復(fù)是否成功ls /opt/bin/opkg /opt/bin/opkg list-installed | head如果opkg存在且能列出已安裝包說(shuō)明恢復(fù)成功。如果失敗檢查備份文件是否存在ls -lh /etc/storage/opt_backup.tar.gz手動(dòng)恢復(fù)tar -xzf /etc/storage/opt_backup.tar.gz -C /etc/storage/opt注意備份文件本身也占/etc/storage空間。如果閃存剩余空間不足備份會(huì)失敗。K2P的16MB閃存/etc/storage可用空間通常只有幾MB備份文件要控制在3MB以內(nèi)。如果超過(guò)考慮只備份/opt/etc和/opt/lib等關(guān)鍵目錄不備份二進(jìn)制文件重啟后重新opkg install。4.3 閃存壽命與備份頻率的權(quán)衡JFFS2的擦寫壽命一般在10萬(wàn)次左右但實(shí)際受寫入放大影響可能更低。每天備份一次一年365次對(duì)閃存壓力不大。但如果每小時(shí)備份一年8760次加上其他寫入閃存可能提前報(bào)廢。我的建議是只在插件變更后備份不做定時(shí)備份。裝完插件、改完配置手動(dòng)執(zhí)行一次備份腳本。這樣備份次數(shù)最少閃存壽命最長(zhǎng)。代價(jià)是斷電后可能丟失最后一次變更但插件配置通常不頻繁改可以接受。如果實(shí)在擔(dān)心斷電可以加一個(gè)“檢測(cè)到電源異常時(shí)立即備份”的機(jī)制但Padavan沒(méi)有UPS支持實(shí)現(xiàn)難度大。折中方案是每天備份一次時(shí)間選在凌晨低峰期。5. 常見問(wèn)題與排查技巧實(shí)錄5.1 opkg安裝報(bào)“No space left on device”這是tmpfs空間不足。先df -h確認(rèn)占用然后清理opkg clean rm -rf /opt/var/log/* rm -rf /opt/tmp/*如果還是不夠卸載不用的插件opkg remove 插件名或者擴(kuò)大tmpfs。但擴(kuò)大tmpfs會(huì)占用更多內(nèi)存可能導(dǎo)致系統(tǒng)OOM。K2P 128MB內(nèi)存下tmpfs最大不要超過(guò)48MB。5.2 重啟后opkg命令找不到檢查/opt軟鏈接是否失效ls -l /opt如果顯示/opt - /etc/storage/opt但目錄不存在說(shuō)明掛載腳本沒(méi)執(zhí)行。手動(dòng)執(zhí)行mkdir -p /etc/storage/opt mount -t tmpfs -o size32M,mode0755 tmpfs /etc/storage/opt然后檢查started_script.sh是否有執(zhí)行權(quán)限以及Padavan是否啟用了自定義腳本。在管理頁(yè)面“系統(tǒng)管理-腳本”里確認(rèn)“啟動(dòng)后執(zhí)行”已開啟。5.3 插件運(yùn)行時(shí)報(bào)“Segmentation fault”通常是架構(gòu)不匹配。Padavan的CPU架構(gòu)和Entware源必須對(duì)應(yīng)。用uname -m查看架構(gòu)mips→ 用mipselsf-k3.4源armv7l→ 用armv7sf-k3.2源aarch64→ 用aarch64-k3.10源如果源選錯(cuò)卸載重裝opkg remove 插件名 sed -i s/舊源地址/新源地址/g /opt/etc/opkg.conf opkg update opkg install 插件名5.4 內(nèi)存占用過(guò)高導(dǎo)致系統(tǒng)卡頓tmpfs用的是內(nèi)存裝太多插件會(huì)擠占系統(tǒng)內(nèi)存。用free -m監(jiān)控如果可用內(nèi)存低于15MB就要精簡(jiǎn)。優(yōu)先卸載體積大且不常用的插件比如python3約20MB、node約30MB。輕量插件如curl、wget、nano可以保留。還可以調(diào)整tmpfs大小把上限從32MB降到16MB強(qiáng)制限制占用。但降太小會(huì)導(dǎo)致安裝失敗需要根據(jù)實(shí)際插件體積調(diào)整。5.5 備份文件損壞導(dǎo)致恢復(fù)失敗備份時(shí)如果斷電tar包可能不完整。恢復(fù)前先驗(yàn)證tar -tzf /etc/storage/opt_backup.tar.gz /dev/null echo OK || echo FAIL如果FAIL刪除損壞備份重新安裝插件。為了避免這種情況備份時(shí)先寫到臨時(shí)文件再原子替換tar -czf /tmp/opt_backup.tar.gz -C /etc/storage/opt . mv /tmp/opt_backup.tar.gz /etc/storage/opt_backup.tar.gz這樣即使備份過(guò)程中斷電原備份文件不受影響。6. 進(jìn)階技巧讓tmpfs方案更穩(wěn)的幾個(gè)細(xì)節(jié)6.1 用zram壓縮內(nèi)存變相擴(kuò)大tmpfs如果內(nèi)存實(shí)在緊張可以啟用zram。zram把一部分內(nèi)存壓縮后當(dāng)塊設(shè)備用再在上面建tmpfs相當(dāng)于用CPU換空間。Padavan內(nèi)核可能沒(méi)編譯zram模塊需要先確認(rèn)modprobe zram ls /dev/zram*如果有/dev/zram0就可以echo 32M /sys/block/zram0/disksize mke2fs -t ext4 /dev/zram0 mount /dev/zram0 /etc/storage/opt這樣32MB的zram實(shí)際可能只占16MB物理內(nèi)存因?yàn)閴嚎s率通常2:1左右。但CPU占用會(huì)上升MT7621的性能足夠影響不大。6.2 把日志和緩存重定向到/tmp/tmp本身就是tmpfs而且系統(tǒng)會(huì)自動(dòng)清理。把opkg的日志和緩存目錄軟鏈接到/tmp可以節(jié)省/opt空間mkdir -p /tmp/opt_log /tmp/opt_cache ln -sf /tmp/opt_log /opt/var/log ln -sf /tmp/opt_cache /opt/var/cache這樣日志和緩存不占/opt的tmpfs空間重啟自動(dòng)清空不用手動(dòng)清理。6.3 用opkg的--dest參數(shù)安裝到其他位置如果/opt空間不夠可以把部分插件裝到/tmp下的另一個(gè)tmpfs。比如mkdir -p /tmp/opt2 mount -t tmpfs -o size16M tmpfs /tmp/opt2 opkg install --dest /tmp/opt2 插件名但這樣插件的可執(zhí)行文件不在PATH里需要手動(dòng)加export PATH/tmp/opt2/bin:/tmp/opt2/sbin:$PATH而且重啟后/tmp/opt2消失需要重新安裝。適合臨時(shí)測(cè)試插件不適合長(zhǎng)期使用。6.4 監(jiān)控tmpfs使用率并自動(dòng)告警寫一個(gè)簡(jiǎn)單的監(jiān)控腳本每小時(shí)檢查一次超過(guò)80%就寫日志#!/bin/sh usage$(df -h /etc/storage/opt | awk NR2 {print $5} | tr -d %) if [ $usage -gt 80 ]; then logger -t tmpfs_monitor tmpfs usage: ${usage}% fi加到crontab里echo 0 * * * * /etc/storage/tmpfs_monitor.sh /etc/storage/cron/crontabs/admin這樣能提前發(fā)現(xiàn)空間不足避免插件突然掛掉。7. 個(gè)人實(shí)操體會(huì)與幾個(gè)不推薦的做法這套方案我用了大半年K2P上跑著curl、wget、htop、nano和一個(gè)小型HTTP服務(wù)內(nèi)存占用穩(wěn)定在20MB左右系統(tǒng)負(fù)載正常。最久一次連續(xù)運(yùn)行47天沒(méi)重啟tmpfs沒(méi)出過(guò)問(wèn)題。唯一一次故障是裝了一個(gè)Python插件內(nèi)存直接爆了系統(tǒng)卡死只能斷電重啟。從那以后我就定了個(gè)規(guī)矩tmpfs方案只裝C語(yǔ)言寫的輕量插件不碰Python、Node這類運(yùn)行時(shí)。不推薦的做法有幾個(gè)。一是把tmpfs掛到/tmp下因?yàn)?tmp會(huì)被系統(tǒng)清理插件可能突然消失。二是備份文件放/tmp重啟就沒(méi)了等于沒(méi)備份。三是用ramfs代替tmpfs風(fēng)險(xiǎn)太高寫滿直接死機(jī)。四是頻繁定時(shí)備份閃存壽命扛不住。如果閃存空間夠其實(shí)更推薦把Entware裝到/etc/storage下不用tmpfs重啟后數(shù)據(jù)還在省去備份恢復(fù)的麻煩。但/etc/storage空間通常只有幾MB裝不下完整Entware。所以tmpfs方案是空間和便利性之間的折中。最后分享一個(gè)小技巧裝完插件后用opkg list-installed /etc/storage/opkg_list.txt把已安裝列表存到閃存里。萬(wàn)一tmpfs數(shù)據(jù)丟了照著列表重新opkg install就行不用一個(gè)個(gè)回憶裝了什么。這個(gè)列表文件很小幾KB不占空間但恢復(fù)時(shí)能省不少事。