⚠️ 提示:本站 AI 助手使用博主个人 API Token,没事别特么瞎用。
运维 · · 约 56 分钟阅读

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
包格式.debar 打包的压缩包,包含 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 自研 deb50300 台 / 200G500G(只镜像常用组件+自研)
B. apt-mirror / debmirror 全量镜像apt-mirror + Nginx 或本地路径把官方源整个 Pool 全拷下来,Ubuntu 一套 600G+、Debian 一套 800G+,只 mirror amd64 能省一半。适合纯离线。不限,但至少 1T SSD,推荐每天 03:00 同步
C. 企业级:Aptly / PulpAptly / 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
EOF

2.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 OK

2.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/*.listdeb [选项] URL suite comp1 comp21990s 延续至今⭐全版本通用,抄就完了
DEB822 结构化格式/etc/apt/sources.list.d/*.sourcesTypes: deb\nURIs: ... 类 YAMLUbuntu 24.04 默认使用⭐新机器优先
单行 flat repo*.listdeb 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 之间有空格,方括号内的多个选项用半角空格分隔
  1. deb vs deb-src:
    • deb 要二进制包(99% 情况)
    • deb-src 要源代码包(apt source pkgname 下载 .dsc + .orig.tar.gz + debian.tar.xz),不做源码编译可以全注释掉
  2. 可选项(方括号内,可以省略):
    • arch=amd64:限定架构,源只提供 amd64 的话一定要加,避免 apt 查 arm64 报 404
    • signed-by=/usr/share/keyrings/xxx.gpg:新版推荐写法,表示用这把 key 验源签名,全局信任的来源不写默认找 /etc/apt/trusted.gpg.d/*.gpg
    • trusted=yes:仅内网自签源临时排障,关闭 GPG 校验,严禁在公网源写
    • allow-insecure=yes:允许 HTTP + 无签名,更危险
  3. URL:源根地址,结尾不要加 /dists/jammy/...,就到根
  4. 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”
  5. 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 随便起

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.sources

3.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 查

#命令什么时候用典型输出 / 说明
1apt list --installed列本机所有包装了啥等价 dpkg -l,但输出更友好;后面加 | grep nginx 搜
2dpkg -l / dpkg -l libssl*底层查包状态ii = 成功安装并安装成功 / rc = 已删但配置还在 / iU = 半装中没配置(排障看这状态列最有用)
3dpkg -L nginx-core某包装了哪些文件到系统= yum/rpm 的 rpm -ql
4dpkg -S /usr/sbin/nginx / dpkg -S /bin/ss这个文件来自哪个包= rpm -qf;找不到就用下面 5
5apt-file search /usr/sbin/ifconfig文件没装到本地时也要查(查源里谁会装它)需先 apt install apt-file && apt-file update;= dnf provides ’*/ifconfig’
6apt-cache search nginx搜源里有没有某包模糊搜;精搜用 apt search --names-only ^nginx
7apt-cache policy nginx ⭐看”候选版本+每个源有啥版本+当前装了哪个”最常用调试命令。类似 yum list --showduplicates,还能看出版本优先级
8apt-cache madison nginx只看候选列表表格式输出一行一个版本,排障用
9apt show nginx-core看元信息(依赖、大小、主页)新版友好;旧版用 apt-cache show nginx-core
10apt-cache depends nginxnginx 依赖哪些包只看硬依赖加 --important;对应配置项 APT::Cache::Important(apt-cache(8) 查的到,用来只打印 Depends/Pre-Depends 降噪音)
11apt-cache rdepends libssl3反向依赖:哪些包依赖 libssl3想升级/删除底层库前先看,防止炸

5.2 装

#命令什么时候用说明
12sudo apt update ⭐每次装/改源后必跑拉 InRelease / Packages;不做的话永远报 Unable to locate package
13sudo apt install -y nginx按名装-y 免确认,脚本里一定要加
14sudo apt install -y nginx=1.18.0* / nginx=1.18.0-0ubuntu1.3装指定版本版本号抄自 apt-cache madison
15sudo apt install -y ./myapp_2.3.1-1_amd64.deb ⭐装本地 deb 自动找依赖最佳实践:必须 ./ 前缀!比 dpkg -i 再 apt install -f 优雅
16sudo dpkg -i myapp.deb强制装不处理依赖装完若报 unconfigured,马上跑 apt install -f 补
17sudo apt build-dep -y nginx编译 nginx 源码前装编译依赖写 Makefile/CI 常用
18sudo apt source nginx下载源码包到当前目录会下 .dsc .orig.tar.gz .debian.tar.xz,对应 sources.list 里要写 deb-src

