1. 报错现场:又卡在 container-selinux 上了
看到这个报错,我一点都不意外。凡是这几年在 CentOS 7 上手动装过 Docker CE 的运维,大概率都被这条依赖卡过至少一次。当时的情况一般是这样的:你按网上教程配好了 docker-ce 的 yum 源,然后执行yum install -y docker-ce,几秒钟后弹出一段大红字:
Error: Package: docker-ce-24.0.7-1.el7.x86_64 (docker-ce-stable) Requires: container-selinux >= 2.107-3, but none of the providers can be installed第一次遇到的人多半会愣住,因为报错里提到“none of the providers can be installed”,意思是 yum 把当前所有启用的软件源都翻遍了,也找不到一个符合条件的 container-selinux 包。更让人懵的是,你之前根本没装过 container-selinux,也不知道它是什么,docker 安装程序却非要它不可。
这个报错的关键点不在 docker 本身,而在于 SELinux 策略包没有到位。CentOS 7 默认是打开 SELinux 的,从内核层面限制容器和进程的权限,而 Docker 的 RPM 包为了确保容器运行时能和 SELinux 和谐相处,就强制要求container-selinux >= 2.107-3这个策略模块。yum 解决依赖时发现系统里没有,源里也没有能装上的,于是整个安装流程直接终止。
这篇博文我会从依赖原理讲起,把三个能真正解决问题的路径逐个走一遍,包括:配好 epel 源规规矩矩装、源失效时手动拿 rpm 包装、以及服务器安全管控严格时直接用二进制包绕过依赖。最后附带我在真实环境里踩过的坑和排查命令清单,照着做基本不会再被这个问题卡住。
2. 为什么 docker 非要 container-selinux 不可
2.1 container-selinux 到底是干嘛的
contaienr-selinux 这个包不参与 docker 的日常功能,它提供的是一组 SELinux 策略模块,专门给容器运行时用的。SELinux 在 RHEL/CentOS 系统上是强制访问控制层,进程能不能访问某个文件、能不能绑定某个端口,都由策略说了算。没有对应策略时,docker 创建的容器进程可能会被 SELinux 直接拦截,导致容器起不来,甚至起来了之后网络异常、挂载目录访问不了。
我见过一个比较典型的现场:有人在完全没有 container-selinux 的环境里用rpm -ivh --nodeps强装了 docker,docker 命令能跑,容器也 start 了,但进入容器以后发现文件系统只读,或者挂载宿主机目录进去没权限。排查下来根本不是 docker 的问题,是 SELinux 策略把容器进程堵死了。所以 docker 的 RPM 包才会在安装阶段把 container-selinux 列为硬性依赖,宁可让你装不上,也不让你装完以后跑得一脸问题。
2.2 版本要求为什么写得这么严格
container-selinux >= 2.107-3不是随便写的,2.107 版本之后才加入了对 docker 容器运行时比较关键的几个策略规则,包括容器内网络端口绑定、挂载卷的权限控制等。如果你系统里已有一个旧版本,比如 2.77,yum 会直接判定不满足要求。即使已经安装了 container-selinux,版本低于 2.107 时同样会报同样的错,这个坑很多人会忽略,以为装过就不用管了,其实要确认的是版本号。
2.3 报了错,为什么 yum 说“找不到可安装的 providers”
这是第二个关键点。yum 在解决依赖时,只能从已经启用的仓库里找包。“none of the providers can be installed”通常有三种情况:
- 根本没有任何仓库提供 container-selinux,最常见,就是没装 epel-release。
- CentOS 7 的基础仓库里也有一个 container-selinux,但版本太老,不满足
>= 2.107-3,yum 筛选之后发现没有可用候选。 - 配置的仓库里虽然有满足条件的新版本,但仓库因为网络、镜像失效等原因被 yum 忽略,或者 GPG 检查失败导致包被排斥。
大多数服务器的默认源都是 CentOS-Base.repo,里面的 extras 仓库历史上确实提供过 container-selinux,但很长一段时间停留在 2.42 左右,远不够 docker 要求的新版本。而 container-selinux 新版本主要发布到 EPEL 仓库里,所以不安装 epel-release,yum 当然找不到。
搞清楚这个原理,你再看网上各种“复制两行代码就解决”的教程,就知道为什么有的在你这台机器上不管用了。很多教程只写了结论,没有解释前提,比如“先装 epel-release 再装 docker”,但如果你的 epel 源因为镜像失效根本没连上,那照着敲自然还是报错。接下来我把三条路径完整走一遍。
3. 最省事的方案:装好 epel 源再装 docker
3.1 先确认当前仓库和依赖状态
动手之前,先花一分钟确认环境状态。用三个命令把当前情况摸清楚:
rpm -qa | grep container-selinux yum repolist yum list available | grep container-selinux第一个命令看系统里有没有装过 container-selinux,以及版本是多少;第二个命令看当前启用的仓库有哪些;第三个命令最关键,看 yum 在现有仓库里能不能找到 container-selinux。实测下来,如果你执行第三条命令输出为空,基本就是缺 epel 源,或者 epel 源没有生效。
3.2 安装 epel-release 并重试
对于 CentOS 7,最标准的做法是安装 epel-release:
yum install -y epel-release yum makecache fast yum install -y container-selinux yum install -y docker-ce安装 epel-release 之后,centos 系统会把 EPEL 仓库加入 yum 源列表,里面就包含满足 docker 版本要求的 container-selinux。先单独安装 container-selinux 是为了让依赖先落位,再装 docker-ce 时就不会再卡在同一个位置。
如果你用的是阿里云、腾讯云等国内云主机,建议装完 epel-release 以后顺手看一下 epel 源里的 baseurl,有时默认指向的是官方源,在国内网络环境下下载速度慢,甚至超时。可以改成以下这样:
sed -e 's|^metalink=|#metalink=|g' \ -e 's|^#baseurl=https://download.fedoraproject.org/pub/epel|baseurl=https://mirrors.aliyun.com/epel|g' \ -i.bak /etc/yum.repos.d/epel.repo改完再yum makecache一次,速度和稳定性就好很多。
3.3 确认版本:不要装了个旧版就算完事
安装 container-selinux 之后,不要急着直接装 docker,先确认版本:
rpm -q container-selinux如果输出是container-selinux-2.107-3.el7或者更高的版本号,那么依赖已经满足,可以直接进入 docker-ce 安装。如果输出版本还是老的,比如 2.42,说明 yum 没有从新源里取到包,这时候你要检查 epel 源是否生效,或者用后面讲到的手动 rpm 方式升级。
另外提醒一句,有些老教程会让你直接yum install -y docker,这个命令在 CentOS 7 上装的是老版本的 docker,来自 base 源,版本停留在 1.13 左右,功能上和现在的 docker-ce 差得远。除非你有特殊兼容需求,否则强烈建议走 docker-ce 官方源安装,方法下面会讲。
3.4 配好 docker 官方源,重装 docker-ce
依赖问题解决后,docker-ce 本身还需要单独配置仓库。这里我建议直接使用阿里云镜像,比官方源在国内稳定得多:
yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sed -i 's|download.docker.com|mirrors.aliyun.com/docker-ce|g' /etc/yum.repos.d/docker-ce.repo yum makecache yum install -y docker-ce安装完毕以后:
systemctl enable docker systemctl start docker docker versiondocker version能正常显示 client 和 server 版本,就说明 docker 已经跑起来了,container-selinux 的问题也顺手解决。
4. 仓库源失效时的后备方案:手动装 RPM 包
4.1 什么时候适合走 RPM 手动安装
epel 源方案虽然最省事,但不是在所有环境里都适用。我遇到过几种情况:
- 服务器在离线内网,yum 源根本连不出去。
- epel 源被公司安全策略限制,不能安装第三方仓库。
- 公司内部源同步不及时,epel 仓库里还没有最新版本。
- 系统是 CentOS 的裁剪版,或者用惯了最小化安装,yum 源本身就缺失。
这些场景下,手动下载 container-selinux 的 RPM 包安装,是绕开仓库问题的最直接办法。
4.2 从哪下载 container-selinux RPM 包
CentOS 7 的 container-selinux 新版本主要发布在 EPEL 仓库。你可以直接用浏览器或者 wget 从 EPEL 的镜像站拉取。以阿里云镜像为例,包的路径一般是:
https://mirrors.aliyun.com/epel/7/x86_64/Packages/c/container-selinux-2.119.2-1.c57_4.7.el7.noarch.rpm注意这个路径里的版本号会随时间变化,如果你在镜像站目录里看到的版本和我写的不一样,以目录列表里的最新版本为准。下载方式:
wget https://mirrors.aliyun.com/epel/7/x86_64/Packages/c/container-selinux-2.119.2-1.c57_4.7.el7.noarch.rpm下载完了以后用 yum 本地安装,让 yum 帮你检查其他依赖:
yum localinstall -y container-selinux-2.119.2-1.c57_4.7.el7.noarch.rpm注意这里我用的是yum localinstall而不是rpm -ivh。localinstall 会解析这个 RPM 包的其他依赖并自动处理,而rpm -ivh只会做版本检测,不会解决依赖。如果这台机器是全新环境,可能还缺 policycoreutils-python-utils 之类的包,用 yum localinstall 能省很多事。
4.3 也要处理 docker-ce 本身的依赖
container-selinux 解决了,docker-ce 如果也要在离线环境里装,同样需要手动处理依赖。比较省心的做法是先把 docker-ce 的 RPM 包全部下载到本地,再统一 localinstall。在能联网的机器上执行:
yum install -y yum-utils yumdownloader --resolve docker-ce docker-ce-cli containerd.ioyumdownloader --resolve会把目标包及其所有依赖包全部下载到当前目录,产生的是一堆 rpm 文件。把这堆文件拷贝到离线服务器上,然后:
yum localinstall -y *.rpmyum 会把目录下所有 rpm 包统一处理依赖并安装。这个方法比在离线环境里一个个找依赖包要靠谱得多。把 container-selinux 的 rpm 也一起放进去,一次性解决问题。
4.4 另一个可行的 PHP 思路:直接从 CentOS Extras 获取旧包再升级
如果你的内核或者系统组件版本比较特殊,用 EPEL 的新包反而有可能因为 glibc 版本不满足而报错。这种情况你也可以先尝试从 CentOS 7 自带的 extras 仓库拉出那个老版本 container-selinux 装上,把环境“哄”过去,再从 EPEL 源下载新版 rpm 强制升级。不过这一步建议只在测试环境里试,生产环境还是尽量按照依赖关系来。
5. 终极兜底方案:用二进制包跳过依赖
5.1 二进制包安装的原理和适用场景
如果你既没有 yum 源,也不方便下载 rpm 包,还有一条路:直接用 Docker 官方发布的二进制压缩包安装。docker 的二进制包不依赖 container-selinux,也不依赖 systemd 服务脚本,它就是一个压缩包里装着 dockerd、containerd、docker CLI 等可执行文件。这种安装方式的缺点是你会失去systemctl start docker这种服务管理便利,需要自己写启动脚本,或者直接用 nohup 方式启动。好处是不受任何 yum 依赖关系限制,离线环境特别适用。
5.2 下载和解压
从 docker 官方 release 页面下载二进制包,地址格式是:
https://download.docker.com/linux/static/stable/x86_64/docker-24.0.7.tgz服务器上执行:
wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.7.tgz tar -xzf docker-24.0.7.tgz cp docker/* /usr/bin/复制完成后验证一下:
dockerd --version docker --version能打出版本号,说明二进制文件没问题。
5.3 写一个能用的启动脚本
因为二进制方式没有生成 systemd 单元文件,这里我给出一个我在生产环境用过的启动脚本思路,保存到/etc/systemd/system/docker.service:
[Unit] Description=Docker Application Container Engine After=network-online.target firewalld.service containerd.service Wants=network-online.target [Service] Type=notify ExecStart=/usr/bin/dockerd ExecReload=/bin/kill -s HUP $MAINPID LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity Delegate=yes KillMode=process Restart=on-failure [Install] WantedBy=multi-user.target然后:
systemctl daemon-reload systemctl enable docker systemctl start docker这里面的关键点在于Type=notify,dockerd 启动时会通过 sd_notify 通知 systemd 自己已经就绪,这样 systemd 不会在服务还没完全跑起来的时候就把状态标记为 active。如果你习惯用 sysvinit 或者 docker 20.10 之前的版本,Type 可以改成simple,不过 notify 方式更稳。
5.4 这种方案的代价:selinux 问题会晚一步暴露
二进制安装绕过了 RPM 依赖,不代表 SELinux 策略问题不存在了。如果你系统 SELinux 处于 enforcing 模式,container-selinux 依旧没有装,那容器运行阶段极有可能遇到权限受限的毛病。我的建议是,二进制安装可以当作临时救场的手段,但正式使用前最好还是想办法把 container-selinux 装上,把系统拉回“标准状态”。
6. 高频问题速查表与踩坑记录
6.1 快速排查套清单
以下命令是我每次遇到这个报错都会依次执行一遍的,按顺序来基本能定位 80% 的问题:
# 1. 确认系统版本 cat /etc/redhat-release # 2. 确认已安装的 container-selinux 版本 rpm -qa | grep container-selinux # 3. 确认当前启用的 yum 仓库 yum repolist # 4. 在仓库中搜索 container-selinux yum list available | grep container-selinux # 5. 如果是 docker-ce 报依赖,查看其完整依赖 yum deplist docker-ce | grep container-selinux第 4 条命令的输出是重点:如果输出为空,说明仓库里没有候选包;如果有输出但版本号低于 2.107,说明源里有旧包但不满足;如果版本号没问题,那 docker-ce 报错可能是 yum 缓存问题,用yum clean all && yum makecache清一遍再试。
6.2 常见问题对照表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
requires container-selinux >= 2.107-3 | 未安装 epel-release | 安装 epel-release 后重试 |
| 已装 epel 仍报错 | epel 源 mirror 失效 | 检查 epel.repo,换阿里云镜像 |
container-selinux版本老 | 默认 base 源里的旧包 | 从 EPEL 手动下载新版本 |
yum install卡在 GPG 检查 | 仓库 key 缺失或过期 | 使用rpm --import导入官方 key |
| docker-ce 官网源无法访问 | 国内网络问题 | 改用阿里云 docker-ce 仓库 |
| 二进制方式启动后容器网络异常 | SELinux enforcing 但缺策略 | 安装 container-selinux 或临时调整策略 |
安装 docker-ce 时提示containerd.io缺失 | docker-ce 仓库中包不完整 | 从更高版本源重新下载或安装 containerd.io |
6.3 一个非常容易忽略的点:CentOS 7 的 extras 仓库
CentOS 7 系统自带的 CentOS-Base.repo 里,extras 仓库出现过 container-selinux 的早期版本。但如果你执行的命令是yum install container-selinux,运气好会装上 2.42 之类的老版本,再装 docker-ce 时还是会报版本不满足。所以我强调要先确认版本,别看到“已安装”就觉得完事。
另外注意,CentOS 7 官方源在项目进入维护状态后,部分 mirrorlist 会指向已经停止同步的地址,导致yum makecache直接报错。这种情况通常要手动把 base 源指向 vault.centos.org,具体操作网上资料很多,这里不展开,但你要有个意识:yum 源本身失效也是一个隐蔽的报错根源。
6.4 关于“清华源/阿里源替换后仍然报错”的情况
很多教程会让你直接替换整套 yum 源成清华或阿里的,我也这么干过。但替换仓库以后并不代表容器相关软件包目录齐全,epel 仓库和 extras 仓库的路径是不同的。比如阿里云的 epel 库地址是mirrors.aliyun.com/epel/7/x86_64/Packages/c/,而阿里云的 centos-vault 里也可能有 container-selinux,但版本不一定新。建议你替换源以后,用上面第 4 条命令再确认一次版本号,不要替换完就盲目相信“能用”。
6.5 一个容易翻车的操作:用--nodeps强装
网上有些答案会教你:
rpm -ivh docker-ce.rpm --nodeps --force这条命令能强制忽略依赖,让 docker 装上,但装完以后可能留下一个残缺环境。比如我之前碰到一台机器,强装完 docker,docker pull一切正常,但容器启动后访问宿主机文件系统时权限乱掉,连docker run -v挂载目录都提示 permission denied。最后查下来就是 SELinux 策略缺失加 docker 服务环境不完整,折腾半天不如一开始老老实实装依赖。所以我的态度很明确:--nodeps是最后一根稻草,不是首选方案。
7. 实测体验与建议
这套处理流程我在几台全新的 CentOS 7.9 最小化安装环境里完整验证过。从裸机状态开始,经历过的实际工时大概是这样的:走 epel-release 方案,加上配阿里云 docker 源、下载 docker-ce 并启动,总共 5 到 8 分钟;走离线 rpm 方案,需要先在有网环境下载依赖包再拷过去,大概 15 分钟左右;走二进制包方案,加上写 systemd 启动脚本,大约 10 分钟能跑起来,但后续还要回头处理 selinux 策略。
我个人的经验是,优先把 epel-release 这个基础步骤做好,它能解决的不只是 container-selinux,后面你再装 redis、nginx 或者任何编译工具时,epel 源都是非常重要的来源。如果你负责的是内网离线集群,建议在一台能联网的机器上把 docker-ce、container-selinux 及相关依赖按时做一个私有仓库,这样后续每台新节点都只需要加一个 repo 文件然后yum install docker-ce而已,不用每次都在依赖地狱里翻来覆去。
另外想分享一个小细节,container-selinux报错其实只出现了一次,只要你把它解决掉并顺利装上了 docker,后续不太会再因为这个包出问题。真正需要持续关注的是 SELinux 和 docker 的共存策略,比如容器要绑定比较特殊的端口或者访问某些受限路径时,可能会触发新的策略拦截。所以即使 docker 跑起来了,journalctl -u docker和ausearch -m avc这两条命令的用法也建议提前掌握,遇到诡异权限问题可以从日志里直接看到被 SELinux 拒绝的痕迹。
最后再提醒一句,如果你是在 Docker Desktop 相关教程里看到这个报错,那说明你跟教程环境不一致。Docker Desktop 是给 Windows/macOS 桌面环境用的,服务器上跑的是 docker-ce,两者安装路径完全不同。别对着桌面的教程去折腾 CentOS 服务器,浪费时间也容易把自己绕晕。