ARTICLE DETAIL

资讯详情

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

CentOS 9源环境部署:自建内网yum仓库与国产化迁移实战

CentOS 9源环境部署:自建内网yum仓库与国产化迁移实战 这两年做国产化迁移的团队越来越多而我接到的第一个硬任务就是在一台 CentOS 9 机器上把整套源环境部署起来。这里的“源”不是源码而是 yum/dnf 软件源——让整个内网都从自建的仓库拉包而不是一台台服务器去公网碰运气。这个需求听起来简单真正落地时牵扯到仓库同步、元数据生成、客户端切换、国产化系统兼容等一堆细节踩一次坑就是几小时起步所以我把完整的部署过程整理成这篇文章给准备做同样事情的同学一个可以照抄的作业。先说一下适合读这篇文章的人正在做系统迁移的运维工程师、信创适配项目的实施人员、需要管理大批 CentOS 机器的 DevOps 同学。文章不会绕弯子直接讲清楚怎么做、为什么这么做以及我在实际环境里遇到的坑。1. 项目背景CentOS 生命周期大限与迁移窗口1.1 CentOS 9 到底是个什么版本很多同学第一次接触这个需求时会被版本号搞懵。CentOS Linux 8 在 2021 年 12 月 31 日停止维护CentOS Linux 7 在 2024 年 6 月 30 日彻底 End of Life而传统的 CentOS Linux 9 这个版本从来就没有独立发布过。现在大家口中说的 CentOS 9实际指的是 CentOS Stream 9——它是 RHEL 9 的滚动预览版跟当年那种“完全免费克隆 RHEL”的 CentOS Linux 定位完全不同。这一点决定了源环境部署的第一个关键决策如果你搜索“centos 9 下载”拿到的是 CentOS Stream 9 的 ISO那么你的源环境必须基于 Stream 仓库结构来建。Stream 的仓库分成 BaseOS、AppStream、CRB、Extras 等几个独立模块而不是老 CentOS 7 那种单个大仓库。理解这个结构后面配置 repoid 才不会出错。1.2 源环境部署在国产化迁移中的位置国产化迁移不是把系统一换就完事它是一条完整的链路硬件选型、操作系统替换、中间件适配、应用改造、测试验收。而源环境部署属于最底层的基础设施环节它决定了后续所有服务器能不能稳定、统一、可控地获取软件包。实际项目里源环境要解决三个具体问题。第一是内网隔离问题很多机房的机器根本没有公网出口装个 nginx 都要从光盘里翻更别说装编译工具链。第二是版本一致性问题如果放任每台机器各自从外网或第三方源拉包三个月后你面对的就是几十种不同版本的 glibc 和 openssl应用一迁移全是兼容性灾难。第三是供应链可控性问题自建源环境意味着所有进入内网的 RPM 包都经过你的审查这对国产生态里的安全合规要求尤其重要。2. 方案选型与整体架构设计2.1 四种源环境方案对比我在动手之前先梳理了市面上常用的四种做法这里直接列成表给你参考方案实施难度可控性适用场景维护成本直接使用公网镜像源低差有外网的开发测试环境无自建本地镜像仓库reposync createrepo中高内网生产环境、国产化迁移项目中离线 RPM 包打包dnf download 批量拉取低中应用依赖数量少、单机部署低源码编译安装高高没有现成 RPM 的特殊软件高对于国产化迁移的内网生产环境绝大多数情况下都应该选第二种自建本地镜像仓库。原因很简单离线 RPM 包打包只适合一次性交付后续补丁更新又得重新打包长期维护成本反而高源码编译则把依赖问题无限放大除非万不得已不要碰。reposync 是 dnf 插件自带的同步工具它跟 dnf 共享仓库配置能按 repoid 精确同步指定仓库再配合 createrepo_c 生成元数据整套链路非常成熟。2.2 目录规划与存储容量计算源环境的目录结构直接影响后续扩展和排查效率。我建议按“操作系统类型 / 版本 / 仓库名 / 架构 / os”的层级来组织例如/data/repo/ ├── centos/ │ └── 9/ │ ├── BaseOS/x86_64/os/ │ ├── AppStream/x86_64/os/ │ ├── CRB/x86_64/os/ │ └── Extras/x86_64/os/ ├── openeuler/ │ └── 22.03/ │ ├── BaseOS/x86_64/ │ └── AppStream/x86_64/ └── anolis/ └── 8/ └── ...容量估算上CentOS Stream 9 的 BaseOS 仓库大约 3-4GBAppStream 仓库随着版本更新会长到 30GB 以上CRB 和 Extras 加起来也有几个 GB。如果只同步最新版本--newest-onlyx86_64 架构下四个仓库总共约 40-50GB如果保留全部历史版本轻松超过 80GB。再加上国产化系统openEuler、Anolis 等都是同样的 RPM 体系两三个系统的源加起来就需要 120-200GB 的存储空间。所以仓库服务器我强烈建议至少配两块 2TB 的盘做 RAID1或者四块盘做 RAID5不要省这点钱——我见过不止一次因为容量不够导致 reposync 中断元数据写一半整条源直接废掉的情况。2.3 仓库服务器的角色划分源环境部署不是单点任务合理的架构里至少要分三类角色仓库源服务器、客户端、验收机。仓库源服务器负责同步和发布客户端是被管理的生产机器验收机则是用来测试源环境是否可用的“试验田”。验收机这个角色容易被忽略但实际价值很大。每次做完仓库同步、客户端切换后先在验收机上跑一遍dnf upgrade和典型应用的安装确认没问题再放量到生产可以避免把错误配置扩散到几十台机器上。这个习惯我后面会反复提到。3. 源环境部署全流程实操3.1 仓库服务器基础环境准备仓库服务器的操作系统我选择直接用 CentOS Stream 9 本尊理由是在做国产化迁移的过程中源服务器本身保持最接近上游的环境最容易排查包层面的差异。安装时记住几个关键配置磁盘用 LVM/data 分区单独划出来放仓库数据时区、主机名这种基础项按公司规范来最小化安装即可不需要带图形界面。装完系统后第一步是安装同步工具链dnf install -y dnf-plugins-core createrepo_c nginx这里有个细节工具刻意选了 createrepo_c 而不是老的 createrepo。createrepo_c 是 C 语言实现生成上百 GB 仓库的元数据时速度优势非常明显而且对新版 dnf 的 zchunk 格式支持更完整。实测在普通服务器上createrepo_c 对 AppStream 仓库做增量更新只要一两分钟而老版本可能要跑十几分钟。然后是防火墙和安全上下文。仓库服务要对外提供 HTTP 访问所以放行 80 端口firewall-cmd --permanent --add-servicehttp firewall-cmd --reloadSELinux 这块是很多人翻车的地方。仓库目录如果放在 /data 下面默认的 SELinux 上下文是 default_thttpd/nginx 进程读不了。必须手动指定semanage fcontext -a -t httpd_sys_content_t /data/repo(/.*)? restorecon -Rv /data/repo这两条命令做完之后nginx 才有权限读取仓库文件。如果你图省事直接setenforce 0那我不建议因为等迁移验收阶段安全检查时SELinux 关闭本身就是不合格项。3.2 使用 dnf reposync 同步 CentOS Stream 9 仓库reposync 的用法核心是搞清楚 repoid。在 CentOS Stream 9 里用dnf repolist能看到默认启用的仓库但我做同步时会显式指定要同步的仓库避免把不需要的东西拉下来dnf reposync \ --repoidbaseos \ --repoidappstream \ --repoidcrb \ --repoidextras \ --download-path/data/repo/centos/9 \ --download-metadata \ --newest-only解释几个关键参数。--download-metadata会同时下载远程仓库的元数据让同步下来的目录立刻具备仓库结构--newest-only只保留每个包的最新版本对大多数迁移场景都够用还能省一半以上的磁盘空间。如果不加这个参数仓库目录会积累一个包的所有历史版本体积爆炸不说客户端解析元数据也会变慢。同步结束后检查一下目录结构是否符合预期tree -L 3 /data/repo/centos/9/BaseOS/x86_64/os/正常情况下应该看到 Packages 目录和 repodata 目录。repodata 里就是远程源带过来的元数据这部分数据可以省掉本地生成的那一步不过为了保险我习惯还是会用 createrepo_c 基于本地内容重新生成一遍。3.3 用 createrepo_c 生成仓库元数据并发布即使 reposync 已经带了远程的 repodata我还是会在同步完成后对每个仓库独立执行一遍元数据生成原因有两点一是本地仓库目录可能被手动调整过比如加了私有包二是让元数据与本地文件指纹严格一致避免客户端在 gpgcheck 之外遇到 checksum 不一致的报错。createrepo_c --update /data/repo/centos/9/BaseOS/x86_64/os/ createrepo_c --update /data/repo/centos/9/AppStream/x86_64/os/ createrepo_c --update /data/repo/centos/9/CRB/x86_64/os/ createrepo_c --update /data/repo/centos/9/Extras/x86_64/os/--update参数非常关键它表示增量更新只重新扫描变化过的包而不是全量重新生成。这个参数在后期定时同步脚本里几乎是必需品配合--check还能先检查是否有变化再决定要不要更新元数据。发布环节用 nginx 最省心。配置一个简单的 server 块server { listen 80; server_name repo.example.com; root /data/repo; autoindex on; autoindex_exact_size off; autoindex_localtime on; location / { index index.html; } }autoindex 打开后浏览器可以直接看到目录结构排查问题非常方便。配置完nginx -t检查语法然后systemctl enable --now nginx启动。3.4 客户端源配置与切换客户端配置是源环境落地的最后一公里。进入 /etc/yum.repos.d/把原来的公网源文件全部禁用新增一个指向内网仓库的 repo 文件[baseos] nameCentOS Stream 9 - BaseOS (internal) baseurlhttp://repo.example.com/centos/9/BaseOS/$basearch/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial [appstream] nameCentOS Stream 9 - AppStream (internal) baseurlhttp://repo.example.com/centos/9/AppStream/$basearch/os/ enabled1 gpgcheck1 gpgkeyfile:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficialGPG 密钥这块很多人会忘记。客户端必须能拿到官方公钥去验证 RPM 包的签名否则 gpgcheck1 会直接报错。两种方式一种是在仓库服务器上把密钥放到 HTTP 目录里客户端通过rpm --import http://repo.example.com/centos/9/RPM-GPG-KEY-centosofficial导入另一种是把密钥手动拷贝到客户端的 /etc/pki/rpm-gpg/ 目录下。我推荐第二种少一次网络依赖离线机器也能处理。配置完成后按顺序执行dnf clean all dnf makecache dnf repolist看到仓库列表正常、缓存生成成功再试装一个包验证比如dnf install -y nginx能装上就算基本跑通了。这里有个小技巧先在一台验收机上做完整流程确认无误后再用 Ansible 或 SaltStack 批量推送 repo 文件和密钥。4. 国产化系统的源适配实践4.1 从 CentOS 源平滑迁移到 openEuler / Anolis国产化迁移项目里纯 CentOS 场景其实只占一半。很多单位的目标操作系统是 openEuler、Anolis OS龙蜥、麒麟 V10 或统信 UOS。好消息是这些系统绝大多数保留了 RPM dnf/yum 的包管理体系意味着你在 CentOS 上学到的源环境技能可以直接迁移。以 openEuler 22.03 LTS 为例它的仓库结构和 CentOS Stream 很像分为 BaseOS、AppStream、EPOL 等。同步方式也是 reposync只是 repoid 名字不同。Anolis OS 走的是“兼容 CentOS”路线它的 8 系列仓库设计上就考虑过和 CentOS 的迁移关系部分包的命名和依赖与 CentOS 保持一致迁移时应用层几乎不用改。我在实际操作中总结了一条经验不要试图用一份 repo 文件打天下。虽然$releasever变量在单系统里好用但跨系统CentOS 切 openEuler时这个变量会自动被 dnf 替换成对应系统的版本号路径就对不上了。正确做法是给每类系统单独建 repo 文件通过 Ansible 的 os_family 判断分发不同模板。4.2 兼容模式下的依赖一致性保障国产化系统跟 CentOS 的“兼容”不是百分之百的二进制兼容。有些包在 openEuler 上编译用的 glibc 版本、openssl 版本和 CentOS Stream 9 不一样强行混用源会导致依赖解析混乱。所以源环境里必须讲到隔离和锁版本。我的做法是在源服务器上对不同系统建独立的仓库目录互不混放同时在客户端启用versionlock插件锁定关键系统包版本。比如 glibc、openssl、systemd 这类底层组件一旦确认当前版本可用就锁住不参与日常dnf upgrade只有经过验收的补丁窗口才手动解锁升级。依赖一致性还有一层含义应用打包时声明的依赖必须在源环境里能找到。迁移验收阶段最容易出的问题就是应用构建机用的源跟生产机的源不一致导致构建出来的 RPM 生产机装不上。所以源环境部署完成后一定要把构建机和生产机配置成同一套 repo 地址这是从源头杜绝依赖漂移的办法。5. 常见问题与排查技巧实录5.1 问题速查表把我在多个项目里遇到的典型问题整理成一个速查表遇到类似现象可以直接对照。现象可能原因排查与解决dnf 报 GPG key retrieval failed客户端没导入密钥或 repo 里 gpgkey 路径错误重新导入密钥检查 gpgkey 指向的文件是否存在404 错误报 repodata/repomd.xml 找不到baseurl 路径错误或 createrepo 没在该目录执行浏览器打开 baseurl 确认目录层级必要时重新生成元数据同步时磁盘写满进程中断仓库体积超过磁盘容量加磁盘或用 --newest-only 重新同步nginx 访问目录 403SELinux 上下文未设置或目录权限不够执行 semanage fcontext restorecon检查 nginx 进程的读权限dnf makecache 报元数据校验失败仓库正在更新时客户端拉取了不完整的元数据清理缓存后等同步脚本跑完再操作安装软件包报依赖冲突提示找不到某依赖缺少对应仓库如依赖在 crb 里但没启用检查 repolist补齐 crb/extras 仓库同步完客户端看不到新包元数据是旧的createrepo 没执行重新执行 createrepo_c --update5.2 三个让我印象深刻的坑第一个坑是 GPG 密钥的“一次性”思维。我最早部署完源环境验收机测试一切正常但一个月后新加一批机器时密钥文件因为当时没保留原件客户端死活导入不了。后来我养成了习惯所有 GPG 密钥在仓库服务器上单独建一个 /data/repo/keys 目录长期保存原文件和校验值一起收录进内部知识库换人交接也不会断。第二个坑是 SELinux 把 nginx 挡了整整半天。现象特别迷惑nginx 配置没有任何问题仓库目录权限也是 755但客户端 curl 就是 403。后来用ausearch -m avc -ts recent查看审计日志才发现 nginx 进程被 SELinux 拦截了因为仓库目录在 /data 下默认上下文不是 httpd_sys_content_t。这之后我把 SELinux 上下文配置直接写进了部署脚本再也没被坑过。第三个坑是 reposync 中断后的“半成品仓库”。有一次同步 AppStream 时网络断了重跑时我忘了加--delete参数导致仓库里残留了一批旧版本包和半个元数据目录。客户端 dnf makecache 一直报 primary.xml.gz 解析错误。这种问题光靠 mtime 看不出来最好的办法是给是不是历史遗留包做一个判断但更简单的方案是同步脚本里固定用--newest-only --delete --download-metadata三个参数一起上保持仓库状态始终干净。5.3 排查思路中最重要的一个原则不管问题表象是什么先做两件事dnf repolist -v看当前启用了哪些仓库和 baseurl以及dnf clean all把本地缓存清干净。很多“看起来像源坏了”的问题其实是客户端本地缓存了旧的元数据清理一遍就正常了。这个动作成本极低却能排除掉一半以上的干扰项我每次排查都从它开始。6. 自动化运维与个人心得6.1 定时同步脚本的完整写法源环境不是配完就一劳永逸必须靠定时同步保持跟上游一致。我在生产环境用的同步脚本大概是这样的#!/bin/bash # /usr/local/bin/repo-sync.sh set -euo pipefail REPO_BASE/data/repo/centos/9 REPOSbaseos appstream crb extras CURRENT_DATE$(date %Y%m%d) # 防止上次同步残留影响先做一次缓存目录检查 mkdir -p ${REPO_BASE} for repo in $REPOS; do echo [$(date %F %T)] syncing ${repo} ... dnf reposync \ --repoid${repo} \ --download-path${REPO_BASE} \ --download-metadata \ --newest-only \ --delete \ || { echo reposync ${repo} failed; exit 1; } # 增量重建元数据 createrepo_c --update \ ${REPO_BASE}/${repo}/x86_64/os/ \ || { echo createrepo ${repo} failed; exit 1; } done # 生成同步状态文件方便监控 echo ${CURRENT_DATE} /data/repo/.last_synccron 配置建议每周日凌晨 2 点执行避开工作时段带宽高峰0 2 * * 0 /usr/local/bin/repo-sync.sh /var/log/repo-sync.log 21这个脚本注意两点一是set -euo pipefail任何一步失败立即退出并留下日志避免带着脏数据继续同步二是同步完一定要执行 createrepo_c仓库元数据是最容易“过期”的部分。6.2 迁移窗口期的版本快照与回滚经验在国产化迁移的正式切换窗口期我的做法是给源环境做一个“快照版本”。具体来说迁移前把仓库目录完整复制一份或打一个 tar 归档到独立存储迁移期间所有客户端固定指向快照迁移验收不通过可以原路回滚验收通过后再切换到增量同步的正式源。这套操作让很多团队在迁移后期有余地处理意外问题而不是被迫在“继续往前改”和“回到老系统”二选一。这个习惯来自我自己的教训有一次迁移过程中上游源更新了一个系统库版本跟我们的应用二进制不兼容整批机器一重启就起不来。后来所有涉及源变更的项目我都强制要求做快照宁可多花半小时也不赌“上游不会动”。6.3 一些实用的部署细节最后分享几个零散但很实用的细节。仓库服务器的主机名和 IP 最好通过内网 DNS 固定客户端 repo 文件里用主机名而不是 IP这方便将来迁移仓库服务器时客户端不用改配置。nginx 访问日志要长期保留当客户端出现问题时日志能很快定位是哪个仓库、哪个包下载失败。磁盘监控必须加仓库目录一旦写满故障会非常隐蔽——客户端表现是间歇性下载失败时好时坏很难直接想到是磁盘问题。我做源环境部署最深的体会是这活儿表面上就是同步几个仓库、写几行配置但真正决定项目顺不顺的往往是你对版本、密钥、权限、回滚这些“边角料”的把控。把这些基础细节做到位国产化迁移后面的应用迁移和验收工作才会顺畅得多。
返回列表