做個人服務器就是一個不錯的用途:功耗低,還自帶UPS。做個個人網站,個人雲或下載機,7x24小時開機,不擔心費電,也不擔心停電。
最近在Android手機上利用Linux Deploy搭建了一個LEMP/LNMP網站,本文講述其中的主要流程和注意事項。有一些坑,也有點小竅門,還有一些測試數據,拿手機做其它框架的網站或其它用途的服務器也可參考。
可行性及方案
先評估下性能。在手機上裝了個Linux,用sysbench測試下CPU,以PC和樹莓派作為對比。
命令:
sysbench --test=cpu --cpu-max-prime=20000 run
結果是耗時,越短越好。
- PC: i7 4770K: 21.2s
- 華為榮耀6 plus: 247s
- 樹莓派3代: 535s
- 樹莓派2代: 1150s
雖然比PC差遠了,但比樹莓派快不少(多核應該更強)。其實我的網站以前是跑在樹莓派2代上的。樹莓派的I/O(SD卡)也是很慢的。
至於方案,理論上來說有這么幾類:
- APK提供http服務器、MySQL服務器、PHP的支持。——功能受限於APK,不能使用標准的工具集。
- 用類似於busybox的方式,把Linux下的命令集和需要的服務器都編譯到Android上。——累。
- 雙系統,既可以引導Linux,也可以引導Android。——樹莓派可以,以前用過RK3188的Android電視盒也可以,理論上手機也可以做到,就看有哪幾個手機廠商願意提供這種支持了。所以,可遇不可求。
- 模擬器:APK是Linux的模擬器。——慢。
- Linux和Android共用內核,使用chroot容器的方式。——這幾乎是最完美的方案了,既不破壞Android,又有一個幾乎完整的Linux。Linux Deploy就是這方面的傑出代表。
手機root及清理
不root會有很多限制,比如不能使用1024以下的端口。而且root也是Linux Deploy要求的。不同的手機型號,請各顯神通尋找root攻略。
對於榮耀6和6 plus,大致流程是(國內的各種一鍵root工具都不行):
- 先升級到5.1(可能要申請);
- 再升級到6.0(可能要申請);
- 申請解鎖碼;
- fastboot下unlock, 刷TRWP recovery;
- TRWP recovery下刷root用的zip文件。
root后,SuperSU是必裝的(通常會自帶),busybox也建議安裝。
既然是專用作服務器,其它不用的程序都卸載掉,包括一些系統程序及服務,以減少對系統資源的占用。方法是列出所有進程,在網上搜索其用途,根據自己的需要進行刪除。網上一些文章也會說明是否可刪除,建議刪除還是保留。不能刪除的可以嘗試禁止啟動。
我的榮耀6 plus最后保留了不到一屏的App。不過,系統內的進程數還是很多,但都處於不活動狀態。最后,手機設置為飛行模式;休眠狀態始終保持WiFi連接。

SD卡分區
Linux Deploy可以將系統完全安裝在內置Flash,或SD卡的image上,也可以裝到某個目錄或分區。
為了比較,我裝了多個系統,並共用home分區。最后的SD卡分區方案是這樣的:
- 2G, FAT,給android系統使用
- 8G, ext2, 安裝第二個Debian Linux用(第一個裝在內置Flash的/data/local目錄里)
- 8G, ext2, 玩玩Kali Linux
- 16G, ext4, 共用的 /home 分區
這里的小經驗是:保留一個小的FAT分區!這是為了讓Android系統看上去還是有一個可用的SD卡。否則,系統會提示SD卡不認識,是否要格式化。萬一哪天自己忘了SD卡里藏了好幾個Linux的系統和數據,或者手快點了一下“是”,就杯具了。

Linux Deploy的關鍵設置
建立多個Profile,對應於多個系統。

然后依次對每個Profile進行配置。

上圖的安裝位置設在內部Flash的/data/local目錄下。如果想安裝在SD卡分區上,則類似於下圖。

ssh服務是必須開啟的。掛載點的語法要注意:一行一個條目;用冒號分隔源路徑和掛載點。

然后開始安裝(裝Debian8時安裝忘了截屏,下圖用Debian9作為示意)。

