第一次在内网机器上装 gcc,我对着报错折腾了大半天,最后发现只是缺了一个看起来无关紧要的 mpfr。这件事让我彻底明白,离线环境里 gcc 从来不是一个"单个软件",而是一整条依赖链:gcc 本体要调 cpp 做预处理,链接阶段要靠 binutils,编译器内部做高精度运算需要 mpfr、libmpc、gmp,想编 C 程序还得有 glibc-devel 提供的头文件。任何一个环节断了,yum 就会抛出一句冷冰冰的 "is needed by..." 然后罢工。
这篇文章就来彻底解决 CentOS 7 离线安装 gcc 的依赖和下载命令问题。我会从依赖拆解讲到下载工具选型,再到搬运到内网后的两种安装方式,全程基于我在 x86_64 架构 CentOS 7.6/7.9 上的实测。内网运维、异地部署、离线环境搭编译工具链的朋友可以直接参考,其他需要离线装软件的场景也可以把这套流程照搬过去用。
1. 为什么离线装 gcc 这么麻烦
1.1 你面对的不是一个包,而是一条依赖链
很多刚从在线环境转离线的人都会有一个错觉:yum install gcc 在联网时只要一条命令,离线时把 gcc 的 rpm 包下载下来装上去不就行了?真实情况远没有这么简单。gcc 是 GNU 编译器套件,它不只是 /usr/bin/gcc 一个可执行文件,背后牵扯到完整编译工具链。在没有网络的情况下,你不仅要找到 gcc 本体,还要找到它的运行时库、头文件、预处理器以及其他编译辅助工具,任何一个缺了都装不上。
在 CentOS 7 上,yum 安装 gcc 时会解析出这样一条依赖链:gcc 依赖 cpp 和 libgcc,cpp 依赖 mpfr,mpfr 依赖 gmp;如果你想编译 C++ 程序,还要装 gcc-c++,它又依赖 libstdc++-devel;而 glibc-devel 依赖 glibc-headers,glibc-headers 又依赖 kernel-headers。这些包之间的依赖关系是成网状交织的,不是简单的一根线性链。我见过最典型的情况是手动下载了 gcc、gcc-c++、cpp 三个包,rpm 安装时报 libmpc.so.2 缺失,然后才顺着错误信息一层层往下找,最后补了 gmp、mpfr、libmpc、glibc-devel 等七八个包才算完。
所以离线安装 gcc 的第一原则是:不要靠猜,不要靠网上某篇文章的固定清单,要用工具自动解析依赖。后面章节给的下载命令,本质上就是帮你把这条依赖链完整地"搬"到内网去。
1.2 三条路线,按场景选择
按照我这几年的实操经验,离线装 gcc 有三条成熟路线,分别适合不同场景。
第一条是拿 CentOS 7 的 DVD ISO 挂载成本地 yum 源。这是最省事的方式,前提是你手头有 CentOS-7-x86_64-DVD 系列镜像,而且对 gcc 版本没有要求。DVD 镜像的 Packages 目录里自带了 gcc、gcc-c++、make 和绝大部分依赖包,挂载后直接当官方源用就行。缺点是版本被锁死在 4.8.5,你没法用它装 GCC 8 或 GCC 9,另外如果还缺个别包,ISO 里不一定都有。
第二条是在联网机器上用 yumdownloader --resolve 下载 gcc 依赖包,打包拷进内网后用 rpm -Uvh *.rpm 安装。这条路适合临时应急,我早期也这么干过。它的问题在于 rpm 命令不会像 yum 那样自动做完整的依赖排序和校验,一旦包的顺序不对或者缺一两个依赖,报错信息会让人很崩溃。
第三条是在联网机器上用 repotrack 全量拉取 gcc 及其所有依赖,拷进内网后先用 createrepo 生成仓库元数据,再配置一个本地 yum 源,最后用 yum 安装。这是我最推荐的方式,等于把"在线安装"的体验完整复刻到了离线环境。yum 会自动处理依赖排序、版本冲突和校验,几乎不会出现莫名其妙的报错。后面我会把这条路线的每一步都写清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 依赖拆解:先看清楚要搬哪些包
2.1 gcc 依赖图谱里都有谁
我先把在 CentOS 7 上实测会拉取的核心依赖包列出来,这张表能帮你对"离线包应该有多大、大概有多少个文件"有个基本概念。
| 包名 | CentOS 7 常见版本 | 作用 |
|---|---|---|
| gcc | 4.8.5 | C 编译器主体 |
| gcc-c++ | 4.8.5 | C++ 编译前端,编译 C++ 必须 |
| cpp | 4.8.5 | C 预处理器,gcc 编译时调用 |
| libgcc | 4.8.5 | 底层运行时库 |
| libstdc++-devel | 4.8.5 | C++ 标准库头文件与静态库 |
| glibc-devel | 2.17 | C 标准库开发头文件 |
| glibc-headers | 2.17 | 系统与内核头文件 |
| kernel-headers | 3.10.0 | Linux 内核头文件 |
| gmp | 6.0.0 | 任意精度整数运算库 |
| mpfr | 3.1.1 | 高精度浮点运算库 |
| libmpc | 1.0.1 | 复数运算库 |
| binutils | 2.27 | 汇编器与链接器 |
这里要特别提醒,这张表只是"最小观察视角"。实际用 repotrack 拉取时,还会带上 elfutils-libelf、pcre、zlib 之类更深层的传递依赖,所以下载目录里有三十到四十个 rpm 文件都很正常。我见过有人照着网上的固定清单手动下载,结果漏了某个小包,现场折腾两小时。这种事情真的别干,让工具自动解析比任何经验清单都可靠。
2.2 用 yum 自己生成依赖清单
如果你还是想提前确认到底要下载哪些包,有两个命令可以帮你手工生成依赖清单,操作同样在联网机器上执行。
第一个是 yum deplist,它会把某个包的直接和间接依赖全部列出来:
bash复制yum deplist gcc
第二个是 rpm -qpR,它针对具体 rpm 文件查询 Requires 字段。比如你先下载一个 gcc 的 rpm,然后查询它的依赖:
bash复制yumdownloader gcc
rpm -qpR gcc-4.8.5-*.x86_64.rpm
这两个命令给我的最大价值不是真的去手动下载,而是排查"为什么 yum 解析出这么多包"时,能快速理清依赖关系。比如你看到一个包被反复依赖,就知道它是链条上的关键节点,搬运时绝对不能漏。
2.3 yumdownloader 与 repotrack 怎么选
工具选型是离线安装里最容易踩坑的环节。yumdownloader 和 repotrack 都来自 yum-utils 包,联网机器上执行一句 yum install -y yum-utils 就都有了,但行为差异很大。
yumdownloader --resolve 的逻辑是:下载指定包,并下载当前系统环境里"还未安装"的依赖包。也就是说,如果下载机器上已经装了某个依赖,它就不会下载那个包。这在全新最小化系统上下载没问题,但如果你的联网机器平时装了五花八门的开发工具,搬运到内网后就可能发现缺了它本来就有、但内网没有的依赖包。排查起来非常痛苦。
repotrack 的逻辑更简单粗暴:把指定包和它的全部依赖一股脑拉下来,不管系统里是否已经安装。这样下载目录里的包会更全、体积更大,但换来的是"搬到内网几乎不会缺包"的确定性。我的习惯一直是优先用 repotrack,宁可多带几个用不上的 rpm,也不能现场缺一个关键依赖。多出来的包对 yum 没有任何坏处,反而能在装其他软件时救急。
3. 实操全流程:从下载机到内网服务器
3.1 下载机准备与系统一致性检查
在整个流程开始之前,先确认三件事:下载机能访问外网、下载机与内网机器的系统版本同属 CentOS 7.x、两者 CPU 架构一致。架构这点容易被忽略,x86_64 的 rpm 包装到 aarch64 机器上会直接报错 "wrong architecture",所以下载前一定先执行 uname -m 确认。
推荐用一台最小化安装的 CentOS 7 作为下载机,不要在你日常开发的主力机器上下。开发机上往往装了高版本 gcc、各种库和自定义 repo,yum 解析出的依赖集合可能跟内网环境差异很大,打包过去容易引入本不该出现的包。我踩过一次坑:开发机上某个自定义源把 glibc-headers 指向了更新版本,打包到内网后跟系统自带的 kernel 版本对不上,折腾了很久。
下载机准备好之后,先安装需要用到的工具:
bash复制yum install -y yum-utils createrepo
createrepo 是用来生成本地仓库元数据的,这一步就一起装了,省得后面发现内网缺它还得再补。
然后创建干净的下载目录:
bash复制mkdir -p /opt/gcc-offline
cd /opt/gcc-offline
3.2 两条下载命令的执行与验证
我首推 repotrack 全量拉取。在下载机执行:
bash复制repotrack gcc gcc-c++ make -p /opt/gcc-offline
把 make 也带上是因为离线装完 gcc 十有八九要编译源码,提前备好编译工具链是必须的。如果你还需要 autoconf、automake、libtool,可以追加在同一行,一次全拉下来。
如果你想用 yumdownloader 方式,命令写法如下:
bash复制yumdownloader --resolve --destdir=/opt/gcc-offline gcc gcc-c++ make
下载完成后检查一下目录内容:
bash复制ls -lh /opt/gcc-offline/
正常会看到几十个 rpm 文件,总大小一般在 150MB 到 200MB 之间。看到这个体量别慌,大部分是 glibc-devel、glibc-headers 和 kernel-headers 这些头文件包,不是浪费。
接下来做一个关键验证——检查下载的包有没有缺依赖。rpm 本身提供了校验命令:
bash复制cd /opt/gcc-offline
for f in *.rpm; do
rpm -K "$f"
done
如果所有包都显示 OK,说明 GPG 签名和完整性没问题。更进一步的依赖完整性检查可以这样做:
bash复制cd /opt/gcc-offline
rpm -Uvh --test *.rpm
--test 参数只做依赖模拟,不真正安装。如果它不报缺失依赖,这批包就是完整的,可以放心打包搬运。这一步我用得特别多,等于在下载机端提前发现 90% 的遗漏问题。
最后压缩打包:
bash复制cd /opt
tar czf gcc-offline.tar.gz gcc-offline/
拷贝到内网机器时,用 scp、U 盘、内网共享目录都行,看你的环境支持哪种。
3.3 rpm 直装路线及其局限性
到了内网机器,先把包解压出来:
bash复制mkdir -p /opt/gcc-offline
tar xzf gcc-offline.tar.gz -C /opt
第一种安装方式是 rpm 直装:
bash复制cd /opt/gcc-offline
rpm -Uvh *.rpm
在依赖包完整的前提下,大多数 CentOS 7 机器上这条命令能顺利跑完。rpm 内部有依赖解析机制,即使文件名的排布不是最优顺序,它也能自动重排。但我必须说清楚这条路线的局限性:如果依赖包不完整、内核版本不匹配,或者系统里已有更高版本的 glibc、libgcc,rpm 直装会立刻报错中断,而且报错信息往往不是"缺哪个包",而是"冲突"或"已被替换",排查路径很长。
所以我把 rpm 直装定位成"应急手段",适合一次性操作、不追求长期维护的环境。如果你希望内网以后还能方便地装其他软件,强烈建议用下面这节的方法。
3.4 createrepo 建本地源安装路线
在《/opt/gcc-offline》目录里生成仓库元数据,让 yum 把这个目录当作一个软件源来识别。
先确认内网机器上有没有 createrepo:
bash复制which createrepo
如果提示没有找到,说明下载机需要把 createrepo 也拉进来。在下载机执行:
bash复制repotrack createrepo -p /opt/gcc-offline
把生成的 createrepo 相关 rpm 一起拷贝到内网,然后安装。createrepo 这个工具本身依赖不多,一般几个包就能搞定。
内网机器装好 createrepo 后,在 rpm 目录下执行:
bash复制cd /opt/gcc-offline
createrepo .
看到 "Directory walk started / Sqlite DBs starting" 之类的输出就成功了。此时目录里会出现一个 repodata 子目录,这就是 yum 需要的仓库元数据。
然后是配置本地源文件:
bash复制vim /etc/yum.repos.d/local.repo
写入以下内容:
code复制[local]
name=Local RPM Repository
baseurl=file:///opt/gcc-offline
enabled=1
gpgcheck=0
gpgcheck 建议设成 0,因为离线 rpm 包通常没有导入对应的 GPG 公钥,开着反而会报签名验证失败。如果你对安全性有要求,可以额外把公钥拷进去并配置 gpgkey 路径,但一般内网环境真的不需要。
清理缓存并刷新源:
bash复制yum clean all
yum makecache
最后执行安装:
bash复制yum install -y gcc gcc-c++ make --disablerepo="*" --enablerepo=local
--disablerepo="*" 是为了彻底屏蔽系统自带的网络源,只使用本地 local 源。这个参数在离线环境里非常重要,否则 yum 会尝试连接外网镜像,白白等半天超时。
安装完成后验证:
bash复制gcc --version
g++ --version
make --version
正常会看到 gcc 4.8.5、g++ 4.8.5、GNU Make 3.82 的输出,这就代表离线安装成功。之后再编译任何 C/C++ 源码,这台机器都具备了完整条件。
3.5 DVD ISO 本地源这条捷径
如果你的离线环境里有 CentOS 7 的 DVD ISO 镜像,或者干脆就是安装光盘,那还有个更快的方案。把这个 ISO 挂载到本地目录,就能直接把它当 yum 源用。
挂载命令:
bash复制mkdir -p /mnt/cdrom
mount -o loop /path/to/CentOS-7-x86_64-DVD-1810.iso /mnt/cdrom
然后写一个独立 repo 文件:
code复制[cdrom]
name=CentOS DVD
baseurl=file:///mnt/cdrom
gpgcheck=0
enabled=1
接着:
bash复制yum clean all
yum install -y gcc gcc-c++ make --disablerepo="*" --enablerepo=cdrom
DVD 镜像里的包非常全,除了 gcc,还有 vim、gdb、net-tools 这些常用工具,覆盖绝大多数离线运维场景。不过它的 gcc 同样是 4.8.5,如果你想装更新版本,还是回到 repotrack 方案。另外要注意 ISO 文件后续使用中不要被移动或卸载,否则 baseurl 失效会导致 yum 报错。
4. 高频问题与避坑实录
4.1 高频问题速查表
我把实际工作中遇到的高频问题整理成一张速查表,方便你到现场排查时快速定位。
| 现象 | 常见原因 | 处理办法 |
|---|---|---|
| rpm -Uvh *.rpm 报 xxx is needed by yyy | 依赖包没下全 | 重新用 repotrack 全量下载,或用 createrepo 建本地源后由 yum 安装 |
| yum install 长时间卡住后报 Failed to connect | 默认源指向外网,离线环境不可达 | 安装时加 --disablerepo="*" --enablerepo=local 强制走本地源 |
| yum 提示 Protected multilib versions | 系统存在 32 位/64 位库冲突 | 先 yum clean all,再限定本地源安装,不要覆盖系统自带的关键库 |
| gcc --version 显示 4.8.5 但实际装了新版本 | 符号链接或 PATH 未指向新版本 | 用 alternatives --config gcc 切换,或检查 /usr/local/bin 是否排在 PATH 前面 |
| mount ISO 报 not a directory | 挂载点目录不存在或忘记 -o loop | mkdir -p /mnt/cdrom 后用 mount -o loop 重新挂载 |
| createrepo 命令找不到 | 没装 createrepo 包 | 在下载机 repotrack createrepo,拷入内网安装后再执行 |
4.2 依赖下全了仍然报缺?问题出在安装方式
有一种情况特别气人:明明下载机端 rpm -Uvh --test *.rpm 已经通过,到内网跑同样的命令却报缺失。原因多半不是真正缺包,而是内网机器上已经装了某些"旧版本"或"被修改过"的包,导致 rpm 在解析依赖时看到的包集合不一致。
举个真实例子:下载机是干净系统,生成的依赖集合里包含 libstdc++-devel 4.8.5。内网机器上如果已经装了从某个第三方源来的 libstdc++-devel 4.8.7,rpm 会因为版本冲突直接停止整个安装流程,而不是忽略那个包。这种时候用 rpm -ivh 单独装某个包往往会加剧冲突,最好的出路就是走 createrepo 建本地源,让 yum 结合已安装情况做整体判断。
我现在的习惯是:只要现场条件允许,一律用 yum 本地源安装。原因只有一个——yum 的依赖求解器比 rpm 直装聪明得多,它能自动规避冲突、跳过已满足的依赖,把人为判断带来的出错概率降到最低。
4.3 关于覆盖系统包版本的警告
这是离线安装里最需要敬畏的一点。glibc、libgcc 是系统最底层的运行时库,几乎所有程序都依赖它们。如果你把离线包里的 glibc 或 libgcc 拿去覆盖内网机器上已有的版本,一旦版本不兼容,可能导致 sshd 起不来、命令全部崩溃这类严重事故。
我在生产环境处理过一起事故:同事为了装某个新软件,从下载机拉了一批 rpm,里头恰好有更高版本的 glibc,他直接 rpm -Uvh 把它装上了,结果系统里的动态链接器版本不匹配,第二天服务全挂。所以这里有一条红线:如果内网机器上已有 glibc、libgcc 等基础库的版本比离线包更高或来自不同源,宁可只装 gcc、gcc-c++、cpp、make 这些业务层包,也不要强行覆盖基础库。真有必要升级 glibc,先找测试机验证完整流程,再挑维护窗口操作。
4.4 下载机与内网机的版本差异处理
下载机和内网机虽然都是 CentOS 7,但具体小版本可能不同,比如下载机是 7.9,内网是 7.4。这种情况在依赖解析时会出现一个细节差异:kernel-headers 的版本可能跟着各自内核版本走。内网机器安装 kernel-headers 时,如果当前运行的内核版本比 rpm 包里的 header 版本低,rpm 检查时往往不会拦,但真正编译内核模块时可能报错。
策略是这样的:如果内网机器的内核版本固定,可以在打包前手动把 kernel-headers 过滤掉,改用 DVD ISO 里的对应版本;如果嫌麻烦,直接用 yum 本地源方式安装,yum 会在满足依赖的情况下跳过已经存在的 kernel-headers,不会强行替换。总体上,下载机保持干净、与内网同系统版本,能避免大多数这类不一致问题。
最后聊一点我自己的体会。离线安装 gcc 这件事,九成失败都栽在"依赖差一两个"上,不是技术难,是确定性差。所以我现在做离线包的习惯一直很简单:下载机上用 repotrack 全量拉,搬运时宁可多带二十个用不上的 rpm,也不要少一个关键的;安装时一律走 createrepo 加 yum 本地源,让 yum 去做排序和校验,把人的失误概率压到最低。这套方法的好处是能复用,以后离线装 node、装 Python 依赖、装其他编译工具链,都可以套用同一个思路——先拆依赖,再全量下载,最后本地源安装。按这个节奏走,基本不会翻车。
