ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RHEL9安装Docker实战:避开Podman兼容层,跑通MySQL/Redis

RHEL9安装Docker实战:避开Podman兼容层,跑通MySQL/Redis RHEL9上装Docker第一次搞的人十有八九会卡在“装”这一步。直接dnf install docker装出来的是一堆Podman兼容层docker compose、buildx这些插件全对不上去网上搜教程大部分又是Ubuntu或CentOS 7的老路子放到RHEL9上不是仓库404就是服务起不来。这篇文章把我从系统检查、仓库配置、启动排错到实际跑通MySQL 8.0、Redis主从、Compose编排的完整过程记下来坑和判断方法都标清楚适合刚接手RHEL9、需要把现有Docker工作流原样搬过去的运维和开发。1. 环境准备RHEL9系统检查与Docker仓库配置1.1 为什么RHEL9上装Docker这么别扭RHEL9官方把默认容器运行时方向定在了Podman系统软件仓库里根本没有docker-ce这种包。有的教程会让你装podman-docker它只是把podman伪装成docker命令的兼容层。平时跑个简单容器没问题但一旦项目里用到Docker守护进程API、docker compose插件、buildx或者CI工具通过/var/run/docker.sock调用Docker时Podman和真正的Docker在行为上会有不少差异排查起来非常痛苦。我见过一个线上事故同事用兼容层跑docker compose up管编排的脚本直接把docker.sock路径当成Podman的socket去连结果权限和路径全对不上服务起半天起不来。所以说如果项目里写的是标准Dockerfile、docker-compose.yml镜像仓库也是按Docker镜像格式管理的那直接在RHEL9上装真正的Docker Engine才是最省心的选择别在“少装一个软件”上花时间。1.2 装之前先确认的三件事动手前先做三个检查能省掉后面一大半排错时间。第一确认系统版本和内核。执行cat /etc/redhat-release和uname -rRHEL9、Rocky Linux 9、AlmaLinux 9、CentOS Stream 9都按本文步骤走这套流程是同源的命令完全通用。第二确认磁盘空间。Docker默认数据目录是/var/lib/docker如果系统盘本身不大建议提前规划。执行df -h /var/lib/docker看看可用空间至少留出20GB以上否则后续拉镜像、跑容器很容易把根分区写满。第三清掉可能冲突的旧包。执行rpm -qa | grep -E docker|podman|containerd重点看有没有podman-docker这个兼容包。它会把/usr/bin/docker这个命令占住导致后面装完真正的docker-ce时命令被覆盖或产生奇怪的冲突。有的话先dnf remove podman-docker -yPodman本体可以留着互不影响。1.3 配置Docker官方仓库的正确姿势RHEL9没有独立的docker-ce仓库路径官方把el9的包放在了CentOS 9的目录下面。所以正确做法是添加CentOS 9版本的repo而不是去找什么rhel开头的路径。sudo dnf config-manager --add-repohttps://download.docker.com/linux/centos/docker-ce.repo添加之后建议顺手导入一下GPG密钥避免密钥缺失导致的安装中断sudo rpm --import https://download.docker.com/linux/centos/gpg sudo dnf makecache sudo dnf repolist | grep docker看到docker-ce-stable出现在列表里仓库配置就算完成了。顺便提一句如果服务器在内网、访问外网受限需要在/etc/dnf/dnf.conf里配置代理或使用公司内网镜像源否则后面的makecache会卡住。这一步是不少内网环境出问题的高发点先确认能不能通到download.docker.com再做下一步。2. 安装Docker引擎并跑起来2.1 需要装的包和各自的角色仓库配好后安装命令很直接sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin这四个包分别干什么我简单说清楚。docker-ce是Docker Engine主体包含守护进程dockerddocker-ce-cli是客户端命令dockercontainerd.io是底层容器运行时Docker现在靠它来拉起容器进程docker-compose-plugin提供docker compose子命令这是新版Compose v2的正确用法比单独装一个docker-compose二进制更干净。安装过程中如果看到需要依赖container-selinux直接确认即可RHEL9自带这种策略包不用额外处理。装完后先别急着跑服务检查一下/etc/docker/目录是否存在历史遗留的daemon.json有旧配置就先备份不然后面启动时容易因为配置格式问题失败。2.2 启动、自启与版本自检启动Docker并设置开机自启sudo systemctl enable --now docker sudo systemctl status docker看到active (running)之后用docker info做一次自检。重点看几项Server Version是Docker引擎版本Storage Driver应该是overlay2Cgroup Driver在RHEL9上正常会是systemd因为RHEL9默认使用cgroup v2这个组合是对的Security Options里会有selinux这是RHEL家族的正常状态不需要看到它就慌。如果服务没起来第一步去看日志命令是journalctl -xeu docker.service不要盲目重启。常见的启动失败原因无非三种daemon.json写坏导致配置解析失败、containerd服务没起来、/var/lib/docker所在磁盘只读。日志里一般都会明确指向是哪种按图索骥即可。2.3 免sudo调用Docker权限设置Docker的socket文件是/var/run/docker.sock默认属组是docker权限660。想让普通用户不用sudo直接敲docker命令把这个用户加进docker组就行sudo usermod -aG docker $USER newgrp dockernewgrp docker是让当前会话立刻加载新组或者直接退出重新登录也一样。要注意的是加了组之后如果还是报permission denied while trying to connect to the docker daemon socket多半是还没重新登录当前进程和shell没有拿到新组身份。另外别忽略一种情况环境变量DOCKER_HOST被设置成了错误地址比如指向了远程或别的socket这种情况下本机权限再正常也没用env | grep DOCKER查一下。还有一点必须提醒docker组本质上等同于root权限组内成员可以通过挂载宿主机目录的方式拿到root能力所以别把不信任的账号随便加进docker组。这条做运维的应该都懂但还是值得多说一遍。3. 镜像下载慢的解法Registry加速配置3.1 为什么拉镜像容易超时装好Docker后第一次docker pull mysql:8.0很多人会挂在下载阶段镜像层数据拉到一半就超时、重试、再超时。原因不在于Docker本身配置有误而在于默认官方镜像仓库在公网上的连通质量不稳定特别是拉一些体量大的镜像时分片多、数据量大超时概率显著上升。这个问题的标准解法是配置registry-mirrors也就是给Docker配置一个镜像加速地址。Docker拉取镜像时会先请求这个加速地址它本质上是官方仓库的缓存站点命中后下载速度会快很多。这里需要注意加速地址是跟着网络环境走的生产环境建议优先看公司内部是否提供了镜像仓库没有的话再申请云厂商容器镜像服务里的加速地址或者使用公共镜像站提供的mirror地址。3.2 daemon.json的正确写法与热加载Docker的配置文件在/etc/docker/daemon.json如果文件不存在就新建。一个适用于RHEL9的稳妥写法如下{ registry-mirrors: [https://你的加速地址], exec-opts: [native.cgroupdriversystemd], log-driver: json-file, log-opts: { max-size: 50m, max-file: 5 } }exec-opts指定cgroup驱动为systemdRHEL9这种cgroup v2环境里这是最稳的组合能避免和systemd的资源管理打架。日志限制的两个参数建议加上不然容器持续打印日志会把磁盘写满这是很多服务器磁盘告警的隐藏原因。改完配置后执行sudo systemctl daemon-reload sudo systemctl restart docker docker info | grep -A2 Registry Mirrors看到配置的地址出现在输出里说明加速生效了。特别提醒daemon.json是严格的JSON格式多了一个逗号、注释或者引号不匹配都会导致dockerd启动失败。改完配置后如果服务起不来第一反应别去动服务先看日志然后用python3 -m json.tool /etc/docker/daemon.json校验一下格式基本能秒定位。3.3 内网环境下拉镜像的兜底方案有些内网环境连加速地址都访问不到这种时候就得换个思路在能联网的机器上docker pull需要的镜像然后导出再导入。docker pull mysql:8.0 docker save mysql:8.0 -o mysql-8.0.tar # 拷贝到目标服务器后 docker load -i mysql-8.0.tar如果公司内部有自建的镜像仓库更推荐的做法是改tag后推送上去docker tag mysql:8.0 registry.internal.example.com/library/mysql:8.0 docker push registry.internal.example.com/library/mysql:8.0然后把目标服务器上的daemon.json里的registry-mirrors指向内部仓库或者直接拉取时带上仓库前缀。这种方案在离线或受限网络环境下最稳定也方便统一管理镜像版本。4. 网络模型与连通性排查4.1 Docker默认网络与RHEL9的防火墙关系Docker默认创建docker0这块网桥容器默认走bridge模式IP段通常是172.17.0.0/16。容器访问外网时Docker会在宿主机iptables上做NAT转发把容器的内网IP转成宿主IP发出去。RHEL9原生防火墙是firewalld底层使用nftables。Docker调用的还是iptables兼容层正常情况下两者能协同工作但如果你手动改过防火墙规则或者系统模板里有额外的安全加固脚本就可能出现Docker自动写入的规则和防火墙策略冲突的情况。最常见的现象是容器能启动但外部访问不了映射端口或者容器里面ping不通外网。排查之前先明确责任边界Docker只管自己写入的那部分iptables规则和端口映射而firewalld决定的是系统层面的数据包是否放行。遇到网络不通先判断是哪一层的问题别一上来就重启docker大概率解决不了。4.2 容器访问外网失败的排查路径容器内ping 8.8.8.8不通或拉取外部资源失败按顺序排查宿主机上ip addr show docker0确认docker0网桥是UP状态并且有IP地址。docker exec 容器名 ip addr确认容器内部拿到了正常的IP地址和网关。在容器里ping 172.17.0.1网关通了说明容器与宿主机二层网络没问题。从宿主机查看sysctl net.ipv4.ip_forward如果输出是0说明内核IP转发被关掉了容器里的包根本出不去。修复方法是写入持久化配置echo net.ipv4.ip_forward1 | sudo tee /etc/sysctl.d/99-docker.conf sudo sysctl -p /etc/sysctl.d/99-docker.conf如果外网IP通了但域名解析不了那就是DNS问题容器默认读/etc/resolv.conf里的配置可以在docker run时加--dns 223.5.5.5指定公共DNS或者改daemon.json里的dns字段。我实际排查下来RHEL9上容器访问外网不通十有八九是ip_forward被系统加固模板关了。这个参数在Ubuntu上几乎默认是1但在RHEL系定制镜像里不一定所以每次新环境我都会先查这一步。4.3 端口映射不通时先分清责任边界用-p 3306:3306映射容器端口后外部机器访问宿主机IP的3306端口不通很多人会先在Docker上折腾半天。其实这种情况先做两个测试在宿主机本机curl 127.0.0.1:3306看通不通通的话说明Docker端口映射和容器进程都没问题问题出在firewalld放行上不通的话才需要查容器进程有没有监听3306、端口有没有被占用。firewalld放行方法sudo firewall-cmd --add-port3306/tcp --permanent sudo firewall-cmd --reload另外-p 3306:3306默认绑定0.0.0.0也就是暴露在所有网卡上公网IP也能直接访问。如果服务不需要对外建议把映射改成-p 127.0.0.1:3306:3306或者-p 内网IP:3306:3306减少不必要的暴露面。这个习惯在云服务器上特别重要。5. 实战一MySQL 8.0容器化部署与数据持久化5.1 直接run还是先做目录规划跑MySQL容器前做目录规划能省掉后面无数麻烦。主机上先建好数据目录、配置目录和日志目录sudo mkdir -p /data/mysql/{data,conf,logs} sudo chown -R 999:999 /data/mysql这里的关键是999:999它是MySQL官方镜像内mysql用户的UID:GID。如果挂载的宿主机目录属主是root容器里的mysql用户就没有写权限启动阶段会直接报Permission denied而导致容器反复重启。这个细节是我见过最多的“MySQL容器起不来”的原因没有之一。配置文件可以放在/data/mysql/conf/my.cnf基础配置写清楚字符集[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci一个需要注意的坑MySQL 8.0里lower_case_table_names参数必须在数据目录初始化时确定中途改配置再启动数据目录元数据和配置不一致MySQL会拒绝启动。所以首次初始化之前就把该定的参数定好装完再反复改配置是大忌。5.2 MySQL容器反复重启的高频原因启动命令我推荐这样写docker run -d --name mysql8 \ --restart unless-stopped \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -e TZAsia/Shanghai \ -v /data/mysql/data:/var/lib/mysql \ -v /data/mysql/conf/my.cnf:/etc/my.cnf \ mysql:8.0.36 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci注意镜像我锁定了mysql:8.0.36不建议用latest因为官方每次发版都可能调整默认参数生产环境镜像不锁版本下次部署很可能行为就变了。容器起不来时第一步永远是docker logs mysql8不要看容器状态瞎猜。常见的高频原因有三个挂载目录权限不对日志里会直接报Cant open /var/lib/mysql配置文件写坏导致mysqld启动失败宿主机3306端口被占用导致端口绑定失败。日志里全都写得明明白白顺着看就能定位。5.3 外部客户端访问与远程用户创建MySQL 8.0的root默认只允许从localhost连接外部客户端用root是连不上的。正确的做法是创建一个专门给应用用的远程账号docker exec -it mysql8 mysql -uroot -p CREATE USER app% IDENTIFIED BY 应用密码; GRANT ALL PRIVILEGES ON *.* TO app%; FLUSH PRIVILEGES;这里还有个老生常谈的兼容问题。MySQL 8.0默认的认证插件是caching_sha2_password一些老版本客户端和旧版JDBC驱动连不上报错信息往往是Authentication plugin caching_sha2_password cannot be loaded。遇到这种情况别急着改全局配置把对应账号的认证方式改成兼容模式即可ALTER USER app% IDENTIFIED WITH mysql_native_password BY 应用密码;只影响指定账号不影响其他安全机制这是最稳妥的兼容方案。5.4 备份与迁移的容器化思路容器化的MySQL做备份思路和传统方式没有区别只是执行入口变成了docker exec。最简单的全量备份方式docker exec mysql8 sh -c exec mysqldump --all-databases -uroot -p$MYSQL_ROOT_PASSWORD /data/mysql/backup.sql数据都在宿主机/data/mysql/data这个挂载卷里所以容器即使被删了、重新创建一个相同挂载的容器数据依然还在。这也是为什么我一直推荐所有有状态服务都要用volume挂载而不是依赖容器内部的文件系统。--restart unless-stopped重启策略也务必加上这样宿主机重启后MySQL能自动拉起不用人肉盯着。6. 实战二Redis主从复制搭建6.1 自定义网络与主节点启动Redis主从复制在Docker环境里有一个关键点主从之间通讯用的地址不能写127.0.0.1因为从节点和主节点不在同一个网络命名空间。正确做法是创建一个自定义的bridge网络让容器通过服务名互相访问。docker network create redis-net启动主节点docker run -d --name redis-master \ --network redis-net \ -p 6379:6379 \ -v /data/redis/master:/data \ redis:7.2 \ redis-server --appendonly yes --requirepass 主密码这里开启AOF持久化重启后数据不丢。生产环境Redis数据尽量挂载到宿主机目录否则容器删除等于数据删除。6.2 从节点配置与验证同步状态从节点启动时通过--replicaof指定主节点的容器名和端口注意端口要用容器内的6379而不是宿主机映射出来的端口。docker run -d --name redis-slave1 \ --network redis-net \ -p 6380:6379 \ -v /data/redis/slave1:/data \ redis:7.2 \ redis-server --appendonly yes --replicaof redis-master 6379 --masterauth 主密码 --requirepass 从密码如果再加一个从节点把容器名改成redis-slave2宿主机映射端口改成6381即可。验证同步状态的命令docker exec -it redis-slave1 redis-cli -a 从密码 info replication关注role:slave和master_link_status:up两个输出只要master_link_status是up说明主从链路已经建立。主从复制原理简单说就是从节点第一次连上主节点触发全量同步主节点BGSAVE生成RDB快照传给从节点后续主节点把写命令流实时推给从节点保持增量同步。这也是为什么主从之间网络必须互通、认证必须一致否则全量同步根本起不来。6.3 主从复制中的认证坑Redis主从复制里最典型的坑是主节点设置了requirepass从节点只配了replicaof没配masterauth结果从节点日志一直刷MASTER auth failed同步状态永远是down。记住一个口诀主节点要密码访问从节点连主节点时就必须提供masterauth如果从节点自己也开启requirepass那从节点被客户端访问时也要用从密码。两者不冲突但缺一个都会出问题。我用一个表格把主从集群的命令参数对照列出来方便实际配置时对照角色参数说明主节点--requirepass 主密码客户端和从节点连接时的认证密码从节点--replicaof redis-master 6379指定主节点容器名和容器内端口从节点--masterauth 主密码从节点连接主节点时提供的认证密码从节点--requirepass 从密码客户端访问从节点时需要的密码另外docker run命令行参数一多就容易乱后续维护也不方便。所以如果想长期维护这套Redis集群建议直接用Compose来定义下一部分我专门讲Compose编排。7. 实战三用Docker Compose编排完整服务7.1 Compose插件的安装与版本确认RHEL9上安装的docker-compose-plugin对应的命令是docker compose中间有个空格没有短横线。新版Compose v2已经不需要单独安装docker-compose那个二进制文件了docker compose version能正常输出版本号就说明环境OK。docker compose version这个细节很多人会忽略RHEL9自带的一些工具文档里提到的还是老版docker-compose装完会发现命令不存在。认准docker compose子命令的形式就不会走弯路。7.2 compose文件结构设计下面这个docker-compose.yml是一个完整的示例包含MySQL、Redis和业务应用三个服务适合作为RHEL9上微服务或多容器应用的起步模板version: 3.8 services: mysql8: image: mysql:8.0.36 restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: 你的密码 volumes: - mysql_data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d networks: - backend ports: - 3306:3306 redis: image: redis:7.2 restart: unless-stopped command: redis-server --appendonly yes --requirepass 你的密码 volumes: - redis_data:/data networks: - backend ports: - 6379:6379 app: build: ./app restart: unless-stopped depends_on: - mysql8 - redis environment: MYSQL_HOST: mysql8 REDIS_HOST: redis networks: - backend ports: - 8080:8080 volumes: mysql_data: redis_data: networks: backend: driver: bridge注意业务应用连接MySQL和Redis时地址直接写服务名mysql8和redis不要写localhost或宿主IP因为Compose创建的自定义网络里通过服务名互相解析是最稳定可靠的方式。7.3 常用命令与volume管理Compose的核心操作命令不长但每个都有明确用途docker compose up -d # 启动所有服务 docker compose ps # 查看服务状态 docker compose logs -f app # 跟踪指定服务日志 docker compose exec app bash # 进入指定服务容器 docker compose down # 停止并删除容器这里有一个高危操作必须提醒docker compose down -v会连volume一起删掉意味着MySQL、Redis的数据全部清空。我在测试环境手滑过一次数据全没了从此养成习惯生产环境宁可多敲两行docker compose down不带-v也不敢顺手加那个参数。数据卷的管理用docker volume ls和docker volume inspect查看确认挂载点后再决定要不要删。7.4 业务镜像构建思路Compose里的app服务通过build: ./app构建RHEL9上构建镜像的思路和别的Linux发行版完全一致。以Java服务为例推荐多阶段构建第一阶段用Maven镜像编译第二阶段用JRE运行时镜像跑这样最终镜像体积能小一大截。FROM maven:3.9-eclipse-temurin-17 AS build WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM eclipse-temurin:17-jre WORKDIR /app COPY --frombuild /build/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]多阶段构建的好处是编译工具链只存在构建阶段不会被打进最终镜像。生产环境镜像越小拉取越快、暴露的攻击面越少。我见过很多人图方便直接把整个JDK、源码塞进镜像里运行是能运行但镜像动不动一个多G部署效率和安全性都不理想。8. 高频报错与问题处理速查表8.1 Docker Engine启动类故障Failed to start docker.service或者failed to start docker application container engine这类报错处理路径是统一的先看日志再动手改。故障现象可能原因处理办法服务启动失败日志报配置解析错误/etc/docker/daemon.json格式错误python3 -m json.tool校验修正JSON后systemctl restart docker日志提示containerd相关错误containerd服务异常systemctl restart containerd再重启docker服务启动后马上退出/var/lib/docker磁盘空间不足或权限异常df -h检查必要时检查/var/lib/docker属主属组改动daemon配置后无法启动配置项使用不当备份原配置逐步最小化配置测试我个人的经验是配置类问题占了启动故障的六成以上而JSON格式错误是最常见的原因。所以每次改完配置先做格式校验再重启能避开很多低级错误。8.2 权限与连接类故障permission denied while trying to connect to the docker api和Got permission denied是搜索量最大的两个Docker报错。原因通常就三种用户不在docker组、socket权限不对、DOCKER_HOST环境变量指错位置。依次检查groups $USER ls -l /var/run/docker.sock env | grep DOCKER如果用户不在docker组sudo usermod -aG docker $USER后重新登录。如果socket权限异常正常应是root:docker且权限660。如果环境变量指向远程或错误地址unset DOCKER_HOST后重试。8.3 容易混淆的Docker Desktop报错网上很多带virtualisation support wasnt detected或virtualization support not detected关键词的报错实际上来自Windows或macOS上的Docker Desktop提示的是宿主机虚拟化能力未开启或Hyper-V/WSL2环境缺失解决方向是进BIOS开启VT-x、启用Windows虚拟化平台。这个报错和RHEL9服务器上的Docker Engine没有关系Linux服务器上装的Docker并不依赖这类虚拟化技术。搜索排错时一定要先确认自己所在的平台和报错归属否则照着Windows的操作去改Linux服务器既浪费精力又解决不了问题。RHEL9上对应的高频报错还是Failed to start docker.service这类服务级错误回到8.1的排查路径就好。最后分享一个我自己的习惯。新环境装完Docker后我从来不急着部署业务而是先跑一套“体检”docker run hello-world验证引擎可用docker network create test-net验证网络正常docker run --rm -v /tmp:/tmp alpine ls /tmp验证挂载和SELinux标签没问题。这套操作五分钟不到能把Docker本身最常见的问题全部暴露出来。后面再部署MySQL、Redis这些有状态服务时遇到问题就能把注意力集中在业务配置上而不是怀疑Docker环境没装好。RHEL9上的Docker只要第一次装对了后面真的可以跑得很省心。
返回列表