權限問題
這里的坑是在Android手機上安裝LAMP/LEMP系統特有的。想不到可能半天也搞不定,想到了只是一行命令。
MySQL啟動失敗
MySQL在安裝的時候報告啟動失敗,日志上也沒有更多信息。
好吧,既然MySQL 5.5失敗,那我裝MariaDB 10.0吧?——結果還是失敗。
會不會是文件系統權限問題?把數據目錄從Flash的/var/lib/mysql換到SD卡的/home/mysql吧?——一樣失敗。
...
其實原因是mysql用戶的權限問題。解決辦法是把mysql這個用戶加到aid_net_raw組。
sudo usermod -a -G aid_net_raw mysql
aid_xxx 這些組其實是Android的用戶組(這些組的映射/創建,應該是Linux Deploy做的),容器內的Linux用戶要具有Android的相應權限才能做相應的事。而MySQL創建/監聽socket,要有aid_net_raw對應的權限。
網上有人把aid_inet也給予mysql用戶,其實這個Internet訪問權限對於MySQL不是必須的。
PHP下載及發郵件
我安裝的這個Web系統(Drupal)有網頁上安裝/升級模塊(插件)的功能。但這個功能用不了:
GuzzleHttp\Exception\ConnectException: cURL error 6: Could not resolve host: updates.drupal.org (see http://curl.haxx.se/libcurl/c/libcurl-errors.html) in GuzzleHttp\Handler\CurlFactory::createRejection() (line 186 of /home/www/drupal-8.3.2/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php).
從字面看,像是DNS或是網絡連接的問題。最后的答案和前面MySQL啟動不了是類似的:Web server進程需要有Internet訪問的權限。
sudo usermod -a -G aid_inet www-data
發郵件也是網站必備的功能。通過PHP發郵件,Web server同樣需要Internet的訪問權限(比如連接smtp.gmail.com的587端口)。
性能測試及調優
WiFi及網絡速度
做服務器肯定是用有線網絡好。但無線網絡是否堪用呢?下面的測試都是在5G的WiFi下,靠近路由器,小米mini一代。
用 ping 測試有線連接的樹莓派和無線連接的手機。

可以看到,往返時間差了兩個數量級。前者平均是0.72ms;后者差不多70ms。
下圖是用 iperf 測試網絡速度。大約是67Mbps.

速度屬於正常水平,對於自娛自樂的個人站點,也可接受吧。
數據庫放在Flash還是SD卡上?
先對文件系統做一簡單的測試。用dd讀寫總共4GB(大於手機內存)的文件,每次讀寫不同大小的塊。
#$count = $totalsize / $blksize
dd if=/dev/zero of=$tmpfile bs=$blksize count=$count && sync
of=/dev/zero if=$tmpfile bs=$blksize count=$count && sync
最后的結果如下圖所示。

可以看到,寫的速度差不多,讀的速度Flash還是比SD卡快不少。
還可以用sysbench測試MySQL性能,或者用wget/curl直接測試網站的速度。總體上,對於這個手機(榮耀6 plus),后面這兩項測試的結果差別不大。最后,我還是把數據庫放在了內部Flash上。
屏幕開還是關?
對於做服務器用途,想當然地會認為要把屏幕關掉。控制開關屏幕的有好幾個地方:
- “顯示”里面的延時熄屏;
- “開發者選項”里的插電時保持屏幕活動;
- Linux Deploy界面里的保持屏幕活動。

在性能測試中發現,屏幕開着和關着的差別很大。
sysbench --test=cpu --cpu-max-prime=20000 run

- 屏幕開:247.5s
- 屏幕關:441.0s
sysbench進行CPU測試的結果還是很穩定的,包括以前用Linux Deploy 2.0.0-215,現在用的2.0.2-220;開啟mysql/nginx等服務或者不開啟,分數波動在1%以內。可見,屏幕開和關的差距是非常顯著的。
在Android/Linux系統里,還有一個CPU Governor的策略會影響CPU的性能。
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
interactive
可以看到,默認的策略是"interactive". 嘗試把它改到性能最高的 "performance",
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
測試結果也並沒有差別。
也許是屏幕關着的時候,CPU被降頻使用了?
最后,不得不選擇讓屏幕常開(當然,亮度調至最低)。
/proc/loadavg的問題
我們通常會用/proc/loadavg查看系統的load
cat /proc/loadavg
- 空閑時:5.16 5.14 5.17 1/1290 8877
- 運行sysbench時:6.02 5.74 5.48 4/1295 9353
作為對比,樹莓派的loadavg:
- 空閑時:0.03 0.07 0.02 2/159 28307
- 運行sysbench時:1.03 0.72 0.33 3/158 28903
Android系統的loadavg空閑時普遍都很高;而用top命令查看各進程的CPU占用率時,並沒有多高。
關於這個問題,網上有一些討論,但我還沒找到權威的說法。總之,並不能用loadavg作為系統load的參考。
自動啟動
在啟動Android系統時,自動啟動容器內的Linux,似乎並不起作用。

另一個層面的自動啟動是自動啟動Linux系統內的服務。這需要在Linux Deploy里啟用 Init 系統,並選用類型 SysV.

這樣,在Linux系統啟動時,runlevel(默認為3)中設置為自動啟動的服務就會自動啟動,像普通的Linux那樣。否則,連cron服務都要手動啟動。

不過在我的手機上有個小問題:在啟用了Init后,Stop Linux時會導致Android重啟,然后root丟失。但再重啟一次會恢復root。
備份
數據庫本來是安裝在Flash上,可以將其備份到SD卡,這樣起到了“兩個籃子”的作用。
考慮到Flash和SD卡都有可能突然失效(都曾經碰到過),遠程備份也是必須的。
本想用NFS掛載NAS上的目錄的,但NFS client的安裝有點問題。沒有去深究,直接用了ssh密鑰登錄加rsync。命令示意:
rsync -auvAX --delete $srcdir nas:/$dstdir
升級
升級包括3個層面:
- Android系統本身:取決於廠家是否還提供升級支持,升級后可能要重新root。總之是比較麻煩的。由於Linux和Android共用內核,除非出現內核級別的安全漏洞,否則沒必要升級。
- Linux系統:可以安裝unattended-upgrades之類的包來自動更新。
- Linux Deploy:根據需要升級。升級前最好保存配置。直接缷載/安裝會把原配置數據全部清除;不過只要把Profile重新配置一下,就可以啟動原系統 。所以也不是大問題。
功耗
用記錄型萬用表UT181A對電流進行24小時監測(手機電池始終是滿電)。

每小時的平均電流在140~150mA之間,功耗相當於0.75W。

小米路由器的USB口實測電流可以超過200mA,所以可以直接用這個路由器的USB口給手機長期供電。

電源/電池狀態
還可以通過讀取目錄 /sys/class/power_supply/Battery 的文件來監控電源和電池的狀態:
- status:充放電狀態;
- capacity: 電池容量百分比;
- charge_now:當前充電的容量(mAh);
- charge_full:電池的總容量(mAh);
- voltage_now:當前電池電壓(V);
- voltage_max:電池充滿電的電壓(V);
- current_now:當前充電電流(mA),充電為正,放電為負;如果系統負載高,電流會是負值;
- temp:電池溫度(有無爆炸隱患早知道)。
可以寫幾行腳本,直接在網站上顯示這些狀態。

結語
在root過的Android手機上通過Linux Deploy安裝Linux,可以達到和普通Linux一樣的完整度和自由度。能靈活地在上面部署各種LAMP, LEMP/LNMP站點。而且,這樣的“服務器”功耗低(小於1W),帶“UPS”(電池)。
主要的要注意的方面有:和Android相關的權限問題,如何自動啟動服務,熄屏對性能有無影響,等等。手機雖小,但跨越Android, Linux及容器,涉及系統的多方面,對技術愛好者也是一個很好的練習。
本文主要是從平台的角度來談論“手機服務器”的,並不涉及平台上的應用(即具體如何建一個網站)。至於建網站的其它方面,后續會有文章。敬請關注。
參考
作者:蘿卜頭實驗室
鏈接:https://www.jianshu.com/p/f5e107180b45
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。