在内网环境里装软件,是我接手运维之后碰到的第一件怵头事。公司内部系统跟外网物理隔离,yum install、apt-get install 全都失效,第一次遇到时我在工位上琢磨了大半天,最后才发现问题出在思路上:离线安装不是不能用包管理器,而是要把“在线下载”和“离线安装”拆成两步走。这篇文章专门聊 Linux 内网环境无法访问外网时怎么装程序,从依赖包怎么获取、怎么传进内网、怎么本地建源,到编译安装和容器镜像兜底,全都按实操顺序过一遍。适合刚接触内网环境的运维新人,也适合需要维护生产离线环境的同学收藏备用。
1. 内网环境装软件,先搞清楚难点在哪
1.1 没外网时包管理器为什么“失灵”
Linux 下的包管理器,不管是 CentOS/RHEL 系的 yum、dnf,还是 Ubuntu/Debian 系的 apt,设计思路都是“从远程仓库拉取软件包和依赖,然后在本机统一安装”。这套模式非常方便,只要机器能联网,一条命令就能把依赖关系理得清清楚楚。
但内网环境一出现,这套机制就断了“源”。yum 默认指向的 mirrorlist 在公网上,apt 的 sources.list 也指向公网仓库,网络不通时这些命令会一直卡在超时状态,最后报一串 Could not resolve host 或者 Connection timed out。很多人误以为这时候只能手动找 tar.gz 编译,其实不是。包管理器本身还能用,只是需要我们把“远程仓库”变成“本地仓库”。
这里有个关键认知:离线安装的本质,是把“下载”这个动作提前放到一台能上网的机器上完成,然后把下载好的软件包搬到内网机器,让本机的包管理器通过本地源完成安装和依赖校验。理解了这一点,后面的操作就顺理成章了。
1.2 离线安装的几条常规路线
针对不同的场景,我一般会把这几种方案排在候选列表里:
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| yumdownloader/apt-get download 拉包 | 目标机器系统版本明确,软件包数量可控 | 操作简单,依赖关系自动处理 | 需要一台同版本可联网机器 |
| repotrack 拉全依赖 | 软件包依赖较多,担心漏包 | 能递归解析全部依赖 | 下载量大,耗时较长 |
| 本地 yum/apt 源 | 内网有多台机器需要装同样的软件 | 一劳永逸,后续安装体验接近在线 | 需要维护仓库目录,占用磁盘 |
| 源码编译安装 | 特定软件没有对应发行版包 | 灵活,可自定义编译参数 | 需要解决编译依赖,周期长 |
| Docker 离线镜像 | 应用本身容器化 | 依赖隔离,迁移方便 | 内网需要安装 Docker 环境 |
实际项目中,我一般优先用包管理器拉包的方式,因为 rpm 和 deb 包能参与本机的依赖校验,直接 dpkg -i 或 rpm -ivh 安装时,系统会提示缺什么依赖,后续补包更有针对性。源码编译通常作为最后手段,因为编译期间可能冒出大量意外依赖,耗时和心智负担都比较大。
2. 动手之前,先把环境准备做扎实
2.1 找一台和线上版本完全一致的可联网机器
这一步是整个离线安装里最容易偷懒、也最容易翻车的环节。很多人在家里用自己电脑下载了一堆 rpm 包,拿到内网环境一装,系统提示版本冲突或者依赖不满足,最后发现是因为操作系统版本不一致。
比如 CentOS 7.9 和 CentOS 7.6 的软件源结构基本一致,但部分依赖库的版本可能不同。更典型的是 CentOS 7 和 CentOS 8,连包名和依赖都可能发生变化。生产环境如果是 RHEL 8,你拿 CentOS 7 的机器拉包,极有可能导致 rpm 包无法安装。
我在实际项目里的做法是:先在可联网机器上执行 cat /etc/os-release 和 uname -m,确认发行版、版本号和架构三者都跟内网机器一致。如果内网机器是 x86_64,可联网机器也尽量选 x86_64,避免跨架构的兼容坑。特殊情况下没有完全一致的版本,尽量选小版本差异最小的,例如同为 7.x 系列,风险会低一些。
2.2 核对系统版本、架构和内核
除了发行版版本,还要关注内核版本,因为部分内核模块或驱动程序的软件包,会绑定一定的内核版本范围。例如 kernel-devel 这类包,版本和内核版本必须对应才能正确编译内核模块。
你可以用下面几条命令把环境信息一次性收集齐:
cat /etc/os-release uname -m uname -r将输出结果记录下来,然后到可联网机器上执行同样命令,比对结果。如果有差异,优先解决差异,而不是继续盲目下载。这个动作不要省,后面所有方案都要以版本匹配为前置条件。
2.3 准备好文件传输通道和校验工具
内网环境与外网隔离,文件传进去的方式通常是 U 盘、光盘、FTP 服务器或者内网共享目录。我建议准备两种传输方式,以防一种不通。
文件传进去之后,一定要做完整性校验。因为 rpm、deb 包如果在中途损坏,安装时会报错,还容易让人误以为是依赖问题。我一般会在可联网机器上生成 sha256sum 校验文件,传到内网后再执行:
sha256sum -c checksum.txt这个动作成本极低,但能避免非常多不明所以的安装失败。曾经有一次我传输一个几百 MB 的 rpm 包,中途 U 盘弹出,重新拷进去后没校验,结果安装时一直报“包已损坏”,排查了一个小时才发现是文件不完整。
3. CentOS/RHEL 系离线安装:下载、打包、建仓库一条龙
3.1 用 yumdownloader 快速下载指定包
RHEL 系中最直观的离线安装思路,是用 yumdownloader 命令下载软件包。这个命令来自 yum-utils 工具包,作用就是从软件源中下载指定的 rpm 文件,但不进行安装。
首先,在可联网机器上确保 yum-utils 已安装:
yum install -y yum-utils接着下载指定软件包,例如要装 nginx:
yumdownloader --resolve --destdir=/root/download nginx其中 --resolve 表示自动解析依赖并一起下载,这一步非常关键。如果不加这个参数,只会下载 nginx 本身的 rpm,安装时依然会因为缺依赖而失败。
下载完成后,会看到 nginx 及一堆依赖包的 rpm 文件。最简单的安装方式是把它们全部拷进内网,然后执行:
rpm -ivh *.rpm或者用 yum 安装到本地目录:
yum install -y ./下载目录/*.rpmyum 这种方式会自动处理本地文件目录中的依赖关系,比直接 rpm -ivh 更稳妥,因为它会读取本机已有的 rpm 数据库来判断依赖是否满足。
3.2 依赖一堆时改用 repotrack
yumdownloader 虽然好用,但在依赖非常复杂的场景下有短板:当某个依赖又被其他多个包共享时,--resolve 的处理结果偶尔会有遗漏。老运维常用的替代工具是 repotrack,它能完整递归解析依赖,把指定包及其所有依赖全部拉下来。
repotrack 同样是 yum-utils 包提供的,使用方法为:
repotrack -p /root/download nginx-p 指定输出目录。与 yumdownloader 不同的是,repotrack 会分析 rpm 元数据中的完整依赖树,把可能依赖的库一并下载,生成的文件数量往往比 yumdownloader 多,但安装在生产环境时基本不会出现“缺一个依赖再回去找”的情况。
这里有个实际经验:无论用哪种工具下载,都别吝啬磁盘空间。曾经我下载一个 PostgreSQL 的 rpm 包,加上依赖十几个包可能就几十 MB,但有些大型软件如 ELK 栈,依赖包加起来可能超过 1 GB,一定要预留充足空间。
3.3 在内网机器上搭建本地 yum 源
如果内网有多台机器需要安装相同软件,每次把一堆 rpm 拷过去再执行 yum install 会比较低效,这时候建议直接搭建一个本地 yum 源,让内网所有机器都能从这个源里安装。
搭建步骤其实不复杂:
- 在可联网机器上下载所有需要的 rpm 包,包括建仓工具 createrepo。
- 把这些 rpm 包同步到内网一台专门作为仓库的机器上,比如放在 /srv/repo。
- 在内网仓库机器上执行命令生成仓库元数据:
createrepo /srv/repo如果没有 createrepo,可以先在可联网机器上下载 createrepo 的 rpm,随包一起带进内网安装。
- 在这台仓库机器上创建本地 repo 配置文件:
vim /etc/yum.repos.d/local.repo文件内容大致如下:
[local-repo] name=Local Repository baseurl=file:///srv/repo enabled=1 gpgcheck=0- 其他内网机器如果想使用该源,可以把 local.repo 配置中的 baseurl 改成 http 或 ftp 地址,前提是这些机器能通过内网访问到仓库机器。
搭建本地 yum 源后,后续安装就回到了熟悉的模式:yum install 软件名,系统会自动从本地仓库解析依赖。对运维工作来说,这是一种真正“一劳永逸”的方式。
4. Ubuntu/Debian 系离线安装:apt 也有离线玩法
4.1 用 apt-get download 加上 depends 递归抓包
Ubuntu/Debian 系的离线安装思路和 RHEL 系类似,也是先在可联网机器上把 deb 包下载下来,再拷进内网安装。最基本的命令是:
apt-get download nginx但这只会下载 nginx 本身,不包含依赖。内网机器上如果缺少依赖库,安装时还是会报错。所以,我们需要借助 apt-cache depends 来递归查找依赖。
我自己常用的一个组合命令是:
apt-get download $(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances nginx | grep "^\w" | sort -u)这条命令的思路是:先递归解析出 nginx 的全部依赖包名,再用 apt-get download 统一下载。注意命令中我特别加了 --no-recommends、--no-suggests 等参数,是为了避免把推荐包和可选包也拉进来,这样能显著减少无用下载。
下载完成后,还是老办法,把所有 deb 文件复制进内网,然后执行:
sudo dpkg -i *.deb如果 dpkg -i 中途报依赖错误,可以先执行:
sudo apt-get install -f它会自动修复本地的依赖关系。虽然这个过程也是离线环境下完成的,但前提是依赖包已经放在当前目录中。
4.2 apt-offline 帮你托管离线包
对于 Debian/Ubuntu 环境,还有一个非常好用的工具叫 apt-offline,它的核心思想是把“依赖分析”和“包下载”彻底分离,适合需要周期性向内网机器同步更新的场景。
先在可联网机器上安装 apt-offline:
sudo apt-get install apt-offline然后在目标内网机器上生成一个请求文件,描述这台机器需要安装或升级哪些软件:
sudo apt-offline set /tmp/netreq.db如果只处理某个软件包,可以带上参数。接着把这个请求文件拷贝到可联网机器,执行:
sudo apt-offline get /tmp/netreq.db --output /tmp/netdata.zip这个命令会根据请求文件自动下载所有需要的软件包和依赖,并以 zip 形式打包。最后把 zip 文件带回内网机器:
sudo apt-offline install /tmp/netdata.zip整个过程对操作人员非常友好,不需要手动解析依赖,apt-offline 自己会处理。唯一需要注意的是,使用 apt-offline 时出入口机的软件源配置要一致,否则依赖版本可能存在偏差。
4.3 在内网搭一个本地 apt 源
Debian 系也可以搭建本地 apt 源,不过实现方式比 yum 源稍复杂一些,核心是使用 dpkg-scanpackages 生成包索引。
在可联网机器上下载好所有 deb 包后,同步到内网仓库机的 /srv/debs 目录。然后在仓库机上执行:
cd /srv/debs sudo dpkg-scanpackages . /dev/null | gzip > Packages.gz接下来配置本机源文件,例如 /etc/apt/sources.list 中追加:
deb [trusted=yes] file:///srv/debs ./如果要给内网其他机器使用,需要先用 web 服务器或 FTP 共享这个目录,然后再配置:
deb [trusted=yes] http://内网仓库IP/debs ./配置完成后执行:
sudo apt-get update之后内网机器就可以直接 apt-get install 安装了。需要注意,不同 Ubuntu 版本的依赖可能有差异,本地源只适用于同一大版本的内网机器。
5. 源码编译和容器镜像,两条兜底路线
5.1 编译安装前你要知道的三个坑
当离线环境下既找不到 rpm 包,也找不到 deb 包时,最后的选择就是源码编译。源码编译最坑的地方不是 ./configure 那一步,而是编译依赖缺一堆:gcc、make、autoconf、libtool 和相关头文件。
第一个坑:编译工具链本身可能没有。如果内网机器没有安装 gcc,你连源码都没法编译。因此,在可联网机器上预先下载好 gcc、make、kernel-devel 等工具包,是编译安装的前提。我一般会把 gcc、gcc-c++、make、binutils、libstdc++-devel 全部下载好,一次性带进去。
第二个坑:编译参数依赖特定库。有些软件在 configure 时会自动检测某些库是否存在,如果缺少会直接禁用相应功能,或者编译报错。解决方法是先看 README 和 INSTALL 文档,确认需要哪些依赖组件。比如编译 nginx 需要 pcre、zlib、openssl 开发包,这些你都得准备。
第三个坑:安装路径和后续管理。源码编译默认安装到 /usr/local 下,后续卸载只能手动清理。我习惯用 ./configure --prefix=/自定义路径 来指定安装目录,方便集中管理。
编译安装不是不能用,但要明确它的定位:在包管理器方案失效时才考虑,且需要专门准备编译依赖包,总成本远比前几种方案高。
5.2 没外网也要用 Docker:离线镜像导入导出
现在很多应用已经容器化,如果你手里有 Docker 镜像,内网环境的安装难度会大大降低,因为镜像里已经带了完整的运行环境,不依赖宿主机的软件包。
前提是内网机器上要能安装 Docker。如果内网机器没有任何可用的包管理器配置,那么 Docker 本身的安装又是一个离线安装问题,这时候照样可以用前面讲的方式先装好 Docker。
Docker 离线镜像的操作思路很简单:
在可联网机器上拉取镜像:
docker pull nginx:latest然后将镜像保存为 tar 文件:
docker save -o nginx.tar nginx:latest把 tar 文件拷贝进内网后加载:
docker load -i nginx.tar之后直接运行容器即可:
docker run -d -p 8080:80 nginx:latest容器方案最大的好处是规避了依赖地狱,软件包全部固化在镜像内层,宿主环境只需要内核和 Docker 运行环境满足基本要求即可。需要注意,Docker 镜像一般体积较大,一个镜像几百 MB 很常见,传输前要留足空间。
6. 常见问题与排查技巧实录
6.1 依赖缺失、版本冲突怎么处理
离线安装中最常见的问题就是安装时报“缺少依赖”或“依赖冲突”。遇到这种情况,先别急着盲目找包,可以先分析本机已经安装了哪些依赖和版本:
在 RHEL 系中:
rpm -qa | grep 依赖名在 Debian 系中:
dpkg -l | grep 依赖名然后看缺少的依赖是否已经存在于本机但版本低于要求。如果版本过低,可以尝试在可联网机器上下载对应版本的依赖包,或者使用软件源中更高版本替代。
遇到依赖冲突,例如两个包需要同一库的不同版本,我的第一反应不是强行安装,而是检查是否有其他软件依赖了冲突包。这时候可以利用 yum 的依赖解析输出:
yum deplist 软件名或者 apt 的依赖关系:
apt-cache showpkg 软件名看清楚依赖图后再决定是升级基础库,还是更换软件版本。不要用 rpm -ivh --force 或 dpkg -i --force-all 强行装,这往往会把系统库弄乱,后续出现更多问题。
6.2 GLIBC 版本不满足,别急着升系统
有些软件在安装时会提示需要 GLIBC_2.32 或更高版本,而本机的 GLIBC 只有 2.28。GLIBC 是系统最底层的 C 运行库,盲目升级风险极大,可能会导致大量现有程序无法运行。
遇到这种情况,我的建议优先级是:
- 寻找该软件的静态编译版本,或通过 Docker 运行。
- 找一个不依赖新版 GLIBC 的旧版本软件包。
- 如果必须升级 GLIBC,建议评估所有现有软件的兼容性,并在测试环境先行验证,不要在核心生产环境直接操作。
这也是为什么我始终强调,如果项目从一开始就考虑容器化,这类 GLIBC 问题根本不会发生。镜像内自带正确的运行库版本,不污染宿主机环境。
6.3 文件传输损坏,校验起来其实很简单
离线环境安装经常遇到“包已损坏”或者“校验和错误”的提示。很多时候不是软件包本身有问题,而是传输过程中文件损坏,比如 U 盘拔得太快、FTP 传输过程中断、共享目录同步不完整。
应对方案有两个层级:
第一,传输前在可联网机器上执行:
sha256sum 需要传输的文件 > checksum.txt第二,传输后在目标机器执行:
sha256sum -c checksum.txt看到 OK 说明文件完整,看到 FAILED 说明文件有问题,需要重新传输。如果文件数量太多,可以打包成单个 tar.gz,校验整体文件,然后内网解压,这一步能大幅减少逐文件校验的时间。
6.4 在线机器与内网版本不一致怎么办
这是离线安装最容易踩的暗坑。经常有人拿 CentOS 7.6 的机器去拉包,结果内网机器是 CentOS 7.9,一些 rpm 虽然能装上,但运行时可能因为某些库版本过低或过高出现异常。
如果真的找不到完全一致的版本,建议注意两点:
一是优先使用软件源里的稳定包,不要选择 latest 测试版。二是安装完软件后,对内网的软件做一次基本功能验证,不要只看 rpm -qa 里有就认为成功。比如装完 nginx 后启动一下服务,curl 一下本地页面,确保链路是通的。
我在实际维护中遇到过一次:内网机器和在线机器同为 CentOS 7,但因为在线机器使用了一个较新的第三方源,拉下来的依赖包版本比内网的高,结果软件启动时报段错误。后来重新用官方基础源拉包,问题才解决。所以还是那句话,拉包机器的软件源配置要尽量贴近内网机器,最好都用发行版默认源。
最后再分享一个小技巧
离线安装这件事,看起来是“缺网就是麻烦”,但只要把流程理顺,其实可以做得非常顺滑。我个人现在最推荐的方式,是优先尝试“可联网机器拉包 + 内网本地源”的组合,因为它既能满足单机安装,又能支持内网批量部署,后续新增软件包只需要在仓库机上执行一次 createrepo 或 dpkg-scanpackages 更新索引就好。
另外,给内网环境做软件管理时,建立一个软件包清单表非常有用,包括包名、版本、来源、校验和、安装日期、安装方式。一旦出了故障,靠这张表能快速定位到当初装的是哪一批包,比翻历史命令要高效得多。
按照这套流程操作,基本上 80% 的 Linux 内网离线安装需求都能解决。剩下 20% 的疑难杂症,大多集中在编译依赖和系统库兼容性上,那就需要结合具体软件包做专项排查了。