ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

关闭Snap自动更新与离线安装:生产环境版本管控实践

关闭Snap自动更新与离线安装:生产环境版本管控实践 (-aa-)这种前缀经常出现在我的运维笔记里意思大概是“这个点非常关键少做一步后面会非常痛”。这次要讨论的就是这种非常痛的主题snap 的自动更新关闭以及 snap 包的离线下载与安装。先说结论这两件事在正经生产环境里不是可选项而是带包进内网、保持版本基线、避免半夜被新版本悄悄搞挂的保命技能。很多朋友第一时间会想到“那就把 snap 整个卸了”这个思路我理解我之前也写过卸载 snap 的利弊但在很多场景里 snap 不是你想卸就能卸的Ubuntu 桌面的软件商店、部分预装组件、甚至 Chromium 这类常用浏览器都默认走 snap直接卸载等于把系统砍掉一块。而“关闭自动更新 离线分发”是在不破坏 snap 生态的前提下把这套机制收回到自己手里的最现实方案。下面我会按“为什么要动它、机制是什么、怎么改、怎么离线装、容易踩哪些坑”这条线把完整的操作路径讲清楚。1. 先回答“必要性”为什么自动更新必须能被按下暂停键1.1 snap 自动更新的设计很优雅但在生产环境里是头号不确定因素snap 的自动刷新机制本身设计得相当巧妙所有应用和运行时被封装成只读的 squashfs 挂载后台会把新版下载到本地安装时如果新旧版本不一致系统还能自动保留旧版本并做回滚。这套机制对普通用户是友好的因为它解决了 Linux 软件依赖混乱、升级中断、卸载不干净这些老问题。但这份“友好”放到服务器和交付环境里就变成了不确定性。snapd 默认不是按照你的维护窗口运行而是根据系统自身的计划随机触发。你永远不会知道今天凌晨三点某台跑了半年的机器会不会因为一个“无关紧要”的更新把和业务服务有间接关联的运行时换掉。我遇到过一次很典型的现场某台内网服务器上跑的图形处理工具基于 snap 分发某天业务突然报错查了一圈才发现前天 snapd 在空闲时自动把底层 core 版本从 20 升到了 22而我们的业务脚本依赖旧 core 里的某个库路径新版本直接把这个路径改掉了。类似的问题一多你就明白为什么“可预测”比“新鲜”重要。生产环境里最理想的状态是明确知道某一组包现在是什么版本并且在没有人工干预的情况下它一定不会自己变。1.2 “离线安装”不是为了折腾自己而是这些场景中被逼出来的硬需求会搜 snap 离线包下载与安装的人很少是因为在家里的电脑上想省流量。真正遇到这个需求的场景通常比较固定企业内网或专网服务器物理上不允许访问软件商店软件包只能通过审批后的移动介质或内部文件服务器传入。现场交付客户机在隔离机房安装团队只带一台笔记本所有依赖必须一次带齐。版本基线复现研发环境里验证过的一套 snap 包组合需要原样搬到另一批机器上不能接受安装时自动拉一个还不确定的新版本。多台机器批量低版本部署先在有网的跳板机上把所有包下载好再到目标机批量安装。在这些场景里“下载”和“安装”两件事必须被拆开而且下载过程必须能控制版本、安装过程必须能离线完成。这就是 snap download 与本地安装命令存在的意义。1.3 先分清两件事控制更新不等于永久禁用离线安装不等于放弃签名校验这篇文章里会反复强调一个区分关闭自动更新目的不是让系统永远停在旧版本而是让“什么时候更新”由你决定。手动执行 snap refresh 仍然随时可以用。所以后面讲的 hold、调整 refresh 定时器、屏蔽刷新服务本质上都是把“自动”去掉不是把“更新”功能砍掉。离线安装也一样。把 .snap 文件拷到目标机执行安装并不代表就放弃了安全校验。正确的离线安装应该把 .assert 签名断言一并带到目标机安装时继续验证文件的来源和完整性。只有当你自己构建 snap 包、没有官方签名时才需要走 --dangerous 这条更有风险的路。这一点很多人理解反了后面第 5 节我会详细说明。2. 动手前先把 snapd 的更新链路摸一遍2.1 snapd 不是只靠一个 timer真正的触发器有两条很多人以为关闭 snap 自动更新就是把某个 cron 删掉实际操作中会发现并不这么简单。snapd 的刷新逻辑在 systemd 里至少由两个 unit 配合完成snapd.refresh.timer负责周期触发刷新检查默认会根据随机偏移每天跑一次。snapd.refresh.service真正执行刷新动作的服务由上面的 timer 拉起也可以在系统启动时被调用。只看名字很容易忽略它们之间的依赖关系。如果你只是把snapd.service停掉系统会很快恢复它因为很多 snap 应用和 socket 激活机制都依赖它如果把上面的 timer 屏蔽了但 refresh service 还可能在开机阶段被别的单元拉起。所以在动手前一定要先确认当前机器上到底是哪条链路在触发刷新。可以先跑这几条命令systemctl list-timers | grep snapd systemctl status snapd.refresh.timer systemctl status snapd.refresh.service执行后你通常能看到一个 timer 在运行。如果之前有人动过也可能显示 disabled 或 masked。不管显示什么状态都要接着看 snap 自己的变更记录snap changes这条命令会列出 snapd 执行过的所有操作包括 refresh、install、remove 等。看到Refresh core22 snap from ...这类记录就说明这台机器的自动刷新确实跑过。2.2 自动更新不仅影响普通应用core 基础运行时才是重灾区这里特别提醒一下snap 自动更新不只是更新 lxd、chromium、certbot 这些应用本体。每个 snap 都基于一个核心运行时比如 core18、core20、core22、core24它们本身也是 snap也会被 snapd 自动刷新。问题就在这应用版本不动但底层 core 换了应用仍然可能出问题。就像你把一栋房子的水管换了材质表面看起来房间没变但水压和接口可能全变了。真正常见的故障恰恰是这些“默默更新”的 core 包带出来的。因此你在评估自动更新的影响面时不能只盯着业务可见的 snap 应用还得把coreXX算进去。2.3 查看当前刷新配置别凭记忆操作在决定使用哪种关闭方式前先看一下 snapd 当前的刷新策略配置是很有价值的snap get system refresh正常情况下返回是空说明走系统默认策略。如果之前配置过时间窗口会看到类似refresh.timerfr5,10:00-12:00的值。了解现状之后再选择下面的方案就不会改错方向。3. 关闭 snap 自动更新的三种做法3.1 想先锁住当前版本用 snap refresh --hold如果你只是希望当前的 snap 版本不要因为后台刷新而变化最简单直接的方式是给 snap 的刷新机制上锁sudo snap refresh --hold不带参数时它默认对系统里所有 snap 生效。执行后snapd 就会停止对已安装 snap 的自动刷新直到你手动解锁。这个动作是可以逆的解除时执行sudo snap refresh --unhold--hold方案适合“版本暂时不想动但以后还是要更新”的场景。比如业务迁到新版本前你希望能保留现在跑着的版本不变等验证完了再手动调用snap refresh或解除 hold 恢复例行更新。这个方案有一个容易让人犯迷糊的地方--hold并不会阻止你主动执行snap refresh。也就是说锁住的是自动刷新而不是手动刷新的能力。如果你锁住之后又手动snap refresh了一下snap 还是会更新到最新版锁定状态并不会阻止这个动作。所以线上操作时要跟团队约定清楚hold 期间别顺手敲刷新命令。3.2 希望更新只发生在固定维护窗口用 refresh.timer 和 refresh.hold如果机器本身允许定期更新只是不想让它在随机时间乱更新可以把刷新限制到你指定的维护窗口。snapd 提供了系统配置项只需把 refresh.timer 设成一个窗口sudo snap set system refresh.timerfri5,10:00-12:00这个配置的含义是只在每周五 10 点到 12 点之间检查并执行刷新。时间窗口之外snapd 不会主动刷新。想恢复默认用snap unset system refresh.timer。如果希望延迟一段时间再更新而不是设置固定窗口refresh.hold 也是可用的。比如延迟 72 小时sudo snap set system refresh.hold72h你可以通过snap get system refresh查看配置是否生效。这种方法在生产环境比纯禁止更实用因为它保留了一条受控的更新通道避免系统长期停留在缺少安全修复的状态。3.3 彻底不让刷新服务被拉起用 systemctl mask如果你维护的是离线内网机器或者这台机器未来很长一段时间都不需要任何自动更新那么更硬核的方式是把整个自动刷新链路停掉sudo systemctl disable --now snapd.refresh.timer sudo systemctl stop snapd.refresh.service如果想防止 refresh.timer 被其他机制意外重新拉起还可以把 timer unit 屏蔽sudo systemctl mask snapd.refresh.timer执行 mask 之后就算某个依赖关系想启动它systemd 也会直接拒绝比 disable 更强。这里要特别说明一个边界无论采取哪种方式都不要顺手把snapd.service也停了。snapd.service 是 snap 的核心守护进程停了之后所有 snap 应用都可能无法启动甚至会影响系统基础组件。通常关闭snapd.refresh.timer和snapd.refresh.service就足以让自动更新停下来手动snap refresh的操作仍然可以使用。3.4 验证“确实关了没”的两种快速确认配置做完后不要只看自己敲进去的命令有没有报错用下面的方式验证一下更稳妥systemctl is-active snapd.refresh.timer systemctl is-enabled snapd.refresh.timer snap changes前两条命令如果输出inactive或disabled/masked说明定时刷新的链路已经被切断。最后看snap changes如果你发现最近没有新的 Refresh 记录或者说刷新记录的时间停在你操作前就可以确认自动更新已经停下来了。踩过几次坑之后我的习惯是先把 hold 和应用层配置都写上再临时手动运行一次snap refresh观察它是否会被允许。如果连手动刷新都成功说明当前机器确实处于可刷新状态千万别误以为关了自动就万事大吉。4. 在有商店访问权限的机器上把 snap 包抓下来4.1 下载前的三个前置判断架构、系统版本、频道离线分发的第一步不是执行下载命令而是把目标机的信息搞清楚。最基础的是架构因为 amd64 上抓下来的 snap 包装不到 arm64 机器上。查看架构用uname -m接着要确认目标机的 snap 支持哪一代 core 运行时。比如 Ubuntu 22.04 默认核心是 core22Ubuntu 24.04 默认是 core24。离线安装时如果带错核心包安装会直接失败。第三是频道选择。snap 商店按稳定程度和发布时间拆分多个渠道最常见的是 stable、candidate、beta、edge。稳定生产环境建议锁定 stable 频道同一个软件的不同大版本也可能有独立频道支撑例如 LXD 5.0 长期支持版就有自己的频道。snap info lxd执行这条命令能看到当前发布到各个频道的版本、发行说明和架构支持情况。下载前先看这个信息比随手抓一个文件靠谱得多。4.2 正式抓包snap download 会同时产出两个文件在有商店访问权限的机器上下载指定快照用现成的snap download命令即可。比如我想下载 LXD 的 5.0 稳定版snap download lxd --channel5.0/stable执行后目录里会出现两个文件命名类似lxd_26318.snap应用本体是一个 squashfs 格式的完整运行时文件。lxd_26318.assert签名与校验信息用来证明这个 .snap 的来源和完整性。很多第一次做离线分发的人会犯同一个错误只带走 .snap把 .assert 扔在下载机上觉得那不过是一个看起来像文本的小文件。实际到了离线目标机安装时如果没有 .assert 文件系统会提示找不到签名信息要么选择危险模式安装要么回头再去取文件来回折腾。正确做法是把这两个文件当作一个整体传到目标机。你甚至可以先把它们打包tar czf lxd-offline.tar.gz lxd_26318.snap lxd_26318.assert.assert文件内容本质上是一组 YAML 形式的断言记录了这个 snap 的哈希、版本、发布者、签名时间等元数据。snapd 通过导入这组断言来验证文件和商店侧的发布者之间是否一致。4.3 若只能拿到 .snap 文件一定会有额外的警告信息部分软件官方会直接提供 .snap 下载入口但未附带 .assert。这种情况下离线安装是能做的但需要走--dangerous。它是整个离线流程里唯一一个让我每次都要提醒自己的参数因为它的存在意味着跳过签名校验。你只能在两个前提下使用它一是包来源足够可信比如是内部自动构建产生的二是你已经手动验证过文件的哈希值。有一个常见的认知错误是有了官方 .assert 就等于高枕无忧。离线安装时如果只 ack 了 assertion但没有校验 .snap 的 SHA256 是否与 assertion 里的一致理论上文件还是可能被替换。下载完文件后手动做一次哈希比对更稳妥sha256sum lxd_26318.snap把结果和下载页面或 assert 文件里记录的哈希对照一下确认无误后再进入安装流程。4.4 基础依赖怎么一起带全snap download 有个容易让新手措手不及的行为它只下载你指定的那一个 snap不会自动携带它依赖的 core 运行时。比如你想离线安装一个基于 core22 的应用目标机恰好全空连 core22 都没有那么安装时会立刻报错。解决办法是主动把目标机可能缺失的基础 runtime 一并下载。先在有网的机器上执行snap list看本机已经有哪些基础 snapsnap list | grep core通常能看到 core18、core20、core22 这类行。如果目标机和下载机的系统底座不完全相同可以把多个 core 都抓下来放一个目录里到现场按需安装snap download core22 --channelstable snap download core24 --channelstable离线场景下宁可多带几个也不要到现场才发现少一个 50MB 的 core 包而卡住半天。5. 离线目标机安装的三步流程与危险参数解析5.1 第一步把 .assert 先交给 snapd 验证把 .snap 和 .assert 传到目标机后不要急着执行 install。第一步是导入并确认断言sudo snap ack ./lxd_26318.assertack 的本质是让 snapd 把这个包对应的签名公钥和发布信息导入本地信任链。snapd 在完成这一层面验证后安装时就不会因为找不到签名而强制要求危险模式。如果这台机器已经 ack 过同一个发布者的其他包重复 ack 同一个或同类 allow 文件通常不会报错但为了干净起见可以在安装前单独对这次要装的包执行一次。ack 成功后系统基本不会输出什么引人注意的文字但你可以通过后续安装是否顺畅来判断.5.2 第二步安装本体时该不该加 --dangerous接下来安装 .snap 本体有两种写法。如果你已经通过 ack 导入了对应断言正常直接安装sudo snap install ./lxd_26318.snap如果目标机上没有 .assert或者这个 snap 来自内部构建没有签名就需要显式告诉 snapd这个文件我没有办法拿到商店签名但我基于其他信任方式决定安装它。sudo snap install ./my-tool.snap --dangerous--dangerous这个单词不是夸张它确实意味着失去官方签名校验这个安全关口后恶意篡改的 snap 也可以进入你的系统。对生产级内网机器这意味着你从下载到传输的整个链条都必须可控。所以我的建议很朴素能带 .assert 一定要带.assert和.snap是一个整体不要拆开。只有在确认不需要签名校验的场合比如公司内部构建的包才使用--dangerous。另外提醒一个细节本地文件安装命令里不要想当然加--channel参数。--channel只对从商店安装的 snap 有效本地安装时强行添加可能报错。你已经在下载时通过snap download --channel确定了包的内容到目标机这里不需要再指定频道。5.3 第三步安装后的版本确认与自我保护离线安装完成后先确认版本没有出入snap list lxd snap info lxd确认无误后建议马上再执行一次自动更新锁定sudo snap refresh --hold lxd理由很实在现在你的机器处于离线状态看起来不会自动更新但这台机器可能以后会重新接入内部网或者商店镜像。如果不提前 hold一旦它能访问商店就会立刻进入自动刷新队列。到那时你之前辛辛苦苦带进来安装的版本基线就失守了。完整卸载也比较直接sudo snap remove lxd如果之后还需要处理旧版本回滚可以在安装完成后通过snap saved查看有没有自动保存快照。快照机制是 snap 相对传统包管理的一大优势它在重装或回滚时能保留配置值得了解。5.4 一套可直接复用的批量离线安装脚本实际交付时不会只装一个包我通常会在下载机和目标机各准备一个小脚本。下载机一侧的核心逻辑是#!/usr/bin/env bash set -euo pipefail mkdir -p offline snap download core22 --channelstable --target-directoryoffline snap download lxd --channel5.0/stable --target-directoryoffline snap download certbot --channelstable --target-directoryoffline tar czf offline_pkg.tar.gz offline目标机一侧的核心逻辑是#!/usr/bin/env bash set -euo pipefail tar xzf offline_pkg.tar.gz for assert in offline/*.assert; do echo ack $assert sudo snap ack $assert done # 先装基础运行时再装业务 snap sudo snap install ./offline/core22_*.snap sudo snap install ./offline/lxd_*.snap sudo snap install ./offline/certbot_*.snap # 全部就绪后统一 hold防止后续接入网络时被自动刷新 sudo snap refresh --hold这里要注意 bash 通配符匹配多个文件时可能报“no matches”如果 core22 没有被下载try 会失败所以脚本里最安全的做法是复制变量明确当前版本号。简单起见可以保持上面对应实际情况改动。脚本里先安装 core22 再安装业务包并不是随便安排的。这是因为 snap 安装时会检查依赖的 core 运行时是否存在如果不在它会尝试连接商店去获取在离线机器上这种行为等于直接失败。提前把基础运行时装好业务包安装才能一路顺畅。6. 现场最常翻车的问题与排查速查6.1 只带 .snap忘带 .assert安装时被强制要求危险模式这是我见人踩得最多的一种坑。现象是安装时报错找不到签名要求要么提供断言文件要么加--dangerous。原因是 .snap 本体里不包含完整签名链签名数据在 .assert 里。处理办法很简单回到下载机把 .assert 文件拿过来先snap ack再正常安装。如果你对包来源完全信任又确实没有 assert可以加--dangerous但要清醒认识到这是在跳过官方签名验证。6.2 安装业务包时报错提示需要 core 运行时目标机却想上网下载场景是离线机里已经有一个应用现在想再装一个新的结果新应用的 core 运行时版本和本机已经有的不匹配。snapd 在本地找不到依赖时并不会直接停在那里而是尝试向商店请求这个行为在离线内网里最终表现为超时或失败。处理办法是提前在下载机执行snap download coreXX把对应的运行时一起带进去然后先安装 core 包再装业务包。判断目标机缺少什么 core可以先snap list看本机已有的或者用snap info查看业务包依赖的基础运行时版本。注意snap 的 core 版本必须与业务包要求的基座匹配小版本不一致通常可接受大版本不同装不上。6.3 明明关了自动更新第二天又发现有新的刷新记录这种情况大多数不是关闭方法失效而是关闭的范围没覆盖到所有触发链路。比如只disable了 timer但 refresh.service 还能在开机阶段触发一次刷新或者只对个别应用设置了 hold其他核心 snap 仍然可以自动跑。处理办法是把snapd.refresh.timer和snapd.refresh.service都检查一遍建议用 mask 而不是 disable防止被重新拉起。另外用sudo snap refresh --hold把所有 snap 一并锁住双保险。6.4 用 --dangerous 安装了未签名 snap事后完全找不到出处出现这个问题的原因通常是习惯性加--dangerous而没有意识到它跳过了签名验证。如果你装的是外部人员给的一个 .snap又没有对应的哈希校验出了问题就很难定位是谁、在什么环节动了包。处理办法是建立一套自己的离线包来源规则官方商店取出的包必须带 .assert 并先 ack内部构建包必须有明确的构建服务器路径和哈希记录才能在安装时允许--dangerous。不要图省事把一个陌生目录里的文件随手装上。6.5 下载机架构与目标机不一致安装直接报错 incompatible现象是下载时没看架构到目标机安装时 snapd 提示架构不兼容。原因很简单uname -m在 x86 下返回 x86_64在 ARM 下返回 aarch64snap 是分架构打包的。处理办法是下载前确认目标机的架构不要默认“服务器都是 x86”。遇到批量服务器建议准备 amd64 与 arm64 两份离线目录分别标注清晰避免现场拿错。6.6 离线安装完成后机器突然又能访问更新源版本被悄悄刷新这个坑发生在封锁不彻底的单机场景里。离线安装成功不代表这台机器永远不会在线它可能在下一阶段接入新的内网更新源然后 snapd 又会自动刷新。处理办法我在第 5 节已经说过在离线包全部装好后立刻手动 holdsudo snap refresh --hold。这条命令应该写进你的脚本里而不是靠人工到现场执行。6.7 问题速查表现场现象大概率原因直接解法安装时提示找不到签名.assert 未导入或未携带先执行 snap ack 再 install离线机安装时卡住并提示去商店拉包缺少依赖的基础 core 运行时提前下载 coreXX 并先安装关闭自动更新后仍有刷新记录关闭范围没覆盖 refresh.service检查并 mask 两个单元安装报架构不兼容包架构与目标机不一致下载前 uname -m 确认装的是未知来源的包没有哈希记录用 --dangerous 前先核对哈希过段时间版本又变了未设置 hold 或后续接入更新源安装后立即执行 snap refresh --hold7. 离线分发和版本锁定的整体经验心得如果你也经常要和 snap 的自动更新以及离线分发打交道我最后说一下这几年来沉淀下来的操作习惯。我在处理一次需要引入离线安装的任务时通常不会先想“用什么命令”而是先把目标机当前快照备份出来执行snap list记录现有的版本列表。随后在有网的下载机上把所有需要的包按目标机架构和系统版本准备好用 tar 打包时不是只打 .snap一定顺手打 .assert。到了现场ack 和安装严格按顺序走全部完成后马上snap refresh --hold并且把snapd.refresh.timer的状态也记录下来。整个过程看起来多花了几分钟实际上避免了后续的版本漂移和半夜被自动刷新惊出一身冷汗的问题。另外还有一个容易被忽略的小窍门snap 下载机最好保持和最终部署机一致的 Linux 发行版基线。例如目标机都是 Ubuntu 22.04下载相关基础包时最好也在 22.04 的机器上执行这样可以避免因为 snapd 的版本差异导致断言格式、安装行为不一致。真正到生产环节你会发现 snap 的自动更新和离线安装从来不是一个技术活而是一个流程控制问题。只要把“包从哪来、装到哪台机器、装完谁也改不了版本”这条链路管住snap 这套机制就能变得非常可控。我自己实践下来先把离线包在参考机上完整安装一遍、导出所有保存文件和清单再上真机迄今没有再被 snap 的版本漂移坑过。
返回列表