包管理
这一课你将能做到
- 用 apt 完成完整生命周期:更新索引 → 安装 → 查看信息/文件清单 → 升级 → 卸载清理,并说清
update 与 upgrade 的区别
- 在 Ubuntu 上装好 CodeRoadMap 的运行时三件套:OpenJDK 17、Nginx、Node 20(NodeSource 源),并用版本命令验证
- 会认 RedHat 系的
dnf/yum 对应命令,理解「软件源」概念,知道装的东西被放到了哪里(dpkg -L)
为什么要学
部署 Java 服务第一步永远是「装环境」:JDK、Nginx、MySQL、Node。装对了吗?为什么 apt install openjdk-17-jdk 报找不到?为什么别人机器上 node -v 是 20 你的还是 12?这些问题的答案全在包管理里。
包管理器解决三个问题:从哪下(软件源/仓库)、装到哪(文件清单与路径)、谁管着(依赖与版本)。理解了这三个问题,你就从「复制安装命令」升级到「知道每条安装命令在干什么」。
先建立图像
包管理的两层结构
apt(高级,推荐日常用) dnf/yum(RHEL 系对应物)
│ 自动解决依赖、整包管理
▼
dpkg(底层) rpm(RHEL 系对应物)
│ 真正解压 .deb 落盘、登记文件清单
▼
系统文件(/usr/bin/java 等)
软件源:apt 的「地址簿」
/etc/apt/sources.list 与 /etc/apt/sources.list.d/*.sources ← Ubuntu 24.04 新格式
│
▼
apt update:只刷新「地址簿」——拉取各源的最新软件清单到本地缓存
apt upgrade:按缓存里的清单,真的去下载并升级已装的软件
这就是面试题「update 和 upgrade 区别」的图像:update 刷目录,upgrade 动真格。不 update 就 upgrade = 拿着旧目录找货,轻则装到旧版本,重则 404。
跟着做
步骤 1:apt 生命周期八连(先在干净机器走一遍)
sudo apt update # ① 刷新软件源索引(每次装软件前先跑)
apt list --upgradable # ② 看有哪些可升级(只看不升)
apt search nginx # ③ 搜索:包名/描述含 nginx 的包
apt show nginx # ④ 看某包详情:版本、依赖、大小、维护者
sudo apt install nginx # ⑤ 安装(自动带上依赖)
dpkg -L nginx | head -15 # ⑥ 这个包装了哪些文件、都在哪
apt list --installed | grep nginx # ⑦ 确认已安装与版本
sudo apt remove nginx && sudo apt autoremove # ⑧ 卸载 + 清理不再需要的依赖
补充两个清理相关:apt purge 包名(卸载且删配置文件)、apt clean(清掉 /var/cache/apt 的下载缓存,磁盘紧张时用)。
步骤 2:装 OpenJDK 17(后端主粮)
sudo apt update
sudo apt install -y openjdk-17-jdk-headless # headless=无GUI组件,服务器首选
java -version # openjdk version "17.0.x"
javac -version # 编译器也有(JDK 而非 JRE)
# 装到哪了?管理方式是「替代方案」机制:
which java # /usr/bin/java(这是个软链)
ls -l /usr/bin/java # → /etc/alternatives/java → 真身
ls /usr/lib/jvm/ # 多个 JDK 共存的目录
# 多版本共存与切换(装了 21 之后再执行才有多个候选):
sudo apt install -y openjdk-21-jdk-headless # (可选)再装一个 21
sudo update-alternatives --config java # 交互选默认版本
生产建议:服务器只装需要的那个版本,别把 8/11/17/21 全装上,alternatives 指来指去容易把 systemd 服务的 java 路径搞乱。
步骤 3:装 Node 20(前端运行时,官方源版太旧)
Ubuntu 自带源的 node 是旧版本(20.04 上是 12),Next.js 14+ 要求 18+。正确姿势是加 NodeSource 源:
# ① 装 curl(若没有)并添加 NodeSource 20.x 源
sudo apt update && sudo apt install -y curl
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
# ② 此时再装,走的就是新源
sudo apt install -y nodejs
node -v # v20.x
npm -v
# ③ 验证源已登记
ls /etc/apt/sources.list.d/ # 多出 nodesource.list(24.04 是 .sources)
看懂那条 | sudo -E bash -:下载脚本 → 管道交给 bash 执行(-E 保留环境,- 表示读 stdin),它做的事就是往 sources.list.d 写 NodeSource 的源配置并导入签名钥匙。先理解再执行管道脚本,不明来源的 curl | bash 不要跑。
步骤 4:装 MySQL 8(验证服务型软件装完自启)
sudo apt update
sudo apt install -y mysql-server
systemctl status mysql --no-pager # 装完即自动启动并 enable(服务型软件的特性)
sudo mysql -e "SELECT VERSION();" # 本机 root 免密(auth_socket)验证版本
体会两点:① apt 装完「服务型软件」会自动注册 systemd 并启动——包管理课与进程课在这里接上了;② 版本命令(java/node/npm/mysql)是每次安装后的第一验收动作。
步骤 5:版本控制——装指定版本、锁定版本
apt list -a nginx # 源里有哪些候选版本
apt show nginx | grep Version
# 装指定版本(版本号先从 apt list -a 查,完整复制):
sudo apt install nginx=1.24.0-2ubuntu7 # 示例:以你查到的为准
# 锁定版本:apt upgrade 不再动它(生产「冻结」常用)
sudo apt-mark hold nginx
apt-mark showhold # 查看所有锁定
sudo apt-mark unhold nginx # 解锁
步骤 6:RedHat 系对照速查(会认,能切换)
# 在 Rocky/Alma/CentOS 上的等价操作:
sudo dnf makecache # ≈ apt update(刷新元数据)
sudo dnf install java-17-openjdk-devel # ≈ apt install openjdk-17-jdk
dnf search nginx # ≈ apt search
dnf info nginx # ≈ apt show
rpm -ql nginx | head # ≈ dpkg -L(列文件清单)
sudo dnf remove nginx # ≈ apt remove
sudo dnf autoremove # ≈ apt autoremove
| 动作 | Ubuntu/Debian | RHEL 系 |
|---|
| 刷新索引 | apt update | dnf makecache(多数命令自动做) |
| 安装 | apt install x | dnf install x |
| 文件清单 | dpkg -L x | rpm -ql x |
| 查已装版本 | dpkg -l | grep x | rpm -qa | grep x |
| 加第三方源 | sources.list.d + curl 脚本 | dnf config-manager / .repo 文件 |
老 CentOS 7 的 yum 命令在 Rocky 9 上基本可无缝替换为 dnf(语法几乎一致)。
步骤 7: deb 包直装与故障排查
# 场景:公司内网给了你一个 .deb(如自研组件、旧版 JDK)
sudo dpkg -i 包名.deb # 直装;若缺依赖会失败
sudo apt install -f # -f fix:让 apt 自动补齐缺失依赖,然后再 dpkg -i 或它会接着装完
# 排查「包状态异常」:
dpkg -l | grep -E "^i[^i]|^..F" # 非 ii 状态的都是问题包
sudo dpkg --configure -a # 中断的安装卡住时,恢复配置流程
讲清楚要点
- apt 管的是「仓库里的软件」,你的 JAR 不归它管:backend.jar 是部署产物(tar/scp/rsync 搬运,进程课托管),nginx/mysql/jdk 才走 apt。别试图把业务包塞进 apt 体系。
- update 与 upgrade 永远先 update:装/升任何软件前先刷新索引,这是肌肉记忆。
apt upgrade 升级所有软件(内核也会动),只升某包用 apt install 包名(会顺带升它)。
- 安装即登记:每个包的文件清单 dpkg 都记着(
dpkg -L),卸载按清单回收——所以装包比手解 tar 干净;反过来,手解 tar 的东西(如自装 JDK 到 /opt)apt 不知道,卸载要自己删。两种方式别混用于同一软件。
- 第三方源是双刃剑:NodeSource/MySQL 官方源/Docker 源能带来新版本,但每个源都是供应链信任的延伸。原则:只加官方/可信源,看清
curl | bash 干了什么,出问题时知道去 /etc/apt/sources.list.d/ 删源。
- 服务型包装完即自启:mysql/nginx 安装后自动 systemctl enable + start。所以「装完要 systemctl status 看一眼」是标准验收;改配置后是 reload 而不是重装。
- 版本锁定用于「冻结」:
apt-mark hold 适合「这个版本验证过,别让 upgrade 动它」。但锁太多包会让安全更新漏掉——锁定是例外,跟随更新是常态。
你可能会懵的地方
- apt、apt-get、dpkg 什么关系?
dpkg 是底层(装单个 .deb、不管依赖);apt-get 是老牌高层工具(稳定,脚本友好);apt 是 16.04 起面向人的新前端(进度条、
apt list/apt search 等好用子命令),日常用 apt 即可。脚本里有人坚持 apt-get(输出格式更稳定),效果等价。
- 为什么 apt install 报 Unable to locate package?
三种可能按顺序排:① 没先
apt update(索引空);② 源里确实没有(如 openjdk-17 在 20.04 的默认源没有?其实有;node 新版就肯定没有)→ 加对应官方源;③ 包名拼错 → apt search 关键词 找正确包名。
- 装了 17 又装 21,Spring Boot 用的是哪个?
默认走
update-alternatives 指向的 java。明确指定:systemd 单元 ExecStart 写死绝对路径(/usr/lib/jvm/java-17-openjdk-amd64/bin/java)最稳——进程课的单元文件建议这么做。
- apt upgrade 会不会把服务搞挂?
正常发行版仓库的更新经过测试,且服务升级通常有自动迁移。生产最佳实践:低峰期升级、升级前快照(云控制台)、升级后检查核心服务
systemctl status + 健康接口。apt-mark hold 冻结个别关键包也是手段。
- Ubuntu 24.04 里 sources.list 变成 .sources 文件了?
是新格式(DEB822),信息相同只是排版结构化。
/etc/apt/sources.list.d/ubuntu.sources。操作方式不变:apt update/install 照旧,加第三方源时注意看教程对应的是哪种格式。
错法对照
| 错法 | 问题 | 正解 |
|---|
不 apt update 直接 install | 找不到包或装到旧版 | 装前必 update |
apt upgrade -y 在生产乱跑 | 升级中断/内核变更不可控 | 生产升级前快照;重要包 hold |
| 手动下载 tar.gz 解压 JDK,又想用 apt 管理它 | apt 不认,升级/卸载两不管 | 二选一:apt 装或 /opt 手装并写死路径 |
| `curl -fsSL xxx | bash` 不看内容就跑 | 供应链风险 |
| dpkg -i 缺依赖后反复 dpkg | 越装越乱 | apt install -f 自动补依赖 |
| 把 backend.jar 放进 apt/dnf 体系 | 业务包不是系统软件 | 部署产物走 scp/rsync + systemd |
卡点急救
- apt update 报 404 / Hash Sum mismatch:多为镜像缓存或源配置损坏。
sudo apt clean && sudo rm -rf /var/lib/apt/lists/* && sudo apt update 重建索引;国内机器换镜像源(如阿里云镜像站,改 /etc/apt/sources.list)后同样操作。
- 安装中断后 apt 全程报锁(Could not get lock):确认没有其他 apt 进程在跑(
ps aux | grep -E "apt|dpkg"),等待或结束后:sudo dpkg --configure -a 恢复;不要直接删 /var/lib/dpkg/lock(除非已确认无进程)。
- Node 装完版本还是旧的:说明装的是发行版自带的 node(旧源)。先
sudo apt remove nodejs,确认 NodeSource 源文件在 /etc/apt/sources.list.d/,再 update + install。
- 卸载后命令还在(java 卸了 java -version 还通):那是 alternatives 软链或另一个 JDK 目录。
ls -l $(which java) 追链看真身;手装的在 /opt 自己删。
- 磁盘被 apt 缓存吃满:
du -sh /var/cache/apt 查看体积,sudo apt clean 清空;已装包的 .deb 缓存删掉不影响已装软件。
面试常问
-
apt update 和 apt upgrade 的区别?
update 只同步软件源的最新包索引到本地(不动任何软件);upgrade 依据本地索引,下载并升级已安装软件。所以 update 是 upgrade 的前置。对应 RHEL 系:dnf makecache 与 dnf upgrade。
-
apt 和 dpkg 的关系?
dpkg 是底层包管理器,负责解包安装单个 .deb、登记文件清单(dpkg -L 查询);apt 是高层前端,自动解决依赖、从仓库下载安装。dpkg -i 装包缺依赖时要 apt install -f 兜底。
-
如何在 Ubuntu 上安装指定版本的软件并防止被自动升级?
apt list -a 包 查候选 → apt install 包=版本 精确安装 → apt-mark hold 包 锁定。解锁 unhold。生产冻结关键组件常用。
-
说说你部署 Java 服务的完整环境安装过程。
一条主线:apt update → apt install openjdk-17-jdk-headless(java -version 验证)→ NodeSource 源装 Node 20(node -v)→ mysql-server(systemctl status 验证)→ nginx(后续反代)。要点:先刷索引、服务型软件装完验收自启状态、多版本 JDK 用绝对路径写进 systemd 避免 alternatives 歧义。
-
软件包安装在系统里的哪些位置?
二进制多在 /usr/bin(软链)与 /usr/lib(库/JVM 在 /usr/lib/jvm);配置在 /etc(如 /etc/nginx、/etc/mysql);数据与日志在 /var(/var/lib/mysql、/var/log/nginx);文档在 /usr/share/doc。dpkg -L 包名 是权威清单。
自测三问
apt update 后执行 apt install openjdk-17-jdk-headless,期间系统发生了哪几件事?
- 写出「安装 Node 20 并验证」的完整命令序列(Ubuntu 22.04),并说明为什么不能直接 apt install nodejs。
- 在 Rocky Linux 上完成「装 JDK17、看它装了哪些文件、锁定版本」需要哪些命令?
小结一句话
包管理 = 软件源(从哪来)+ 依赖与清单(怎么装)+ 版本控制(装哪个、锁不锁);Ubuntu 走 apt/dpkg,RedHat 走 dnf/rpm,装完必验版本与服务状态——环境安装从此告别「复制粘贴玄学」。
延伸