2026/10/15 20:32:41

RHEL 8在线安装Docker并运行Nginx容器完整指南

RHEL 8在线安装Docker并运行Nginx容器完整指南 在RHEL 8上在线安装Docker并跑一个nginx容器这事听起来不就是几条命令的事吗真上手之后你会发现比在CentOS或者Ubuntu上要“弯弯绕”不少。RHEL 8的官方软件仓库里默认不给你docker-ce系统自带的是podman那一整套容器工具链Docker官方那边又因为CentOS 8的仓库生命周期问题不再同步更新对应的rpm源。所以装Docker本身不难难的是理解“为什么这么装”以及报错时知道去哪里找原因。这篇文章我打算从零开始把仓库配置、版本选择、存储驱动、防火墙、SELinux这些点全部串一遍最后让nginx容器真正跑起来。适合刚接触RHEL的运维或开发朋友也适合被各种报错搞得头晕的老手查漏补缺。1. 为什么RHEL 8装Docker要“绕路”1.1 RHEL 8选了一条跟Docker不同的路RHEL 8发布的时候红帽在容器体系上做了一个重要转向。系统默认提供的是podman、buildah、skopeo这套工具AppStream仓库里虽然有container-tools模块但Dockerdocker-ce并不在官方支持的软件包列表里。也就是说你直接在RHEL 8上执行dnf install docker-ce系统会提示没有这个包。这不是你的操作有问题而是发行版的容器策略本身就变了。红帽为什么这么干最核心的原因是podman不需要一个常驻的守护进程。传统Docker架构中dockerd一直占着系统资源容器启动、停止、网络配置、日志收集全部依赖这个后台进程。而podman采用无守护进程模型管理容器的每个命令都直接通过fork/exec调用运行时来干活交互方式更接近普通进程管理。对服务器这种强调最小化安装和攻击面收敛的场景来说少一个进程就意味着少一份风险这是RHEL 8敢把“去Docker化”做到底的底气。1.2 Docker和Podman的相同与不同从命令行使用习惯上看Docker和Podman的相似度非常高。docker run、docker ps、docker pull、docker tag这些操作podman都有对应的命令甚至很多参数也保持一致。如果你在Linux服务器上已经有一套成熟的Docker操作习惯平移到podman上基本不需要重新学命令。但底层架构差异是绕不开的。Docker是典型的client-server架构docker命令行工具只是客户端真正执行容器生命周期管理的是dockerd守护进程容器之间要共享网络、存储、镜像管理都得通过这个daemon中转。podman没有这样一个中央进程每个命令直接利用crun或runc启动容器天然支持rootless运行模式普通用户不需要加入docker组也能管理自己的容器。还有一点值得提的是编排工具。Docker生态里docker-compose几乎是事实标准很多团队的一键部署脚本就是一堆docker-compose.yml。podman这边虽然也有兼容方案但在生产环境的成熟度上不如Docker系顺手。你在RHEL 8上如果只是单机跑几个容器podman完全够用但如果你要承接一个已经有大量docker-compose编排文件的项目Docker反而能让迁移成本降到最低。1.3 什么场景下我建议还是装Docker我从来不建议在RHEL 8上“为了用Docker而用Docker”。如果需求是本地开发调试、跑几个一次性容器podman反而更轻。真正让我决定装Docker的场景通常满足下面几个条件里的至少一个团队现有的CI/CD脚本全部是docker build、docker push一整套流水线替换成podman命令需要改的轮子太多。项目里有一堆docker-compose.yml要原样运行docker-compose对网络、卷、健康检查的处理更符合团队已有预期。镜像构建工艺里依赖Docker的BuildKit缓存机制临时换podman容易产生行为差异。运维同学的知识体系都建在Docker上换工具意味着排障经验清零。这篇文章后面的所有操作都是围绕“在RHEL 8上把真正的Docker装起来”这个目标展开的而不是用podman去模拟docker命令。搞清楚这一点再往下看思路会顺很多。2. 在线安装前的环境准备和仓库配置2.1 确认系统版本与网络连通性动手之前先看一下系统版本不同RHEL 8的小版本在依赖处理上会有细节差别cat /etc/redhat-release只要是大版本8.x后面的步骤基本通用。然后确认机器能正常访问外部网络因为题目说的是在线安装仓库里的rpm包和依赖都需要实时下载curl -I --connect-timeout 5 https://download.docker.com如果这个地址能正常返回HTTP状态码说明到Docker官方仓库的网络链路是通的。如果返回超时或者连接被拒后面要么调整DNS、要么换镜像源这一步提前发现能省很多时间。RHEL 8的软件源机制和之前的RHEL 7不太一样默认有AppStream和BaseOS两个核心仓库。我们要安装的docker-ce不在里面所以需要额外添加仓库。这里顺便提醒一个前提如果你的系统已经注册了订阅先确认dnf repolist能看到正常的AppStream和BaseOS仓库因为docker安装过程中有些依赖比如container-selinux需要从AppStream拿。2.2 添加Docker官方仓库Docker官方没有单独给RHEL维护仓库但给CentOS准备了对应的rpm源。RHEL 8和CentOS 8在包格式、glibc版本、systemd接口上高度一致所以最常用的做法就是把CentOS的docker-ce仓库地址加到RHEL 8里。使用dnf自带插件来添加是最省事的方式dnf install -y dnf-utils dnf config-manager --add-repohttps://download.docker.com/linux/centos/docker-ce.repo如果你的环境里找不到dnf config-manager命令说明缺少dnf插件包安装dnf-utils就能解决。添加成功后可以查看仓库状态dnf repolist如果你想把仓库配置手动写得更可控也可以直接创建/etc/yum.repos.d/docker-ce.repo文件内容大致如下[docker-ce-stable] nameDocker CE Stable - $basearch baseurlhttps://download.docker.com/linux/centos/$releasever/$basearch/stable enabled1 gpgcheck1 gpgkeyhttps://download.docker.com/linux/centos/gpg这里面的$releasever变量在RHEL 8上会被替换成8仓库路径最终指向/linux/centos/8/x86_64/stable。这个细节很关键后面排查404问题的时候会用到。2.3 一个高频坑官方CentOS 8仓库已经停止同步很多人在这一步就会卡住dnf repolist能够看到docker-ce-stable但执行安装时却报“failed to download metadata”或者404。原因正如前面所说Docker官方已经停止同步CentOS 8的仓库目录仓库路径下面可能已经拿不到最新元数据。这不是RHEL的问题而是上游仓库策略调整导致的。我实测下来最顺的处理方案是换镜像源。把repo文件里的baseurl替换成你所在网络可以稳定访问的docker-ce镜像地址比如你公司内部源或者一些云厂商提供的公共软件镜像服务sed -i s#baseurlhttps://download.docker.com#baseurlhttps://你的镜像地址/docker-ce#g /etc/yum.repos.d/docker-ce.repo替换之后先清理缓存再重建dnf clean all dnf makecache还有一个小细节如果镜像源地址不支持$releasever的特斯拉同款自动识别可以直接把repo文件里的$releasever改成8这样路径就固定成了/linux/centos/8/x86_64/stable。手动改的时候注意gpgkey也要跟着换到镜像源对应的地址不然gpg校验会失败。3. 安装Docker引擎并完成基础配置3.1 安装docker-ce、CLI和containerd仓库就绪后安装过程本身并不复杂dnf install -y docker-ce docker-ce-cli containerd.io如果不想每次安装都拿最新版可以指定一个已知稳定的版本号。以20.10.24为例dnf install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io为什么我建议在RHEL 8上优先用20.10系列因为Docker官方维护CentOS 8仓库的最后稳定期基本就在这个系列。新版本虽然在功能上更好但el8的包依赖在后期处理上不够平滑为了一个稳定可控的生产环境锁定老版本反而更省心。如果你想要更新的版本可以先执行dnf list docker-ce --showduplicates看看当前仓库里有哪些版本可选再决定装哪个。安装过程中有可能会提示需要container-selinux这个依赖。这个包在RHEL 8的AppStream仓库里是存在的正常情况下dnf会自动装上。如果自动解决依赖失败手动补一下dnf install -y container-selinux这个包的作用是让容器和SELinux之间有一个正确的策略接口后面会展开讲。3.2 启用服务并验证安装结果安装完成后把Docker守护进程设置为开机自启并立即启动systemctl enable --now docker systemctl status docker --no-pager然后验证客户端和服务端是否都正常工作docker version docker infodocker version会分别打印Client和Server的版本信息如果Server部分能正常显示说明dockerd已经和命令行工具成功对接。docker info里我一般重点看两项Storage Driver是否显示overlay2正常应该是overlay2如果显示vfs就说明存储驱动有问题性能会差很多。Server Version和你安装的docker-ce版本是否对得上。如果systemctl status docker显示activerunning确认无误后就可以进入镜像部署环节。3.3 基础配置日志限制与镜像加速RHEL 8默认没有生成/etc/docker/daemon.json文件但我建议一上来就自己建一个至少把容器日志大小限制住。这是我在生产环境吃过亏的地方日志不限制的话一个疯狂的程序能把整个系统盘写满。基础配置如下{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }这段配置的意思是每个容器的日志文件最大10MB最多保留3个文件。修改配置后重启Docker生效systemctl restart docker另外如果你所在的内网环境拉取Docker Hub镜像速度不理想可以在daemon.json里追加镜像加速地址。不同云厂商提供的加速服务地址不同填入你实际拿到的那一个即可。配完同样要重启docker。4. 拉取nginx镜像并运行容器4.1 选择合适的nginx镜像nginx在Docker Hub上的官方镜像维护得不错常见的tag有带alpine后缀的精简版、带stable后缀的稳定版以及perl等集成了额外模块的版本。对大部分项目来说我推荐优先使用带具体版本号的alpine版本比如docker pull nginx:1.26-alpinealpine镜像体积小基础系统精简受攻击面也小。当然如果你需要保持和现有环境一致拉nginx:1.26或者nginx:stable也完全可以。演示起见我们拉一个稳定的版本docker pull nginx:1.26 docker imagesdocker images能看到镜像的仓库名、标签、镜像ID和大小。如果这一步网络拉取很慢大概率需要检查daemon.json里的镜像加速配置。4.2 用一条命令把nginx跑起来最简单的运行方式是这样的docker run -d --name nginx-web -p 8080:80 nginx:1.26参数说明-d后台运行容器终端不挂住。--name nginx-web给容器起个名字后面管理全靠这个名字。-p 8080:80把宿主机的8080端口映射到容器内的80端口。nginx:1.26指定镜像名和标签。为什么这里映射8080而不是80因为在RHEL 8上80端口可能被其他服务或系统自带的httpd占掉先拿8080做验证确认没问题再切换到80端口。运行成功后用docker ps看一下容器状态正常会显示Up状态。端口映射的原理可以这么理解nginx进程监听在容器内部网络命名空间的80端口这个端口对宿主机不可见。docker通过iptables规则在网络层把宿主机8080端口收到的流量转发到容器IP的80端口所以外部访问宿主机IP:8080就等价于访问容器内的80端口。4.3 验证nginx是否真的跑起来了容器起来了不等于服务可用验证这步不能省docker ps curl -I http://127.0.0.1:8080正常情况curl会返回HTTP/1.1 200 OK。浏览器访问如果通不了第一嫌疑是防火墙。RHEL 8默认启用firewalld外部流量访问8080端口会被拦下来。放行命令如下firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload如果本机curl正常但局域网内其他机器访问不了百分之九十五以上都是这个原因。SELinux可能性也存在但一般来说nginx容器并不涉及复杂的文件访问防火墙优先级更高。4.4 看日志和进入容器nginx跑起来之后你可能会想确认一下访问日志或者改配置。常用命令docker logs -f nginx-web docker exec -it nginx-web bash进入容器后nginx的配置在/etc/nginx/nginx.conf默认网页目录在/usr/share/nginx/html。如果你想给容器里放一个测试页面验证效果可以这样echo test page /tmp/test.html docker cp /tmp/test.html nginx-web:/usr/share/nginx/html/test.html curl http://127.0.0.1:8080/test.html这里有个新手特别容易懵的地方容器里没有systemdsystemctl reload nginx这种命令是走不通的。修改完配置文件后要用nginx -s reload来让配置生效。记住容器就是一个精简的运行环境你把它当成一台完整的Linux服务器去操作反而会碰壁。5. 常见问题与排查实录5.1 仓库下载失败或404症状dnf install docker-ce时报Curl error或者提示找不到repomd.xml。原因和解决办法前面已经说过核心是仓库地址失效。排查顺序建议这样走dnf repolist cat /etc/yum.repos.d/docker-ce.repo curl -I https://download.docker.com/linux/centos/8/x86_64/stable/repodata/repomd.xml如果最后一步curl返回404确认仓库地址已经无法访问直接换镜像源并重建缓存。需要提醒的是不要用--releasever7这种老套路强行去拉EL7的包这样会把RHEL 8系统的依赖关系搅乱后患无穷。5.2 overlay2存储驱动异常症状docker info显示Storage Driver为vfs或者启动容器时报“failed to mount overlay: operation not permitted”。首先要确认内核是否加载了overlay模块modprobe overlay lsmod | grep overlay然后是文件系统层面。RHEL 8默认的XFS文件系统如果开启了ftype1属性才能支持overlay2。检查方法df -T /var/lib/docker xfs_info /var/lib/docker | grep ftype如果ftype0那overlay2没法直接用需要备份数据后重新格式化分区。RHEL 8安装程序默认创建的分区一般没问题但你要是从老系统迁移过来的旧分区就可能踩到这个坑。vfs存储驱动虽然也能跑容器但每次写入都要做全量复制性能差好几个量级不适合长期使用。5.3 防火墙拦截端口症状容器日志没报错curl http://127.0.0.1:8080正常但局域网其他机器访问超时。原因就是firewalld默认没有放行对应端口。解决方式firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload firewall-cmd --list-ports如果想更规范一点可以开放HTTP服务而不是裸端口firewall-cmd --permanent --add-servicehttp firewall-cmd --reload这种情况在云服务器上还要额外关注安全组策略RHEL 8本身的防火墙只是第一道关。5.4 SELinux拦截容器操作症状nginx容器正常启动日志也没异常但访问被拒绝系统日志里出现avc: denied。RHEL 8默认SELinux处于Enforcing模式对容器的文件访问有严格限制。尤其是挂载宿主机目录到容器时最容易触发。查看方法ausearch -m avc -ts recent如果确实有denied记录优先考虑给挂载卷加上正确的SELinux标签。比如docker run挂载目录时追加:Z后缀docker run -d --name nginx-web -p 8080:80 \ -v /data/nginx/html:/usr/share/nginx/html:Z \ nginx:1.26:Z会让docker自动为挂载目录打上适合容器的SELinux标签宿主机上其他进程访问这个目录会受限但这是正常的安全行为。不建议用setenforce 0关掉SELinux来解决问题这会降低整个系统的安全防护水平。把几个高频问题整理成速查表方便大家快速定位症状直接原因快速解决安装时仓库404官方CentOS 8源失效换镜像源、清缓存重建Storage Driver是vfsoverlay驱动或ftype不支持modprobe overlay、检查xfs ftype本机通、外部不通firewalld未放行端口firewall-cmd放行对应端口挂载目录无权限SELinux拦截挂载参数加:Z标签端口被占用podman或其他服务抢占docker ps/podman ps查占用并处理5.5 podman先占了端口RHEL 8自带podman如果之前用podman跑过一个监听相同端口的容器docker再去映射同一个端口就会报Address already in use。排查时别只看dockerss -lntp | grep 8080 podman ps看到是哪个进程占用的端口停掉旧容器或者docker换个映射端口。podman和docker共存本身没问题但对同一个端口的使用要协调好避免互相打架。6. 把nginx容器跑成长驻服务的一些建议6.1 设置容器重启策略本地验证用的容器挂掉就挂掉了但生产环境里的nginx容器不能这样。最简单的保护是把重启策略加上docker run -d --name nginx-prod -p 80:80 \ --restart unless-stopped \ nginx:1.26如果容器已经创建了也可以用docker update --restart unless-stopped nginx-produnless-stopped的意思是除非是你手动执行docker stop停掉的容器否则Docker服务重启或宿主机关机重启后容器都会自动拉起来。这比always更温和适合作为Web服务的默认策略。6.2 把配置和数据挂载出来官方镜像里的html目录和nginx配置都在容器层容器一旦删掉数据就没了。对于要长期运行的nginx服务建议把关键目录挂载到宿主机mkdir -p /data/nginx/{conf,html,logs} docker run -d --name nginx-prod -p 80:80 \ -v /data/nginx/html:/usr/share/nginx/html:ro \ -v /data/nginx/conf:/etc/nginx/conf.d:ro \ -v /data/nginx/logs:/var/log/nginx \ --restart unless-stopped \ nginx:1.26这里有几个容易踩的细节如果挂载的是conf.d目录目录里可以放多个conf文件容器启动时会自动include。如果挂载单个文件比如nginx.conf前提是这个文件在宿主机上已经存在否则Docker会为其创建一个目录而不是文件容器直接起不来。只读挂载ro能防止容器内误修改宿主机文件日志目录则不需要ro让nginx正常写日志。6.3 用Compose管理多个服务单个nginx容器用docker run还顶得住但如果后面要加后端应用、数据库、Redis一长串docker run命令就不好维护了。我习惯在项目目录下放一个compose文件比如services: web: image: nginx:1.26 container_name: nginx-prod restart: unless-stopped ports: - 80:80 volumes: - /data/nginx/html:/usr/share/nginx/html:ro - /data/nginx/conf:/etc/nginx/conf.d:ro - /data/nginx/logs:/var/log/nginx然后在同一目录执行docker compose up -dRHEL 8上安装docker-compose插件的方式比较多根据你的当前docker版本选择匹配的插件安装方式即可。重点是服务定义、网络、卷三块写清楚后续扩容和迁移都会方便很多。6.4 日常更新镜像的正确姿势nginx镜像版本更新后我不建议直接在运行中的容器里改文件也不建议用docker exec进容器去升级软件包。正确流程是docker pull nginx:1.26 docker compose up -d --force-recreate web这个流程的思路是容器始终是无状态的运行环境配置和数据都在挂载卷里镜像更新只负责把运行环境替换成新版本数据不丢。这个习惯养成后升级回滚都很快。还有一个小建议每次操作前先看一眼docker ps -a搞清楚哪些容器是验证时临时起的哪些是需要长期跑的。很多人排查问题时发现“系统里一堆僵尸容器”基本都是当初图省事没有按名字和用途区分事后再去清理就非常麻烦。好的习惯是一开始就给容器起规范的名字目录结构也统一后面管理成本会低很多。我在实际使用中最大的体会是RHEL 8上跑Docker最大的风险往往不是安装那一步而是安装完之后你可能会忘掉系统本身的SELinux和firewalld策略。容器明明活着服务却怎么都不通这种问题排查起来比直接报错还折磨人。装完Docker后第一件事就检查docker info和SELinux状态端口映射之前先想清楚防火墙规则再决定挂载目录要不要加:Z标签。这套流程跑熟之后RHEL 8上部署nginx、数据库、应用服务其实跟Ubuntu的体验差距并不大。希望这篇文章能帮你少踩几个我已经踩过的坑。