DEB 软件源完全指南:自建内网 apt 源、sources.list 配置、以及 44 个 apt/dpkg 常用命令速查
Ubuntu/Debian 机器少的时候官方源+国内镜像就够用,到了几十台上百台统一装机、要离线装自研 deb、要稳定可复现的部署环境时就该自建 apt 源了。本文从 0 开始用 Nginx + reprepro 搭一个支持签名、每天自动同步、几百台并发不卡的内网 deb 源,再讲客户端 sources.list 的 dists/pool 结构、signed-by 代替 apt-key 的正确姿势、sources.list.d/*.list 与新版 DEB822 .sources 双格式模板,最后把生产上最常用的 44 条 apt / dpkg / apt-cache 命令按《查 / 装 / 更 / 删+缓存 / 管源+锁版本 / 排障修复》六类全部列好示例。
DEB 软件源完全指南:自建内网 apt 源、sources.list 配置、以及 44 个 apt/dpkg 常用命令速查
适用:Ubuntu 18.04/20.04/22.04/24.04、Debian 10(buster)/11(bullseye)/12(bookworm);命令在所有发行版通用,区别主要是版本代号(suite)和组件名不同。
一. 前置概念:dpkg / deb / apt 三层关系 + 何时该自建源
0.1 三层关系
和 CentOS 的 rpm/yum/dnf 对应,Debian/Ubuntu 也分三层:
| 层级 | 工具 | 职责 | 常见操作 |
|---|---|---|---|
| 底层 | dpkg | 解包 .deb、注册文件、执行 pre/post 脚本、写入 /var/lib/dpkg/status 数据库 | dpkg -i、dpkg -L、dpkg -S、dpkg --purge |
| 包格式 | .deb | ar 打包的压缩包,包含 debian-binary(版本)+ control.tar.xz(元信息 + 脚本)+ data.tar.xz(真实文件,dpkg 1.17 起默认 xz 压缩) | dpkg-deb -R xxx.deb /tmp/x 解包看内容 |
| 上层依赖管理器 | apt / apt-get / aptitude | 算依赖树、从多个源下载、调用 dpkg 依次装、锁版本、回滚、修 broken 状态 | apt update / install / upgrade / full-upgrade / autoremove / install -f |
小常识:Ubuntu 16.04 后官方主推
apt(合并了 apt-get / apt-cache 最常用子命令 + 带进度条),写脚本或要求稳定时仍用apt-get,它输出格式更”可机器解析”且不回退交互。两者参数几乎完全一致,本文全部示例默认apt,需要脚本化时把apt换成apt-get即可。
0.2 什么情况下要自建 apt 源
| 场景 | 要不要自建 | 选哪方案(下文 §1) |
|---|---|---|
| 3 台实验机,能连外网 | 不用,用阿里云/清华源 | - |
| 20~50 台办公开发机,要快、要控制装什么版本 | 建议自建 | 方案 A:Nginx + reprepro |
200+ 台生产服务器,要离线内网,同时要装自研 .deb | 必须自建 | 方案 A 或 C |
| 纯离线环境(物理内网断外网) | 必须自建 | 方案 B:先在有网机 apt-mirror 全量同步 → 拷盘进内网 |
| 合规要求:所有包必须签名 + 审计谁装了什么 | 必须自建 | 方案 C:Aptly 或 Pulp |
| 只想发几个自研的小 deb(<20 个)给少数几台机 | 不用源,scp + apt install ./xxx.deb 就行 | - |
二. 三方案选型速查
| 方案 | 工具栈 | 特点 | 机器数 / 磁盘建议 |
|---|---|---|---|
| A. reprepro + Nginx ⭐推荐中小团队 | nginx / reprepro / gnupg | 支持 InRelease 自动签名、多 suite 多组件、按包名查找方便;每天定时从上游 merge 或手工 include 自研 deb | 50 |
| B. apt-mirror / debmirror 全量镜像 | apt-mirror + Nginx 或本地路径 | 把官方源整个 Pool 全拷下来,Ubuntu 一套 600G+、Debian 一套 800G+,只 mirror amd64 能省一半。适合纯离线。 | 不限,但至少 1T SSD,推荐每天 03:00 同步 |
| C. 企业级:Aptly / Pulp | Aptly / Pulp 3 | 有 Web UI、快照、回滚、多租户、内容视图、合规审计;复杂度高 | 500+ 台 / 多个团队共享源 |
如果你的诉求是「既要能同步官方源的 Base 又要塞自研包进去」——方案 A(reprepro)性价比最高,这篇就按它展开。如果只做自研 deb 发布,连官方镜像都不需要,那方案 A 更轻量。
三. 从 0 搭内网 deb 源(方案 A:Nginx + reprepro)
2.1 机器要求
- OS:Ubuntu 22.04 / Debian 12 都行
- 规格:2C4G(500 台内够用)+ SSD(机械盘做 repodata/签名会很慢)
- 磁盘:
- 只放自研包:100G 撑死
- 要镜像官方 jammy 的 main/restricted:amd64 一个架构约 120G
- 要镜像 jammy 四组件 + backports:300G+
- 防火墙:开 TCP 80(HTTP 内网)或 443(要公网 BasicAuth),如果有 SELinux / AppArmor 记得开 Nginx 的访问目录
2.2 安装依赖
sudo apt update
sudo apt install -y nginx reprepro gnupg apt-utils dpkg-dev curl
sudo systemctl enable --now nginx
sudo systemctl enable --now cron # reprepro 定时同步要用2.3 目录规划:理解 dists/ 与 pool/(这是 deb 源的核心)
新人看 deb 源结构最懵的是为什么有 dists 和 pool 两层。一句话解释:
pool/存真实的 .deb 文件(按”包名首字母”分目录,避免单目录 10w 文件爆炸);dists/<suite>/<component>/binary-amd64/Packages(.gz/.xz)是索引,告诉 apt 哪些 deb 在哪个 pool 路径里、MD5/SHA256 是多少。
源根目录建议放 /srv/apt-repo:
/srv/apt-repo/
├── dists/
│ ├── jammy/ ← suite = Ubuntu 22.04 代号
│ │ ├── InRelease ← 明文+内联GPG签名(推荐)
│ │ ├── Release ← 明文总清单(各 Packages 的大小/Hash)
│ │ ├── Release.gpg ← Release 的 detached 签名
│ │ ├── main/
│ │ │ └── binary-amd64/
│ │ │ ├── Packages ← 文本索引
│ │ │ ├── Packages.gz
│ │ │ ├── Packages.xz
│ │ │ └── Release ← 本段清单
│ │ └── mycompany/ ← 自研组件名(自定义)
│ ├── noble/ ← Ubuntu 24.04
│ ├── focal/ ← Ubuntu 20.04
│ └── bookworm/ ← Debian 12
├── pool/
│ ├── main/ ← 与组件对应
│ │ └── n/ ← 包名首字母
│ │ └── nginx/
│ │ └── nginx-core_1.18.0-0ubuntu1.3_amd64.deb
│ └── mycompany/
│ └── m/
│ └── myapp/
│ └── myapp_2.3.1-1_amd64.deb
└── conf/ ← reprepro 自己的配置(不对外)
├── distributions ← 告诉 reprepro 有哪些 suite/components/arch
└── options ← reprepro 工作目录/压缩方式2.4 配置 reprepro
新建 reprepro 目录:
sudo mkdir -p /srv/apt-repo/conf写 conf/distributions(最关键的一份)。下面例子支持 Ubuntu jammy(22.04) + noble(24.04) 两个发行版,每个发行版有 main(官方同步用)和 mycompany(自研包)两个组件,架构都只用 amd64:
cat | sudo tee /srv/apt-repo/conf/distributions > /dev/null <<'EOF'
Origin: MyCompany
Label: MyCompany Internal APT Mirror
Suite: jammy
Codename: jammy
Architectures: amd64
Components: main mycompany
Description: MyCompany internal apt repository for Ubuntu 22.04
SignWith: default
Pull: jammy-ship-base
Suite: noble
Codename: noble
Architectures: amd64
Components: main mycompany
Description: MyCompany internal apt repository for Ubuntu 24.04
SignWith: default
EOF参数解释:
- Origin/Label:元信息,客户端
apt-cache policy会看到 - Suite:大多数情况和 Codename 一样;若是 stable/testing 抽象 suite 才会不一样
- Architectures:只写
amd64能省一半空间;需要 arm64 再加arm64 - Components:空格分隔;
main/restricted/universe/multiverse是官方四个自定义加mycompany opsdata都行 - SignWith: default:用默认 GPG key 给 Release 签,生成
InRelease+Release.gpg - Pull:高级玩法,可在另一个
conf/pulls/jammy-ship-base定义”从公网官方源拉哪些包塞进来”;简单情况不配也行,用 reposync 式脚本
写 conf/options(工作目录和压缩):
cat | sudo tee /srv/apt-repo/conf/options > /dev/null <<'EOF'
basedir /srv/apt-repo
outdir /srv/apt-repo
dbdir /srv/apt-repo/db
logdir /srv/apt-repo/logs
listdir /srv/apt-repo/lists
morguedir /srv/apt-repo/morgue
ask-passphrase
verbose
EOF2.5 生成 GPG 签名用的私钥(强烈建议做)
虽然 Nginx 纯 HTTP 裸跑也能让客户端
[trusted=yes]用,但那会让 apt 的安全校验形同虚设,等于装任何恶意 deb 都不验签。生产一定要配签名。
# 非交互式生成 RSA 4096 的 GPG key(名字按你公司)
cat >/tmp/gpg-batch <<'EOF'
%no-protection
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: MyCompany APT Repo
Name-Email: infra@mycompany.com
Expire-Date: 0
%commit
EOF
gpg --batch --gen-key /tmp/gpg-batch
# 记下指纹的长 ID,例如 ABCDEF1234567890ABCDEF1234567890AAAAAAAA
gpg --list-secret-keys --keyid-format LONG infra@mycompany.com导出公钥给客户端用(后面客户端 3.4 节会用到):
# 新方式:导出成 GPG keyring 二进制,文件名惯例 <厂商>-archive-keyring.gpg
# 放到源根,客户端 wget 它到 /usr/share/keyrings/ 即可
gpg --export --armor infra@mycompany.com | sudo tee /srv/apt-repo/MyCompany-archive-keyring.asc > /dev/null
gpg --export infra@mycompany.com | sudo tee /srv/apt-repo/MyCompany-archive-keyring.gpg > /dev/null
# 顺便把 UID / 指纹写到一个 README,客户端排错要用
gpg --fingerprint infra@mycompany.com | sudo tee /srv/apt-repo/KEY_FINGERPRINT.txt > /dev/null改 conf/distributions 的 SignWith: 为具体 KEYID(避免默认 key 多了搞混):
SignWith: 1234567890AAAAAAAA ← 长 KEYID 最后 16 位2.6 塞进第一个自研 deb:reprepro include
方式一:手工 include(自研包路径 /tmp/myapp_2.3.1-1_amd64.deb)
cd /srv/apt-repo
# 语法:reprepro include <suite> <changes文件> 或直接 reprepro includedeb <suite> <deb文件>
sudo reprepro includedeb jammy /tmp/myapp_2.3.1-1_amd64.deb # 塞进 jammy 的默认第一个组件
sudo reprepro includedeb -C mycompany jammy /tmp/myapp_2.3.1-1_amd64.deb # 指定组件
sudo reprepro includedeb noble /tmp/myapp_2.3.1-1_amd64.deb # 同时也给 24.04 一份检查现在源里有没有:
sudo reprepro list jammy # 列出 jammy 所有包
sudo reprepro list jammy myapp
# 期望输出:jammy|mycompany|amd64: myapp 2.3.1-1方式二:简单粗暴 dpkg-scanpackages(不用 reprepro 的极简做法)
如果团队就 5 台机、嫌 reprepro 学起来烦,一个目录 + dpkg-scanpackages 也行:
sudo mkdir -p /srv/apt-simple/jammy
cp ~/myapp_*.deb /srv/apt-simple/jammy/
cd /srv/apt-simple/jammy
dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz
dpkg-scanpackages . /dev/null > Packages
# 客户端 baseurl 直接写 "http://repo/jammy ./",末尾这个 "./" 很重要(表示 flat repo 结构)注意:这个极简方案没法自动签名(需要手动写 Release + gpg —clearsign 生成 InRelease),不建议生产。
2.7 从官方源同步常用包(两种方法)
选一个就行:
方法 1:apt download 手工挑(省空间最推荐)
# 在另一台同版本 Ubuntu 上把依赖全拉下来
sudo apt install -y --download-only nginx openssl build-essential libssl-dev
# 下载的 .deb 都在 /var/cache/apt/archives/
ls /var/cache/apt/archives/*.deb
# 拷过去批量 include
for f in /var/cache/apt/archives/*.deb; do
sudo reprepro -C main includedeb jammy "$f"
done方法 2:debmirror 批量拉(需要外网)
注意:debmirror 拉下来的是”裸 dists + pool”,reprepro 的 Packages/Release 由内部数据库生成——不能直接把 .deb 拷进 pool/ 再跑
reprepro export(export 不扫描 pool/)。正确的合并方式是:debmirror 拉完后遍历.deb逐个reprepro includedeb,或者改走 reprepro 原生的conf/updates+reprepro update机制(更稳,自动验签名 + 重建索引)。
sudo apt install -y debmirror
mkdir -p /tmp/debmirror-work
debmirror --arch=amd64 \
--dist=jammy \
--section=main,universe \
--method=http \
--host=mirrors.aliyun.com \
--root=ubuntu \
--ignore-release-gpg \
/tmp/debmirror-work
# 逐个 includedeb 合进仓库(includedeb 会自动 export,无需手动 export)
find /tmp/debmirror-work/pool -name '*.deb' -print0 | \
xargs -0 -I{} sudo reprepro -C main --basedir /srv/apt-repo includedeb jammy {}2.8 Nginx 配置
cat | sudo tee /etc/nginx/conf.d/apt-repo.conf > /dev/null <<'EOF'
server {
listen 80 default_server;
server_name apt.mycompany.com _;
root /srv/apt-repo;
autoindex on; # 方便浏览器直接看包列表
disable_symlinks off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# 索引 InRelease 永远别缓存(否则客户端 apt update 拿旧 Hash)
location ~ ^/dists/.*/(InRelease|Release|Release\.gpg)$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
expires -1;
}
# 真正的 .deb 不会改 hash,缓存一个月省带宽
location ~ \.deb$ {
add_header Cache-Control "public, max-age=2592000";
expires 30d;
}
access_log /var/log/nginx/apt.access.log;
error_log /var/log/nginx/apt.error.log;
}
EOF
sudo nginx -t && sudo systemctl reload nginx测试能拿到 InRelease:
curl -I http://127.0.0.1/dists/jammy/InRelease
# HTTP/1.1 200 OK2.9 每天凌晨自动「合并外部同步 → export → 签 InRelease」
写同步脚本 /usr/local/sbin/apt-repo-sync.sh:
cat | sudo tee /usr/local/sbin/apt-repo-sync.sh > /dev/null <<'EOF'
#!/bin/bash
set -euo pipefail
REPO=/srv/apt-repo
LOG=$REPO/logs/sync-$(date +%Y%m%d).log
mkdir -p "$REPO/logs"
echo "=== $(date) sync start ===" >> "$LOG"
# 1) 你自己的逻辑:比如从制品库拉最新自研 deb 到 $REPO/incoming/
# if [ -d "$REPO/incoming" ]; then
# for suite in jammy noble; do
# for deb in $REPO/incoming/${suite}/*.deb; do
# [ -f "$deb" ] || continue
# reprepro -V -C mycompany --basedir "$REPO" includedeb "$suite" "$deb" && rm -f "$deb"
# done
# done
# fi
# 2) export 重新生成 dists/* 并签 InRelease
reprepro -V --basedir "$REPO" export 2>&1 | tee -a "$LOG"
# 3) 清 7 天前日志
find "$REPO/logs" -mtime +7 -name '*.log' -delete 2>/dev/null || true
echo "=== $(date) sync done ===" >> "$LOG"
EOF
sudo chmod +x /usr/local/sbin/apt-repo-sync.sh加到 crontab 每天 03:15 跑(避过官方 mirror 的 00:00~03:00 流量高峰):
(crontab -l 2>/dev/null; echo "15 3 * * * /usr/local/sbin/apt-repo-sync.sh") | crontab -四. 客户端配置:sources.list / sources.list.d/*.list / DEB822 .sources 三种格式
3.1 历史演进 + 选哪个
| 格式 | 文件 | 形态 | 何时引入 | 推荐度 |
|---|---|---|---|---|
| 一行一源经典格式 | /etc/apt/sources.list、/etc/apt/sources.list.d/*.list | deb [选项] URL suite comp1 comp2 | 1990s 延续至今 | ⭐全版本通用,抄就完了 |
| DEB822 结构化格式 | /etc/apt/sources.list.d/*.sources | Types: deb\nURIs: ... 类 YAML | Ubuntu 24.04 默认使用 | ⭐新机器优先 |
| 单行 flat repo | *.list | deb URL ./(suite 部分写 ./) | - | 极简 dpkg-scanpackages 本地源用 |
⚠️ 新手坑:Ubuntu 24.04 起
/etc/apt/sources.list默认是空的,真正的源在/etc/apt/sources.list.d/ubuntu.sources(DEB822 格式)。所以 24.04 机器要关公网源就去改这个.sources,别老盯着 sources.list 找不到。
3.2 经典 .list 格式每个字段拆开讲
deb [ arch=amd64 signed-by=/usr/share/keyrings/mycompany-archive-keyring.gpg ] http://apt.mycompany.com jammy main mycompany
|___| |____________________________________________________________________________| |_________________________| |____| |____________|
1 │ 2 3 4 5
│
└─ 注意方括号和后面 URL 之间有空格,方括号内的多个选项用半角空格分隔debvsdeb-src:deb要二进制包(99% 情况)deb-src要源代码包(apt source pkgname下载.dsc + .orig.tar.gz + debian.tar.xz),不做源码编译可以全注释掉
- 可选项(方括号内,可以省略):
arch=amd64:限定架构,源只提供 amd64 的话一定要加,避免 apt 查 arm64 报 404signed-by=/usr/share/keyrings/xxx.gpg:新版推荐写法,表示用这把 key 验源签名,全局信任的来源不写默认找/etc/apt/trusted.gpg.d/*.gpgtrusted=yes:仅内网自签源临时排障,关闭 GPG 校验,严禁在公网源写allow-insecure=yes:允许 HTTP + 无签名,更危险
- URL:源根地址,结尾不要加
/dists/jammy/...,就到根 - Suite(发行代号):
jammy / noble / focal / bookworm / bullseye / buster- 也能写抽象 suite:
stable / testing / unstable(Debian)、jammy-updates / jammy-security / jammy-backports - ⚠️ 写什么就看源根的
dists/下有什么目录,不存在的 suite 会报 “Release file for X is not available yet”
- 也能写抽象 suite:
- Components(组件):空格分隔多个
- Ubuntu 官方四件套:
main restricted universe multiverse - Debian 官方四件套(Debian 12 bookworm 起):
main contrib non-free non-free-firmware(前三者合称传统三件套,Debian 12 起 non-free-firmware 单独拆出成第四件) - 你自己的组件:
mycompany / opsdata / infra-bin随便起
- Ubuntu 官方四件套:
3.3 新版 DEB822 .sources 格式(Ubuntu 24.04+ 推荐)
对应上面同一行,DEB822 写法更易读:
Types: deb
URIs: http://apt.mycompany.com
Suites: jammy
Components: main mycompany
Architectures: amd64
Signed-By: /usr/share/keyrings/mycompany-archive-keyring.gpg优点:多行;不会把 signed-by、arch 塞一行里难看清;Ubuntu 24.04 默认。
3.4 公钥导入的正确姿势(不要再用 apt-key add!)
apt-key 从 2020 年起就被官方标 deprecated:其中 add 子命令在 22.04 起会打大段弃用警告,man page 明确「apt-key(8) will last be available in Debian 12 and Ubuntu 24.04」——24.04 仍带该命令(仅弃用),Ubuntu 25.04+ / Debian 13(trixie) 起彻底移除。不管机器上还能不能用,新的部署一律不要再写 apt-key add。
原因是:
apt-key add会把 key 塞进全局可信池/etc/apt/trusted.gpg.d/,这把 key 等于能”签发任何源”,一旦这把 key 泄露/恶意制作,你所有第三方源都能被污染。现在要求:一把 key 只验它自己那个源,靠signed-by=和源一一绑定。
三种推荐方式,任一即可,优先方式 A:
# ====== 方式 A:源上已提供 .gpg(二进制 keyring),直接下到 /usr/share/keyrings/ ======
sudo mkdir -p /usr/share/keyrings
sudo curl -fsSL http://apt.mycompany.com/MyCompany-archive-keyring.gpg \
-o /usr/share/keyrings/mycompany-archive-keyring.gpg
# 校验 keyring 存在
gpg --no-default-keyring --keyring /usr/share/keyrings/mycompany-archive-keyring.gpg --list-keys# ====== 方式 B:源只提供 .asc(ASCII 装甲),转成 gpg ======
curl -fsSL http://apt.mycompany.com/MyCompany-archive-keyring.asc \
| gpg --dearmor \
| sudo tee /usr/share/keyrings/mycompany-archive-keyring.gpg >/dev/null# ====== 方式 C:你只有 KEYID 最后 8 位,从公网 keyserver 拉 ======
gpg --no-default-keyring \
--keyring /usr/share/keyrings/mycompany-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 \
--recv-keys AAAAAAAA公司内网没 keyserver 就别用 C。
3.5 完整的可复制模板:MyCompany-EL9 对应 → MyCompany-Ubuntu.list / .sources
模板 1:经典 .list(Ubuntu 20.04/22.04 推荐,全版本兼容)
cat | sudo tee /etc/apt/sources.list.d/10-mycompany.list > /dev/null <<'EOF'
# MyCompany 内部 deb 源:Ubuntu 22.04 jammy
# 公钥位置:/usr/share/keyrings/mycompany-archive-keyring.gpg
deb [ arch=amd64 signed-by=/usr/share/keyrings/mycompany-archive-keyring.gpg ] http://apt.mycompany.com jammy main mycompany
# 如果要源码包再打开下面这行
# deb-src [ arch=amd64 signed-by=/usr/share/keyrings/mycompany-archive-keyring.gpg ] http://apt.mycompany.com jammy main mycompany
EOF模板 2:DEB822 .sources(Ubuntu 24.04+ 推荐)
cat | sudo tee /etc/apt/sources.list.d/10-mycompany.sources > /dev/null <<'EOF'
Types: deb
URIs: http://apt.mycompany.com
Suites: noble noble-updates noble-security
Components: main mycompany
Architectures: amd64
Signed-By: /usr/share/keyrings/mycompany-archive-keyring.gpg
EOF版本代号速查表:别把 jammy/noble 写反了—— Ubuntu:18.04 = bionic / 20.04 = focal / 22.04 = jammy / 24.04 = noble Debian:10 = buster / 11 = bullseye / 12 = bookworm / 13 = trixie
3.6 关公网默认源(强制只用内网源)
# Ubuntu 22.04:注释 /etc/apt/sources.list
sudo sed -i 's/^deb /# deb /g' /etc/apt/sources.list
# Ubuntu 24.04:关公网默认源。注意默认 ubuntu.sources 没有 Enabled 字段(缺省即启用),所以要补这行,而不是替换不存在的 Enabled: yes
sudo sed -i '/^Types:/i Enabled: no' /etc/apt/sources.list.d/ubuntu.sources
# 想恢复就 sudo sed -i '/^Enabled: no$/d' /etc/apt/sources.list.d/ubuntu.sources3.7 生效三板斧:clean → update → 真装一个验证
sudo apt clean
sudo apt update # 这里会出现命中我司源的 Hit: X http://apt.mycompany.com jammy InRelease
# 验证:找个自研包装
apt-cache policy myapp
# 期望能看到 Candidate 是 2.3.1-1 且来源是 apt.mycompany.com
sudo apt install -y myapp五. 进阶:公网 BasicAuth + HTTPS
内网源直接 HTTP 够了,要跨公网或对外开的时候再做。
4.1 服务端 Nginx 加 BasicAuth
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.apt-users opsuser # 设密码
sudo htpasswd /etc/nginx/.apt-users deployer # 加第二个用户,去掉 -c在 Nginx server 块里补:
auth_basic "MyCompany APT";
auth_basic_user_file /etc/nginx/.apt-users;4.2 客户端配密码:不要写在 sources.list 明文里!
很多人会写成 deb https://opsuser:Passw0rd@apt.mycompany.com ...,这等于把密码写进世界可读的 .list 文件。用独立的 auth.conf:
cat | sudo tee /etc/apt/auth.conf.d/mycompany.conf > /dev/null <<'EOF'
machine apt.mycompany.com
login opsuser
password Passw0rd
EOF
sudo chown root:root /etc/apt/auth.conf.d/mycompany.conf
sudo chmod 600 /etc/apt/auth.conf.d/mycompany.conf如果用自签 HTTPS,要么把 CA 装进系统信任,要么在 /etc/apt/apt.conf.d/99-mycompany 里关:
Acquire::https::apt.mycompany.com::Verify-Peer "true";
Acquire::https::apt.mycompany.com::Verify-Host "true";
# 如果真要跳过(不推荐):
# Acquire::https::apt.mycompany.com::Verify-Peer "false";
# Acquire::https::apt.mycompany.com::Verify-Host "false";六. 44 条最常用 apt / dpkg / apt-cache 命令速查
按《查 / 装 / 更 / 删+缓存 / 管源+锁版本 / 排障修复》六大类,每条带最典型场景的示例。Ubuntu 22.04 / Debian 12 均实测可用。
5.1 查
| # | 命令 | 什么时候用 | 典型输出 / 说明 |
|---|---|---|---|
| 1 | apt list --installed | 列本机所有包装了啥 | 等价 dpkg -l,但输出更友好;后面加 | grep nginx 搜 |
| 2 | dpkg -l / dpkg -l libssl* | 底层查包状态 | ii = 成功安装并安装成功 / rc = 已删但配置还在 / iU = 半装中没配置(排障看这状态列最有用) |
| 3 | dpkg -L nginx-core | 某包装了哪些文件到系统 | = yum/rpm 的 rpm -ql |
| 4 | dpkg -S /usr/sbin/nginx / dpkg -S /bin/ss | 这个文件来自哪个包 | = rpm -qf;找不到就用下面 5 |
| 5 | apt-file search /usr/sbin/ifconfig | 文件没装到本地时也要查(查源里谁会装它) | 需先 apt install apt-file && apt-file update;= dnf provides ’*/ifconfig’ |
| 6 | apt-cache search nginx | 搜源里有没有某包 | 模糊搜;精搜用 apt search --names-only ^nginx |
| 7 | apt-cache policy nginx ⭐ | 看”候选版本+每个源有啥版本+当前装了哪个” | 最常用调试命令。类似 yum list --showduplicates,还能看出版本优先级 |
| 8 | apt-cache madison nginx | 只看候选列表表格式 | 输出一行一个版本,排障用 |
| 9 | apt show nginx-core | 看元信息(依赖、大小、主页) | 新版友好;旧版用 apt-cache show nginx-core |
| 10 | apt-cache depends nginx | nginx 依赖哪些包 | 只看硬依赖加 --important;对应配置项 APT::Cache::Important(apt-cache(8) 查的到,用来只打印 Depends/Pre-Depends 降噪音) |
| 11 | apt-cache rdepends libssl3 | 反向依赖:哪些包依赖 libssl3 | 想升级/删除底层库前先看,防止炸 |
5.2 装
| # | 命令 | 什么时候用 | 说明 |
|---|---|---|---|
| 12 | sudo apt update ⭐ | 每次装/改源后必跑 | 拉 InRelease / Packages;不做的话永远报 Unable to locate package |
| 13 | sudo apt install -y nginx | 按名装 | -y 免确认,脚本里一定要加 |
| 14 | sudo apt install -y nginx=1.18.0* / nginx=1.18.0-0ubuntu1.3 | 装指定版本 | 版本号抄自 apt-cache madison |
| 15 | sudo apt install -y ./myapp_2.3.1-1_amd64.deb ⭐ | 装本地 deb 自动找依赖 | 最佳实践:必须 ./ 前缀!比 dpkg -i 再 apt install -f 优雅 |
| 16 | sudo dpkg -i myapp.deb | 强制装不处理依赖 | 装完若报 unconfigured,马上跑 apt install -f 补 |
| 17 | sudo apt build-dep -y nginx | 编译 nginx 源码前装编译依赖 | 写 Makefile/CI 常用 |
| 18 | sudo apt source nginx | 下载源码包到当前目录 | 会下 .dsc .orig.tar.gz .debian.tar.xz,对应 sources.list 里要写 deb-src |
5.3 更
| # | 命令 | 什么时候用 | 说明 |
|---|---|---|---|
| 19 | apt list --upgradable | 看哪些包可升级 | 不会装任何东西,纯看 |
| 20 | sudo apt update && sudo apt upgrade -y ⭐ | 日常安全更新(最推荐) | 不删包,遇到要新增/删依赖的升级会把那几个包”hold back”不升 |
| 21 | sudo apt full-upgrade -y | 大版本内核/跨大版本前 | 允许智能删包,相当于旧的 dist-upgrade;会把 held back 的一起处理 |
| 22 | sudo apt autoremove -y | 清没人用的孤儿依赖 | 装了一堆 build-essential 后卸载主包,用这个能彻底清掉 |
| 23 | unattended-upgrades | 系统静默打安全补丁 | dpkg-reconfigure -plow unattended-upgrades 可一键开启只装 security |
5.4 删 + 清缓存
| # | 命令 | 什么时候用 | 说明 |
|---|---|---|---|
| 24 | sudo apt remove -y nginx | 删包,留配置文件 | /etc/nginx 里的东西不会动 |
| 25 | sudo apt purge -y nginx | 删包 + 删配置 | 生产谨慎!purge 会把 /etc/nginx 全删 |
| 26 | sudo apt autoremove --purge -y | 孤儿依赖+配置都清 | 干净 |
| 27 | sudo apt clean | 清 /var/cache/apt/archives/*.deb 下载缓存 | 磁盘爆时常用;更新没空间也跑它 |
| 28 | sudo apt autoclean | 只清”源里已经不存在旧版本”的缓存 | 更温和 |
5.5 管源 + 锁版本
| # | 命令 | 什么时候用 | 说明 |
|---|---|---|---|
| 29 | apt edit-sources | 快速打开默认 sources.list 编辑 | 会走 select-editor 选 nano/vim;真正管第三方源还是去 sources.list.d/ 更清晰 |
| 30 | sudo add-apt-repository ppa:nginx/stable | Ubuntu 加 PPA | 自身未被弃用、无 deprecated 提示;Ubuntu 23.04+ 写 .sources 并把 key 放在 /etc/apt/keyrings/ 绑定 Signed-By(22.04 及以前进 trusted.gpg.d/ 全局) |
| 31 | find /etc/apt/sources.list.d -type f -print -exec cat {} \; | 排”到底装了哪些第三方源” | 一次看全 |
| 32 | sudo apt-mark hold linux-image-generic ⭐ | 锁版本不让升(比如怕内核升挂) | unhold 解锁、showhold 看哪些锁了;等价 /etc/apt/preferences.d/ 配优先级 |
| 33 | sudo apt-mark unhold linux-image-generic | 解锁 | |
| 34 | apt-mark showhold | 列所有被 hold 的 | 排查”为什么某包一直不更新”第一条查这个 |
5.6 排障 + 修复 broken 状态
| # | 命令 | 什么时候用 | 说明 |
|---|---|---|---|
| 35 | sudo apt install -f ⭐⭐⭐ | 装一半失败了、报 “Unmet dependencies. Try ‘apt —fix-broken install’.” | 第一条就该跑,修依赖 + 补 dpkg 半拉子状态 |
| 36 | sudo dpkg --configure -a ⭐⭐⭐ | 上次装到一半关机/断电了,报 “dpkg was interrupted, you must manually run dpkg —configure -a” | 先 36 再 35,组合拳能修 80% broken state |
| 37 | dpkg --audit / apt-get check | 查有没有半装/半配 | 没输出就是没问题 |
| 38 | sudo rm -rf /var/lib/apt/lists/* && sudo apt update ⭐⭐ | “Hash Sum mismatch” 首修 | 客户端缓存了新旧混合的 Packages;这是 deb 世界第一经典坑,先清再 update 10 次能中 9 次 |
| 39 | 锁 /var/lib/dpkg/lock-frontend:`ps -ef | grep -E ‘apt | dpkg | unattended’ ; kill |
| 40 | dpkg 状态裂了终极修复(/var/lib/dpkg/status 坏):sudo cp /var/lib/dpkg/status-old /var/lib/dpkg/status ; sudo cp /var/lib/dpkg/available-old /var/lib/dpkg/available ; sudo dpkg --clear-avail ; sudo dpkg --configure -a ; sudo apt install -f | 手动删了 lock 或 rm 了 dpkg 目录 | 慎用,真到 dpkg 打不开状态才用 |
| 41 | DEBIAN_FRONTEND=noninteractive sudo apt install -y tzdata | CI / Dockerfile 里装包不要弹交互 | tzdata / keyboard-configuration 最爱弹;这个 env 让 debconf 全选默认 |
| 42 | apt-get -o Debug::pkgAcquire::Worker=1 install -y myapp | 下包慢 / 要走代理 / 想观察 TCP 行为 | 调 apt 代理的调试器 |
| 43 | “packages have been kept back” 三种解决: ① sudo apt install --with-new-pkgs <包名>(推荐,只升被卡的那几个)② sudo apt full-upgrade -y(全处理,胆大的来)③ phased update 被卡 → 等 1~2 天自然推进;急就加 -o APT::Get::Always-Include-Phased-Updates=true | 跑 upgrade 后有包 “kept back” | 原因通常三选一:新增依赖 / 要删旧依赖 / Ubuntu phased updates 灰度比例还没轮到你 |
| 44 | sudo apt clean && sudo df -h /var/cache/apt | /var/cache/apt/archives 满了装不上包 | 第一反应 clean |
七. 10 个生产最高频的坑(按出现率排序)
🐞 1. E: Unable to locate package myapp
排查顺序(按概率从高到低):
- 没
apt update→ 先跑sudo apt update,还报错看它报的是哪个源 404/Release 找不到 - sources.list/sources 的 suite 和组件写错:比如 jammy 的包写成 noble、或者
mycompany组件没写进那行 - 架构对不上:源只提供 amd64,但这台是 arm64;加
[arch=amd64]或者源端加 arm64 - 源端根本没生成 Packages / InRelease:最常见!去服务端:
sudo reprepro list jammy myapp # 如果没东西,重 includedeb sudo reprepro export curl -sI http://apt.mycompany.com/dists/jammy/mycompany/binary-amd64/Packages.gz - 代理缓存旧 InRelease:源端改了东西客户端拿旧,客户端
rm /var/lib/apt/lists/*重 update
🐞 2. W: GPG error: http://apt.mycompany.com jammy InRelease: The following signatures couldn’t be verified because the public key is not available: NO_PUBKEY AAAAABBBBBCCCC1234
99% 原因: 公钥没装对位置或 signed-by 路径指错。 修:
# 先从报错里取 NO_PUBKEY 后面那串(现在是 KEYID 后 16 位)
# 看 /usr/share/keyrings/ 里对应的公钥文件存在不
ls -la /usr/share/keyrings/mycompany-archive-keyring.gpg
# 看这个 keyring 里真的有那把 key 吗
gpg --no-default-keyring --keyring /usr/share/keyrings/mycompany-archive-keyring.gpg --list-keys --keyid-format LONG AAAAABBBBBCCCC1234如果 key 不在:重按 §3.4 下公钥;如果在但还是报 → 检查 .list 里的 signed-by= 路径有没有写绝对路径(不能相对)。
🐞 3. E: Failed to fetch … 404 Not Found [IP: xxxxx]
看它缺啥:
... dists/jammy/mycompany/binary-arm64/Packages 404→ 源端没提供 arm64,在 .list 里加[arch=amd64]... pool/main/x/xxx.deb 404→ 源端 pool 里真没这个 deb(InRelease 写了但文件丢了),服务端重reprepro export
🐞 4. E: Hash Sum mismatch / E: Unable to fetch … Hash mismatch
首修 §5.6 的 38:rm -rf /var/lib/apt/lists/* ; apt update。 还不行:
- 源端在同步中 → 等 2 分钟再试
- 中间有 CDN/Nginx 缓存了旧的 InRelease → 源端 Nginx 里 InRelease/Release 加了
no-cache no-store(见 2.8)没? - 真正该检查时间差的是 “Release file … is not valid yet / expired”:客户端
timedatectl status看 NTP,注意 Hash mismatch 本身与系统时间无关,别走错排查方向
🐞 5. “The following packages have been kept back”
见 §5.6 表格 43。90% 情况用 apt install --with-new-pkgs <那几个包名>,不要上来就 full-upgrade(会删一堆意想不到的)。
🐞 6. dpkg frontend lock 被占:另一个 apt/dpkg 在跑
# 先 ps 看是谁,等它自己退出。不要直接 rm lock 文件!
# 急的话停掉相关服务再装:
sudo systemctl stop unattended-upgrades.service apt-daily.service apt-daily-upgrade.service🐞 7. dpkg -i 装完报 dependency problems - leaving unconfigured
错误姿势:dpkg -i xxx.deb → 报依赖错 → 然后 dpkg --configure -a 还不行。 正确姿势:用 §5.2 的 15 apt install -y ./xxx.deb(./ 不能省)。一次搞定依赖。
🐞 8. debconf: unable to initialize frontend: Dialog
装包环境没有终端。Docker / CI 里永远加:
DEBIAN_FRONTEND=noninteractive sudo apt install -y <packages>系统层面设默认:echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
🐞 9. You don’t have enough free space in /var/cache/apt/archives/
首修 apt clean。如果 /var 分区就小,考虑:
# 移动缓存目录到大盘(比如 /data)
sudo mkdir -p /data/apt-cache
echo 'Dir::Cache::Archives "/data/apt-cache";' | sudo tee /etc/apt/apt.conf.d/00-cache-dir🐞 10. apt-key is deprecated / Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8))
别再用 apt-key add 和 /etc/apt/trusted.gpg.d/(全局可信)了。
- 改用 §3.4:
/usr/share/keyrings/<厂商>-archive-keyring.gpg+.list里加signed-by= - 已有的老机器要整改:把
apt-key list里第三方 key 一个个export出来搬到/usr/share/keyrings/再在所有对应.list里绑 signed-by,最后apt-key del <KEYID>从全局池删掉。
自检清单(每次搭完一套新源对着勾):
-
curl -sI http://apt/dists/<suite>/InRelease→ 200 且没被缓存(Cache-Control: no-store) -
curl -sL http://apt/MyCompany-archive-keyring.gpg \| gpg --list-packets能看见 key UID - 新装一台干净客户端:按 §3 配 →
apt update不报 NO_PUBKEY 与 404 -
apt-cache policy myapp能看到候选版本且来源是内网源 -
sudo apt install -y myapp能装上,dpkg -L myapp有文件 - 断公网只留内网 DNS 的情况下,
apt install -y nginx也能装(镜像完整性验证) - 每天同步脚本日志里
reprepro export没报错
后续需要的常见扩展方向:镜像 Ubuntu 全部四组件(apt-mirror)、Aptly 快照 + 版本视图一键回滚、Harbor/JFrog 等制品库原生 APT 支持(无需自搭 Nginx+reprepro)。