限到自動化:Linux服務(wù)器運(yùn)維核心實(shí)踐全解析)
1. 這篇“Linux(11)(下)”到底在解決什么問題1.1 從“會敲命令”到“能管機(jī)器”運(yùn)維視角的Linux進(jìn)階路徑如果你能看到這里說明你已經(jīng)不是那個對著終端發(fā)怵的新手了。前面十篇內(nèi)容我們聊過基礎(chǔ)命令、文件權(quán)限、進(jìn)程管理、網(wǎng)絡(luò)配置甚至可能手寫過幾個簡單的Shell腳本。但現(xiàn)實(shí)中的Linux學(xué)習(xí)曲線有個很尷尬的斷層你跟著教程敲完了所有命令感覺自己“會了”可一旦真正需要你獨(dú)自去管理一臺生產(chǎn)服務(wù)器你會發(fā)現(xiàn)腦子里全是零散的碎片。這篇“Linux(11)(下)”系列文章的定位非常明確把那些零散的碎片拼成一張可以落地的運(yùn)維地圖。它不再糾結(jié)于單個命令怎么用而是聚焦于“場景”和“決策”。舉個例子你懂chmod和useradd但你真的知道一個多人在用的服務(wù)器上權(quán)限應(yīng)該怎么分配才既安全又不影響協(xié)作效率嗎你懂df -h但磁盤真的滿了的時候你知道該按什么順序排查才能最快定位到那個占滿空間的元兇嗎之所以把這一篇放在第11篇的下半部分是因?yàn)樵酵蟮膬?nèi)容越接近生產(chǎn)環(huán)境的真實(shí)復(fù)雜度。前面的知識是磚塊這一篇要教你怎么用這些磚塊砌墻。它解決的核心問題是當(dāng)系統(tǒng)不再是你學(xué)習(xí)用的虛擬機(jī)而是承載著實(shí)際業(yè)務(wù)的服務(wù)器時你的操作習(xí)慣、排查思路、故障應(yīng)對方式必須完成一次從“實(shí)驗(yàn)心態(tài)”到“工程心態(tài)”的轉(zhuǎn)變。1.2 這一篇覆蓋的高頻運(yùn)維場景清單很多朋友學(xué)Linux容易陷入一個誤區(qū)越學(xué)越偏門天天研究那些一輩子都用不上的冷門命令。作為經(jīng)歷過不少線上故障的從業(yè)者我建議你把精力集中在真正決定系統(tǒng)穩(wěn)定性的事情上。這一篇“Linux(11)(下)”選擇的內(nèi)容全部來自我在實(shí)際工作中反復(fù)用到的高頻場景。大致可以分為六個方向用戶與權(quán)限的規(guī)范化管理、存儲的規(guī)劃與故障恢復(fù)、服務(wù)托管與開機(jī)自啟、網(wǎng)絡(luò)與防火墻排查、日志分析與故障定位、自動化腳本與巡檢。這六個方向基本覆蓋了一臺Linux服務(wù)器從日常維護(hù)到出事?lián)尵鹊耐暾窂?。你會發(fā)現(xiàn)這里面幾乎沒有炫技的內(nèi)容全是實(shí)打?qū)嵉摹氨C奔寄?。如果這一篇你能吃透去面試中小型公司的運(yùn)維或后端開發(fā)崗位至少技術(shù)面聊到Linux這塊時不會心虛。我把每一塊的思路拆開放在后面幾章詳細(xì)展開。每一章都會遵循一個邏輯先告訴你為什么需要這么做再給你可以直接抄的實(shí)操步驟最后把我踩過的坑同步給你。有些坑官方文檔里不會寫但你在生產(chǎn)環(huán)境里大概率會碰到。2. 用戶與權(quán)限管理別再頂著root滿世界跑了2.1 最小權(quán)限原則下的用戶與組設(shè)計(jì)先用一個場景問問你如果你剛接手一臺服務(wù)器發(fā)現(xiàn)所有人都在用root干活你會怎么做直接改掉所有人的權(quán)限恐怕不行業(yè)務(wù)會受影響。繼續(xù)留著風(fēng)險太高一旦賬號泄露整個系統(tǒng)等于裸奔。正確做法是引入一套可落地的用戶和組規(guī)劃。基本原則很簡單能不用root就不用root能不給權(quán)限就不給權(quán)限。具體到實(shí)操我會先把業(yè)務(wù)角色梳理清楚。比如一臺應(yīng)用服務(wù)器上可能有這幾個角色應(yīng)用部署人員、日志查看人員、備份執(zhí)行人員。不同角色需要的權(quán)限完全不同。我的常用方案是創(chuàng)建三個組appdeploy應(yīng)用部署組、logviewer日志查看組、backup_ops備份操作組。然后按需創(chuàng)建用戶并加入對應(yīng)組。關(guān)鍵在于給組賦予合理的文件權(quán)限而不是直接給用戶授權(quán)。比如應(yīng)用部署目錄/opt/myapp歸屬為appdeploy組組權(quán)限設(shè)為rwxrws---這樣組內(nèi)成員可以部署更新但其他人一丁點(diǎn)權(quán)限都沒有。日志目錄同理logviewer組的成員只有r-x權(quán)限能看不能改。這套設(shè)計(jì)的好處在于人員變動時你只需要調(diào)整用戶的組關(guān)系不用滿服務(wù)器去改文件權(quán)限。新員工入職加組離職移組十分鐘搞定。我曾經(jīng)見過一個團(tuán)隊(duì)因?yàn)闄?quán)限設(shè)計(jì)不合理每次人員變動都要改幾十個目錄的屬主改完之后還提心吊膽生怕哪里漏了。用組來管理權(quán)限是運(yùn)維生涯中最不該省的那一步。2.2 sudo授權(quán)與特殊權(quán)限位的實(shí)戰(zhàn)用法有了用戶和組接下來要解決“有些操作確實(shí)需要臨時提權(quán)”的問題。sudo在這里就是唯一推薦路徑。我見過不少人直接給用戶加了ALL(ALL) ALL這本質(zhì)上等于還是root橫行只是穿了件馬甲。更好的做法是精確到命令級別。舉個例子我要讓logviewer組的成員能重啟某個特定的應(yīng)用服務(wù)但不給他們?nèi)魏蝧hell管理權(quán)限。在/etc/sudoers.d/下新建一個文件寫入%logviewer ALL(ALL) NOPASSWD: /usr/bin/systemctl restart myapp注意我用了NOPASSWD因?yàn)樵谧詣踊_本里跑sudo不需要交互輸入密碼這能省掉很多麻煩。但代價是任何拿到這個用戶密碼的人都能直接重啟該服務(wù)所以授權(quán)命令一定要窄能精確到服務(wù)名絕不授權(quán)通配符。另外千萬別把vim這類可以執(zhí)行shell的編輯器授權(quán)給普通用戶否則他可以借編輯器的shell功能直接拿到root權(quán)限。這條坑我很多年前就踩過。再說說特殊權(quán)限位。setuid和setgid這兩個東西在日常運(yùn)維里必須慎用。chmod us設(shè)置setuid后無論誰執(zhí)行這個程序都會以文件屬主的身份運(yùn)行典型例子就是/usr/bin/passwd。但如果你把一個bash腳本設(shè)置成setuid那基本等于把root鑰匙掛在門口。我的建議是生產(chǎn)環(huán)境上不要自己創(chuàng)建任何setuid程序現(xiàn)有的系統(tǒng)文件也定期檢查一遍用下面的命令找出所有帶特殊權(quán)限位的文件find / -perm -4000 -o -perm -2000 2/dev/null檢查輸出結(jié)果凡是你沒見過的、不在系統(tǒng)標(biāo)準(zhǔn)列表里的都要查明來源。安全這個東西說白了就是習(xí)慣問題每次登錄服務(wù)器先花一分鐘看看有沒有異常遠(yuǎn)好過出事了再追。2.3 權(quán)限排查的一個典型翻車現(xiàn)場聊一個我早年間實(shí)際遇到的場景方便你理解權(quán)限設(shè)計(jì)為什么重要。當(dāng)時某團(tuán)隊(duì)反映他們寫的日志文件總是無法被另一個模塊讀取兩邊都在喊冤叫屈排查了半天發(fā)現(xiàn)是文件創(chuàng)建時的umask出了問題。進(jìn)程以某個用戶身份啟動默認(rèn)的umask值是0027創(chuàng)建出來的文件自然就是這個權(quán)限屬主能讀寫、組只能讀、其他人一律拒絕。而讀取方恰好在“其他人”這個范圍里。這類問題的本質(zhì)不是某個人的操作失誤而是整個系統(tǒng)的權(quán)限策略沒有統(tǒng)一設(shè)計(jì)。后來我給系統(tǒng)里的服務(wù)進(jìn)程統(tǒng)一設(shè)置了啟動環(huán)境里的umask值并且明確要求所有應(yīng)用日志文件在寫入前必須顯式設(shè)置權(quán)限掩碼不允許依賴默認(rèn)值。從那以后這類跨進(jìn)程讀寫失敗的問題就基本絕跡了。所以這里給你一個建議遇到“文件讀不到”“目錄進(jìn)不去”的報錯第一反應(yīng)不是去改目標(biāo)文件的權(quán)限而是去想清楚整個讀寫鏈路涉及哪幾個用戶、哪幾個進(jìn)程、各自的權(quán)限范圍是什么。揪出鏈路中權(quán)限最小的一環(huán)往往才是真正的病灶。3. 存儲管理進(jìn)階LVM規(guī)劃與文件系統(tǒng)修復(fù)3.1 為什么我強(qiáng)烈建議生產(chǎn)環(huán)境用LVM“分區(qū)不夠用了怎么辦”大概是我在技術(shù)交流群里看到頻率最高的問題之一。如果你在裝系統(tǒng)時用了傳統(tǒng)的物理分區(qū)方案數(shù)據(jù)盤滿了之后擴(kuò)容量要么得搬家要么得用growpart這類工具在線折騰無論哪種都夠喝一壺的。而如果你一開始就采用了LVM邏輯卷管理擴(kuò)容就是一條命令的事。LVM的基本邏輯可以這樣理解你把物理硬盤先聲明為物理卷PV然后像搭積木一樣把多個物理卷合并成一個卷組VG之后在卷組上切出任意大小的邏輯卷LV給系統(tǒng)用。這就好比你先買了一塊地皮物理卷把相鄰幾塊地合在一起形成開發(fā)區(qū)卷組然后按需劃分幾塊地分別蓋樓邏輯卷。生產(chǎn)環(huán)境中我?guī)缀跛械臄?shù)據(jù)盤都會用LVM。理由很簡單未來的擴(kuò)容、縮容、快照都需要它。比如某業(yè)務(wù)系統(tǒng)的數(shù)據(jù)目錄最初分配了100GB用了半年達(dá)到85%這時候你不需要重裝系統(tǒng)只需要加一塊新硬盤擴(kuò)展卷組然后再把邏輯卷擴(kuò)大文件系統(tǒng)也會跟著變大。整個過程對業(yè)務(wù)幾乎是平滑的不會產(chǎn)生長時間停機(jī)。還有一點(diǎn)容易被忽略LVM的快照功能在數(shù)據(jù)庫備份場景里極其好用。通過LVM快照可以在秒級生成一個一致性的數(shù)據(jù)鏡像然后基于鏡像做備份數(shù)據(jù)庫本身幾乎無感知。這比拿mysqldump去鎖表導(dǎo)數(shù)據(jù)優(yōu)雅太多了。3.2 從零構(gòu)建一個LVM卷組并動態(tài)擴(kuò)容具體操作我之前在文章里提過一次這里再完整走一遍流程。假設(shè)新加了一塊硬盤/dev/sdb想把它全部用于LVM。首先創(chuàng)建物理卷pvcreate /dev/sdb然后創(chuàng)建卷組命名隨意比如vg_datavgcreate vg_data /dev/sdb接著創(chuàng)建邏輯卷比如我要劃一個200GB的邏輯卷lvcreate -L 200G -n lv_web vg_data最后格式化并掛載mkfs.xfs /dev/vg_data/lv_web mkdir /data/web mount /dev/vg_data/lv_web /data/web為了讓重啟后還能掛載還需要在/etc/fstab里寫一條記錄這里我強(qiáng)烈建議用UUID而不是設(shè)備路徑因?yàn)樵O(shè)備名在重啟后可能會變化。用blkid命令查出邏輯卷的UUID然后這樣寫UUIDxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /data/web xfs defaults 0 0過了幾個月200GB不夠了。假設(shè)你模塊里還剩了空間一條命令擴(kuò)容邏輯卷lvextend -L 50G /dev/vg_data/lv_web如果用的是xfs文件系統(tǒng)接著執(zhí)行xfs_growfs /data/web如果用的是ext4則是resize2fs /dev/vg_data/lv_web擴(kuò)容完成后用df -h確認(rèn)一下你就會看到容量已經(jīng)悄悄變大了。整個過程不需要重啟不需要卸載分區(qū)。我這里想強(qiáng)調(diào)一個細(xì)節(jié)很多人在擴(kuò)容時只執(zhí)行了lvextend忘了擴(kuò)展文件系統(tǒng)然后跑來問“為什么df沒變化”。其實(shí)邏輯卷和文件系統(tǒng)是兩層概念邏輯卷變大了文件系統(tǒng)如果還保持原樣系統(tǒng)自然不認(rèn)賬。像我上面寫的順序先擴(kuò)邏輯卷再擴(kuò)文件系統(tǒng)一步都不能少。3.3 文件系統(tǒng)損壞時的應(yīng)急處理流程談及文件系統(tǒng)修復(fù)必須先立個規(guī)矩任何修復(fù)操作的前提都是先卸載分區(qū)絕不允許在掛載狀態(tài)下用fsck去修。原因很簡單掛載狀態(tài)下文件系統(tǒng)還在被內(nèi)核持續(xù)寫入你一邊修復(fù)它一邊改數(shù)據(jù)只會破壞得更厲害。應(yīng)急流程應(yīng)該是這樣的。首先確認(rèn)故障表現(xiàn)是啟動時報錯還是運(yùn)行中df看到異?;蛘遜mesg里刷滿了I/O錯誤。然后立即對數(shù)據(jù)盤做只讀掛載嘗試或者干脆先不掛載。接著用fsck做檢查umount /data/web fsck -y /dev/vg_data/lv_web-y參數(shù)的意思是自動回答yes對重要數(shù)據(jù)盤建議先跑一遍無-y的檢查模式看清楚報告內(nèi)容再決定。常見的結(jié)果有這么幾類clean表示文件系統(tǒng)沒有一致性問題直接掛載即可recover表示修復(fù)了一些臟數(shù)據(jù)塊modify表示做了結(jié)構(gòu)性修復(fù)。如果輸出里出現(xiàn)corrupt這類字樣或者修復(fù)過程非常耗時就不要再強(qiáng)行加載了該考慮從備份恢復(fù)數(shù)據(jù)。備份這件事我在任何一篇Linux相關(guān)的文章里都會提LVM不是備份RAID也不是備份。它們只是提高可用性。真正的安全感來自定期、經(jīng)過驗(yàn)證的備份。我見過不少團(tuán)隊(duì)因?yàn)槊孕臠VM快照和RAID10結(jié)果磁盤陣列卡故障時全盤數(shù)據(jù)一起完蛋連回滾的機(jī)會都沒有。所以建議你每個季度做一次備份恢復(fù)演練別把備份當(dāng)成拷完就扔到角落里的文件。4. Systemd服務(wù)托管把服務(wù)的生死管起來4.1 Unit文件里的關(guān)鍵配置項(xiàng)拆解日常管理Linux服務(wù)總繞不開systemd。它取代了早期混亂的init腳本體系核心價值就一句話一個工具統(tǒng)一管理服務(wù)的啟動、停止、重啟、開機(jī)自啟和崩潰拉起。但很多初學(xué)者對它的理解停留在systemctl start xxx寫Unit文件時完全照抄模板出了詭異問題就干瞪眼。一個最小可用的Unit文件其實(shí)不難常見路徑是/etc/systemd/system/xxx.service。核心部分是[Unit]和[Service]這兩段。我在實(shí)際寫服務(wù)單元時最常用的配置大概是這樣的[Unit] DescriptionMY Web Application Service Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Userappdeploy Groupappdeploy WorkingDirectory/opt/myapp ExecStart/usr/bin/python3 /opt/myapp/run.py Restarton-failure RestartSec5 EnvironmentCONFIG_PATH/etc/myapp/config.ini [Install] WantedBymulti-user.target這里幾個參數(shù)我逐個說清楚。Typesimple表示ExecStart啟動的進(jìn)程就是主進(jìn)程systemd不會另外做派生檢測。適用于絕大多數(shù)常駐進(jìn)程。User和Group就是指定以哪個身份運(yùn)行配合前面權(quán)限那塊內(nèi)容服務(wù)絕不以root身份運(yùn)行。Restarton-failure表示只有非正常退出時才重啟這是正確姿勢。如果寫成always手動停止服務(wù)后它也會被systemd再拉起來到時候你會以為服務(wù)出了靈異問題。RestartSec5是崩潰后等待5秒再拉起避免服務(wù)陷入頻繁崩潰重啟的死循環(huán)。寫完Unit文件后一定要重新加載systemctl daemon-reload systemctl enable --now myappenable是設(shè)置開機(jī)自啟--now意思是立即啟動兩個動作一步到位。4.2 怎么判斷服務(wù)是真的“活著”而不是“沒死透”用systemd管理服務(wù)你還會遇到一個問題進(jìn)程在但已經(jīng)卡死了算不算活著systemd默認(rèn)只看進(jìn)程是否存在而無法感知業(yè)務(wù)層面的心跳。這類問題最常見的場景是服務(wù)因?yàn)槟硞€外部依賴比如數(shù)據(jù)庫連接池耗盡僵住進(jìn)程不退出但業(yè)務(wù)已經(jīng)完全中斷。我的經(jīng)驗(yàn)是給關(guān)鍵業(yè)務(wù)服務(wù)配置ExecStartPre來做啟動前檢查再配合自定義的健康檢查機(jī)制。舉個例子服務(wù)端可以暴露一個簡單的HTTP健康檢查接口而systemd本身沒有內(nèi)置HTTP探測所以這個活通常是交給外部監(jiān)控來做。但在沒有外部監(jiān)控的情況下你可以利用Typenotify讓服務(wù)啟動完成后主動給systemd發(fā)一個“我準(zhǔn)備好了”的信號服務(wù)內(nèi)部再起一個線程定時探測業(yè)務(wù)健康度一旦異常就主動退出觸發(fā)systemd的Restarton-failure拉起邏輯。我看到不少剛?cè)胄械呐笥言谶@塊容易想復(fù)雜其實(shí)核心就是systemd負(fù)責(zé)進(jìn)程級別的守護(hù)業(yè)務(wù)級別的健康要靠應(yīng)用程序自己上報狀態(tài)。兩邊配合好服務(wù)才算是真正被管住了。講到這里順帶提一句查看服務(wù)日志時不要再用一個個/var/log/messages翻。如果服務(wù)是systemd托管的直接journalctl -u myapp -n 200 --no-pager輸出里會帶上時間、進(jìn)程號和標(biāo)準(zhǔn)化日志級別排查問題效率高得多。后面有一章我會專門展開日志排查技巧。5. 網(wǎng)絡(luò)與防火墻從排查到加固一氣呵成5.1 一套好用的網(wǎng)絡(luò)排查命令組合拳網(wǎng)絡(luò)問題在運(yùn)維里是最容易讓人血壓升高的因?yàn)榘Y狀五花八門連不上、時通時不通、慢、丟包。我的排查思路是按鏈路從底層到高層走先確認(rèn)物理層和鏈路層再確認(rèn)IP層再確認(rèn)傳輸層最后看應(yīng)用層。對應(yīng)的常用命令組合如下。第一步先看本機(jī)存活和IP配置ip addr show ip route show緊接著檢查有沒有丟包和錯誤ping -c 10 目標(biāo)IP第二步通過ss查看本機(jī)的監(jiān)聽端口和服務(wù)連接狀態(tài)ss -tunap | head -50你會看到每個連接的本地地址、端口、遠(yuǎn)端地址、狀態(tài)和所屬進(jìn)程。排查“端口明明開著但外部連不上”的問題時這一步能快速看出來是不是服務(wù)只綁定在了127.0.0.1上。對這是個非常高頻的坑。很多程序默認(rèn)只監(jiān)聽回環(huán)地址外部當(dāng)然連不上。第三步查看ARP和鄰居表ip neigh show如果發(fā)現(xiàn)網(wǎng)關(guān)的MAC地址解析不出來那問題可能在二層網(wǎng)絡(luò)上。再往下走就是查連通性路徑traceroute -n 目標(biāo)IP看看是到哪一跳之后斷了。而如果連接是通的但具體某個端口不通用nc或telnet做端口測試nc -vz 目標(biāo)IP 端口這一套打下來大部分網(wǎng)絡(luò)問題都能定位到大概范圍。順序千萬別亂我見過有人一上來直接traceroute結(jié)果本機(jī)路由表就是錯的白白浪費(fèi)半小時。5.2 firewalld管理規(guī)則的幾個要點(diǎn)防火墻配置是網(wǎng)絡(luò)安全的另一道閘門。以firewalld為例我的配置習(xí)慣是先確定這臺機(jī)器要對外提供哪些服務(wù)然后只開必要的端口或服務(wù)其余全部默認(rèn)拒絕。一種常見的錯誤是把firewalld當(dāng)成擺設(shè)在云廠商安全組和本機(jī)防火墻之間又開一層寬松放行等于層層設(shè)防層層裸奔。新增一個端口的放行規(guī)則可以這樣操作firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload注意--permanent不會立刻生效必須reload。如果只是臨時測試一下不加--permanent直接--add-port但重啟服務(wù)后規(guī)則會消失這也算一個新手容易迷糊的地方。我在生產(chǎn)機(jī)器上更傾向于使用富規(guī)則rich rule而不是單純放行端口。比如只允許某個來源IP訪問特定端口firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.100/32 port protocoltcp port9090 accept這樣做的好處是權(quán)限收得足夠窄。對外放行任何一個端口都意味著攻擊面擴(kuò)大一分能限定來源就一定要限定。還有個小提醒修改防火墻規(guī)則后務(wù)必留一條后路給自己。我最擔(dān)心的運(yùn)維事故之一就是遠(yuǎn)程改防火墻把自己擋在外面。所以建議你在改任何防火墻規(guī)則之前先敲一條定時清理計(jì)劃任務(wù)比如10分鐘后自動reload之前的規(guī)則。如果確認(rèn)改動沒問題再把這個計(jì)劃取消掉。這個土辦法救過我很多次。6. 日志分析與故障排查實(shí)錄6.1 journald日志的正確打開方式把systemd日志用好大概是排查效率提升最明顯的一步。以前看日志要跑到各個應(yīng)用目錄去翻文件現(xiàn)在只需要統(tǒng)一在journald這一個入口查。但journald的日志量很大你得學(xué)會怎么精準(zhǔn)地?fù)?。常用的過濾維度有三個按服務(wù)、按時間、按優(yōu)先級。比如查看某個服務(wù)最近20分鐘內(nèi)的錯誤級日志journalctl --since 20 minutes ago -p err -u myapp再比如查看某個時間段內(nèi)所有日志方便慢定位一個跨系統(tǒng)的故障窗口journalctl --since 2025-01-15 10:00:00 --until 2025-01-15 10:30:00當(dāng)journald日志本身占用的磁盤空間過大時可以通過journalctl --vacuum-time7d來清理超過7天的日志或者修改/etc/systemd/journald.conf里的SystemMaxUse參數(shù)限制最大體積。這個細(xì)節(jié)也容易引發(fā)磁盤寫滿的事故下面這個案例就是。6.2 一次“磁盤寫滿導(dǎo)致服務(wù)假死”的完整排查過程說一個典型的故障場景也是我用來帶新人練習(xí)的經(jīng)典案例。某天業(yè)務(wù)方反饋某個核心服務(wù)響應(yīng)極其緩慢甚至偶發(fā)超時。第一反應(yīng)當(dāng)然是去看監(jiān)控面板發(fā)現(xiàn)CPU和內(nèi)存都還算健康但磁盤使用率到了99%。這個數(shù)字一出來心里基本就有數(shù)了大概率是磁盤寫滿導(dǎo)致日志寫入阻塞進(jìn)而拖住了服務(wù)的主進(jìn)程。接下來按順序做三件事。第一用df -h確認(rèn)哪個分區(qū)滿了。第二用du -sh /var/log/*掃一遍日志目錄定位最占空間的文件。第三用journalctl --vacuum-time3d清理系統(tǒng)日志。如果應(yīng)用日志很大直接考慮開啟logrotate讓日志按天滾動并壓縮。這里有個值得注意的點(diǎn)日志文件被進(jìn)程打開著的情況下直接刪除文件并不會釋放磁盤空間。因?yàn)檫M(jìn)程還持有該文件的句柄刪除只是摘掉了目錄項(xiàng)。正確的做法是用truncate -s 0 /var/log/xxx.log把日志文件清空讓進(jìn)程繼續(xù)往里面寫等合適時機(jī)再配合logrotate做滾動。磁盤滿這個故障幾乎每個團(tuán)隊(duì)都會遇到但每次踩的感受都一樣痛。它最好的治療方法不是出事之后去清理而是提前用logrotate把日志增長控制住。你需要做的是在/etc/logrotate.d/下寫一個配置舉個例子/var/log/myapp/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }關(guān)鍵參數(shù)說一下rotate 7保留7份compress壓縮舊日志copytruncate可以先復(fù)制再清空原文件避免服務(wù)進(jìn)程句柄失效對不主動重開日志文件的程序非常友好。這套配置放上去服務(wù)日志在磁盤上不再無限膨脹你離“磁盤被日志塞滿”這個坑又遠(yuǎn)了一步。7. Shell腳本加固與自動化巡檢7.1 巡檢腳本的思路和骨架手動巡檢的缺點(diǎn)是真有事情發(fā)生時你未必能及時發(fā)現(xiàn)。自動化巡檢腳本的價值就體現(xiàn)在這里。我的思路是寫一個小而精的巡檢腳本定時執(zhí)行把異常結(jié)果匯總發(fā)送到告警群或郵箱。腳本不需要很復(fù)雜貴在穩(wěn)定和精準(zhǔn)。一個我常用的巡檢腳本骨架大概是這樣的檢查系統(tǒng)負(fù)載、檢查磁盤使用率、檢查關(guān)鍵端口存活、檢查內(nèi)存余量、檢查最近是否有異常重啟記錄。每個檢查項(xiàng)盡量輸出清晰的狀態(tài)標(biāo)識比如OK和FAIL方便一眼定位問題。如果你還是Shell腳本新手不用追求代碼看起來多高深保證正確性和可讀性更重要。我之前見過有人為了展示自己會寫復(fù)雜語法在巡檢腳本里搞了一堆花活兒結(jié)果部署后各種語法兼容問題。巡檢腳本是給系統(tǒng)穩(wěn)定服務(wù)的不是給你炫技的。7.2 讓Shell腳本在崩潰邊緣也能體面退出既然聊到Shell腳本多說幾句核心的健壯性設(shè)計(jì)。寫腳本時我第一行就會加上#!/usr/bin/env bash set -euo pipefail這四個選項(xiàng)分別解決不同的問題-e讓腳本在遇到未處理錯誤時立刻退出而不是帶著錯誤繼續(xù)執(zhí)行污染后續(xù)邏輯-u強(qiáng)制變量必須先定義再使用能攔截掉許多拼寫錯誤導(dǎo)致的詭異行為-o pipefail則保證管道中任何一個命令失敗都讓整個管道返回失敗狀態(tài)避免“前面失敗了但后面管道成功導(dǎo)致整體看起來成功”的假象。對沒有加set -euo pipefail習(xí)慣的朋友我給你一個場景你就能體會腳本里有一行cp a.txt /data/目錄不存在命令失敗了但腳本繼續(xù)往下跑最后把一個沒拷貝成功的文件當(dāng)成成功結(jié)果傳給下一個流程最終產(chǎn)生一個詭異的數(shù)據(jù)錯誤。這個鍋?zhàn)詈蟮降姿阏l的恐怕要在日志里查半天才能翻出來。加上這些選項(xiàng)之后腳本會變得“脆弱”很多一有風(fēng)吹草動就退出。這其實(shí)是好事因?yàn)橥顺龅迷皆鐔栴}暴露得越早排查成本越低。但有的時候你確實(shí)希望允許某條命令失敗而不退出比如檢測某個可選文件是否存在那就用|| true或者顯式地處理錯誤邏輯把控制權(quán)拿回到自己手里。腳本里還有一個很實(shí)用的習(xí)慣永遠(yuǎn)寫完整的路徑不要用相對路徑。比如rm -rf這種命令一旦腳本在錯誤的工作目錄下執(zhí)行相對路徑會把不該刪的東西刪掉。能嚴(yán)謹(jǐn)?shù)侥銓懙拿總€路徑都帶上/開頭腳本的生產(chǎn)級信任感才算到位。8. 自動化巡檢的定時任務(wù)配置8.1 crontab安排任務(wù)的具體做法巡檢腳本寫好了還得按時跑起來。crontab設(shè)計(jì)上非常簡單但就是有些常見的坑需要提前繞開。比如很少有人意識到cron任務(wù)執(zhí)行時的環(huán)境變量和你在終端里手動執(zhí)行腳本時并不完全相同。PATH會更短很多工具可能找不到。所以腳本里第一件事通常是顯式聲明路徑。我慣用的做法是給每個腳本加一個自定義環(huán)境變量文件或者干脆在腳本里把關(guān)鍵目錄全部用絕對路徑寫死。crontab的具體格式文檔到處都是我只強(qiáng)調(diào)幾個實(shí)際經(jīng)驗(yàn)。第一時間建議避開整點(diǎn)向0分和30分?jǐn)D不然同一個時刻大量任務(wù)同時觸發(fā)IO和CPU會瞬時飆高。第二crontab任務(wù)輸出的日志一定要重定向到文件不然輸出會通過郵件發(fā)給root默認(rèn)的mail服務(wù)還可能沒安裝日志直接丟失。一個推薦的crontab寫法是這樣的20 2 * * * /usr/bin/bash /opt/scripts/disk_check.sh /var/log/cron_disk.log 21這表示每天凌晨2點(diǎn)20分跑一次磁盤檢查標(biāo)準(zhǔn)錯誤也一并追加到同一個日志里。選了20分而不是0分就是錯開系統(tǒng)里其他常用的整點(diǎn)任務(wù)。8.2 巡檢發(fā)現(xiàn)異常后的響應(yīng)機(jī)制腳本和定時任務(wù)只是自動化的一半另一半是觸發(fā)后的響應(yīng)。很多團(tuán)隊(duì)的巡檢腳本跑了好幾年異常日志堆成山卻沒人去看這等于白搞。所以我在設(shè)計(jì)巡檢系統(tǒng)時一定會留一個“出口”一旦檢測到異常指標(biāo)立即發(fā)出告警。如果你暫時沒有成熟的監(jiān)控告警平臺可以先從一個最簡單的思路做起寫一個告警函數(shù)檢測到異常時調(diào)用系統(tǒng)里的mailx或者curl調(diào)用一個webhook把腳本輸出的異常信息發(fā)到群。這不需要引入任何重型組件只需一條命令的代價。最重要的還是保證告警不能誤報過于頻繁否則團(tuán)隊(duì)會產(chǎn)生“告警疲勞”看見通知也麻木了。我通常會給指標(biāo)設(shè)定合適的閾值和持續(xù)時間。比如磁盤使用率的閾值設(shè)置成85%持續(xù)異常兩次才告警一次這樣可以過濾掉瞬時波動帶來的騷擾。真正一次次踩過坑之后你會明白自動化不是為了替代人而是為了替人記得那些容易被遺忘的檢查項(xiàng)。一個系統(tǒng)管理員的成長往往就是從這些細(xì)節(jié)里堆出來的。前面從權(quán)限到存儲、從服務(wù)托管到日志排查、從腳本加固到定時巡檢點(diǎn)到為止地串了一遍。Linux的邊界很深但掌握這些實(shí)戰(zhàn)能力至少你已經(jīng)有了一整套處理日常和突發(fā)問題的底氣。剩下的深度就在一臺臺真實(shí)的服務(wù)器里慢慢打磨吧。