ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

gcc离线安装完整指南:从RPM收集到本地yum源批量部署

gcc离线安装完整指南:从RPM收集到本地yum源批量部署 简介在无法连接互联网的服务器、学校机房或边缘设备中搭建编译器工具链常因缺少依赖而失败。这份面向红帽系列Linux 6系统的GCC离线安装包共包含十二个文件十一份软件安装包与一份安装脚本。其中十一份安装包覆盖基础编译器、C编译器、预处理程序、系统头文件与内核头文件等关键组件并已处理版本匹配关系安装脚本则负责自动检查环境、解包并完成安装使运维人员无需逐条执行命令即可完成部署。软件包整体压缩后约二十四兆字节体积小巧便于使用移动介质拷入内网。目前已有五百五十六人学习使用特别适合需要在内网或受限网络环境中搭建开发环境的系统管理员与开发者同时通过查看安装包内各文件的依赖结构也能帮助初学者理解编译器集合的组成和软件包管理的基本思路。1. gcc 离线安装一台内网裸机的刚需前几天帮某公司机房做环境交付机器是全新的最小化安装的 Linux没有外网权限gcc --version直接提示 command not found而所有业务系统都需要编译源码。更麻烦的是不是一台机器而是三十台同配置的机器都得统一装好同样的工具链。我手头没有光盘也没有内网 yum 镜像只能靠一个离线包把整个 gcc 编译链端过去。这就是这类“gcc 离线安装包脚本”资源的典型用途把 gcc 本体以及所有依赖打包成 RPM 集合再通过脚本在内网批量安装。适合运维工程师、实施人员、测试环境搭建者以及所有被内外网隔离折磨过的人。2. 在联网机器上收集 rpmyumdownloader 与 reposync 的取舍2.1 为什么不能只拷贝 gcc 的单个 rpm很多人第一次做离线安装会直接在一台有网的机器上yum install -y gcc然后跑到/var/cache/yum里翻那个 RPM 文件拷到内网去装。结果是rpm -ivh gcc-xxx.rpm报一堆依赖缺失libmpc.so.3()(64bit) is needed、libmpfr.so.4()(64bit) is needed、cpp x.x.x is needed。gcc 从来不是一个孤立软件包它跟 cpp、binutils、glibc-devel、gmp、mpfr、mpc、isl 这些包绑定得很紧只拷本体等于只带了发动机没带轮胎。所以第一步不是下载而是先想清楚需要哪些包。在一个标准的 x86_64 的 CentOS/RHEL 类系统上gcc 的工具链至少包含这些gcc、gcc-c、gcc-gfortran可选、cpp、binutils、glibc-devel、libstdc-devel、libgcc、libgomp以及被依赖的gmp、mpfr、mpc、isl。这里还不算 kernel-headers如果机器要编译内核模块还得补上kernel-devel但那是另一个话题不是 gcc 的基本盘。2.2 用 yumdownloader 收集指定包及自动依赖我的常规做法是用yumdownloader它只下载不安装也不会去动当前系统的包状态。优点是干净、可控缺点是必须手动把依赖列全漏一个在内网就会卡住。下面的命令在联网机器上执行# 先安装 yum-utils提供 yumdownloader 命令 yum install -y yum-utils # 建一个目录后面所有 rpm 都放这里 mkdir -p /tmp/gcc-offline-pkgs # 下载 gcc 及常用工具链加 --resolve 自动解析依赖 yumdownloader --destdir/tmp/gcc-offline-pkgs --resolve \ gcc gcc-c gcc-gfortran cpp binutils \ glibc-devel libstdc-devel libgcc libgomp \ gmp-devel mpfr-devel mpc-devel isl-devel这里--resolve是关键参数它会像yum install一样做依赖解析把缺的依赖包一起拉到 destdir 里。跟直接yum install不同它不会安装只负责下载。--destdir指定输出目录建议用绝对路径后面做本地 repo 时路径不容易写错。gcc-gfortran如果业务不需要 Fortran 编译器完全可以去掉能少拉一堆依赖。gmp-devel、mpfr-devel这一类带-devel的包主要是给以后要手工编译其他软件时提供头文件和.so软链如果纯装 gcc 自身有些并非强制但为了一个包一次到位我一般都会收进去。2.3 用 reposync 拉整个 base 仓库的备选方案如果内网机器数量多、后续还要装别的软件yumdownloader就不够用了因为你每装一个软件都得重新收集一次。这种时候建议改用reposync直接把远程的 base/updates 仓库同步到本地内网服务器再配合createrepo生成仓库元数据等于在内网搭建了一个迷你 yum 源。命令如下# 安装 createrepo 和 yum-utils yum install -y createrepo yum-utils # 只同步 base 仓库到本地目录带元数据 reposync -p /tmp/yum-mirror --repoidbase --download-metadata # 为镜像目录生成仓库索引 createrepo /tmp/yum-mirror--repoidbase指定同步哪个仓库可以用yum repolist查看当前系统启用的仓库 ID。--download-metadata会同步 primary、filelists 这类元数据生成后就能直接做成内网 repo 源。注意reposync会把整个仓库几十 GB 都拉下来如果只是装个 gcc 没必要但它确实是一劳永逸的方案。如果磁盘紧张还可以用--filter或者只同步几个特定包但那样语法会复杂不少不适合新手折腾。2.4 收集完成后的自检清单下载完之后别急着打包先做一遍清点不然到了内网才发现缺包来回折腾很浪费时间。我一般会跑这几条检查# 检查数量、大小和架构 ls -lh /tmp/gcc-offline-pkgs/*.rpm | wc -l du -sh /tmp/gcc-offline-pkgs # 确认都是 x86_64没有混入 i686 或 noarch rpm -qp --qf %{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n /tmp/gcc-offline-pkgs/*.rpm | grep -v x86_64\|noarch || echo 架构全部一致 # 检查签名信息确保没拉坏包 rpm -K /tmp/gcc-offline-pkgs/*.rpm 2/dev/null | grep -i OK | wc -l这个检查非常关键。之前我遇到过一台 64 位系统yumdownloader 因为某个依赖的编译环境问题意外把 i686 的 glibc-devel 也拉了进来内网安装时 RPM 对架构非常敏感i686 和 x86_64 同时出现会导致依赖判断错乱。所以检查架构那一步最好每次都跑。另外用rpm -K校验签名时如果系统没有导入对应 GPG 公钥会提示 NOKEY那种情况只要确认 sha256 校验通过即可不必过分紧张.3. 安装脚本设计本地 repo 源与幂等安装3.1 rpm -Uvh 直接硬装为什么容易翻车把 rpm 包拷到内网后最常见的主观想法是rpm -Uvh /tmp/pkgs/*.rpm一把梭。但这样做的失败率极高因为rpm -Uvh不会做依赖解析它只是逐个安装遇到依赖缺失就报错退出。更麻烦的是gcc 依赖链里有循环情况比如gcc依赖mpc、mpc依赖mpfr、mpfr依赖gmp表面看是一长串但实际用rpm -Uvh *.rpm一起装时RPM 内部会尝试解决顺序问题多数时候能成但一旦其中一个包版本未满足另外包的精确要求整个事务就会回滚连已装好的也全部撤销。运气坏的时候还会把 glibc 相关包动到弄完系统直接起不来那已经不是翻车是翻沟里了。所以我给配套脚本设计的核心思路是先建本地 yum 仓库再让 yum 去解决依赖。yum 是完整的依赖解析器比人肉排序可靠得多。3.2 用 createrepo 把 rpm 目录变成 yum 源在离线机器上先把包解压到固定目录然后用createrepo生成元数据。这里有一个细节离线机器的软件源里必须提前有createrepo这个工具否则你连源都建不出来。所以在收集阶段我就把createrepo和它的依赖python-deltarpm、libxml2-python加进了 yumdownloader 的清单里。# 假设离线包在 /opt/gcc-pkgs 下 cd /opt/gcc-pkgs # 生成 repodata 目录 createrepo . # 写 repo 文件到 /etc/yum.repos.d/ cat /etc/yum.repos.d/gcc-local.repo EOF [gcc-local] nameGCC Local Repository baseurlfile:///opt/gcc-pkgs enabled1 gpgcheck0 EOF # 清缓存并验证 repo 是否生效 yum clean all yum repolistcreaterepo .会在当前目录生成repodata/文件夹里面是 primary.xml.gz 等元数据。repo 文件里的gpgcheck0是因为离线包没有对应的 GPG 公钥如果设成 1yum 会拒绝安装并报错。file://协议指向本地目录注意路径必须和包实际位置一致写成file:///opt/gcc-pkgs是三个斜杠少了就加载不到。3.3 脚本幂等安装、日志记录与退出码写安装脚本时我坚持三个原则可以重复跑、跑完有日志、失败有明确退出码。很多离线安装脚本都是写死的命令装过一次再跑就乱套没有日志排查时全靠猜这种脚本我不愿意交付给别人。下面这个是精简版的脚本骨架#!/bin/bash # gcc 离线安装脚本 # 用法: ./install_gcc_offline.sh [pkgs_dir] PKG_DIR${1:-/opt/gcc-pkgs} LOG_FILE/var/log/gcc-offline-install.log # 判断是否已安装过 gcc if rpm -q gcc /dev/null 21; then echo [$(date)] gcc 已安装跳过 | tee -a $LOG_FILE exit 0 fi # 检查 repo 文件是否存在 if [ ! -f /etc/yum.repos.d/gcc-local.repo ]; then echo [$(date)] 未找到 gcc-local.repo请先配置本地源 | tee -a $LOG_FILE exit 1 fi # 用 yum 安装自动解析依赖 yum install -y gcc gcc-c gcc-gfortran 21 | tee -a $LOG_FILE # 验证安装结果 if rpm -q gcc gcc-c /dev/null 21; then echo [$(date)] gcc 工具链安装成功 | tee -a $LOG_FILE exit 0 else echo [$(date)] 安装失败检查 /var/log/gcc-offline-install.log | tee -a $LOG_FILE exit 1 fi这个脚本的逻辑很直白开头用rpm -q gcc判断是否已安装避免重复装一遍中间用yum install而不是rpm -Uvh让 yum 解析本地 repo 里的依赖最后再验证一次 gcc 是否真的可用。tee -a把命令输出同时打到终端和日志文件这样出问题可以回看日志。${1:-/opt/gcc-pkgs}是给路径留了一个参数位默认值是/opt/gcc-pkgs如果你把包解压到别处执行时把路径传进去就行。3.4 批量部署时的参数化修改那台服务器是三十台机器我一般不会一台台跑而是配合 ansible 的 copy 模块把 rpms 目录和脚本分发下去再批量执行。脚本里唯一要改的就是PKG_DIR的默认路径如果内网每台机器的授权目录不同建议改成从配置文件读路径而不是硬编码。还一个重要参数是yum install那一行的包名列表如果不需要 gfortran删掉即可。这个脚本尽量保持单文件、零依赖因为它本身就是离线安装用的如果它再依赖别的东西就成鸡生蛋问题。4. 依赖链拆解gcc 从 rpm 到源码编译所需的完整包清单4.1 gcc 的依赖树底层库的层级关系gcc 的依赖不像普通软件那样只是一条线而是分三层。最底层是被 gcc 运行库直接调用的数学库包括gmp大整数运算、mpfr浮点运算、mpc复数运算这三个在 gcc 编译阶段就必须存在它们的.so文件会被 gcc 自身链接。再上层是isl这是一个整数线性规划库GCC 在做循环优化和并行优化时依赖它。最上面才是gcc、cpp、gcc-c这些编译前端。如果只是用yum install gccyum 会把这些底层库自动拉进来但离线手工收集时很多人会漏掉isl因为它在老的 CentOS 6 里甚至不是独立包而是被编进 gcc 的内部目录。到了 CentOS 7/8 上才把它拆成独立isl包。所以收集时不要想当然按文档里常见的依赖列表来最好用yumdownloader --resolve自动抓抓完再用rpm -qpR逐个验证一遍.so是否被满足、依赖包是否都在本地。4.2 编译期依赖与运行期依赖的差异另一个容易错的重点是区分编译期和运行期。glibc-devel提供的是stdio.h、stdlib.h这些头文件它是编译 C 程序时才需要的运行时程序用的是/lib64/libc.so.6这是 glibc 包提供的。所以在离线机器上如果你只装了 gcc 而没装glibc-develgcc -v能正常输出版本号但编译hello.c时会报stdio.h: No such file or directory。这不是 gcc 坏了是缺开发头文件。同样的道理适用于libstdc-devel。gcc-c包本身含 g 编译器和基础运行库但标准模板库STL的头文件在libstdc-devel里。很多人在内网编译 C 项目时报iostream: No such file就是他只收了gcc-c忘了libstdc-devel。至于编译后的程序跑到另一台机器上还需要什么那是另一套ldd检查的思路不是离线安装 gcc 重点。4.3 版本匹配不能用新版 RPM 包覆盖旧系统在联网机器上收集 rpm 时有个隐含风险联网机器如果比内网机器系统版本新比如联网机是 CentOS 8内网机是 CentOS 7拉下来的 gcc RPM 拿到内网是装不上的因为 RPM 打包时依赖的 glibc 版本高于内网系统自带的东西。装的时候会直接报libc.so.6(GLIBC_2.18)(64bit) is needed然后拒绝安装。收集安装包前先确认好目标内网系统的版本最好的做法是找一台和离线机器完全同版本、同小版本的联网机器来采集或者在内网里随便选一台同样系统的机器先临时联网用它自己的 yum 拉包再断网。实在找不到同版本的也可以退而求其次用--releasever指定系统版本去收集比如# 在联网机器上指定系统版本收集防止拉到太新的包 yumdownloader --destdir/tmp/gcc-pkgs --releasever7 --resolve \ gcc gcc-c glibc-devel libstdc-devel--releasever7会让 yum 以 CentOS 7 的仓库视图去解析依赖这样收集到的 RPM 版本匹配 CentOS 7。如果连这个参数都不加默认用你当前系统的版本装到低版本系统上就会出现上面的 GLIBC 错误。常见依赖包对应系统版本如下表格可以在写离线部署文档时直接抄用目标系统gcc 默认版本glibc 版本注意点CentOS 74.8.52.17gcc 较老C11 支持不完整CentOS 88.5.02.28支持 C17注意 repo 源差异Ubuntu 18.047.5.02.27包格式为 deb不适用于 rpm 脚本5. 离线安装避坑五条翻车经历与排查方法5.1 现象rpm -ivh 报内存不足安装到一半被杀原因同一时间把所有 rpm 用一条命令提交RPM 会为每个包都做事务检查内存占用叠加在低配的 1G 内存机器上直接 OOM。这是我在某项目的 512M 小机器上踩过的坑三十多个包一起rpm -ivh进度到一半进程被系统 kill连已装的包也全部回滚。解决不要一次性rpm -Uvh *.rpm改用yum installyum 是逐个处理事务的内存峰值低得多。如果一定要用 rpm把安装批次拆成每次 5-10 个包且先装底层库再装 gcc 本体。5.2 现象yum install gcc提示 Nothing to do但 gcc 命令确实不存在原因本地 repo 配置文件baseurl指向的路径不对或者目录里没有repodata子目录。yum 在 repo 加载失败时不会明确报错而是把该 repo 静默忽略最后 Nothing to do。解决用yum repolist看看gcc-local是否出现在启用的仓库列表里。没出现就检查 repo 文件里的路径和file://写法路径尾部不要加多余的/。另外当心 tar 解压时如果带着顶层目录包实际在/opt/gcc-pkgs/gcc-pkgs/下repo 指向/opt/gcc-pkgs就空了。5.3 现象gcc 能编译 C 程序但编译 C 时报找不到 iostream原因gcc 本体和 gcc-c 是分离的gcc包只带 C 编译器和基础库。g命令可能根本不存在或者存在但缺少 STL 头文件。解决确认离线包清单里有gcc-c和libstdc-devel。验证方法rpm -q gcc-c如果已装但仍然报错检查g -print-search-dirs输出里的 include 路径是否有对应的/usr/include/c目录。5.4 现象编译时报cannot find crtbegin.o或crt1.o原因这是 binutils 和 glibc-devel 的问题不是 gcc 本体。crt1.o和crtbegin.o分别位于 glibc-devel 和 gcc 包内部两者缺失就会缺链接启动文件。有人为了省空间故意去掉 glibc-devel结果就是这个错。解决把glibc-devel加进包里并确认 gcc 安装目录下lib/gcc/x86_64-redhat-linux/{version}/crtbegin.o确实存在。有时候还需要binutils的ld命令可用离线时记得把binutils包也带上。5.5 现象脚本第二次运行时把 gcc 降级到了旧版本原因脚本里用了rpm -Uvh直接装旧版 RPM或者yum install时本地 repo 里的包版本低于已装版本yum 默认不会降级但 rpm -Uvh 会强制覆盖为低版本。内网机器如果被升级过再跑脚本就会出现版本倒退。解决脚本开头加rpm -q gcc判断已装则直接跳过。我在脚本骨架里已经写了这个逻辑执行exit 0前会先检查避免误伤。6. 装完别急着收工版本验证与一键卸载回退6.1 三步验证编译环境是否真正可用装完之后光看rpm -qa | grep gcc不算完编译器这种工具必须跑一次真实编译才算验证通过。我一般固定走这三个步骤缺一不可# 第一步确认 gcc/g 版本在 PATH 里且版本匹配 gcc --version g --version # 第二步写最小 C 程序编译运行 echo #include stdio.h /tmp/test.c echo int main() { printf(gcc offline ok\n); return 0; } /tmp/test.c gcc /tmp/test.c -o /tmp/test_c /tmp/test_c # 第三步写最小 C 程序编译运行验证 STL 头文件可用 echo #include iostream /tmp/test.cpp echo int main() { std::cout g offline ok std::endl; return 0; } /tmp/test.cpp g /tmp/test.cpp -o /tmp/test_cpp /tmp/test_cpp注意第二步和第三步必须都跑很多离线安装 C 的验证能过死在 C 上。gcc --version看到的版本号只说明编译器本身能运行不代表头文件、链接库都齐全。跑到std::cout这一行才把libstdc-devel和头文件路径全部验证到了。6.2 留一条回退路径卸载脚本与包清单快照离线安装最怕的不是装不上而是装到一半系统环境被搞乱。以前我在一台机器上装新版本 gcc它把系统原有的libstdc.so.6覆盖掉结果所有依赖旧版本 ABI 的程序全部启动报错那真是灾难。从那以后我每次离线安装前都会先执行一次包清单快照把改动前的 rpmdb 状态保存下来安装完成后再生成一份差异清单留着做回退依据# 安装前快照 rpm -qa --qf %{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n /tmp/rpmdb-before.txt # 安装后快照 rpm -qa --qf %{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n /tmp/rpmdb-after.txt # 找出新增和升级的包 diff -u /tmp/rpmdb-before.txt /tmp/rpmdb-after.txt | grep ^ | cut -c2-把 diff 出来的结果送到卸载脚本里变成rpm -e命令列表。注意卸载顺序要反过来先卸载 gcc 和 gcc-c再考虑动底层库但底层库尽量不要回滚因为其他已装的软件可能已经依赖新版本。这套快照方案不能保证百分百还原但至少给了你一份明确的改动记录排查和回退都有迹可循。另外一个小技巧安装完成之后可以把gcc --version的完整输出、g --version的完整输出连同编译测试的返回码一起写进交付文档里这样后面接手的人一眼就能看出工具链状态不用自己再搭一套环境重新验证一遍。从那以后我每次部署完 gcc 都强制走一遍这个三步验证流程并把版本号记成文件放到/etc/gcc-install-info.txt里这样后面再出问题至少不用猜测机器上装的是哪个版本希望这套方法和避坑记录能帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表