容器到主從復(fù)制與運(yùn)維排坑)
最近好幾個(gè)朋友來(lái)問(wèn)我同一個(gè)問(wèn)題docker啟動(dòng)redis 到底卡在哪一步了。有人是鏡像拉下來(lái)了但容器幾秒就退出有人是容器起來(lái)了可客戶端怎么都連不上還有人更慘卡在Docker Desktop本身啟動(dòng)不了報(bào)錯(cuò)信息在搜索引擎里一搜一大片。這些坑我早期全都踩過(guò)而且回頭看絕大多數(shù)都不是Redis本身的問(wèn)題而是對(duì)Docker運(yùn)行機(jī)制的一兩個(gè)關(guān)鍵點(diǎn)沒(méi)想清楚。這篇文章我就按自己實(shí)際的操作順序來(lái)寫(xiě)怎么把第一個(gè)Redis容器跑起來(lái)、怎么讓數(shù)據(jù)持久化、怎么把Docker Desktop啟動(dòng)失敗的坑填平、怎么用Compose做一主一從以及跑起來(lái)之后的日常運(yùn)維。無(wú)論你用Windows、macOS還是Linux這個(gè)思路都通用適合剛上手Docker的新手也適合那些已經(jīng)能啟動(dòng)但不知道怎么配置持久化和安全的同學(xué)。1. 為什么我建議用Docker跑Redis而不是直接裝1.1 一條命令解決版本與環(huán)境的老大難問(wèn)題如果你在裸機(jī)環(huán)境裝過(guò)Redis大概率經(jīng)歷過(guò)這么幾個(gè)場(chǎng)景Ubuntu上用apt裝了個(gè)老版本macOS上用brew install又裝了個(gè)新版本公司服務(wù)器上可能還是編譯安裝的3.2。版本之間命令有差異配置文件散落在不同目錄升級(jí)一次還要小心翼翼處理數(shù)據(jù)兼容性。Docker把這些麻煩全擋在外面了。鏡像就像打包好的運(yùn)行時(shí)里面帶了Redis二進(jìn)制、依賴庫(kù)和默認(rèn)配置你只需要關(guān)心數(shù)據(jù)放在哪、端口暴露在哪、密碼是什么。更重要的是多個(gè)版本可以共存。docker run -d --name redis7 -p 6379:6379 redis:7 docker run -d --name redis6 -p 6380:6379 redis:6.2這兩個(gè)容器互不干擾一個(gè)用6379一個(gè)用6380本質(zhì)上就是兩個(gè)獨(dú)立進(jìn)程。不想用了直接docker rm -f宿主機(jī)干干凈凈不會(huì)留下編譯殘留、系統(tǒng)服務(wù)和路徑配置。這就是我推薦Docker跑Redis的最重要原因隔離帶來(lái)的干凈。1.2 官方鏡像這么多 tag到底該選哪個(gè)Redis官方鏡像的tag很多常見(jiàn)的有redis:7、redis:7.2-alpine、redis:7-bookworm、redis:6.2-alpine。選的時(shí)候主要看基礎(chǔ)系統(tǒng)和體積。鏡像tag基礎(chǔ)系統(tǒng)體積適合場(chǎng)景redis:7Debian bookworm約120MB生產(chǎn)主力調(diào)試工具全redis:7-alpineAlpine Linux約35MB本地快速驗(yàn)證追求體積redis:6.2-alpineAlpine Linux約35MB老版本兼容需求redis:7.2Debian bookworm約120MB指定7.2小版本我的建議是本地開(kāi)發(fā)用redis:7-alpine體積小、啟動(dòng)快生產(chǎn)環(huán)境用redis:7這種Debian系鏡像出問(wèn)題的時(shí)候容器里可以apt-get裝排查工具。盡量別用latest因?yàn)槟悴恢滥奶炖聛?lái)的最新版Redis改了什么行為對(duì)比版本差異的時(shí)候會(huì)很痛苦。1.3 Docker Desktop 和裸 Docker Engine 怎么選本地開(kāi)發(fā)跑Docker最常見(jiàn)的選擇是Docker Desktop。它自帶了Docker Engine、Compose插件還有圖形界面能看容器狀態(tài)和volume。Linux用戶則直接用Docker Engine就行不需要桌面端。Windows和macOS的Docker Desktop本質(zhì)上是靠虛擬機(jī)來(lái)模擬Linux內(nèi)核Windows下面走的是WSL2或者Hyper-VmacOS走的是Apple虛擬化框架。這就是為什么網(wǎng)上那么多Docker Desktop failed to start because virtualisation support wasnt detected的報(bào)錯(cuò)虛擬化支持是它的命門(mén)。如果你現(xiàn)在還無(wú)法啟動(dòng)Docker Desktop別急著往下讀Redis命令先跳到第4章把虛擬化問(wèn)題解決。2. 跑起來(lái)再說(shuō)第一個(gè)Redis容器的最小可用命令2.1 五條命令把Redis拉起來(lái)并完成自檢第一步別想太復(fù)雜先跑一個(gè)不帶任何持久化、不帶密碼的最小容器。docker pull redis:7 docker run -d --name redis-demo -p 6379:6379 redis:7 docker ps docker exec -it redis-demo redis-cli pingdocker pull redis:7是把鏡像拉到本地。docker run里的-d表示后臺(tái)運(yùn)行不然終端會(huì)一直掛著。--name redis-demo是給容器起名之后操作都用這個(gè)名字不需要記容器ID。-p 6379:6379是端口映射左邊的6379是宿主機(jī)端口右邊的6379是容器內(nèi)Redis監(jiān)聽(tīng)的端口。如果一切正常最后一條命令會(huì)輸出PONG。這說(shuō)明容器里的Redis進(jìn)程活著redis-cli也能和它正常對(duì)話。2.2 你連不上Redis八成是少了 -p 這個(gè)參數(shù)我見(jiàn)過(guò)不少新手執(zhí)行docker run -d --name redis-demo redis:7然后拿著可視化客戶端去連127.0.0.1:6379結(jié)果連接被拒絕。原因很簡(jiǎn)單沒(méi)有-p的時(shí)候宿主機(jī)6379端口根本沒(méi)有監(jiān)聽(tīng)Redis只存在于容器內(nèi)部網(wǎng)絡(luò)里。-p做的事情是把宿主機(jī)的一個(gè)端口轉(zhuǎn)發(fā)到容器內(nèi)部的端口。用-p 16379:6379這種寫(xiě)法宿主機(jī)端口可以是16379容器內(nèi)Redis照常監(jiān)聽(tīng)6379。這樣你即使本機(jī)有別的服務(wù)占了6379也能用16379訪問(wèn)Redis。驗(yàn)證宿主機(jī)到容器的通路是否正??梢灾苯釉诒緳C(jī)執(zhí)行redis-cli -h 127.0.0.1 -p 6379 ping如果本機(jī)沒(méi)有安裝Redis客戶端也可以用Dockerexec進(jìn)去執(zhí)行redis-cli但這樣就繞過(guò)了端口映射無(wú)法驗(yàn)證網(wǎng)絡(luò)轉(zhuǎn)發(fā)。2.3 容器管理的基本動(dòng)作和端口占用的坑日常操作容器就三組命令docker stop redis-demo docker start redis-demo docker rm -f redis-demostop是優(yōu)雅停止start是重新啟動(dòng)rm -f是先強(qiáng)制停止再刪除。注意rm不會(huì)刪除鏡像只是把容器銷(xiāo)毀。端口占用是最常見(jiàn)的報(bào)錯(cuò)場(chǎng)景。假設(shè)你6379端口被一個(gè)殘留容器占著重新run時(shí)會(huì)看到類(lèi)似這樣的錯(cuò)誤Error response from daemon: driver failed programming external connectivity on endpoint redis-demo: Bind for 0.0.0.0:6379 failed: port is already allocated解決辦法不是換端口而是先看看是哪個(gè)容器占了端口docker ps --format table {{.Names}}\t{{.Ports}}找到占用的容器判斷能不能刪。如果只是端口被本機(jī)進(jìn)程占了那就在run命令里換一個(gè)宿主機(jī)端口比如-p 6380:6379。3. 持久化與配置文件從玩具級(jí)變成能用3.1 容器一刪數(shù)據(jù)就消失不是玄學(xué)用第2章的命令跑起來(lái)的Redis一旦執(zhí)行docker rm -f里面存的Key全部消失。這不是Redis不持久化而是容器文件系統(tǒng)本身是臨時(shí)的。容器被刪除時(shí)它的可寫(xiě)層也跟著沒(méi)了。要讓數(shù)據(jù)活下來(lái)就必須把Redis的數(shù)據(jù)目錄掛載到宿主機(jī)。Redis默認(rèn)會(huì)把數(shù)據(jù)寫(xiě)到/data目錄所以掛載時(shí)瞄準(zhǔn)這個(gè)路徑。docker run -d --name redis-stable -p 6379:6379 -v redis-data:/data redis:7 --appendonly yes這里的-v redis-data:/data是創(chuàng)建一個(gè)名為redis-data的命名卷掛到容器的/data。--appendonly yes是讓Redis開(kāi)啟AOF持久化因?yàn)槟J(rèn)情況下Redis只存快照可能好幾秒才寫(xiě)一次掛載了目錄也不一定能第一時(shí)間看到數(shù)據(jù)。AOF開(kāi)啟后每次寫(xiě)操作都會(huì)被追加到文件里數(shù)據(jù)安全性高很多。驗(yàn)證一下docker exec -it redis-stable redis-cli set foo bar docker rm -f redis-stable docker run -d --name redis-stable2 -p 6379:6379 -v redis-data:/data redis:7 --appendonly yes docker exec -it redis-stable2 redis-cli get foo最后一條命令輸出bar說(shuō)明數(shù)據(jù)成功跨容器存活。注意用的是同一個(gè)命名卷redis-data。3.2 要讓 redis.conf 生效啟動(dòng)命令必須這樣寫(xiě)官方鏡像雖然自帶一份配置但很多時(shí)候我們需要改maxmemory、requirepass、appendonly這些關(guān)鍵項(xiàng)。正確姿勢(shì)是把配置文件掛載進(jìn)容器然后在啟動(dòng)命令里顯式指定配置路徑。先準(zhǔn)備一份配置文件建議放在~/docker/redis/redis.confbind 0.0.0.0 protected-mode yes port 6379 daemonize no appendonly yes appendfsync everysec requirepass your-strong-password maxmemory 512mb maxmemory-policy allkeys-lru然后啟動(dòng)docker run -d \ --name redis-conf \ -p 6379:6379 \ -v ~/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf \ -v redis-data:/data \ redis:7 \ redis-server /usr/local/etc/redis/redis.conf注意最后一行。官方鏡像的默認(rèn)入口會(huì)執(zhí)行redis-server但你給了配置文件路徑之后Redis會(huì)讀指定文件而不是鏡像內(nèi)置配置。這里有兩個(gè)必須說(shuō)的坑一daemonize一定要寫(xiě)成no。Docker容器要求前臺(tái)保持一個(gè)主進(jìn)程如果Redis自己fork到后臺(tái)變成守護(hù)進(jìn)程容器會(huì)認(rèn)為主進(jìn)程退出然后直接停止。二文件權(quán)限對(duì)不上可能報(bào)錯(cuò)。官方鏡像里Redis是以UID 999的用戶運(yùn)行的如果掛載進(jìn)來(lái)的配置文件屬主不是999有可能讀到日志目錄時(shí)出現(xiàn)Permission denied。簡(jiǎn)單粗暴的辦法是sudo chown -R 999:999 ~/docker/redis3.3 密碼、bind、protected-mode三件套缺一不可把Redis暴露到Docker端口之后千萬(wàn)別裸奔。公網(wǎng)上有大量掃描器在掃6379端口一旦發(fā)現(xiàn)沒(méi)有密碼的Redis分分鐘給你寫(xiě)入挖礦程序成為肉雞。這不是危言聳聽(tīng)真實(shí)環(huán)境里我見(jiàn)過(guò)太多因?yàn)镽edis沒(méi)設(shè)密碼被打穿的案例。設(shè)置密碼有三種方式方式命令/配置特點(diǎn)配置文件在redis.conf里寫(xiě)requirepass xxx推薦可版本管理命令行參數(shù)docker run ... redis-server --requirepass xxx快速測(cè)試但容易被ps看到環(huán)境變量官方鏡像7.x支持REDIS_PASSWORDxxx適合Compose里管理同時(shí)還要理解Redis的默認(rèn)保護(hù)邏輯。Redis 3.2之后默認(rèn)protected-mode yes如果沒(méi)設(shè)密碼Redis只允許本機(jī)回環(huán)地址訪問(wèn)宿主機(jī)從外部連過(guò)來(lái)會(huì)被拒絕。當(dāng)你用-p 6379:6379把端口暴露出來(lái)時(shí)docker網(wǎng)絡(luò)轉(zhuǎn)發(fā)里的源IP不是127.0.0.1所以必須設(shè)密碼并且把bind設(shè)為0.0.0.0才能穩(wěn)定訪問(wèn)。如果不想讓Redis對(duì)宿主機(jī)所有網(wǎng)卡開(kāi)放只給局域網(wǎng)用可以這樣寫(xiě)配置bind 127.0.0.1 192.168.1.100 protected-mode yes requirepass your-strong-password這會(huì)在宿主機(jī)IP上監(jiān)聽(tīng)但不會(huì)暴露到公網(wǎng)網(wǎng)卡。4. Docker Desktop 啟動(dòng)失敗排查從報(bào)錯(cuò)到跑通4.1 先把報(bào)錯(cuò)原文記住Docker Desktop在Windows上最常見(jiàn)的啟動(dòng)報(bào)錯(cuò)就是Docker Desktop failed to start because virtualisation support wasnt detected.有些版本也會(huì)顯示Virtualization support not detected這行字一旦出現(xiàn)基本可以斷定Docker Desktop想要的虛擬化后端沒(méi)就緒。它要么是WSL2沒(méi)裝好要么是Hyper-V無(wú)效要么是CPU的硬件虛擬化沒(méi)開(kāi)啟。別第一時(shí)間重裝先按下面的鏈路排查。4.2 虛擬化是什么為什么Docker Desktop偏偏依賴它Docker Desktop運(yùn)行原理和Linux上的Docker Engine不太一樣。Linux上的Docker直接調(diào)用Linux內(nèi)核的命名空間和cgroup天生就是原生的。而macOS和Windows的進(jìn)程并不是Linux程序Docker Desktop必須在系統(tǒng)里養(yǎng)一個(gè)輕量級(jí)Linux虛擬機(jī)所有容器都跑在這臺(tái)虛擬機(jī)里。這臺(tái)虛擬機(jī)總得有東西支撐。Windows上要么用WSL2要么用Hyper-VmacOS上用的是Apple虛擬化框架。這些基礎(chǔ)設(shè)施全都依賴CPU硬件虛擬化能力也就是Intel的VT-x或AMD的AMD-V。如果CPU虛擬化沒(méi)打開(kāi)或者WSL2/Hyper-V組件沒(méi)啟用Docker Desktop當(dāng)然無(wú)法啟動(dòng)。你可以理解成Docker Desktop像個(gè)雜技演員虛擬化就是舞臺(tái)。舞臺(tái)沒(méi)搭好演員再專(zhuān)業(yè)也上不了場(chǎng)。4.3 Windows 修復(fù)鏈路WSL2、系統(tǒng)功能、BIOSWindows上排查順序我建議從軟件到硬件第一步看任務(wù)管理器。按CtrlShiftEsc切到性能標(biāo)簽底部有個(gè)虛擬化狀態(tài)。如果顯示已啟用說(shuō)明CPU沒(méi)問(wèn)題問(wèn)題出在系統(tǒng)組件如果顯示已禁用得進(jìn)BIOS打開(kāi)。第二步啟用Windows功能。WinR輸入optionalfeatures把這三項(xiàng)勾上適用于Linux的Windows子系統(tǒng)虛擬機(jī)平臺(tái)Windows虛擬機(jī)監(jiān)控程序平臺(tái)第三步安裝WSL2。以管理員身份打開(kāi)PowerShellwsl --install wsl --set-default-version 2裝好以后重啟電腦再打開(kāi)Docker Desktop。如果還是報(bào)錯(cuò)在Docker Desktop設(shè)置里找到Resources - WSL Integration確認(rèn)WSL2后端已經(jīng)啟用。第四步如果任務(wù)管理器里虛擬化顯示已禁用那就重啟進(jìn)BIOS/UEFI找Intel Virtualization TechnologyIntel平臺(tái)或SVM ModeAMD平臺(tái)把它設(shè)為Enabled。不同主板叫法不同但核心詞就是Virtualization。我遇到過(guò)一臺(tái)Windows 11的機(jī)器所有組件都開(kāi)了虛擬化也開(kāi)了Docker Desktop還是起不來(lái)。最后發(fā)現(xiàn)是之前裝過(guò)舊版本Hyper-V沒(méi)完全卸載干凈跟WSL2后端沖突。解決辦法是把Docker Desktop的后端切換成Hyper-V試試或者徹底卸載重裝。這種問(wèn)題很折騰但能擺平。4.4 macOS 上的啟動(dòng)授權(quán)與資源分配macOS上的Docker Desktop啟動(dòng)失敗相對(duì)少一些但有兩個(gè)常見(jiàn)的坎。一個(gè)是首次安裝后需要授權(quán)。第一次打開(kāi)Docker Desktop系統(tǒng)會(huì)彈窗提示需要Docker訪問(wèn)某些資源必須去系統(tǒng)設(shè)置 - 隱私與安全性里手動(dòng)允許不然界面會(huì)卡在Docker Engine is starting。另一個(gè)是資源不夠。如果你用Docker Desktop同時(shí)跑Redis、MySQL、Kibana多個(gè)容器默認(rèn)內(nèi)存配額很容易不夠。柚子容器起一個(gè)崩一個(gè)Redis容器起來(lái)以后docker logs里全是Cannot allocate memory。這時(shí)去Docker Desktop的Settings - Resources把Memory調(diào)高到4GB以上CPU也可以給2到4核。蘋(píng)果芯片的Mac跑Docker性能通常比Intel版流暢但個(gè)別舊版本Docker Desktop在Rosetta模式下會(huì)有兼容性問(wèn)題。直接選Use Rosetta for x86/amd64 emulation on Apple Silicon這個(gè)選項(xiàng)如果勾了反而啟動(dòng)慢就把勾去掉。4.5 Docker起來(lái)了但Redis容器網(wǎng)絡(luò)不通怎么辦Docker Desktop本身能啟動(dòng)了可Redis容器還是連不上這時(shí)候按三個(gè)方向排查。第一確認(rèn)容器真的在跑。docker ps看看redis-demo的狀態(tài)是不是Up。如果顯示Exited (0)大概率是redis.conf里daemonize yes導(dǎo)致的回第3章改成no。第二看日志。docker logs redis-demo如果有網(wǎng)絡(luò)相關(guān)錯(cuò)誤直接搜索錯(cuò)誤關(guān)鍵字基本上能找到答案。第三查看容器IP和網(wǎng)絡(luò)模式。如果不做任何網(wǎng)絡(luò)配置Redis容器默認(rèn)在bridge網(wǎng)絡(luò)上宿主機(jī)通過(guò)-p映射訪問(wèn)即可。有些同學(xué)為了讓Redis連MySQL自己創(chuàng)建了自定義網(wǎng)絡(luò)卻忘了-p不能和自定義網(wǎng)絡(luò)同時(shí)生效于容器啟動(dòng)命令的場(chǎng)景導(dǎo)致宿主機(jī)永遠(yuǎn)連不上Redis。我的建議是開(kāi)發(fā)環(huán)境一律用-p映射多容器互通靠Docker Compose的自定義網(wǎng)絡(luò)不要手工混用。還有一個(gè)最容易被忽略的點(diǎn)Docker Desktop重啟后之前那些沒(méi)有設(shè)置自動(dòng)重啟的Redis容器不會(huì)自己起來(lái)。啟動(dòng)Redis時(shí)加上--restart unless-stopped能省下一半的運(yùn)維事故。5. 從單機(jī)到主從用Docker Compose編排一主一從5.1 為什么要多一個(gè)從節(jié)點(diǎn)單機(jī)Redis能跑通只是第一步。實(shí)際項(xiàng)目里Redis承擔(dān)緩存、分布式鎖、臨時(shí)數(shù)據(jù)存儲(chǔ)這些職責(zé)一旦單節(jié)點(diǎn)掛了緩存全部穿透到數(shù)據(jù)庫(kù)很容易把服務(wù)打垮。最輕量級(jí)的容災(zāi)方案就是主從復(fù)制一個(gè)主節(jié)點(diǎn)負(fù)責(zé)寫(xiě)一個(gè)或多個(gè)從節(jié)點(diǎn)負(fù)責(zé)同步數(shù)據(jù)讀請(qǐng)求可以分散到從節(jié)點(diǎn)。這種方案還有個(gè)額外好處備份的時(shí)候可以從從節(jié)點(diǎn)導(dǎo)出數(shù)據(jù)不影響主節(jié)點(diǎn)性能。做分布式鎖之類(lèi)的場(chǎng)景主從也能提供一些基礎(chǔ)保障但要真正保證安全得配合紅鎖或哨兵這里先不展開(kāi)。主從本身不是高可用方案主節(jié)點(diǎn)掛掉后從節(jié)點(diǎn)不會(huì)自動(dòng)頂上需要哨兵或人工介入。但對(duì)于個(gè)人項(xiàng)目、中小型內(nèi)部系統(tǒng)一主一從的內(nèi)存型Redis已經(jīng)足夠爽了。5.2 目錄結(jié)構(gòu)和配置文件先寫(xiě)好用Docker Compose管理主從最清晰。先建一個(gè)目錄比如redis-clusterredis-cluster/ ├── docker-compose.yml ├── master.conf └── slave.confmaster.confbind 0.0.0.0 protected-mode yes port 6379 daemonize no appendonly yes requirepass 123456slave.confbind 0.0.0.0 protected-mode yes port 6379 daemonize no appendonly yes requirepass 123456 masterauth 123456 replicaof redis-master 6379關(guān)鍵點(diǎn)是slave.conf里必須有兩行masterauth填主節(jié)點(diǎn)的密碼replicaof指定主節(jié)點(diǎn)的服務(wù)名和端口。Docker Compose默認(rèn)會(huì)創(chuàng)建一個(gè)網(wǎng)絡(luò)服務(wù)名redis-master在容器內(nèi)可以直接當(dāng)作DNS解析。docker-compose.ymlservices: redis-master: image: redis:7 container_name: redis-master ports: - 6379:6379 volumes: - ./master.conf:/usr/local/etc/redis/redis.conf command: [redis-server, /usr/local/etc/redis/redis.conf] redis-slave: image: redis:7 container_name: redis-slave ports: - 6380:6379 depends_on: - redis-master volumes: - ./slave.conf:/usr/local/etc/redis/redis.conf command: [redis-server, /usr/local/etc/redis/redis.conf]從節(jié)點(diǎn)的ports寫(xiě)6380:6379意思是宿主機(jī)用6380訪問(wèn)從節(jié)點(diǎn)。容器內(nèi)部從節(jié)點(diǎn)依然監(jiān)聽(tīng)6379和主節(jié)點(diǎn)不沖突。5.3 啟動(dòng)并驗(yàn)證復(fù)制狀態(tài)在redis-cluster目錄下執(zhí)行docker compose up -d docker compose ps兩條命令都正常后分別看主從的復(fù)制信息docker exec -it redis-master redis-cli -a 123456 info replication docker exec -it redis-slave redis-cli -a 123456 info replication主節(jié)點(diǎn)預(yù)期輸出role:master connected_slaves:1從節(jié)點(diǎn)預(yù)期輸出role:replica master_host:redis-master master_link_status:up如果master_link_status是down說(shuō)明同步?jīng)]建立。接著實(shí)測(cè)數(shù)據(jù)復(fù)制docker exec -it redis-master redis-cli -a 123456 set hello world docker exec -it redis-slave redis-cli -a 123456 get hello主節(jié)點(diǎn)寫(xiě)入hello從節(jié)點(diǎn)能讀到world主從復(fù)制鏈路就通了。5.4 主從常見(jiàn)兩個(gè)坑NOAUTH和bind限制第一次搭主從時(shí)我踩過(guò)最深的坑就是NOAUTH Authentication required?,F(xiàn)象是主節(jié)點(diǎn)能正常寫(xiě)從節(jié)點(diǎn)一直連不上docker logs redis-slave刷出來(lái)MASTER - REPLICA sync started Error reply to MASTERAUTH: NOAUTH Authentication required.原因很簡(jiǎn)單主節(jié)點(diǎn)設(shè)置了requirepass 123456但從節(jié)點(diǎn)沒(méi)有配置masterauth 123456所以每次握手都被主節(jié)點(diǎn)拒絕。解決辦法就是在slave.conf里補(bǔ)上masterauth。另一個(gè)坑是bind限制。如果主節(jié)點(diǎn)的配置里只寫(xiě)了bind 127.0.0.1從節(jié)點(diǎn)在容器網(wǎng)絡(luò)里訪問(wèn)redis-master:6379時(shí)會(huì)發(fā)現(xiàn)主節(jié)點(diǎn)只監(jiān)聽(tīng)回環(huán)地址連接直接被拒絕。我在3.2節(jié)特意強(qiáng)調(diào)Docker環(huán)境要把bind設(shè)為0.0.0.0就是這個(gè)原因。這兩個(gè)問(wèn)題都不難但報(bào)錯(cuò)信息很有迷惑性第一次遇到可能花掉一下午。6. 容器跑起來(lái)之后的日常運(yùn)維與備份6.1 日志、資源占用以及給Redis限量Redis啟動(dòng)后第一時(shí)間看日志docker logs -f redis-demo-f是持續(xù)跟蹤日志輸出CtrlC退出查看。啟動(dòng)階段有沒(méi)有加載配置文件、主從同步有沒(méi)有異常都能在日志里看到。資源占用用docker stats它像個(gè)任務(wù)管理器實(shí)時(shí)顯示每個(gè)容器的CPU、內(nèi)存、網(wǎng)絡(luò)IO。如果Redis內(nèi)存不設(shè)上限一旦業(yè)務(wù)里寫(xiě)入大量緩存它可能會(huì)把宿主機(jī)內(nèi)存吃光。開(kāi)發(fā)環(huán)境下可以在啟動(dòng)命令里加上資源限制docker run -d --name redis-limited --memory 512m --cpus 1 redis:7更優(yōu)雅的做法是在配置文件里設(shè)maxmemory讓Redis自己控制內(nèi)存超了就走maxmemory-policy指定的淘汰策略。allkeys-lru是常用策略適合緩存場(chǎng)景如果只是做分布式鎖和臨時(shí)狀態(tài)存儲(chǔ)建議用noeviction寧可報(bào)錯(cuò)也不丟數(shù)據(jù)。6.2 可視化客戶端怎么連、連不上怎么查命令行用多了總想偷懶可視化客戶端確實(shí)更適合日??幢O(jiān)控和key分布。市面上的選擇很多客戶端特點(diǎn)Another Redis Desktop Manager輕量、跨平臺(tái)、免費(fèi)Redis Insight官方出品功能全面自帶分析和集群管理redis-cli命令行排查最快連接參數(shù)就是三件套host填宿主機(jī)IPport填映射出來(lái)的宿主機(jī)端口password填配置里的密碼。比如本機(jī)Docker映射的Redishost填127.0.0.1、port填6379。連不上的時(shí)候照著這個(gè)順序查docker ps確認(rèn)容器是Up狀態(tài)docker logs redis-demo看有沒(méi)有報(bào)錯(cuò)宿主機(jī)直接curl或者用nc測(cè)端口通不通確認(rèn)不是防火墻攔截如果Redis運(yùn)行在云服務(wù)器上還要檢查云廠商的安全組是否放行了6379端口。這個(gè)坑特別隱蔽本地客戶端死活連接超時(shí)容器明明在跑最后發(fā)現(xiàn)是安全組沒(méi)開(kāi)。6.3 備份Redis數(shù)據(jù)別只靠Docker的volume掛載volume能解決容器刪除的數(shù)據(jù)丟失問(wèn)題但解決不了整塊磁盤(pán)損壞和誤刪容器卷的問(wèn)題。備份是另一回事。Redis持久化文件就兩種RDB快照和AOF日志。日常備份最簡(jiǎn)單的做法是直接打包volume比如把redis-data卷導(dǎo)出成一個(gè)tar包docker run --rm -v redis-data:/data -v $(pwd):/backup alpine tar czf /backup/redis-data.tar.gz -C /data .這條命令會(huì)啟動(dòng)一個(gè)臨時(shí)Alpine容器把redis-data卷的數(shù)據(jù)打包到當(dāng)前目錄。也可以趁Redis運(yùn)行中用客戶端觸發(fā)一次持久化docker exec -it redis-demo redis-cli -a 123456 --rdb /tmp/dump.rdb但它寫(xiě)的是容器內(nèi)的臨時(shí)路徑還是要把文件拷貝出來(lái)docker cp redis-demo:/tmp/dump.rdb ./dump.rdb恢復(fù)的時(shí)候先把容器停掉把備份文件放到正確的persist目錄再啟動(dòng)容器。千萬(wàn)別在Redis運(yùn)行的時(shí)候直接往數(shù)據(jù)目錄塞文件文件損壞概率極高。6.4 順手把緩存治理和容器清理做了Redis做中間件用久了最大的敵人不是性能而是緩存本身。Key設(shè)計(jì)不合理、value過(guò)大、過(guò)期時(shí)間設(shè)成永久慢慢把內(nèi)存拖垮。容器環(huán)境下我見(jiàn)過(guò)太多人把所有數(shù)據(jù)塞進(jìn)Redis然后看著內(nèi)存報(bào)警。緩存治理的基礎(chǔ)操作就是給Redis設(shè)置淘汰策略。6.1里提到的maxmemory-policy allkeys-lru適合大部分緩存場(chǎng)景內(nèi)存滿了自動(dòng)淘汰最久沒(méi)用的Key。同時(shí)要在業(yè)務(wù)層給Key加統(tǒng)一的過(guò)期時(shí)間別指望兜底策略扛住所有臟數(shù)據(jù)。另外容器體積會(huì)隨著鏡像的更新越來(lái)越大。定期清理一下沒(méi)用的容器和鏡像能避免磁盤(pán)爆掉docker container prune docker image prune這些命令不會(huì)影響正在運(yùn)行的容器但會(huì)清掉已停止的容器和懸空鏡像。如果要強(qiáng)制清空所有未使用資源用docker system prune -a執(zhí)行前想清楚Redis的volume如果沒(méi)有掛載宿主機(jī)路徑會(huì)連數(shù)據(jù)一起刪掉。最后說(shuō)一點(diǎn)我自己的習(xí)慣。用Docker跑Redis踩過(guò)的最大跟頭就是一開(kāi)始只圖能啟動(dòng)密碼不設(shè)、數(shù)據(jù)不掛volume、配置不持久化結(jié)果一次docker system prune把開(kāi)發(fā)環(huán)境的緩存全清空了?,F(xiàn)在我的做法是每個(gè)項(xiàng)目第一次拉Redis容器時(shí)先把redis.conf、命名卷、密碼三類(lèi)事情配齊再考慮端口和主從。你按這篇文章的順序先把最小命令跑通然后立刻補(bǔ)上持久化和密碼再研究主從后面基本不會(huì)有大坑。如果你在Windows上還卡在Docker Desktop啟動(dòng)階段別急著繼續(xù)往下折騰容器回頭把虛擬化那一章看完要先讓舞臺(tái)搭起來(lái)演員才有地方施展。