如何对 Debian 软件包进行版本特定的备份/恢复?

Tom*_*che 5 backup debian package-management restore

概括

请注意version-specific问题标题!这个问题不是,如何

  1. 在一台 Debian 主机上,捕获所有已安装软件包的列表。
  2. 在另一台 Debian 主机上,安装列出的软件包的最新版本。

这个问题更确切地说是如何

  1. 在某个时间点的 Debian 主机上,
    1. 捕获包含以下内容的 映射
      • 每个已安装包的名称
      • 当前安装的每个软件包的版本
    2. ... 所需的任何附加数据
  2. 稍后在同一主机上恢复包映射,以便随后
    1. 仅安装包映射中的包
    2. 对于地图中的每个包,仅安装适当的版本

细节

动机

最近,我对我的一个 Debian 机器进行了一次“大更新”(即涉及大量软件包或内核等主要软件包,或两者兼而有之),但它没有“干净地”更新(即没有重大问题)。将来,当我对 Debian 机器应用错误更新时,我希望能够将(a)同一机器上设置的软件包(b)回滚到以前的版本。(之前没有进行图像备份,这很慢。)不幸的是我不知道该怎么做。我知道如何

  1. 在一个 Debian 机器上,捕获其已安装软件包集中的每个软件包名称的列表。
  2. 在另一个框中,安装列表中每个软件包的最新版本。

感谢许多讨论如何做到这一点的网页,包括几个 SE。大多数(包括这个这个这个)都会或多或少地进行复杂的变化

  1. 备份: dpkg --get-selections > ${PACKAGE_LIST_FILEPATH}
  2. 恢复: dpkg --set-selections < ${PACKAGE_LIST_FILEPATH}

可能是关于这个主题的最全面的(也是最脚本化的,我更喜欢的)SE 是这个:它是少数几个推荐备份存储库列表(使用cp -R /etc/apt/sources.list*)和存储库密钥(使用apt-key exportall)的 SE 之一,但它也使用dpkg上面的方法。此 SE建议使用aptitude(FWIW,我更喜欢,并且怀疑它比dpkg) 进行备份和安装,并且此 SE建议deborphan进行备份。

但以上都没有保存包版本,因此没有一个适合我的用例。(IIUC——我错过了什么吗?)

假设

显然,人们必须从自己的设备备份(并能够根据需要恢复)任意文件。同样,人们应该能够备份/恢复软件包,并将其集成到定期运行的备份工具中。幸运的是,包列表的备份/恢复是很好理解的。可以使用bash以下代码进行备份

## setup

PACKAGE_LIST_FILENAME='package.list'         # see use below
REPO_KEYS_FILENAME='repo.keys'               # ditto
DATE_FORMAT="%Y%m%d_%H%M%S"
BACKUP_TIMESTAMP="$(date +${DATE_FORMAT})"   # get current timestamp, to the second

# Parent directory of that to which you will backup.
BACKUP_ROOT="/media/$(whoami)/my_backup_drive"
BACKUP_DIR="${BACKUP_ROOT}/backup_${BACKUP_TIMESTAMP}"
echo -e "Backing up to ${BACKUP_DIR}" # debugging
# TODO: check BACKUP_DIR is writable, has sufficient freespace, etc

# ASSERT: all following are subdirs of BACKUP_DIR
ETC_BACKUP_DIR="${BACKUP_DIR}/etc"           # backup /etc , typically not large
PKG_BACKUP_DIR="${BACKUP_DIR}/packages"
REPO_BACKUP_DIR="${BACKUP_DIR}/repos"   # duplicates some of /etc, but disk is cheap ...
for DIR in \
  "${ETC_BACKUP_DIR}" \
  "${PKG_BACKUP_DIR}" \
  "${REPO_BACKUP_DIR}" \
; do
  # make the backup dirs
  mkdir -p "${DIR}"  # TODO: test retval/errorcode, exit on failure
done

PACKAGE_LIST_FILEPATH="${PKG_BACKUP_DIR}/${PACKAGE_LIST_FILENAME}"
touch "${PACKAGE_LIST_FILEPATH}"             # TODO: check that it's writable, exit if not

REPO_KEYS_FILEPATH="${REPO_BACKUP_DIR}/${REPO_KEYS_FILENAME}"
touch "${REPO_KEYS_FILEPATH}"                # TODO: check that it's writable, exit if not

## backup
## TODO: for all following: test retval/errorcode, exit on failure
## TODO: give user some progress indication (e.g., echo commands)

deborphan -a --no-show-section > "${PACKAGE_LIST_FILEPATH}" # or other op you prefer
sudo cp -R /etc/apt/sources.list* "${REPO_BACKUP_DIR}/"
sudo apt-key exportall > "${REPO_KEYS_FILEPATH}"
rsync --progress /etc "${ETC_BACKUP_DIR}"
Run Code Online (Sandbox Code Playgroud)

