学了半个多月Linux,到今天第十四天,终于敢说自己对“装软件”和“管服务”这两件事有点底气了。前面十几天基本都在跟命令、文件、权限、进程这些基础概念打交道,说白了都是单点操作,今天开始碰系统里最日常、也最容易出问题的两块内容:软件包管理和服务管理。这篇文章就当给自己做一份学习版总结,也希望能给正在走同一条路的人一点参考。
我当前的学习环境是一台2核4G的虚拟机,装的是主流的RHEL系发行版,全程用终端操作,没有装图形界面。选择RHEL系纯粹是因为单位生产环境基本都是这个体系,早一天熟悉早一天上手。今天主要实践了yum源配置、rpm与yum命令互操作、编译安装的取舍,以及systemd服务单元的全流程管理。这几个技能点不仅是日常运维的刚需,也是后面学自动化、学容器编排的地基。
1. 软件包管理:为什么第十四天一定要啃这块硬骨头
1.1 RPM、YUM、DNF到底是什么关系
很多初学者看到rpm、yum、dnf几个命令就直接懵了,我一开始也是。今天实际反复用过之后,总算把三者关系理顺了。
把系统比作一个房子,软件包就是你要搬进来的家具。rpm命令是“人工搬家具”,你拿到一个.rpm文件,手动把它安装到系统里。它能干活,但有个致命弱点:不会主动帮你解决家具之间的配套问题。你装A家具,A需要B家具当底座,rpm只会告诉你“缺B”,然后就不管了。这就是所谓的依赖关系处理。
yum则是“智能搬运工”,它知道每个家具需要什么配件,并且会先帮你把配件准备好再安装主件。yum背后有一个软件仓库,仓库里预先整理好了成千上万个软件包以及它们之间的依赖关系数据。所以你执行一条yum install,它能把所有需要的包一次性装好。
dnf是新一代的yum,可以理解为同一个东西的增强版,主要在依赖解析算法和性能上做了优化。我们系统里现在直接敲yum命令,底层实际上就是dnf在工作。新手没必要纠结用哪个,记住一句话:优先使用yum,只有拿到单独的.rpm安装包时才用rpm。
1.2 从Windows习惯过渡到Linux装软件思维
Windows下装软件是下载一个install.exe,双击一路下一步就完事了。Linux完全不是这个逻辑,刚切换过来的人会觉得很不适应。
Linux下装软件的途径可以概括为三种。第一种是yum安装,适合绝大多数常用软件,一条命令搞定,依赖自动处理,这是首选。第二种是rpm直接安装,适合离线的.rpm包,但依赖需要自己操心。第三种是源码编译安装,适合需要定制功能或官方未提供现成包的特殊软件,流程是./configure、make、make install三步走,自由度最高,也最容易踩坑。
今天我把三种途径都各试了一遍。实际操作下来,yum安装最无脑,rpm安装最考验对依赖的理解,源码编译则像一次小型开发过程,耗时和变数都大。对第十四天的学习阶段来说,yum必须熟练,rpm要会用,源码编译至少知道流程和原理,这就够了。别想着一天吃成胖子,后面很多天还会反复跟这些命令打交道。
2. 配置软件源:一切安装工作的地基
2.1 搭建本地YUM源实操
软件源就是一串告诉yum“去哪里下载软件包”的地址配置。今天的第一个实操就是搭建本地yum源,因为我们的虚拟机处于内网环境,外网访问不通畅,而本地镜像光盘里正好有整套软件包,这是最快、也最稳定的一种源。
准备工作很简单,把安装镜像ISO挂载到系统上。我的做法是在虚拟机设置里把ISO文件关联到CD/DVD光驱,然后在终端里执行mount操作。挂载完成后,iso里的内容会出现在一个目录下,通常是/run/media/root/目录名或者/mnt下的自定义目录。我在/media下创建了一个iso目录作为挂载点。
挂载后的目录里有个repodata目录,这是整个仓库的关键所在,里面存放着所有软件包的元数据和依赖关系索引。yum能不能正常工作,就看这个目录存不存在。如果挂载的目录里没有repodata,说明这个ISO不适合直接当yum源,或者镜像版本太特殊。
然后我在/etc/yum.repos.d/目录下新建了一个本地源配置文件,名字叫local.repo,内容大概是下面这样:
[local] name=Local Repository baseurl=file:///media/cdrom enabled=1 gpgcheck=0这里有几个字段要说明一下。方括号里的local是仓库标识,必须全局唯一。name字段是仓库描述性名称,随便写。baseurl就是仓库地址,file://表示本地文件路径。enabled=1表示启用这个仓库,gpgcheck=0表示不对软件包做GPG签名校验,内网环境为了省事可以这样配置,但公网环境建议保持gpgcheck=1并配置好公钥。
配置完成后,建议先缓存清理一下:
yum clean all yum makecache测试源是否可用,最直接的方法是随便装个小软件,比如执行yum install -y tree,如果顺利安装成功,说明本地源完全可用。我第一次配置时忘了关闭系统自带的网络源配置,两个源同时启用,结果yum在解析时出现了耗时较长的问题,后来把默认源配置文件全部移走后一切正常。建议新手在实践本地源时,先临时改动或者移走那些自带的源配置文件,只保留自己这一个新的,避免干扰。
2.2 网络源与官方源的选择取舍
离线练习结束之后,我又恢复了网络环境,对比了一下本地源和网络源的实际差异。网络源的好处是软件包版本更新、选择面广,而且能自动获取最新的安全补丁。缺点是依赖公网的稳定性,如果公司网络有防火墙限制,yum很容易卡在连接超时上。
用网络源时,我记得执行过一次yum install nginx,结果提示要下载依赖包,数量和总大小会先列出来,然后问我是否确认。这个过程看似简单,背后其实请求了远程仓库的元数据来做依赖解析。如果你所在环境网络波动大,yum makecache阶段就可能会卡很久,这时候可以把仓库配置里的超时时间加大一点,或者干脆像前面那样切到本地源。
选择建议其实不难:内网环境优先本地源或者公司内部搭建的镜像源;公网环境优先官方源,但要注意设置gpgcheck=1。对于学习阶段,两种源都应该亲手配一遍,只有体会过本地源那种“秒装”的感觉,再对比网络源的速度,才会真正理解软件源配置在生产环境里的重要性。
3. 高频命令实操:拿着终端就像拿着工具箱
3.1 rpm命令的增删查与细节坑
rpm命令虽然日常用得比yum少,但一旦要用就是必须精准的场景,比如离线排查问题、验证某个包是否已安装、提取某个包里的配置文件等等。
我今天重点练习了rpm的查询功能,这是日常工作里最常用的。rpm -q是查询指定包是否安装,格式是rpm -q 包名,比如rpm -q vim-enhanced,如果已安装会返回完整的包名和版本信息,未安装则会提示not installed。rpm -qa是列出所有已安装的包,数量多到惊人,建议加上管道配合grep过滤用,比如rpm -qa | grep nginx。rpm -ql是列出某个已安装包的所有文件列表,这个在看软件到底安装了哪些内容时特别有用。rpm -qf是反向查询,比如你看到一个配置文件但不知道属于哪个包,直接rpm -qf /etc/nginx/nginx.conf就能查出来。
安装单独的.rpm文件用rpm -ivh 包名.rpm,升级是rpm -Uvh,卸载是rpm -e 包名。这里有个细节坑,rpm卸载只接受包名,不接受文件名,例如rpm -e nginx-1.20.1.rpm这种写法是错的,正确写法是rpm -e nginx。我开始时就因为记错这个走了弯路,花了几分钟才反应过来。
另一个要记住的是rpm安装时的-vh参数,v表示显示详细过程,h表示显示进度条,不加也能装,但输出只有一行,出了问题不好判断卡在哪。rpm还有校验功能rpm -V,如果包的文件被改动过会输出提示,做安全排查时很实用。今天我没有深入这块,但知道有这个东西,后期再做安全加固时是必考项。
3.2 yum命令组合:安装、分组、历史回滚
yum命令表面看就一个install,实际上它有一整套组合玩法。今天我把常用的都过了一遍,其中几个值得重点说。
首先是安装与卸载。yum install -y 包名和yum remove -y 包名是最基础的。这里的-y表示自动应答yes,否则每个装包确认都会问你一遍,尤其是依赖多的时候特别烦人。卸载时要注意,yum remove会把依赖这个包的其他包也一并删掉,所以生产环境卸载前要看清提示列出的受影响包列表。
yum search 关键词用于在仓库中查找模糊匹配的软件包,不知道确切包名时的第一选择。yum provides 文件名则是按文件反查包名,比如你执行某个命令提示找不到文件,可以用yum provides */该文件名来定位属于哪个包。这一招我实操时用过一次,解决了一个“明明装了某个软件但提示缺库文件”的疑惑,实际上缺的是另一个依赖包。
分组安装是yum的一个特色功能。yum grouplist能列出所有软件组,比如“Development Tools”“Security Tools”等。yum groupinstall “组名”可以一次性安装一整组软件。我测试时安装了一组开发工具,一下就装好了几十个包,省去了逐个安装的麻烦。注意组名通常带空格,必须用引号包裹。
yum history和yum history undo是回滚功能。今天模拟了一个场景:先安装了一个包,接着又把它卸载了,再用yum history查看事务记录,最终用yum history undo 事务编号把卸载动作撤销。这个功能在处理“我到底刚才做了什么”的后悔场景时特别好用。yum历史就像操作日志,跟shell history不同,它记录的是完整的包管理事务,包括安装了哪些依赖,改动过哪些文件,适合出问题后做溯源。
4. systemd服务管理:学了才能理解“启动”这件事
4.1 服务单元文件的结构与语法
刚开始学Linux时看到的“服务”是个特别抽象的词。其实把服务理解为“一个常驻后台运行的程序”就行了,比如nginx是网页服务,sshd是远程登录服务。systemd负责管理这些服务,它接管了启动、停止、重启、开机自启等一系列工作。
systemd管理每个服务靠的是服务单元文件,这些文件以.service结尾,通常存放在/usr/lib/systemd/system/目录下,这是系统自带服务单元的地方。用户自定义的服务可以放到/etc/systemd/system/目录,这个目录优先级更高,而且不应该随便改动系统自带的文件,自定义的内容放到这里就对了。
今天我自己编写了一个极简服务单元,内容大概如下:
[Unit] Description=My Test Service After=network.target [Service] Type=simple ExecStart=/usr/bin/sleep 500 [Install] WantedBy=multi-user.target[Unit]区块里的Description是服务描述,After表示这个服务要在网络服务之后启动,这是一种依赖声明,但不等于强依赖,只是顺序保证。[Service]区块里的Type=simple是默认类型,表示ExecStart启动的程序就是主程序,直接前台运行。如果程序会自行守护并后台化,则可能要设置Type=forking。ExecStart指定启动命令,这里我用sleep命令模拟一个常驻进程。[Install]区块里的WantedBy=multi-user.target表示这个服务在系统进入多用户模式时被启用,本质就是控制开机自启的挂载点。
编写完服务单元后执行systemctl daemon-reload重新加载配置,然后systemctl start 服务名就能启动。如果改过ExecStart等重要字段但不执行daemon-reload,启动时用的还是内存里的旧配置,这个坑我在实操时踩过,改了文件后发现服务没按预期执行,排查了半天才发现是配置没重载。
4.2 systemctl常用操作与开机自启逻辑
systemctl是systemd的控制命令,每天都会用。基础操作无非是start、stop、restart、status、enable、disable这几项,配合起来已经能覆盖绝大多数需求。
status输出里包含的信息很丰富,我一般重点看几个字段:loaded表示服务单元是否已加载;active表示运行状态,active (running)代表正常运行,inactive (dead)代表已停止;还有进程PID、内存占用、最近日志。启动失败时,status给出的信息往往是第一手线索。今天有一次我故意把ExecStart命令路径写错,结果status明确提示“Failed to start”,后面跟着一行错误原因,定位问题基本不用看别的日志。
开机自启的逻辑要理解清楚。enable命令做的事情,是把服务单元文件软链接到multi-user.target.wants目录下,disabled则相反。systemctl is-enabled 服务名可以查询当前是否开机自启,返回enabled或disabled。有个常见误解是把start和enable混为一谈,start只是“现在运行”,系统重启后不保证再运行;enable是“每次开机都运行”。两者可以独立设置,也常常配合使用。
还有一个组合操作systemctl enable --now 服务名,它的含义是同时完成“开机自启”和“立即启动”,一条命令干两件事,非常高效。日常配置服务时我推荐直接使用这个组合形式,省得先enable再start来回敲。
服务重启后想确认是否真的起来了,别只看systemctl status的结果,我习惯再用ps或ss命令交叉验证端口和进程是否真实存在。今天我用nginx测试过,systemctl restart nginx返回正常,但用ss -lntp查看端口时发现进程起在了一个不同端口上,仔细一看是配置文件里把监听端口改掉了,服务的启动状态是真实的,只是它启动后读了新的配置。这类“服务正常但不符合预期”的场景,只能靠交叉验证来发现。
5. 实操中踩过的坑:第14天最值钱的几段经历
5.1 依赖地狱的几个典型解法
依赖问题就是软件A依赖B,B又依赖C,环环相扣,一旦仓库里找不到某个中间依赖,安装就会失败。今天实操中遇到的最典型问题,是yum install时报“Requires: libxxx.so.1()(64bit) = 1.2.3”错误,提示找不到某个共享库。
遇到这类问题先不要慌,也不要试图去网上乱下.so文件然后硬塞进系统,这样做非常危险,可能把系统搞崩溃。正确做法分几步走。第一步,用yum provides “*/libxxx.so.1”这个语法查一下这个库由哪个包提供,如果仓库里有,直接安装那个包。第二步,如果本地或当前仓库搜不到,可能是仓库源不全或版本不对,尝试启用更多扩展仓库。第三步,如果确属仓库里没有的定制库,再考虑源码编译方式安装到/usr/local目录,并且注意用ldconfig刷新动态链接库缓存。
另一个容易踩的坑是i686和x86_64架构混淆,比如64位系统装了32位包,提示缺的库全是32位的,安装依赖时又拉来一堆32位包,系统会越装越乱。解决办法是装包前强制指定架构,或者尽量保持所有软件包都是同一个架构系列。今天我一台小虚机上测试了交叉架构安装,虽然最终能装上,但总感觉会有隐患,实际生产环境没人这么干。
依赖问题的本质是仓库元数据不完整或源配置错误,遇到后先在源层面找原因,比就地解决报错更治本。
5.2 服务启动失败时的三分法排查思路
服务起不来是每个Linux学习者迟早都要面对的事,今天至少碰到了三次,好在通过整理出一套排查思路,后面没再懵过。
第一步看状态:systemctl status 服务名,关注错误行和Active状态。这一步能过滤掉大约一半的问题,比如路径写错、权限不足,这些错误信息都会直接显示出来。
第二步看日志:journalctl -u 服务名 -n 50,查看该服务最近50行日志。日志里通常有更具体的报错原因,比如端口被占用、配置文件语法错误。如果日志量太大,可以加上--since "5 min ago"限定时长。今天nginx启动失败,就是通过日志发现listen端口被另一个服务占用了,停掉那个服务后问题解决。这里补充一个小技巧,端口占用排查可以用ss -lntp查看监听状态,配合lsof -i:端口号定位占用进程,两个命令组合一次就能查明白。
第三步看权限和文件:如果日志没有明确线索,检查服务单元文件里ExecStart指向的命令是否存在、是否有执行权限、运行服务的用户对相关目录是否有读写权限。今天有一个服务起不来,原因就是二进制文件存在的目录权限是700,而服务运行用户没有访问权限,日志只报了一个很泛的“Failed”,加上对目录的ls检查才发现是权限问题。
这套排查路径基本覆盖了大多数服务启动故障,建议新手记录下来,遇到问题时按顺序执行,效率会明显提升。
5.3 离线环境里的备选方案:从本地RPM包到源码编译
学习过程中可以花一点时间,专门验证一下没有网络时的安装方案。我今天的最后一个实操,是把一个常用软件包从一台联网机器上把.rpm文件全部下载下来,拷贝到离线虚拟机上手动安装。
下载一个包的依赖集合有两套思路。简洁的办法是用yumdownloader这个工具,它可以只下载指定的rpm包而不安装。而要用yum下载一个包及其全部依赖,更便捷的方式是使用yum install --downloadonly --downloaddir=/tmp/pkg 包名,不过这个玩法在不同版本的yum上参数略有差异,我这次是在RHEL系上操作的,用起来没问题。
离线机器上执行rpm -ivh *.rpm时,如果目录里的依赖包齐全,一般能装上。但顺序有讲究,需要先装被依赖的包,后装依赖它的主包。这个顺序手动判断起来很吃力,所以更推荐在目录里执行一条yum localinstall *.rpm,让yum从本地目录自动解析依赖并安装,省心很多。这个命令现在也可以直接用yum install ./包名.rpm的格式,效果类似。
源码编译这次我试了一个体积较小的工具包,整个流程其实更适合后面单独花一天专门学习。今天只求一个整体感知:./configure阶段负责环境检测和生成Makefile,make阶段调用编译器生成可执行文件,make install把文件复制到系统目录。如果没有安装gcc等开发工具,编译会直接报错,所以源码编译前通常需要先把Development Tools这个软件组装上。
6. 今天的收尾心得与下一步计划
一天高强度操作下来,最大的感受是Linux的知识点像一张网,命令只是一个个节点,真正有用的是节点之间的连线。软件包管理和服务管理这两块正好把前面学的文件系统、权限、进程给串起来了,比如理解服务单元文件就需要文件权限知识,排查服务日志就要用上文本处理命令,而软件安装时包里文件的归属又逼着你去复习rpm -ql之类的查询。
对正在学Linux的人,我的建议很具体:先把本地源和网络源各配一遍,然后专门拿一台虚拟机反复安装卸载同一个软件,再看它的依赖关系;之后自己写一个简单的服务单元文件,启动一个测试进程,再故意改坏配置去排查。这套操作下来,你会比死记一堆命令公式的学生强得多,因为每个命令都对应着你亲手解决过的问题。
第十四天就这么过完了。想清楚下一天可以往Shell脚本方向走,把今天手动执行的一堆命令写成自动化脚本,让系统替自己干活。学Linux最难的是坚持,但每闯过一个坑,后面会越来越有意思。