5.3 更

#命令什么时候用说明
19apt list --upgradable看哪些包可升级不会装任何东西,纯看
20sudo apt update && sudo apt upgrade -y ⭐日常安全更新(最推荐)不删包,遇到要新增/删依赖的升级会把那几个包”hold back”不升
21sudo apt full-upgrade -y大版本内核/跨大版本前允许智能删包,相当于旧的 dist-upgrade;会把 held back 的一起处理
22sudo apt autoremove -y清没人用的孤儿依赖装了一堆 build-essential 后卸载主包,用这个能彻底清掉
23unattended-upgrades系统静默打安全补丁dpkg-reconfigure -plow unattended-upgrades 可一键开启只装 security

5.4 删 + 清缓存

#命令什么时候用说明
24sudo apt remove -y nginx删包,留配置文件/etc/nginx 里的东西不会动
25sudo apt purge -y nginx删包 + 删配置生产谨慎!purge 会把 /etc/nginx 全删
26sudo apt autoremove --purge -y孤儿依赖+配置都清干净
27sudo apt clean清 /var/cache/apt/archives/*.deb 下载缓存磁盘爆时常用;更新没空间也跑它
28sudo apt autoclean只清”源里已经不存在旧版本”的缓存更温和

5.5 管源 + 锁版本

#命令什么时候用说明
29apt edit-sources快速打开默认 sources.list 编辑会走 select-editor 选 nano/vim;真正管第三方源还是去 sources.list.d/ 更清晰
30sudo add-apt-repository ppa:nginx/stableUbuntu 加 PPA自身未被弃用、无 deprecated 提示;Ubuntu 23.04+ 写 .sources 并把 key 放在 /etc/apt/keyrings/ 绑定 Signed-By(22.04 及以前进 trusted.gpg.d/ 全局)
31find /etc/apt/sources.list.d -type f -print -exec cat {} \;排”到底装了哪些第三方源”一次看全
32sudo apt-mark hold linux-image-generic ⭐锁版本不让升(比如怕内核升挂)unhold 解锁、showhold 看哪些锁了;等价 /etc/apt/preferences.d/ 配优先级
33sudo apt-mark unhold linux-image-generic解锁
34apt-mark showhold列所有被 hold 的排查”为什么某包一直不更新”第一条查这个

5.6 排障 + 修复 broken 状态

#命令什么时候用说明
35sudo apt install -f ⭐⭐⭐装一半失败了、报 “Unmet dependencies. Try ‘apt —fix-broken install’.”第一条就该跑,修依赖 + 补 dpkg 半拉子状态
36sudo dpkg --configure -a ⭐⭐⭐上次装到一半关机/断电了,报 “dpkg was interrupted, you must manually run dpkg —configure -a”先 36 再 35,组合拳能修 80% broken state
37dpkg --audit / apt-get check查有没有半装/半配没输出就是没问题
38sudo 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 ‘aptdpkgunattended’ ; kill`
40dpkg 状态裂了终极修复(/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 打不开状态才用
41DEBIAN_FRONTEND=noninteractive sudo apt install -y tzdataCI / Dockerfile 里装包不要弹交互tzdata / keyboard-configuration 最爱弹;这个 env 让 debconf 全选默认
42apt-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 灰度比例还没轮到你
44sudo apt clean && sudo df -h /var/cache/apt/var/cache/apt/archives 满了装不上包第一反应 clean

七. 10 个生产最高频的坑(按出现率排序)

🐞 1. E: Unable to locate package myapp

排查顺序(按概率从高到低):

  1. 没 apt update → 先跑 sudo apt update,还报错看它报的是哪个源 404/Release 找不到
  2. sources.list/sources 的 suite 和组件写错:比如 jammy 的包写成 noble、或者 mycompany 组件没写进那行
  3. 架构对不上:源只提供 amd64,但这台是 arm64;加 [arch=amd64] 或者源端加 arm64
  4. 源端根本没生成 Packages / InRelease:最常见!去服务端:
    sudo reprepro list jammy myapp   # 如果没东西,重 includedeb
    sudo reprepro export
    curl -sI http://apt.mycompany.com/dists/jammy/mycompany/binary-amd64/Packages.gz
  5. 代理缓存旧 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)。

问问 AI