的避坑指南)
容器跑得穩(wěn)不穩(wěn)一半看鏡像另一半看存儲。我見過太多團隊把容器編排玩得飛起結果日志把磁盤寫爆、數(shù)據(jù)庫容器重啟后數(shù)據(jù)蒸發(fā)、多個容器搶寫同一個目錄導致文件損壞。這些問題根子上都不是容器技術本身的問題而是Docker存儲配置與管理沒做扎實。這個主題聽起來像是運維文檔里的老生常談但真正踩過坑的人都知道存儲這塊的細節(jié)密度極高從分層文件系統(tǒng)到存儲驅(qū)動選型從數(shù)據(jù)卷生命周期到掛載權限每一層都有能讓人熬夜的坑。這篇內(nèi)容面向已經(jīng)能把容器跑起來、但一遇到數(shù)據(jù)持久化和磁盤管理就心里沒底的朋友也適合想系統(tǒng)梳理存儲知識體系的從業(yè)者。我會把存儲驅(qū)動原理、卷與綁定掛載的取舍、空間配額、清理策略、備份恢復這些環(huán)節(jié)拆開講透每個選擇背后為什么這么做、參數(shù)怎么算、坑在哪里都給你交代清楚。1. 先搞清楚容器存儲的底層邏輯再動手1.1 分層文件系統(tǒng)到底解決了什么問題容器鏡像的本質(zhì)是一堆只讀層的疊加這是理解一切存儲配置的起點。你拉下來的一個鏡像可能是基礎系統(tǒng)層、運行時層、依賴庫層、應用代碼層依次疊上去的每層都是只讀的。容器啟動時Docker會在最上面加一個薄薄的可寫層所有運行時的寫操作都落在這個可寫層里。這個設計的核心價值在于鏡像層可以被無數(shù)個容器共享你起十個基于同一鏡像的容器磁盤上那份只讀數(shù)據(jù)只有一份省空間也省拉取時間。但這里有個關鍵認知必須建立起來可寫層是跟著容器生命周期走的。容器刪了可寫層就沒了里面寫的東西全部消失。很多人第一次遇到容器重啟數(shù)據(jù)沒了的困惑根源就是沒意識到自己把數(shù)據(jù)寫進了可寫層??蓪憣舆€有個性能問題它依賴存儲驅(qū)動來實現(xiàn)寫時復制Copy-on-Write你第一次修改某個文件時驅(qū)動要把整個文件從只讀層復制到可寫層再改文件大的話這個復制開銷很可觀。所以數(shù)據(jù)庫這類頻繁隨機寫的負載絕對不能把數(shù)據(jù)放在可寫層里跑。分層結構帶來的另一個隱性成本是層數(shù)。每多一層元數(shù)據(jù)管理和聯(lián)合掛載的開銷就多一點雖然現(xiàn)代存儲驅(qū)動優(yōu)化得不錯但構建鏡像時合并冗余層、減少層數(shù)依然是值得堅持的習慣。我一般建議把不常變的部分放底層頻繁變的部分放上層這樣構建緩存命中率高運行時層結構也清爽。1.2 存儲驅(qū)動選型的判斷依據(jù)存儲驅(qū)動是分層文件系統(tǒng)的具體實現(xiàn)不同驅(qū)動在性能、穩(wěn)定性、功能支持上差異明顯。選型不是越新越好而是要看你的宿主系統(tǒng)、文件系統(tǒng)類型和負載特征三者匹配。存儲驅(qū)動典型適用場景關鍵特性注意事項overlay2絕大多數(shù)現(xiàn)代Linux發(fā)行版默認多層合并效率高inode占用相對可控要求內(nèi)核版本較新底層文件系統(tǒng)建議ext4或xfsfuse-overlayfs無root權限場景、用戶態(tài)運行不需要特權即可工作性能略低于內(nèi)核態(tài)overlay2btrfs底層已用btrfs文件系統(tǒng)原生支持快照和子卷與btrfs特性耦合深遷移受限zfs底層已用zfs數(shù)據(jù)校驗、快照能力強內(nèi)存占用較高配置復雜vfs調(diào)試、兼容性兜底不做寫時復制每層完整復制性能差、占空間大僅作最后手段選型的實操判斷很簡單先看宿主文件系統(tǒng)ext4或xfs上優(yōu)先overlay2這是當前最成熟穩(wěn)妥的組合如果底層已經(jīng)是btrfs或zfs并且你確實想利用它們的快照能力再考慮對應驅(qū)動vfs基本只在排查詭異問題時臨時用。我踩過的一個坑是在xfs上沒開ftype1就上了overlay2結果某些操作報inode相關錯誤后來查明白是xfs創(chuàng)建時沒啟用d_type支持只能重建文件系統(tǒng)。所以部署前用xfs_info確認ftype1這一步別省。提示存儲驅(qū)動一旦選定并產(chǎn)生數(shù)據(jù)后直接切換驅(qū)動往往需要重建整個Docker數(shù)據(jù)目錄生產(chǎn)環(huán)境務必提前規(guī)劃別等數(shù)據(jù)堆起來再換。1.3 數(shù)據(jù)目錄規(guī)劃與磁盤布局Docker默認把鏡像、容器、卷都放在/var/lib/docker下這個目錄會隨著使用不斷膨脹。生產(chǎn)環(huán)境我強烈建議把它單獨掛到一個獨立分區(qū)或獨立磁盤上理由很實在鏡像層和容器日志的增長速度往往超出預期如果和系統(tǒng)盤混在一起一旦寫滿會連帶系統(tǒng)服務出問題。獨立分區(qū)的好處是即使Docker數(shù)據(jù)寫滿也只影響容器不會把整臺機器拖垮。布局上還有個細節(jié)值得說如果條件允許把鏡像存儲和卷存儲分到不同磁盤。鏡像層是讀多寫少、可重建的卷里往往是不可再生的業(yè)務數(shù)據(jù)兩者IO特征和重要性都不同。分開之后鏡像頻繁拉取刪除產(chǎn)生的IO不會干擾卷上的業(yè)務讀寫備份策略也能區(qū)別對待。當然這需要額外硬件投入中小規(guī)模部署至少做到數(shù)據(jù)目錄獨立分區(qū)這是性價比最高的做法。2. 數(shù)據(jù)卷與綁定掛載的取舍之道2.1 兩種持久化方式的本質(zhì)區(qū)別數(shù)據(jù)卷volume和綁定掛載bind mount是Docker提供的兩種主要持久化手段很多人混著用但它們的生命周期管理、權限模型、可移植性差別很大選錯了后期維護成本很高。數(shù)據(jù)卷由Docker管理存儲在數(shù)據(jù)目錄下的專用區(qū)域生命周期獨立于容器你可以顯式創(chuàng)建、命名、刪除容器刪了卷還在。綁定掛載則是把宿主機上任意路徑直接映射進容器路徑由你自己指定Docker不參與管理。打個比方數(shù)據(jù)卷像是Docker幫你租的一個倉庫鑰匙它管著你只管往里放東西綁定掛載像是你直接把自家某個房間的門開到容器里房間在哪、里面有什么全憑你自己維護。從可移植性看數(shù)據(jù)卷明顯占優(yōu)。用卷的容器換臺機器部署只要卷跟著遷移過去配置基本不用改綁定掛載寫死了宿主機路徑換環(huán)境就得改配置路徑不存在還會自動創(chuàng)建空目錄容易埋下數(shù)據(jù)看起來丟了的隱患。從權限角度看綁定掛載直接沿用宿主機文件權限容器內(nèi)用戶和宿主機用戶的UID/GID映射沒處理好就會遇到寫不進去的問題數(shù)據(jù)卷由Docker初始化時會處理權限相對省心。2.2 什么場景該用哪種方式判斷標準其實就一條這份數(shù)據(jù)是不是容器專屬、需不需要跟著容器走。數(shù)據(jù)庫數(shù)據(jù)、應用上傳文件、緩存目錄這類容器專屬數(shù)據(jù)優(yōu)先用數(shù)據(jù)卷管理清晰、遷移方便。而配置文件、開發(fā)時的源碼目錄、宿主機上已有的大量數(shù)據(jù)需要讓容器讀取這類場景綁定掛載更合適因為你要的就是讓容器直接看到宿主機上的特定內(nèi)容。開發(fā)環(huán)境里綁定掛載源碼目錄是常見做法改代碼容器里立刻生效不用重新構建鏡像。但要注意一個坑容器內(nèi)進程對掛載目錄的寫權限。如果容器以非root用戶運行而宿主機目錄屬主是root寫操作會失敗。解決辦法要么調(diào)整宿主機目錄屬主要么在啟動時用--user指定匹配的UID。我一般推薦在開發(fā)環(huán)境用--user $(id -u):$(id -g)把當前用戶映射進去省去權限折騰。生產(chǎn)環(huán)境還有個折中方案值得提命名卷配合初始化容器。用命名卷保證數(shù)據(jù)持久化和可移植用一個一次性容器在卷首次使用時把默認配置或初始數(shù)據(jù)寫進去主容器啟動時卷里已經(jīng)有內(nèi)容了。這樣既享受了卷的管理便利又解決了空卷啟動應用報錯的問題。2.3 卷的創(chuàng)建、掛載與生命周期管理卷的操作命令不多但每個都有講究。創(chuàng)建命名卷用docker volume create 卷名不指定驅(qū)動默認用local驅(qū)動數(shù)據(jù)落在數(shù)據(jù)目錄的volumes子目錄下。掛載時用-v 卷名:容器內(nèi)路徑或更明確的--mount typevolume,source卷名,target容器內(nèi)路徑。這兩種寫法有區(qū)別-v在卷不存在時會自動創(chuàng)建--mount則要求卷必須已存在后者更嚴謹生產(chǎn)腳本里我傾向用--mount避免拼寫錯誤悄悄創(chuàng)建出一個新空卷。卷的生命周期管理要養(yǎng)成習慣。docker volume ls看列表docker volume inspect 卷名看詳情包括實際存儲路徑和掛載點docker volume rm 卷名刪除。這里有個高頻坑正在被容器使用的卷刪不掉會報錯提示卷正在使用中。這是保護機制別想著強刪正確做法是先停掉并刪除使用該卷的容器再刪卷。還有個更隱蔽的坑docker volume prune會刪除所有未被任何容器引用的卷如果某個卷你暫時沒掛載但里面存著重要數(shù)據(jù)一執(zhí)行就沒了。所以prune類命令執(zhí)行前務必先docker volume ls確認沒有游離的重要卷。注意匿名卷啟動時只寫容器內(nèi)路徑不指定卷名自動生成的卷最容易變成孤兒容器刪了卷還在但沒人記得它是什么。生產(chǎn)環(huán)境盡量用命名卷匿名卷只適合臨時測試。3. 空間配額與磁盤占用控制實戰(zhàn)3.1 容器可寫層的空間限制容器可寫層不設限的話一個跑飛的進程能把磁盤寫滿進而影響整臺機器上所有容器。限制可寫層空間用--storage-opt size大小參數(shù)但這個參數(shù)有前提只對支持配額能力的存儲驅(qū)動生效overlay2在xfs且啟用pquota的情況下支持ext4上則不一定。所以用之前先確認你的驅(qū)動和文件系統(tǒng)組合是否支持不支持的話參數(shù)會被忽略或直接報錯。具體操作上xfs文件系統(tǒng)需要在掛載時啟用pquota選項然后啟動容器時加--storage-opt size10G這樣的參數(shù)。這里有個計算上的經(jīng)驗配額要留出余量別卡著業(yè)務峰值設。比如應用正常寫500M峰值可能到2G那配額設5G比較穩(wěn)妥既限制了失控增長又不會在正常波動時觸發(fā)寫失敗。配額觸發(fā)后的表現(xiàn)是寫操作報no space left on device應用側要能優(yōu)雅處理這種錯誤別直接崩潰。3.2 日志驅(qū)動的空間黑洞與治理容器日志是磁盤占用的隱形大戶默認的json-file日志驅(qū)動會把標準輸出全部落盤而且默認不輪轉。一個高頻打日志的應用幾天就能寫出幾十G。治理手段是配置日志輪轉在daemon配置里設置log-driver和log-opts比如限制單文件大小和保留文件數(shù)。{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }這段配置的意思是單個日志文件最大50M最多保留3個單個容器日志上限就是150M。這個值怎么定看你的日志量和排查需求。排查問題通??醋罱鼛仔r到幾天的日志150M對多數(shù)應用夠用日志特別密集的應用可以適當調(diào)大單文件、減少文件數(shù)或者干脆把日志驅(qū)動換成把日志轉發(fā)到集中日志系統(tǒng)的方式本地只留少量緩沖。改完daemon配置要重啟Docker服務生效已存在的容器需要重建才能應用新配置這點很多人會忽略改完發(fā)現(xiàn)老容器日志還在瘋長。3.3 鏡像與構建緩存的定期清理鏡像和構建緩存是另一塊容易失控的空間。每次構建產(chǎn)生的中間層、懸空鏡像dangling image沒有標簽引用的層會慢慢堆積。docker image prune清理懸空鏡像加-a則清理所有沒有被容器使用的鏡像后者要謹慎可能刪掉你還要用的鏡像。docker builder prune清理構建緩存構建頻繁的機器上這塊能釋放出驚人空間。清理策略我建議做成定期任務而不是等磁盤告警了再手動清。比如每周執(zhí)行一次懸空鏡像和過期構建緩存清理每月審視一次未被使用的鏡像。但自動清理腳本里絕對不要無腦加-a和--force組合我見過腳本把正在用于快速回滾的舊版本鏡像一起清掉出事時想回滾發(fā)現(xiàn)鏡像沒了。穩(wěn)妥做法是保留最近若干個版本或者給重要鏡像打上明確標簽并排除在清理范圍外。清理對象命令風險等級建議頻率懸空鏡像docker image prune低每周未使用鏡像docker image prune -a中高手動確認后執(zhí)行構建緩存docker builder prune低每周停止的容器docker container prune中確認后執(zhí)行未引用卷docker volume prune高極度謹慎4. 掛載權限、性能與故障排查實錄4.1 權限問題的根源與解法掛載相關的報錯里權限問題占了大多數(shù)表現(xiàn)是容器內(nèi)進程報Permission denied。根源在于容器內(nèi)進程的UID/GID和宿主機文件屬主不匹配。容器默認以root運行寫綁定掛載的宿主機目錄通常沒問題但以非root用戶運行的安全實踐下問題就來了。解法有三條路。第一條是調(diào)整宿主機目錄屬主讓它的UID/GID和容器內(nèi)運行用戶一致適合目錄專屬某個容器的場景。第二條是啟動容器時用--user指定UID/GID讓容器進程以宿主機上已有用戶的身份運行適合開發(fā)環(huán)境。第三條是用數(shù)據(jù)卷代替綁定掛載讓Docker處理初始化權限。三條路各有適用面我一般建議生產(chǎn)用命名卷規(guī)避大部分權限問題開發(fā)用--user映射當前用戶。還有個SELinux相關的坑值得單獨說。啟用了SELinux的系統(tǒng)上綁定掛載默認可能被拒絕訪問需要在掛載參數(shù)后加:z或:Z標簽。:z表示多個容器共享該目錄:Z表示該目錄專屬當前容器。加錯了標簽會導致其他容器訪問不了或者安全上下文沖突。這個細節(jié)在排查明明權限都對卻讀寫失敗時經(jīng)常是元兇。4.2 掛載性能的影響因素綁定掛載的性能受宿主機文件系統(tǒng)影響大尤其是大量小文件讀寫的場景。跨文件系統(tǒng)掛載、網(wǎng)絡文件系統(tǒng)掛載進容器性能損耗可能非常明顯。數(shù)據(jù)庫這類對IO敏感的應用數(shù)據(jù)目錄一定要放在本地高性能存儲上別掛到網(wǎng)絡存儲上跑延遲和吞吐都扛不住。數(shù)據(jù)卷的性能則取決于它落在什么存儲上。默認local驅(qū)動把卷放在數(shù)據(jù)目錄所在磁盤磁盤性能就是卷的性能上限。如果業(yè)務對IO要求高可以考慮把卷放到獨立的高性能磁盤上通過卷驅(qū)動的選項指定落盤位置。另外要注意容器可寫層的寫時復制開銷在大量寫場景下不可忽視把高頻寫目錄掛成卷能繞過這層開銷這也是數(shù)據(jù)庫容器必須掛卷的原因之一。4.3 常見故障速查與排查思路存儲相關的故障排查核心是快速定位問題出在哪一層是空間滿了、權限不對、掛載沒生效還是底層存儲出問題。我整理了一張速查表覆蓋高頻場景?,F(xiàn)象可能原因排查命令處理方向容器啟動報no space left磁盤或配額滿df -h、docker system df清理空間或調(diào)整配額容器內(nèi)寫文件Permission deniedUID/GID不匹配或SELinuxls -ln 掛載路徑、getenforce調(diào)屬主或加掛載標簽數(shù)據(jù)重啟后丟失數(shù)據(jù)寫在可寫層未掛卷docker inspect看Mounts補掛數(shù)據(jù)卷卷刪不掉仍被容器引用docker ps -a查引用先刪引用容器掛載目錄內(nèi)容為空綁定掛載覆蓋了鏡像內(nèi)目錄docker inspect看掛載配置確認掛載意圖必要時先復制數(shù)據(jù)IO性能異常低掛載到網(wǎng)絡存儲或跨文件系統(tǒng)mount看掛載類型遷到本地高性能存儲排查時我習慣先跑docker system df看整體空間分布鏡像、容器、卷、構建緩存各占多少一目了然比逐個目錄du快得多。然后docker inspect 容器名看Mounts字段確認掛載是否按預期生效很多時候問題就是掛載配置寫錯了沒生效。最后才深入到宿主機層面看文件系統(tǒng)、權限、SELinux。提示綁定掛載一個非空目錄到容器內(nèi)已有內(nèi)容的路徑時容器內(nèi)原內(nèi)容會被遮住看不見但沒被刪除。這個行為經(jīng)常讓人誤以為鏡像里的文件丟了其實是掛載覆蓋。需要保留原內(nèi)容的話先把它復制到宿主機目錄再掛載。5. 備份、遷移與存儲安全實踐5.1 數(shù)據(jù)卷的備份與恢復卷里的數(shù)據(jù)往往是業(yè)務核心資產(chǎn)備份不能靠復制數(shù)據(jù)目錄這種粗暴方式因為卷可能正被容器寫入直接復制可能拿到不一致的快照。穩(wěn)妥做法是起一個臨時容器掛載目標卷在容器內(nèi)做打包輸出到宿主機或另一個卷。docker run --rm \ -v 目標卷名:/source:ro \ -v /宿主機備份路徑:/backup \ alpine tar czf /backup/backup-$(date %Y%m%d).tar.gz -C /source .這條命令的關鍵點目標卷以只讀方式掛載避免備份過程意外寫入用輕量基礎鏡像執(zhí)行打包不引入額外依賴備份文件名帶日期便于版本管理?;謴蜁r反向操作把壓縮包解到目標卷即可。要注意恢復前最好停掉使用該卷的容器避免恢復過程中應用還在寫導致數(shù)據(jù)錯亂。備份頻率和保留策略看數(shù)據(jù)重要性和變化速度。核心業(yè)務數(shù)據(jù)每天備份、保留最近若干天加每月歸檔是常見組合。備份文件本身也要驗證定期做一次恢復演練別等真出事才發(fā)現(xiàn)備份是壞的。我見過備份腳本跑了一年沒報錯結果恢復時發(fā)現(xiàn)壓縮包一直是空的原因是掛載路徑寫錯了。5.2 跨主機遷移卷數(shù)據(jù)的注意事項遷移卷數(shù)據(jù)到另一臺主機思路和備份類似源主機打包傳輸?shù)侥繕酥鳈C目標主機先創(chuàng)建同名卷再解包。這里有幾個細節(jié)容易翻車。目標主機的卷要先創(chuàng)建好直接解包到一個不存在的路徑可能落到錯誤位置。UID/GID要保持一致否則遷移后權限錯亂解包時用--numeric-owner保留數(shù)字屬主。傳輸過程注意完整性校驗大文件傳輸出錯很隱蔽。遷移前還要確認目標主機的存儲驅(qū)動和文件系統(tǒng)兼容雖然卷數(shù)據(jù)本身是普通文件但掛載后的行為可能受驅(qū)動影響。遷移完成后先在目標主機上起一個測試容器驗證數(shù)據(jù)可讀可寫再切換正式流量別直接上生產(chǎn)。5.3 存儲層面的安全加固要點存儲安全這塊幾個基礎實踐必須做到。卷和綁定掛載的宿主機路徑權限要收緊別給全局可寫最小權限原則同樣適用于存儲。敏感數(shù)據(jù)卷考慮加密至少做到磁盤級加密容器層面可以用支持加密的卷驅(qū)動。備份文件里可能含敏感數(shù)據(jù)備份存儲的訪問控制要和源數(shù)據(jù)同等對待。還有個容易忽視的點容器內(nèi)不要掛載宿主機的敏感目錄比如整個/etc、Docker的socket文件。掛載Docker socket等于把宿主機控制權交出去這是嚴重的安全隱患。綁定掛載時只掛業(yè)務真正需要的路徑范圍越小越好。定期審計容器的掛載配置用docker inspect批量檢查有沒有意外的敏感掛載這個習慣能幫你發(fā)現(xiàn)配置漂移帶來的風險。6. 把存儲配置固化成可復用的規(guī)范6.1 用編排文件聲明存儲配置手工敲docker run帶一堆掛載參數(shù)容易出錯也難以復現(xiàn)。把存儲配置寫進編排文件compose文件是更可靠的做法卷定義、掛載點、只讀標志都能聲明清楚版本可控團隊共享。services: app: image: 應用鏡像 volumes: - app-data:/data - ./config:/app/config:ro logging: driver: json-file options: max-size: 50m max-file: 3 volumes: app-data: driver: local這段配置里app-data是命名卷由編排文件統(tǒng)一管理生命周期配置文件用綁定掛載并標記只讀防止容器意外修改日志輪轉直接在服務級別聲明。把這類配置模板化新項目直接套用能避免大量重復的配置錯誤。只讀掛載這個習慣特別值得推廣配置文件、證書這類不該被容器改的內(nèi)容一律加:ro既防誤寫也是安全加固。6.2 監(jiān)控存儲用量并設置告警存儲問題最好的處理方式是提前發(fā)現(xiàn)。監(jiān)控要覆蓋幾個維度宿主機磁盤使用率、Docker數(shù)據(jù)目錄增長趨勢、各卷的用量、容器日志增長速度。磁盤使用率設閾值告警比如80%預警、90%嚴重告警留出處理時間。卷用量監(jiān)控能提前發(fā)現(xiàn)異常增長的業(yè)務日志增長速度異常往往是應用出問題的信號。監(jiān)控工具選型上能采集磁盤指標并支持告警的方案都行關鍵是指標要采全、告警要能觸達。我建議至少把Docker數(shù)據(jù)目錄所在分區(qū)的使用率做成趨勢圖觀察增長速度比看瞬時值更有價值能預判什么時候會滿。另外定期跑docker system df把結果記錄下來形成空間使用的歷史基線異常時對比一目了然。6.3 團隊協(xié)作中的存儲規(guī)范約定存儲配置的混亂往往源于團隊沒有統(tǒng)一約定。幾個值得寫進團隊規(guī)范的約定命名卷統(tǒng)一命名規(guī)則比如項目名-用途避免匿名卷所有持久化數(shù)據(jù)必須掛卷禁止依賴可寫層日志輪轉作為服務標配不允許裸奔綁定掛載的宿主機路徑統(tǒng)一規(guī)劃不允許隨意指向系統(tǒng)目錄清理操作必須人工確認禁止在自動化腳本里無差別prune。這些約定看起來瑣碎但能避免大量低級問題。新成員入職時把存儲規(guī)范作為環(huán)境搭建的一部分講清楚比出了問題再救火高效得多。規(guī)范落地可以借助編排文件的模板和代碼審查掛載配置在審查階段就能發(fā)現(xiàn)不合規(guī)的地方。7. 幾個我踩過的坑和對應心得第一個坑是關于卷的刪除保護。早期有次清理磁盤圖省事直接跑了帶force的批量刪除把一個暫時沒掛載但存著歷史數(shù)據(jù)的卷刪了數(shù)據(jù)找不回來。從那以后我定了個規(guī)矩任何刪除卷的操作前先docker volume inspect看清楚它是什么、什么時候創(chuàng)建的確認無用再動手force參數(shù)能不用就不用。第二個坑是日志輪轉配置改了沒生效。有次磁盤告警查出來是某個容器日志寫了上百G明明daemon配置里設了輪轉。后來發(fā)現(xiàn)那個容器是在配置修改之前創(chuàng)建的舊容器不會自動應用新的日志配置必須重建。這件事讓我養(yǎng)成了改完daemon配置后檢查存量容器的習慣該重建的重建別以為改了配置就萬事大吉。第三個坑是綁定掛載覆蓋導致的數(shù)據(jù)丟失假象。有次部署新版本容器起來后應用報找不到默認配置文件排查半天發(fā)現(xiàn)是掛載了一個空的宿主機目錄到配置目錄把鏡像里的默認配置遮住了。解決辦法是先把鏡像里的默認配置復制到宿主機目錄再掛載或者改用命名卷加初始化容器的方式。這個行為本身是Docker的既定邏輯但第一次遇到確實容易懵。第四個坑和xfs的ftype有關前面提過這里補充細節(jié)。當時在一批機器上部署部分機器overlay2工作正常部分報錯。對比發(fā)現(xiàn)報錯機器的xfs是舊版本工具創(chuàng)建的沒啟用ftype1。這個參數(shù)在文件系統(tǒng)創(chuàng)建時就定了后期改不了只能備份數(shù)據(jù)、重建文件系統(tǒng)、恢復數(shù)據(jù)。所以新機器上線前的檢查清單里我把xfs_info確認ftype加進去了這一步花幾秒鐘能省掉后面幾小時的折騰。這些坑的共同點是都不是什么高深技術問題而是對存儲行為細節(jié)的理解不到位。Docker存儲的每個設計都有其邏輯理解了邏輯坑自然就少了。配置存儲時多問一句這個數(shù)據(jù)容器刪了還在嗎這個路徑權限對不對空間漲起來有沒有上限大部分問題在配置階段就能規(guī)避掉。