尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Rocky Linux容器化实战:用Docker构建企业基础镜像平台

Rocky Linux容器化实战:用Docker构建企业基础镜像平台 1. 为什么企业基础镜像优先选 Rocky Linux一场 CentOS 停更后的现实选择如果你在 2020 年底前后接手过任何一套线上运维体系多半经历过那个记忆犹新的时刻官方宣布 CentOS 8 的生命周期从 2029 年大幅缩短到 2021 年 12 月 31 日终止CentOS 7 也只撑到 2024 年 6 月。这意味着大量基于 RHEL 兼容生态构建的服务器、中间件、内部系统一夜之间失去免费的长期维护底座。我在当时维护着几十台 CentOS 8 虚拟机消息出来当天群里就炸了全是换什么系统和怎么迁移的讨论。Rocky Linux 就是在这个背景下被推到台前的。它的发起人 Gregory Kurtzer 是 CentOS 最初的联合创始人项目定位很明确做一个与 RHEL 完全兼容、可以免费使用、并且社区驱动的发行版。所谓完全兼容落到实际操作上就是你熟悉的 dnf、SELinux、firewalld、systemd、RPM 包管理、EPEL 仓库全部原样可用甚至可以把 CentOS 7/8 通过官方迁移脚本直接切过去。相比重新折腾一套新系统这种换个内核底座、业务不动的迁移路径对企业来说成本低得多。而今天这篇文章要聊的是把这套 RHEL 兼容体系再往前推一步用 Docker 来部署 Rocky Linux把它作为企业级基础镜像平台。换句话说不再把 Rocky 当作裸机或虚拟机里的操作系统来供着而是把它变成容器镜像按需拉取、秒级启动、统一管理。无论是给内部自研服务打底还是承载 JumpServer、Zabbix、GitLab 这类需要在 RHEL 兼容环境里运行的企业组件这条路都很实用。1.1 CentOS 停更后的迁移候选对比当时摆在面前的选择其实有好几个AlmaLinux、Rocky Linux、Oracle Linux、以及直接上 RHEL Developer Subscription。Oracle Linux 在企业里推广会牵扯到商业授权和法务顾虑RHEL 订阅对有预算的大厂没问题但很多中小团队和小公司并不想背这个成本。剩下的就是 AlmaLinux 和 Rocky Linux 二选一。这两个发行版我都实际用过一段时间给我的感觉是AlmaLinux 背靠 CloudLinux 公司资金和基础设施更稳定Rocky Linux 则由社区基金会主导和 RHEL 源码的同步节奏也很勤快。如果纯粹从容器基础镜像的角度看两者差别不大选谁更多是团队习惯和运维熟悉度的问题。我后来选择 Rocky主要是因为团队成员之前用 CentOS 的习惯太重Rocky 的包管理、目录结构、SELinux 策略几乎是无感切换培训成本几乎为零。1.2 RHEL 兼容性到底意味着什么很多人把兼容 RHEL理解成能用 CentOS 的软件包这其实只说对了一半。真正的价值在于RHEL 生态里经过验证的软件组合在 Rocky 上同样成立。比如你要跑 Oracle 数据库、WebLogic、或者某些只认 RHEL 内核版本的商业软件Rocky 的内核版本和 glibc 版本跟 RHEL 保持在同一个序列里这才叫兼容。放到 Docker 场景下这个特性直接决定了基础镜像的可用性。容器镜像只包含用户态文件系统内核还是复用宿主机的所以镜像里最关键的就是 glibc 版本、OpenSSL 版本、编译器工具链和系统工具包是否完整。Rocky 9 系列基于 RHEL 9glibc 2.34OpenSSL 3.0这些和很多商业软件、压测工具、代理组件的编译要求对得上。我见过不少人图省事用 Ubuntu 镜像跑一些只发 RHEL 系 RPM 包的软件结果各种依赖对不上最后还是灰溜溜换回 Rocky 或 CentOS 系镜像。需要提醒的是兼容不意味着你可以直接打电话找 RHEL 官方客服。Rocky 的维护靠社区安全公告的响应速度整体不错但没有商业 SLA。1.3 镜像仓库里的版本怎么选9.x 还是 8.x到 2025 年年中这个时间点Rocky Linux 主推的是 9.x 系列8.x 已进入维护收尾阶段。官方在 Docker Hub 上维护了rockylinux镜像仓库常用标签有这些标签对应版本适用场景9最新 9.x新项目默认选择9.5固定 9.x 小版本追求可复现的生产环境8最新 8.x兼容旧软件/旧内核要求8.108.x 最终版存量 CentOS 8 迁移minimal对应版本最小化只跑单一进程的场景base带基础工具的镜像需要 dnf 安装额外包我的建议很简单新项目一律用 9 系老项目如果软件有硬性依赖才考虑 8 系。8 系到 2025 年后陆续进入 EOL现在还在 8 系上铺新容器等于给自己埋雷。但注意9这种大标签会跟着小版本走构建出来的镜像内容可能过半年就不一样了所以真正进生产环境我习惯锁定到具体的小版本标签或者直接锁 Digest后面会专门讲。2. 环境准备Docker 安装与守护进程配置的常见弯路不管你要构建还是运行 Rocky 容器宿主机上先得有一个能用的 Docker 环境。这块看起来简单实际踩坑率不低尤其是 Windows 用户和内核版本偏旧的 Linux 用户。我把自己在不同平台上折腾过的经验整理一下。2.1 Linux 主机上装 Docker Engine 的正确姿势如果你用的是 Rocky Linux 当宿主机安装 Docker Engine 最干净的办法是通过 Docker 官方仓库sudo dnf install -y dnf-utils sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker注意第一行dnf-utils不能省config-manager是它提供的命令。另外官方仓库地址里写着 centos因为 Rocky 兼容 RHEL 系仓库结构直接能用不用改。装完后用一个docker run --rm hello-world验证如果输出正常说明守护进程和网络都没问题。很多人在这一步就卡住了常见原因是公司内网需要配代理但 Docker 守护进程不走系统代理必须在/etc/systemd/system/docker.service.d/http-proxy.conf里单独配置HTTP_PROXY和HTTPS_PROXY环境变量然后systemctl daemon-reload systemctl restart docker。2.2 Windows 上 Docker Desktop 的虚拟化前置条件Windows 上安装 Docker Desktop 最头疼的问题就是启动时报错Virtualization support not detected 或者 Docker Desktop failed to start because virtualisation support wasnt detected。这个报错的本质是 Docker Desktop 依赖 WSL2 后端或 Hyper-V/Arm 虚拟化而你的机器没满足条件。排查顺序我建议是这样进 BIOS/UEFI 检查虚拟化开关Intel 的 VT-x或叫 Virtualization Technology和 AMD 的 SVM Mode这两项在部分品牌机上默认是关闭的。开机进 BIOS 找到开启保存退出。确认 Windows 功能是否打开控制面板 → 启用或关闭 Windows 功能勾上适用于 Linux 的 Windows 子系统和虚拟机平台。勾选后需要重启。更新 WSL2 内核去微软官方下载 WSL2 Linux 内核更新包安装装完在 PowerShell 里执行wsl --status确认默认版本是 2。在 Docker Desktop 设置里把后端切到 WSL 2Settings → General → Use WSL 2 based engine 勾上。这三步做完90% 的启动失败都能解决。还有 10% 的情况是 Windows 版本太老不满足 WSL2 要求直接升级系统到新版本最省事。2.3 守护进程配置镜像加速、存储驱动与日志上限Docker 装好后我建议立刻编辑/etc/docker/daemon.json把下面这些基础项配好{ registry-mirrors: [ https://docker.xuanyuan.me ], storage-driver: overlay2, log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, max-concurrent-downloads: 10 }逐项说下原因。registry-mirrors是镜像加速配置默认情况直接拉 Docker Hub 镜像在部分网络条件下很慢配置一个能稳定访问的加速地址能明显改善。你可以用大厂提供的免费加速服务也可以局域网里自建 Registry。这块我不展开后面专门讲。storage-driver固定为 overlay2这是当前主流且性能最好的存储驱动除非你还在用老掉牙的版本被迫用 vfs否则别动它。log-driver和log-opts是我每次帮别人排查磁盘爆满时一定会检查的项。Docker 默认不限制容器日志大小一个不写日志切分的容器跑半年能吃掉几十 GB 磁盘。配了max-size和max-file后单个容器日志超过 100MB 就轮转最多保留 3 份旧日志磁盘压力小很多。max-concurrent-downloads是同时下载镜像层数的上限默认 3网络好的时候拉到 10 能明显加快拉取速度但要注意别把出口带宽占满影响业务。配置好后执行sudo systemctl restart docker然后docker info能看到这些配置是否生效。3. 拉取并验证 Rocky Linux 镜像动手前必须做对的几件事环境准备好之后就可以拉 Rocky Linux 镜像了。但拉下来和拉下来能用是两回事尤其在企业场景里镜像来源、标签、完整性每一步都要较真。3.1 官方镜像源与 Tag 规范Docker Hub 上的官方仓库是rockylinux拉取命令docker pull rockylinux:9这条命令会拉取最新的 9.x 版本。但我不建议在脚本和 Dockerfile 里直接用这种浮动标签原因很简单今天构建和半年后构建拉到的可能是不同小版本的镜像。对于可复现的构建流程要么锁小版本标签docker pull rockylinux:9.5要么锁 Digest。Digest 是镜像内容的 SHA256 校验值代表一个不可变的镜像版本格式类似docker pull rockylinuxsha256:0d5e65e1b3c0e3a1b7b1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e锁 Digest 的缺点是要手动更新适合对内容一致性要求极高的环境。我的折中做法是开发环境用9浮动标签生产构建固定到小版本标签彻底锁死只在安全紧急更新时临时做一次。3.2 用 docker inspect 验证镜像元数据拉下来后先别急着docker run花 30 秒验证一下docker inspect rockylinux:9 | head -n 60重点看这几个字段Architecture必须是 amd64、arm64 等实际运行架构别在 x86 宿主机上拉到 arm 镜像。Oslinux。RootFS.Typelayers查看层数能大致判断镜像精简程度。Config.Entrypoint和Cmd了解默认启动命令。顺便看一眼镜像大小docker images rockylinux标准版 Rocky 9 镜像一般在一百多 MB 这个量级minimal 版本更小大概五十多 MB。如果拉下来发现镜像大得离谱多半是仓库名拉错了拉到了某个第三方封装了很多东西的镜像。所以再次强调只从官方仓库拉第三方镜像除非你逐行看过 Dockerfile否则别用。3.3 官方签名与供应链安全企业基础镜像还有个容易被忽略的点供应链安全。Rocky Linux 官方镜像文件发布时带有签名但由于容器镜像的签名验证体系还没在 Docker Hub 完全普及普通docker pull并不会强制验证签名。要做到更严谨有几个方向记录并比对你首次从官方渠道拿到的 Digest后续通过 Digest 拉取。在 CI 管道里对镜像做漏洞扫描工具用开源的 Trivy 或 Grype 都行。比如trivy image rockylinux:9几秒钟就能扫出已知 CVE 列表再决定是否升级或补丁。如果团队有 Harbor 这类私有镜像仓库可以把通过扫描的镜像推送到私有仓库并用 Harbor 的签名功能给镜像打信任标签后续发布流程只允许跑签名过的镜像。这套流程在只有几个人的小团队里可能显得过度但一旦镜像要流向多台服务器、或多条业务线共享供应链风险就会指数级上升。我见过因为用了来路不明的第三方镜像里面被塞了挖矿程序的事故不是危言耸听。4. 跑起第一个容器systemd、静态 IP 与特权参数怎么搭配镜像没问题了现在真正开始跑容器。很多人第一次docker run -it rockylinux:9 /bin/bash进去后发现dnf 能用但 systemctl 报错。这不是镜像坏了而是对容器角色定位的理解问题。4.1 普通容器模式 vs systemd 容器模式Docker 容器默认只跑你指定的那个前台进程不启动 init 系统。这对微服务架构是好事一个容器一个进程干净利落。所以如果你只是想在 Rocky 容器里跑 nginx、跑 Java 应用、跑 Python 服务正常的做法是docker run -d --name myapp rockylinux:9 sleep infinity docker exec -it myapp bash进去之后用 dnf 装你要的东西装好把服务前台启动就行。这套方式不需要 systemd也别去纠结 systemctl 不好使的问题。但另一种场景很常见你在部署一些企业级软件——比如开源的运维平台、监控系统、堡垒机它们内部依赖 systemd 来管理子服务、启动脚本里直接写systemctl。这时候就得让容器里能跑 systemd。对 Rocky 9 这类 EL9 镜像启动命令改成docker run -d \ --name rocky-systemd \ --privileged \ --cgroupnshost \ -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ rockylinux:9 \ /sbin/init进去之后systemctl status就能正常工作了。要注意--privileged是高风险参数它把很多宿主机设备访问权限都放给了容器只在明确需要 systemd 的场景用。如果你的软件只是安装时需要 systemd运行时可以单独跑那就装完在普通容器里跑别给不必要的权限。4.2 给容器配静态 IP自定义 bridge 网络容器默认 IP 是 Docker 分配的一次性地址重启就变。Kubernetes 场景下 IP 本来就是动态的没问题但有些自建系统、或者老业务依赖固定 IP 做白名单/回调地址就必须要静态 IP。做法是创建一个自定义 bridge 网络并把容器指定到固定 IP 上docker network create \ --driver bridge \ --subnet172.20.0.0/24 \ --gateway172.20.0.1 \ rocky-net docker run -d \ --name rocky-static \ --network rocky-net \ --ip 172.20.0.10 \ rockylinux:9 \ sleep infinity为什么必须用自定义网络而不是默认 bridge因为默认 bridge 不支持手动指定--ip。只有用户自定义网络才允许在创建时显式分配 IP而且自定义网络还有个好处容器名就是 DNS 名同一网络里的容器可以直接用服务名互相访问天然解决了容器间通信问题。4.3 从容器访问宿主机与外部网络容器网络搞明白后还有个经典问题容器里怎么访问宿主机上的服务比如宿主机跑着 MySQL 监听在 3306容器里连 localhost 肯定不行。通过默认 bridge 网络跑的老式容器访问宿主机用网关 IP也就是docker network inspect bridge里看到的 Gateway通常是172.17.0.1。通过自定义网络跑的容器访问宿主机同样可以用网络创建时指定的 Gateway。如果你用--network host模式容器直接共享宿主机网络栈访问 localhost 就是宿主机但代价是没法用端口映射端口冲突自己管理。还有一个更进阶的玩法是 macvlan让容器在局域网里拥有一个和物理机同网段的独立 IP外部机器可以直接访问容器。典型配置docker network create \ -d macvlan \ --subnet192.168.1.0/24 \ --gateway192.168.1.1 \ -o parenteth0 \ macvlan-net docker run -d \ --name rocky-macvlan \ --network macvlan-net \ --ip 192.168.1.50 \ rockylinux:9 \ sleep infinity这个方案的坑是宿主机和 macvlan 容器之间默认无法互通因为物理网卡被虚拟出了子接口宿主机自己反而不在同一个虚拟网段上。要解决就得额外加一个同网段的 bridge 网卡做桥接比较折腾。我建议非必要不用 macvlan优先用端口映射-p 宿主机端口:容器端口简单直接。5. 用 Dockerfile 沉淀企业基础镜像最小化、时区、用户与安全手动docker exec进去装包只能算临时方案企业级玩法是用 Dockerfile 把镜像内容固化下来一次构建处处运行。这一节分享我搭建 Rocky Linux 基础镜像的完整思路。5.1 基础镜像的分层设计与构建顺序Dockerfile 每一行指令都可能产生一个镜像层层越多镜像越大、构建越慢。但这不意味着要追求一层流而是要把能缓存的指令放前面、频繁变化的放后面。一个典型的 Rocky 基础镜像 DockerfileFROM rockylinux:9.5 ENV TZAsia/Shanghai \ LANGen_US.UTF-8 \ LANGUAGEen_US.UTF-8 RUN dnf install -y \ dnf-utils \ epel-release \ dnf install -y \ curl \ wget \ vim \ tar \ xz \ git \ iproute \ iputils \ procps-ng \ net-tools \ lsof \ tcpdump \ htop \ openssh-clients \ ca-certificates \ tzdata \ glibc-langpack-en \ dnf clean all \ rm -rf /var/cache/dnf RUN useradd -u 1001 -m appuser WORKDIR /app USER appuser CMD [/bin/bash]第一层FROM锁定了基础协议第二层ENV设置环境变量第三层一次性安装所有依赖并把 yum/dnf 缓存清理干净。最关键的技巧是所有安装动作写在一个 RUN 里这样软件包清单变化时只重建这一层基础层可以复用缓存。dnf clean all经常被人漏掉。dnf 默认会把下载的 RPM 包缓存在/var/cache/dnf里不清理的话每个镜像凭空多出几百 MB而且这些缓存对运行期毫无用处纯属浪费。5.2 软件包取舍与 EPEL 仓库上面对软件包列表是我在一台基础镜像上反复验证过的组合简单说下各自的用途iproute提供ip命令iputils提供ping、traceroute排查网络问题离不开。procps-ng提供ps、top、free容器里查进程和内存都要用。net-tools是老牌ifconfig、netstat虽然逐渐边缘化但有些运维脚本还在用装上兼容性更好。openssh-clients提供ssh、scp用于容器里做跳转或者连接其他主机。tzdata和glibc-langpack-en是时区与语言包没有它们即使设了 TZ 环境变量日志里的时间也可能显示成 UTC中文环境还有可能需要额外的 langpack。epel-release是 RHEL 生态的社区扩展仓库里面有大量不在基础仓库里的常用软件比如 htop、jq 就是在 EPEL 里装它是为了后续不用每次进容器再手动加仓库。要注意的是基础镜像里装的包要克制。装得越少攻击面越小启动越快。我见过有人把 GCC、make、Java JDK 一股脑塞进基础镜像理由是后面可能用到。等你真的要用时再基于基础镜像写一个专用的构建镜像不迟。基础镜像只做通用运行时支撑不做业务功能预装这个边界要清楚。5.3 时区、语言、系统用户与清理动作时区这块再单独强调很多应用日志乱码或时间差 8 小时根因就是容器默认 UTC 时区。除了ENV TZAsia/Shanghai有些老应用不认 TZ 环境变量只认/etc/localtime所以更稳妥的做法是在 Dockerfile 里直接RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo ZONEAsia/Shanghai /etc/sysconfig/clock系统用户appuser的设计也很有讲究。默认容器里跑的是 root一旦应用被攻破攻击者直接获得容器内最高权限且这种权限在 docker 配置不当的情况下可能扩散到宿主机。所以基础镜像里最好预置一个非 root 用户并让应用以这个用户身份运行。用户 UID 我固定用1001这是有讲究的如果宿主机上挂载数据卷容器内用户 UID 和宿主机用户 UID 不一致会出现权限拒绝。企业里一般统一规划 UID 范围比如 1000-1999 给服务账户2000 给人工账户基础镜像用固定 UID 方便后续挂载时对齐 chown。5.4 基础镜像的命名、打标签与版本管理构建命令的命名和标签也要有规范不然镜像多了之后根本分不清哪个是哪个。推荐格式docker build -t registry.internal/base/rockylinux:9.5-20250601 .前缀registry.internal/base表示这是基础镜像和业务镜像区分开。标签里既有 EL 版本9.5又有构建日期20250601出现问题时能快速定位到具体是哪天构建的。如果团队内有多架构需求用 buildx 构建多平台镜像docker buildx build \ --platform linux/amd64,linux/arm64 \ -t registry.internal/base/rockylinux:9.5-20250601 \ --push .arm64 在信创 ARM 服务器上越来越常见提前把多架构构建跑通后面不会出幺蛾子。构建完再用 Trivy 扫一遍漏洞确认没有高危 CVE 再推到私有仓库这个流程别省。6. Docker Compose 编排 Rocky Linux 服务把单容器串成平台单容器玩明白了下一步就是用 Docker Compose 把多个服务编排起来。你的内部自研应用跑在 Rocky 基础镜像上旁边配上 MySQL 8.0、Redis这就是一个可复现的服务平台。6.1 一个典型的企业内网服务编排示例假设我们要部署一个内部 Web 服务后端跑在 Rocky 基础镜像里依赖 MySQL 和 Redis。写一个docker-compose.ymlservices: app: build: . image: registry.internal/app/myapp:1.2.0 restart: unless-stopped hostname: myapp networks: app-net: ipv4_address: 172.21.0.10 environment: - DB_HOSTdb - DB_PORT3306 - REDIS_HOSTredis volumes: - app-data:/app/data - ./config:/app/config:ro depends_on: db: condition: service_healthy redis: condition: service_healthy healthcheck: test: [CMD, curl, -f, http://localhost:8080/health] interval: 30s timeout: 5s retries: 3 start_period: 30s deploy: resources: limits: memory: 1g cpus: 1.0 db: image: mysql:8.0 restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD_FILE/run/secrets/db_root_pwd - MYSQL_DATABASEmyapp volumes: - db-data:/var/lib/mysql networks: - app-net healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine restart: unless-stopped command: [redis-server, --appendonly, yes] volumes: - redis-data:/data networks: - app-net healthcheck: test: [CMD, redis-cli, ping] interval: 10s timeout: 5s retries: 5 networks: app-net: driver: bridge ipam: config: - subnet: 172.21.0.0/24 gateway: 172.21.0.1 volumes: app-data: db-data: redis-data:这个编排文件里藏着不少值得说的细节。6.2 网络、数据卷与健康检查的配置细节app-net网络我手动指定了子网一方面是为了给 app 容器分配静态 IP172.21.0.10另一方面是让 MySQL 和 Redis 的 IP 在每次重建时不会乱跳。应用系统如果写了固定的数据库白名单这个静态 IP 就很有用。数据库密码我用了MYSQL_ROOT_PASSWORD_FILE/run/secrets/db_root_pwd配合 Docker Secrets 或者挂载文件方式传入密码。为什么要用文件而不是环境变量因为环境变量在docker inspect里是明文暴露的任何能访问 Docker API 的人都能看到。文件形式至少把密码落在一个权限受控的位置虽然也不是绝对安全但比环境变量好一个档次。depends_on配了condition: service_healthy加了这个条件之后app 容器会等 db 和 redis 通过健康检查才启动。不加这个条件的话compose 只保证启动顺序不保证依赖服务可用很可能 app 起来时 MySQL 还没初始化完直接连接失败。健康检查是 compose 编排里必须做的一环别省。healthcheck里curl -f后端应用的健康接口这个接口要在你的应用代码里实现。健康检查不只是给 compose 用的Docker Swarm 和 Kubernetes 探针也是同一个思路提前写好后面容器化平台迁移时少改一轮。6.3 资源限制与日志轮转deploy.resources.limits下面限制了内存 1G、CPU 1 核。没有这个限制的话容器进程可以吃掉宿主机全部资源一个内存泄漏的容器就能拖垮整台机器。注意deploy字段在 compose 里默认对docker compose up是生效的不需要 Swarm 模式。日志轮转我在前面 daemon.json 里已经做了全局兜底但如果你某个服务日志量特别大比如 Web 服务的 access log可以在服务下单独指定logging: driver: json-file options: max-size: 50m max-file: 5这会覆盖全局配置按服务的实际情况精细调整。跑一段时间后留意一下du -sh /var/lib/docker/containers/*/*.log看看哪些容器日志增长最快再针对性地调。Compose 编排的实际价值在于整套服务栈用一个docker compose up -d就能拉起换一台服务器也能完全复现。对于企业里环境一致性这种老大难问题这是最轻量、最容易被团队接受的解法。7. 高频问题排查复盘容器起不来、拉取慢、DNS 不通最后这部分把我这些年跑 Rocky 容器踩过的坑集中复盘一遍。每个问题都直接给排查链路方便你照着走。7.1 systemd 容器启动失败的根因与修正症状运行docker run -d --name test --privileged rockylinux:9 /sbin/init后容器秒退docker logs test里出现类似Failed to mount tmpfs at /run或Failed to install release agent的报错。根因Docker 容器的 cgroup 命名空间和宿主机不匹配。如果你宿主机是 cgroup v2新版 systemd 系统默认容器要正确挂载 cgroup 文件系统必须显式指定--cgroupnshost并把宿主机的 cgroup 目录挂载进容器docker run -d \ --name test \ --privileged \ --cgroupnshost \ -v /sys/fs/cgroup:/sys/fs/cgroup:rw \ rockylinux:9 \ /sbin/init还有一个隐蔽坑不同 Docker 版本的--privileged对 cgroup 挂载行为有细微差别如果你用旧版 Docker19.x 及更早有时还要额外加-v /sys/fs/cgroup:/sys/fs/cgroup:rw才能让 systemd 正常启动。升级 Docker 到新版本能少踩不少这种兼容性坑。如果是老系统上的 cgroup v1 环境systemd 容器还要挂载对应的 cgroup 子系统目录参数更繁琐。我的建议是宿主机能升到新版本就升级不要为了迁就旧环境把容器配置搞得特别复杂越复杂越难维护。7.2 镜像拉取慢的本地化解法症状docker pull rockylinux:9卡住不动或者每秒只有几十 KB。根因默认直连 Docker Hub 的分布式镜像 CDN 在你所在网络下链路质量不稳定。解法的优先顺序我给一下配置 daemon.json 的registry-mirrors加速地址重启 Docker重新拉取。如果公司内网有 Harbor 或者 Nexus 私服把registry-mirrors指向私服地址或者直接改--registry-mirror参数。拉取时不带 tag 可以并行下多个层搭配前面 daemon.json 里的max-concurrent-downloads: 10体验会好很多。排除思路很简单先docker pull hello-world测基础链路如果 hello-world 也慢就是整体网络问题如果只有特定镜像慢可能是该镜像层特别多或源仓库本身带宽受限。还有种情况是拉国内区云厂商镜像仓库里的镜像很快拉 Docker Hub 很慢那就是加速器没生效检查一下 daemon.json 语法和 Docker 是否真重启了。7.3 容器内 DNS 与时间同步问题症状一容器里curl https://example.com报域名解析失败但宿主机正常。排查先cat /etc/resolv.conf看容器内 DNS 配置。Docker 默认会把宿主机的 DNS 配置复制进容器如果宿主机用的是 systemd-resolved容器里的/etc/resolv.conf可能指向一个仅供宿主机本机使用的127.0.0.53地址容器里根本访问不到。解法在docker run时显式指定 DNSdocker run -d --dns 223.5.5.5 --dns 8.8.8.8 rockylinux:9 sleep infinity或者在 compose 文件的网络配置里指定networks: app-net: dns: - 223.5.5.5症状二容器里date看到的时间总是 UTC或者容器内时钟和宿主机差 8 小时。这里分两种情况差 8 小时是时区问题按前面第 5 节的方法设置/etc/localtime和 TZ 即可。差 N 秒且持续漂移是时钟同步问题。容器内跑chronyd或ntpd需要特殊权限而且和宿主机争抢时钟源不推荐。更稳妥的做法是容器内直接用宿主机的时间创建容器时挂载宿主机的 locatime 只解决时区真正的时间源统一由宿主机保证容器无需感知。还有一个细节老应用在容器里如果发现系统时间倒退比如跨时区调整可能出现缓存不一致、证书校验失败等怪问题。所以生产环境里尽量别在运行中的容器里手动date -s改时间要改就在创建容器前把环境配好或者直接重建容器。7.4 容器内 dnf 更新失败与 EPEL 元数据过期症状dnf install时报错Failed to download metadata for repo appstream或Cannot download repomd.xml。排查链路一般是这几步先确认容器能访问外网curl -I https://mirrors.rockylinux.org能访问就是缓存问题执行dnf clean all dnf makecache重新拉元数据。不能访问就检查宿主机网络和代理配置dnf 也有自己的代理配置在/etc/dnf/dnf.conf里如果宿主机走代理容器内 dnf 也要配proxyhttp://宿主机IP:端口。另外注意容器里 dnf 更新完系统后基础镜像层并没有变重启容器就回到初始状态。所以如果你要升级容器内系统包正确的方式是修改 Dockerfile 重新构建镜像而不是在运行中的容器里 dnf update。这个认知对很多刚开始用容器的人是个坎跨过去之后思路就顺了。回头看我自己的实践路径其实并不复杂先把 Rocky 从虚拟机搬到容器里跑通再逐步沉淀出基础镜像和编排模板。真正花时间的不是那几条命令而是搞明白每一种选择背后的适用边界——什么时候用 systemd、什么时候配静态 IP、基础镜像里装什么不装什么。这些边界弄清楚了Rocky Linux 这套 RHEL 兼容生态在容器化环境里就能成为很顺手的基础设施而不是又一个需要伺候的虚拟机替身。
返回列表