并且可以使用例如bash类似的代码进行恢复

## setup
## (remember to transfer constants used above)

RESTORE_DIR="set this equal to the BACKUP_DIR from which you are restoring!"
ETC_RESTORE_DIR="${RESTORE_DIR}/etc"
PKG_RESTORE_DIR="${RESTORE_DIR}/packages"
REPO_RESTORE_DIR="${PKG_RESTORE_DIR}/repos"
PACKAGE_LIST_FILEPATH="${PKG_RESTORE_DIR}/${PACKAGE_LIST_FILENAME}"
REPO_KEYS_FILEPATH="${REPO_RESTORE_DIR}/${REPO_KEYS_FILENAME}"

## restore
## TODO: test that all following are readable, exit if not
## TODO: for all following: test retval/errorcode, exit on failure
## TODO: give user some progress indication (e.g., echo commands)

rsync --progress "${ETC_BACKUP_DIR}" /etc
# following will overwrite some of /etc restored above
sudo apt-key add "${REPO_KEYS_FILEPATH}"
sudo cp -R "${REPO_RESTORE_DIR}/sources.list*" /etc/apt/
# TODO: CHECK THIS SYNTAX!
# see my question @ https://serverfault.com/questions/56848/install-the-same-debian-packages-on-another-system/61472#comment920243_61472
sudo xargs aptitude --schedule-only install < "${PACKAGE_LIST_FILEPATH}" ; aptitude install
Run Code Online (Sandbox Code Playgroud)

所以IIUC,我们必须扩展上面的内容来处理包版本。使用如下策略似乎是可行的:

备份

  1. 编写包映射文件,而不是包列表文件。感谢RobertLcas,我现在知道我可以以一种有用的格式获取 Debian 主机的包映射文件(每行一个包,每行包含 {包名称(和体系结构,但仅限于多个),TAB , 包版本字符串}。然后就可以访问
    • 包名与awk '{print $1}' < "${PACKAGE_LIST_FILEPATH}"
    • 软件包版本awk '{print $2}' < "${PACKAGE_LIST_FILEPATH}"
  2. (额外学分)通过将包名称提供给 来存档原始/.deb包的当前版本(如cas 所示dpkg-repack) 。
  3. (额外学分)将存档的包转换为本地 APT 存储库。希望Debian wikipage 对此进行了详细的操作描述,但我仍在尝试弄清楚这一点。

恢复

术语表: *current指用户尝试恢复之前主机的状态。*current PMF指为当前主机生成的包映射文件,包含当前安装的包元组(key=包名称,value=包版本)。*backup PMF指之前某个时间为主机生成的包映射文件,包含要恢复的包元组。

  1. 创建当前 PMF。
  2. 通过“通常的方式”恢复存储库和密钥(见上文)。
  3. 读取备份 PMF。
  4. 对于当前 PMF 中而非备份 PMF 中的每个包元组:卸载该包(例如sudo aptitude remove [package_name])。
  5. 对于备份 PMF 中的每个包元组:安装给定版本的包(例如sudo aptitude install [package_name]=[package_version])。感谢Rui F Ribeirobigbenaugust的关注aptitudeapt-get支持install package=version
    • (额外加分)如果恢复的存储库不包含所需版本的软件包,请从备份本地 APT 存储库安装它。

测试

去做!我将首先对一个我还没有像上面这样的备份的盒子进行手动恢复(因为,当然,我还没有任何盒子像上面这样的备份:-)但是对于我有控制台喷出

cas*_*cas 3

一些有用的注释可以帮助您实现目标:

  1. 您可以通过运行以下命令来获取已安装软件包及其版本的列表:

    dpkg-query -W
    
    Run Code Online (Sandbox Code Playgroud)
  2. dpkg-repack您可以通过安装并运行如下命令来创建所有当前安装的软件包的 .deb 软件包的存档:

    dpkg-query -W | awk '{print $1}' | xargs dpkg-repack
    
    Run Code Online (Sandbox Code Playgroud)

    这将是dpkg-repack所有当前安装的软件包。此存档是恢复过程中缺少的关键部分 - 没有它,您可能无法恢复完全相同的包集(特别是如果您使用testingunstable)。

    警告:重新打包所有conffilesdpkg-repack的当前内容(可能已修改)。如果您想要原始的原始软件包,则必须从Debian 镜像获取 .deb 文件。/var/cache/apt/archives

    可以按照https://wiki.debian.org/HowToSetupADebianRepository上的说明将 .deb 存档转换为 apt-gettable 存储库,或者您可以将它们全部安装dpkg -iBE *.deb(或者dpkg -iRBE /path/to/deb/files/如果太多而无法在一个命令行中安装) )。

  3. 您仍然需要使用-get-selections--set-selections来保留已卸载的软件包等详细信息。