
最近在 CentOS 8 上敲下dnf install docker-ce之后屏幕上彈出一串以problem with installed package podman-1.6.4-10.module_el8.2.03055e198a41.x86_64開頭的報錯先別急著懷疑軟件源配錯了。這個問題我前前后后遇到過好幾次它跟倉庫地址沒關(guān)系真正原因是 CentOS 8 系統(tǒng)自帶的 podman 與 docker-ce 在同一個“坑位”上撞了車。這篇文章把報錯原理、處理思路、手把手安裝步驟和后續(xù)容易踩的坑都過一遍給準(zhǔn)備在 CentOS 8 上轉(zhuǎn)向 Docker 的同學(xué)一份可以直接照著操作的參考。1. 報錯現(xiàn)場podman 到底和 docker 搶了什么1.1 一條典型的安裝報錯長什么樣假設(shè)你已經(jīng)配置好了 Docker 官方軟件源執(zhí)行dnf install -y docker-ce docker-ce-cli containerd.io正常情況下 DNF 會直接解析依賴并安裝但 CentOS 8 默認(rèn)預(yù)裝了 Podman 容器工具鏈于是很可能出現(xiàn)下面這種輸出Error: Problem: problem with installed package podman-1.6.4-10.module_el8.2.03055e198a41.x86_64 - package podman-1.6.4-10.module_el8.2.03055e198a41.x86_64 conflicts with docker-ce provided by docker-ce-3:19.03.13-3.el7.x86_64 - docker-ce-3:19.03.13-3.el7.x86_64 conflicts with podman provided by podman-1.6.4-10.module_el8.2.03055e198a41.x86_64 - problem with installed package buildah-1.11.6-10.module_el8.2.03055e198a41.x86_64 - package buildah-1.11.6-10.module_el8.2.03055e198a41.x86_64 conflicts with docker-ce provided by docker-ce-3:19.03.13-3.el7.x86_64注意報錯里的關(guān)鍵詞是conflicts with也就是軟件包沖突。DNF 不是想說你的 podman 壞了而是說當(dāng)前系統(tǒng)里已經(jīng)裝好的 podman 包與你要安裝的 docker-ce 包之間存在 RPM 層面的沖突關(guān)系兩邊都有同一個“勢力范圍”包管理器不允許它們同時存在。標(biāo)題中出現(xiàn)的1.6.4-10.module_el8.2.03055e198a41也不是隨意生成的版本號。module_el8.2.0說明這個 podman 來自 CentOS 8.2 的 AppStream 模塊倉庫由container-tools模塊構(gòu)建。x86_64是架構(gòu)。這個版本號本身就暴露了它的來源后面排查模塊時用得上。1.2 沖突根源CentOS 8 的默認(rèn)容器棧要理解這個沖突得先知道 CentOS 8 和之前的 CentOS 7 在容器生態(tài)上的態(tài)度變了。CentOS 7 時代Docker 是默認(rèn)的容器方案系統(tǒng)里幾乎沒有 Podman 的影子。到了 CentOS 8 / RHEL 8Red Hat 主推的是 Podman、Buildah、Skopeo 這一套“無守護(hù)進(jìn)程”容器工具鏈系統(tǒng)安裝時甚至默認(rèn)就把 podman 和相關(guān)組件裝上。這套工具鏈由 AppStream 倉庫里的container-tools模塊統(tǒng)一管理。我們可以在安裝前用下面命令查看dnf module list container-tools正常情況下會看到類似輸出Name Stream Profiles Summary container-tools rhel8 common Most container tools container-tools 2.0 common Most container tools也就是說podman、buildah 這些包是以模塊形式分發(fā)的。正因?yàn)槿绱四銜阱e誤信息里看到module_el8.2.0這種帶著模塊標(biāo)識的版本號。那它到底和 Docker 沖突在哪簡單說兩者都想控制同一個命令入口。Podman 為了兼容習(xí)慣使用 Docker 命令的用戶提供了/usr/bin/docker的模擬入口而 Docker CE 安裝后也要提供/usr/bin/docker。命令名重疊管理方式又不一致RPM 包維護(hù)者為了防止裝完系統(tǒng)一團(tuán)亂直接在包的Conflicts字段里聲明了互斥關(guān)系。于是 DNF 檢查依賴時發(fā)現(xiàn)已經(jīng)有 podman 占著位置再裝 docker-ce 就違反了沖突規(guī)則只能停下。1.3 為什么 DNF 不自動把 podman 卸掉很多新手第一反應(yīng)是既然沖突你 DNF 為什么不自動卸載舊包這個想法很危險。包管理器的核心原則是盡量不破壞用戶已有的系統(tǒng)狀態(tài)。當(dāng)前系統(tǒng)里已經(jīng)安裝了 podman也許有用戶正用它在跑容器也許有其他軟件依賴 podman比如 CentOS 8 的 Web 管理面板組件cockpit-podman。如果 DNF 默認(rèn)就自動刪除現(xiàn)有包很容易把用戶的運(yùn)行環(huán)境搞壞。所以 DNF 的策略是發(fā)現(xiàn)沖突后先報錯把沖突的包列出來等用戶明確表態(tài)。你可以手動卸載也可以用--allowerasing參數(shù)讓它“自行其是”。我見過不少人在這一步直接上--allowerasing后面把系統(tǒng)里某些依賴 Podman 的功能也順帶走弄得手忙腳亂。正確的做法是搞清楚誰依賴 podman再決定刪除范圍。2. 動手前倉庫、模塊、備份一個都別漏2.1 先確認(rèn)系統(tǒng)版本和已裝組件在清理之前我強(qiáng)烈建議先摸清系統(tǒng)現(xiàn)狀。第一步看版本cat /etc/os-release確認(rèn)系統(tǒng)確實(shí)是 CentOS 8且知道是 8.幾。不同小版本的模塊流名稱可能不同報錯信息里的el8.2.0說明當(dāng)前環(huán)境大概率是 8.2。不過即便小版本不同處理思路完全一致。接著看當(dāng)前已經(jīng)裝了多少相關(guān)包rpm -qa | grep -E podman|buildah|skopeo|runc|containernetworking這條命令能列出 podman、buildah、skopeo、runc 等一系列容器組件的版本。在我的經(jīng)驗(yàn)里光刪一個 podman 往往不夠因?yàn)?buildah 也會和 docker-ce 沖突報錯里已經(jīng)寫了。所以觀察完整列表避免刪一次再報一次。再看倉庫狀態(tài)dnf repolist如果之前折騰過各種軟件源先看一眼有哪些倉庫是啟用的。尤其是epel、extras、AppStream、BaseOS。Docker 官方源的包名有些會和 EPEL 里的包重名或沖突提前知道有助于判斷后續(xù)依賴問題。2.2 添加 Docker 官方軟件源如果你還沒加 Docker 官方源先補(bǔ)上。還是那套經(jīng)典命令dnf install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoyum-utils提供yum-config-manager工具后面可以用它管理倉庫。添加完成后建議先dnf makecache刷新一下元數(shù)據(jù)避免裝出“找不到包”的靈異問題。添加完倉庫順手檢查一下 repo 文件的開關(guān)狀態(tài)cat /etc/yum.repos.d/docker-ce.repo這個文件里會有docker-ce-stable、docker-ce-test、docker-ce-nightly三個源默認(rèn) stable 是啟用的。除非你在做測試否則不建議啟用 nightly 和 test否則裝上非穩(wěn)定版可能給自己添亂。2.3 清理前務(wù)必備份容器數(shù)據(jù)這一步很多人忽略但真出了事才后悔。比如你當(dāng)前正是在用 podman 跑業(yè)務(wù)容器或者本地編過鏡像一旦刪除 podman 相關(guān)包默認(rèn)存儲目錄/var/lib/containers下的鏡像、容器數(shù)據(jù)可能直接不可用。因此在移除前先把該導(dǎo)出的東西導(dǎo)出podman ps -a podman images podman save -o my-images.tar 鏡像名 podman commit 容器名 backup-image:latest如果 podman 里的容器已經(jīng)不需要了那可以跳過備份。但如果里面有幾個跑了很久的數(shù)據(jù)庫容器我建議老老實(shí)實(shí)把數(shù)據(jù)目錄也備份一份tar czf containers-backup.tar.gz /var/lib/containers ~/.config/containers /etc/containers別嫌這一步多余。之前我在處理類似問題時遇到過一位同事沒備份就直接dnf remove podman結(jié)果里面一個本地測試環(huán)境的數(shù)據(jù)全沒了連回滾的余地都沒有。容器工具替換是小操作數(shù)據(jù)安全是大事情。3. 三招解決 podman 沖突親測可用3.1 方案A手動移除沖突包最直觀既然 DNF 說已安裝包和 docker-ce 沖突那直接刪掉這些沖突包是最符合直覺的做法。執(zhí)行dnf remove -y podman buildah這里只刪除 podman 和 buildah 是否夠?qū)嶋H上報錯里還可能出現(xiàn)skopeo、runc、containernetworking-plugins等包沖突。為了減少來回折騰我習(xí)慣把這一套默認(rèn)容器工具鏈一起移除dnf remove -y podman buildah skopeo runc containernetworking-plugins執(zhí)行過程中 DNF 會提示有哪些包會被連帶刪除比如cockpit-podman。這時候要仔細(xì)看一眼如果系統(tǒng)里有需要保留的組件就要考慮是不是只用 Docker 這個決定值得重新評估。當(dāng)前環(huán)境如果沒有其他軟件依賴 podman移除后就可以直接安裝dnf install -y docker-ce docker-ce-cli containerd.io這個方案的優(yōu)點(diǎn)是邏輯簡單每一步都可以確認(rèn)。缺點(diǎn)是如果只刪了 podman 而忘了 buildah第二次安裝可能還會報類似的problem with installed package buildah...所以建議一次把相關(guān)工具鏈清干凈。3.2 方案B禁用 container-tools 模塊更穩(wěn)妥如果只是想刪一次包那方案A足夠。但如果你希望以后系統(tǒng)更新時不會被莫名拉回 podman 相關(guān)組件或者你想從“模塊層面”把整條容器工具鏈暫時關(guān)掉可以配合禁用模塊一起操作。先看模塊狀態(tài)dnf module list container-tools然后執(zhí)行禁用dnf module disable container-tools這里要注意一個細(xì)節(jié)module disable只是讓 DNF 不再把這個模塊作為可用軟件包來源并不會自動刪除已經(jīng)安裝的 podman 和 buildah。所以真正安裝前仍然要手動卸載已裝的包dnf remove -y podman buildah skopeo runc containernetworking-plugins為什么建議把禁用模塊和刪除包一起做因?yàn)槿绻阒粍h包但不禁用模塊下一次執(zhí)行dnf update時只要 AppStream 模塊仍處于啟用狀態(tài)系統(tǒng)有可能因?yàn)槟承┙M件的依賴把 podman 重新拉回來于是沖突再次出現(xiàn)。先禁用模塊等于提前把“再安裝”的路徑堵死。在正式的服務(wù)器環(huán)境中我更推薦這個組合拳。它多執(zhí)行一條命令但后續(xù)維護(hù)更省心不容易莫名其妙復(fù)發(fā)。3.3 方案C用 --allowerasing 臨時繞過但不建議如果只是臨時測試機(jī)、或者你不在乎系統(tǒng)里原本裝了啥可以用一條命令強(qiáng)行讓 DNF 決定刪除沖突包dnf install -y docker-ce docker-ce-cli containerd.io --allowerasing--allowerasing的含義是“允許刪除已安裝包以解決依賴問題”。DNF 會自行分析哪些現(xiàn)有包需要被移除然后繼續(xù)安裝。好處是快一條命令結(jié)束壞處是 DNF 可能刪掉一些你并沒有想到會被牽連的包。舉個例子如果系統(tǒng)里裝了cockpit-podman而這個包又依賴 podman那么--allowerasing可能直接把 cockpit 的容器管理模塊一起刪掉。你在 Web 管理界面里可能會發(fā)現(xiàn)相關(guān)功能不見了排查半天才發(fā)現(xiàn)是安裝 Docker 時被連帶清理的。所以在生產(chǎn)環(huán)境或多人共用環(huán)境里我不太建議直接--allowerasing。它適合個人開發(fā)機(jī)、無狀態(tài)臨時環(huán)境但不適合作為標(biāo)準(zhǔn)操作流程寫進(jìn)文檔。3.4 三種方案怎么選一張表說清楚方案核心操作優(yōu)點(diǎn)風(fēng)險推薦場景A 手動移除dnf remove podman buildah ...每一步可控邏輯清晰需要自己識別所有沖突包普通服務(wù)器、個人開發(fā)機(jī)B 禁用模塊移除dnf module disable container-tools再移除防止后續(xù)更新時 podman 回來最穩(wěn)定多一條模塊命令生產(chǎn)環(huán)境、長期維護(hù)系統(tǒng)C--allowerasingdnf install ... --allowerasing一條命令搞定速度快可能誤刪依賴組件臨時環(huán)境、測試機(jī)我的建議是只要你愿意花一分鐘多敲一條命令就用方案B。它帶來的“防復(fù)發(fā)”效果是另外兩個方案替代不了的。4. 裝完只是開始驗(yàn)證、啟動、命令切換4.1 從清理到安裝的一條龍命令把上面方案整理成一套可以在新環(huán)境上直接復(fù)制的流程推薦組合是方案B加 Docker 官方源安裝# 1. 安裝 yum-utils 工具 dnf install -y yum-utils # 2. 添加 Docker 官方倉庫 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 3. 刷新倉庫緩存 dnf makecache # 4. 禁用默認(rèn)容器模塊避免后續(xù) podman 被拉回 dnf module disable -y container-tools # 5. 刪除默認(rèn)容器工具鏈 dnf remove -y podman buildah skopeo runc containernetworking-plugins # 6. 安裝 docker-ce dnf install -y docker-ce docker-ce-cli containerd.io執(zhí)行第 5 步時如果提示會刪除一些額外包請逐條看一眼確認(rèn)沒有關(guān)鍵服務(wù)依賴。第 6 步安裝完成后DNF 會自動處理好 docker-ce 本身的依賴正常情況下不會再出現(xiàn)開頭的 podman 沖突報錯。整體跑下來的體驗(yàn)是最關(guān)鍵的操作其實(shí)不是安裝 docker-ce而是前面把 podman 那條線收拾干凈。只要模塊禁用和包刪除都做對了后面基本一路綠燈。4.2 啟動 Docker 并完成基礎(chǔ)驗(yàn)證安裝完成不代表 Docker 已經(jīng)能用。CentOS 8 里 Docker 服務(wù)默認(rèn)不會自動啟動需要手動啟用并設(shè)置開機(jī)自啟systemctl enable --now docker查看服務(wù)狀態(tài)systemctl status docker --no-pager如果狀態(tài)是active (running)說明服務(wù)正常。接下來最經(jīng)典的驗(yàn)證方式docker version正常會分別顯示 Client 和 Server 的版本信息。注意如果只看到 Client 部分而 Server 部分報錯說明服務(wù)端可能沒起來繼續(xù)查systemctl status docker或查看日志journalctl -u docker --no-pager -n 50再進(jìn)一步驗(yàn)證能真的拉取并運(yùn)行鏡像可以跑docker run --rm hello-world如果看到類似 “Hello from Docker!” 的輸出說明整個容器鏈路已經(jīng)打通。這一步建議一定跑一次它能一次性驗(yàn)證 Docker 守護(hù)進(jìn)程、鏡像倉庫連通性、容器運(yùn)行時是否正常。4.3 一個容易忽略的坑docker 命令被 podman 兼容層占用在 CentOS 8 里有個特殊現(xiàn)象系統(tǒng)有時候可以通過podman-docker這個包提供/usr/bin/docker的兼容入口目的是讓習(xí)慣了 Docker 命令的人無縫切換到 podman。如果你在安裝 docker-ce 前系統(tǒng)里本來就有這個兼容層那么即便 docker-ce 安裝成功docker命令的指向也可能仍然落在 podman 的兼容腳本上。安裝完成后可以檢查一下which docker ls -l /usr/bin/docker如果/usr/bin/docker是一個指向/etc/alternatives/docker的符號鏈接說明系統(tǒng)使用了 alternatives 機(jī)制來管理 docker 命令指向。這時可以執(zhí)行update-alternatives --config docker在列出的候選項(xiàng)里選擇對應(yīng) docker-ce 的那個。例如可能是/usr/bin/docker-ce或類似路徑。選完后docker version才能真正顯示出 Docker 的版本信息而不是 podman 的模擬輸出版本。如果之前沒用過 podman-docker也沒有 alternatives 符號鏈接那么安裝 docker-ce 后/usr/bin/docker就來自 docker-ce 包本身無需處理。但做一次which docker的確認(rèn)不虧幾秒鐘的事。5. 后續(xù)常遇到的問題快速排查清單5.1 安裝階段containerd.io 與 runc 的糾纏除了 podman 沖突CentOS 8 上安裝 docker-ce 時還有一類高頻報錯看起來和containerd.io、runc相關(guān)。典型的錯誤信息是Error: Problem: package containerd.io-1.x.x-x.el8.x86_64 requires runc 1.x, but none of the providers can be installed這類問題的本質(zhì)是系統(tǒng)自帶的runc版本和 containerd.io 要求的版本對不上。處理思路是先把系統(tǒng)里已有的 runc 升級或移除dnf update runc如果升級后仍然無法滿足版本要求那就手動刪除帶版本沖突的 runc再重新安裝 containerd.io。當(dāng)然更簡單的兜底手段是在安裝 containerd.io 時加上--allowerasing。但和前面說的一樣這個參數(shù)有副作用使用前先確認(rèn)系統(tǒng)里沒有其他服務(wù)依賴舊版 runc。在我實(shí)際處理中CentOS 8 的標(biāo)準(zhǔn)倉庫里runc版本通常能滿足 Docker 官方 containerd.io 的需求遇到這個報錯更多是因?yàn)橹盎煊昧似渌谌皆磳?dǎo)致包版本變得混亂。因此第一步永遠(yuǎn)是檢查倉庫狀態(tài)看看有沒有奇怪的源在起作用。5.2 倉庫緩存與源配置導(dǎo)致的奇怪問題有段時間我在一臺 CentOS 8 機(jī)器上執(zhí)行安裝報錯信息和 podman 完全不相關(guān)只是提示某些依賴找不到。排查到最后發(fā)現(xiàn)是倉庫元數(shù)據(jù)太老系統(tǒng)里實(shí)際上已經(jīng)有新版本包但 DNF 的緩存里沒有索引。遇到這種“迷之無法滿足依賴”的情況執(zhí)行dnf clean all dnf makecache然后重新安裝。大多數(shù)情況下刷新倉庫緩存就能解決。另外如果 Docker 官方倉庫所在網(wǎng)絡(luò)不穩(wěn)定DNF 可能因?yàn)橄螺d失敗而報各種奇怪的錯。這時可以配置該倉庫在不可用時跳過yum-config-manager --save --setoptdocker-ce-stable.skip_if_unavailabletrue這個配置的意思是倉庫暫時不可用時不阻塞其他安裝事務(wù)而不是永遠(yuǎn)忽略。用起來比較靈活。這里特別提醒一句不要輕易用--skip-broken跳過依賴錯誤。它會把真正的問題掩蓋住導(dǎo)致系統(tǒng)里留下殘缺依賴后面再更新其他軟件時可能爆炸。寧可多花幾分鐘把沖突根源找出來也不要長期帶著隱患跑。5.3 容器啟動后的網(wǎng)絡(luò)與 SELinux 問題docker-ce 安裝并啟動成功很多人會在docker run階段遇到新問題。最常見的是端口映射不通、容器訪問外網(wǎng)受阻。CentOS 8 默認(rèn)開啟了 firewalld。如果容器端口要從外部訪問需要顯式放行firewall-cmd --add-port8080/tcp --permanent firewall-cmd --reload如果不想使用防火墻直接停掉 firewalld 也能快速驗(yàn)證但服務(wù)器正式環(huán)境不建議裸奔。另一個高頻坑是 SELinux。CentOS 8 默認(rèn) SELinux 為 enforcing 狀態(tài)Docker 掛載宿主機(jī)目錄時可能因?yàn)榘踩呗员痪芙^表現(xiàn)為容器啟動后目錄為空或直接報權(quán)限錯誤??焖衮?yàn)證的方法setenforce 0臨時切到 permissive 模式如果容器立刻正常說明就是 SELinux 策略問題。但長期方案不是關(guān)閉 SELinux而是給掛載目錄加上正確的標(biāo)簽或者在 Docker daemon 配置里調(diào)整 SELinux 支持方式。只要記住“一句話驗(yàn)證認(rèn)真配置收尾”的原則就能少花很多時間。5.4 問題速查表癥狀可能原因快速處理安裝提示problem with installed package podman...系統(tǒng)預(yù)裝容器工具與 docker-ce 沖突刪除 podman/buildah禁用 container-tools 模塊提示containerd.io requires runc ...runc 版本過低或來源混亂dnf update runc必要時移除后重裝安裝時找不到某些依賴包倉庫元數(shù)據(jù)過舊或源不可用dnf clean all dnf makecache配 skip_if_unavailabledocker命令顯示 podman 版本信息alternatives 鏈接仍指向 podmanupdate-alternatives --config docker切換服務(wù)啟動但docker docker run無法連接Docker 服務(wù)未運(yùn)行或日志報錯檢查systemctl status docker與journalctl容器端口外部無法訪問firewalld 未放行firewall-cmd --add-port.../tcp --permanent掛載目錄權(quán)限異常SELinux 策略阻止臨時setenforce 0驗(yàn)證再配置標(biāo)簽這張表基本覆蓋了從安裝到運(yùn)行的常見問題。把這幾類問題提前了解一遍再遇到報錯就不會手忙腳亂。6. 幾次折騰之后說點(diǎn)個人體會從第一次看到problem with installed package podman開始到最后完全搞清楚整套邏輯我最大的體會是這種報錯與其說是“安裝失敗”不如說是系統(tǒng)在強(qiáng)制你重新思考容器運(yùn)行時選型。CentOS 8 的默認(rèn)方向本來就是 podmanDocker 是一個“逆向走回去”的選擇。如果你只是圖 docker-compose 生態(tài)順手那清理并替換沒有問題如果生產(chǎn)環(huán)境已經(jīng)有大量依賴 podman 的自動化腳本和系統(tǒng)組件反而應(yīng)該停下來確認(rèn)一下是否真要換。最后分享一個小技巧不管用哪套方案處理完安裝成功后先做一次系統(tǒng)級別的快照或備份。以我這幾年操作經(jīng)驗(yàn)來看容器運(yùn)行時替換這件事安裝階段的報錯只是第一關(guān)真正讓人頭疼的往往是安裝成功之后出現(xiàn)的網(wǎng)絡(luò)、SELinux、倉庫混源問題。但只要你理解沖突背后的原理后續(xù)遇到類似問題基本上都能舉一反三。