ARTICLE DETAIL

资讯详情

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

tmux 会话自动保存与恢复:resurrect + continuum 完整实操指南

tmux 会话自动保存与恢复:resurrect + continuum 完整实操指南 前阵子机房一台开发服务器因为内核更新重启了等我重新连上去的时候整个人是懵的——开了四五个 tmux 窗口里面跑着编译任务、日志 tail、还有一堆临时写的调试命令全没了。倒不是说数据丢了关键是那些窗口上下文、当前目录、滚动缓冲区里的输出重新搭一遍怎么也得十分钟。更难受的是这种事它不是第一次发生。从那之后我认真研究了一下 tmux 会话的自动保存和恢复方案折腾完发现其实并不复杂但确实有几个坑值得单独写出来说一下。这篇文章就是围绕“服务器自动保存 tmux 会话以及恢复 tmux 会话”这件事的完整实操记录包含方案选型、安装配置、开机自恢复、问题排查。适合被 SSH 断线、服务器重启折磨过的开发者和运维同学也适合那些刚接触 tmux、想一步到位把持久化方案配好的新手。1. 为什么 tmux 会话需要自动保存真实痛点与方案选型1.1 SSH 断线、服务器重启——会话丢失的三大场景先说说我自己的使用习惯。我几乎所有的远程开发工作都在 tmux 里进行vim 开在 tmux 里日志分析在 tmux 里连数据库查询也是在 tmux 面板里来回切换。tmux 本身解决的最大问题就是“SSH 断开后命令不中断”因为 tmux server 跑在远端机器上只要远端机器不重启、tmux server 不退出会话理论上可以一直挂着。但问题恰恰出在几个边界场景一是 SSH 连接断开。这个是 tmux 最擅长的场景断开重连后tmux attach就能回到原会话这个基本不会丢。真正让人头疼的是后面两种。二是服务器重启。无论是有计划的维护重启还是内核 panic 之后的自动重启只要机器一重启所有进程全部结束tmux server 也不复存在。重启之后你面对的是一台干干净净的机器之前精心布置的所有窗口、面板、当前目录、运行环境全部归零。三是 tmux server 意外退出。比如手动误执行了tmux kill-server或者磁盘满导致 tmux 写入异常退出又或者是别的进程把 tmux 的 socket 文件清理掉了。团队里不止一个人干过pkill -9 tmux这种操作一瞬间所有会话灰飞烟灭连后悔的机会都没有。手动保存工具不是没有tmux 本身支持通过tmux dump之类的方式手动把布局导出来但也只是布局环境变量、当前目录、窗口顺序这些细节很难完整还原。而且手动这个动作本身就是反人性的——人只有在吃过亏之后才会想起来保存可那时候通常已经来不及了。1.2 保存恢复方案对比resurrect continuum 为什么是首选市面上解决 tmux 会话持久化的方案有好几种我大概梳理了一下第一种是 Tmuxinator它是通过 YAML 配置文件把你想要的窗口布局提前定义好然后按需启动。这个方案适合“标准化开发环境”比如每次开机固定开三个窗口、每个窗口固定跑什么命令。但它解决不了“恢复现场”的问题因为你不能让它记住你昨天随手开的第五个窗口里在跑什么。第二种是自己写脚本用 tmux 的内置命令把当前布局 dump 出来然后注册到 crontab 定时执行。这个思路可行但脚本的健壮性很难保证。比如转义字符、面板激活顺序、当前目录中的空格这些细节不踩几次坑处理不干净。第三种就是这次要重点讲的 tmux-resurrect tmux-continuum 组合方案也是目前社区用得最多、维护最活跃的方案。tmux-resurrect 负责把会话状态完整保存到磁盘包括窗口布局、当前目录、环境变量、运行中的命令甚至能保存 vim/ssh 等特定程序的恢复现场。tmux-continuum 则是一个守护型插件每隔一段时间自动触发 resurrect 的保存动作并且在 tmux server 启动时自动执行恢复动作把“自动保存”和“自动恢复”这两件事完全串起来。我选择这个方案的核心原因是它遵循了“默认就能用”的原则。装好之后不需要每天手动操作它自己会每隔 15 分钟保存一次机器重启后只要 tmux server 被拉起来它就会自动恢复最近一次的状态。这比我之前自己写的 crontab 脚本要省心太多而且恢复细节的完整度也远远高于脚本方案。2. 环境准备tmux 与 TPM 插件的安装配置2.1 安装 tmux 本体在配置任何插件之前先把 tmux 本体装好。大部分 Linux 发行版的软件源里都有 tmux直接安装就行# Debian / Ubuntu sudo apt install -y tmux # CentOS / RHEL sudo yum install -y tmux # 或者用编译安装拿最新版 sudo apt install -y libevent-dev ncurses-dev build-essential wget https://github.com/tmux/tmux/releases/download/3.3a/tmux-3.3a.tar.gz tar -zxvf tmux-3.3a.tar.gz cd tmux-3.3a ./configure make sudo make install这里提一个我踩过的坑如果你用的是 CentOS 7 这类老系统yum 源里的 tmux 版本可能是 1.8 甚至更老而 tmux-resurrect 和 tmux-continuum 对 tmux 版本是有要求的。至少需要 1.8 以上实测 2.x 和 3.x 工作良好1.8 容易出现恢复不完整的问题。所以尽量装新版本Ubuntu 20.04 直接用apt install tmux拿到的就是 3.0 以上问题不大。安装完可以用tmux -V验证版本。接下来我建议先把~/.tmux.conf写好再启动 tmux。因为后续装 TPM 和插件都依赖这个配置文件。2.2 安装 TPM 插件管理器tmux 的插件生态比较分散最好用 TPMTmux Plugin Manager统一管理。TPM 的安装方式很简单就是把它 clone 到~/.tmux/plugins/tpm路径下git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm然后编辑~/.tmux.conf在文件底部加上 TPM 的初始化和插件列表声明# ~/.tmux.conf set -g plugin tmux-plugins/tpm set -g plugin tmux-plugins/tmux-resurrect set -g plugin tmux-plugins/tmux-continuum # 初始化 TPM放在配置文件的最后一行 run ~/.tmux/plugins/tpm/tpm注意run ~/.tmux/plugins/tpm/tpm这行一定要放在配置文件的末尾因为 TPM 需要在这一行之后才能加载所有在它之前声明的插件。配置写好后需要先在 shell 里加载一次新配置tmux kill-server tmux new-session -d tmux source ~/.tmux.conf然后进入 tmux按prefix I默认前缀是Ctrlb大写 i安装插件。TPM 会从 GitHub 拉取所有声明的插件到~/.tmux/plugins/目录安装完成后会看到一条安装成功的提示。2.3 配置 tmux-resurrect 与 tmux-continuum 的关键选项插件装上只是第一步两个插件各自有几个关键配置需要根据自己的需求调整。tmux-resurrect 的配置写在~/.tmux.conf里常用的是这几个# 恢复窗口内的程序比如 vim、ssh、htop set -g resurrect-processes vim ssh htop # 保存 bash 历史需要 bash-preexec 支持 set -g resurrect-save-bash-history on # 保存 shell 的当前目录默认已经开启 set -g resurrect-capture-pane-contents on第一条resurrect-processes的意思是保存时把这些命令行里的进程也记录下来恢复时会尝试重新启动它们。注意这个选项默认值是空也就是说如果不配置恢复出的窗口是干净 shell不会自动重新跑你当时的命令。这个配置对还原现场非常重要我加了 vim、ssh、htop 还有一部分自定义脚本。第二条resurrect-save-bash-history用来保存每个 pane 的 bash 历史需要额外安装 bash-preexec这个在 Ubuntu 上直接 clone 下来 source 到 bashrc 里就行。开启之后恢复的会话里命令行历史是连续的换了个窗口也知道之前敲过哪些命令。tmux-continuum 的配置相对简单# 自动保存间隔单位是分钟默认 15 set -g continuum-save-interval 15 # 启动时自动恢复默认是 on set -g continuum-restore on # 启动 tmux 时不显示恢复提示可选 set -g continuum-boot oncontinuum-save-interval控制多少分钟自动保存一次。我实测默认 15 分钟在绝大多数场景下够用如果你经常改窗口布局、对恢复及时性要求高可以调成 5。但注意间隔越短意味着磁盘写入越频繁虽然这些文件都很小几 KB不至于有什么 IO 压力但完全没必要每分钟都写。恢复的及时性其实不取决于保存间隔因为重启之后恢复的是最近一次成功保存的快照间隔 5 和间隔 15 只影响丢失的窗口变更窗口不决定能否恢复。continuum-restore必须设置为on这样当 tmux server 启动时continuum 检测到之前保存过会话就会自动执行恢复。如果这个选项不打开你装了 continuum 也只是自动保存不会自动恢复还得手动按恢复快捷键。3. 核心参数与工作原理自动保存到底是怎么运作的3.1 tmux-resurrect把会话状态写进文件要理解自动保存和恢复的机制得先看懂 tmux-resurrect 到底在干什么。resurrect 的本质是一次“状态序列化”。触发保存时它会遍历当前 tmux server 里的所有 session对每个 session 里的每个 window 和 pane提取以下几类信息窗口索引、窗口布局横向还是纵向分隔、每个 pane 的尺寸每个 pane 的当前工作目录每个 pane 的 shell 类型和当前命令行例如 vim 就记 vimssh 就记 ssh环境变量白名单中的值pane 的滚动缓冲区内容如果你开启了resurrect-capture-pane-contents这些信息会按 session 组织写到~/.tmux/resurrect/目录下文件名是类似tmux_resurrect_20241025T153000.txt这种带时间戳的文本文件。目录里通常会有多个历史快照continuum 每次保存都会生成新文件但通过 symlink 指向最新一份。可以直接用cat查看这些文件的内容格式是 tmux 自己的命令序列每一行都是一条 tmux 指令。看到这里你应该明白了resurrect 的“恢复”过程本质上就是把这些保存的命令逐条重新执行一遍创建 session、创建 window、split-window、设置环境变量、切目录、启动指定进程。这就是为什么恢复速度很快因为本来就是在内部执行命令而已。有一点需要特别注意resurrect 保存的是“能序列化的状态”它不可能保存进程内部的内存态。比如你 vim 里有一堆未保存的修改resurrect 会重新拉起 vim 并打开对应文件但未保存的修改只能靠 vim 自己的 swap 文件恢复你跑着的tail -f日志恢复出来后会重新启动 tail但之前屏幕上打印的历史输出如果不开启 pane 内容捕捉是看不到的。所以这个方案解决的是“工作现场”的还原而不是“进程快照”的还原理解这一点才能正确评估它对你的价值。3.2 tmux-continuum后台守护与按计划存储tmux-continuum 的定位更偏“调度器”。它通过 tmux 的if-shell和 hook 机制在 tmux server 内部起一个周期性任务。它的核心逻辑大致是每隔continuum-save-interval分钟检查一次当前 tmux server 是否在运行如果在运行就触发一次 resurrect 保存如果检测到 tmux server 是刚刚启动的比如重启后第一次进入并且continuum-restore是on就自动执行 resurrect 恢复。这个设计有个很巧妙的地方它不需要外部 cron不依赖系统服务而是作为 tmux 自身的一个 hook 在跑。这样带来的好处是tmux 只要在运行自动保存就在运行不需要你额外维护一组 crontab 条目而且你多开几个 tmux 会话也不会出现多个守护进程互相冲突的问题。另一个好处是它天然感知 tmux server 的生命周期。每次 tmux server 启动时continuum 都会触发一轮恢复检查这样即使你不是通过 systemd 启动 tmux而是手动打开了一个新 tmux只要机器上有 saved 文件它也会自动尝试恢复。对于“服务器重启后第一次手动 SSH 连接进去、随便开个 tmux 就发现现场回来了”这种体验非常友好。3.3 恢复时机的自动化这里有一个容易被忽略的细节tmux-continuum 的“自动恢复”是在 tmux server 启动时发生的而不是在操作系统启动时发生的。意思就是如果服务器重启后你一直没 SSH 上去或者 SSH 上去后没有启动任何 tmux server那么恢复不会发生resurrect 保存的文件只是静静躺在磁盘里。所以要实现真正意义上的“服务器重启后全自动恢复现场”需要把“启动 tmux server”这个动作也自动化。我的做法是借助 systemd 用户服务在系统启动后自动拉起一个独立的 tmux server这样 continuum 的恢复逻辑就会被触发等你的 SSH 连上去时发现 tmux 里已经是重启前的完整现场了。这个方案的详细配置在下一节讲。另外还有一个小细节手动恢复的快捷键也要配好。resurrect 默认的保存快捷键是prefix Ctrl-s恢复快捷键默认是prefix Ctrl-r但很多人的配置文件里可能没有定义这两个键。建议在~/.tmux.conf里显式设置bind C-s run ~/.tmux/plugins/tmux-resurrect/scripts/restore.sh bind C-r run ~/.tmux/plugins/tmux-resurrect/scripts/save.sh注意我自己把快捷键调换了一下因为恢复操作的频率其实更低Ctrl-r 留给更常用的反向搜索。这只是个人习惯不是通用建议。如果你担心 keybinding 冲突可以直接用插件默认行为或者指定到一个不常用的前缀组合上。4. 服务器重启后的完整自动恢复流程4.1 用 systemd 用户服务实现开机自动拉起 tmux先把目标说清楚服务器重启后我希望不需要任何人工 SSH 登录操作tmux server 就已经在运行并且已经完成了会话恢复。这样我只需要一个tmux attach就能回到现场。做法是用 systemd 的 user 实例loginctl enable-linger 启用用户实例的常驻模式或者更简单一点直接写一个 systemd system 服务以指定用户身份启动 tmux。我推荐用户级 service因为不需要 root 权限也更干净。首先创建一个用户级目录mkdir -p ~/.config/systemd/user然后创建服务文件~/.config/systemd/user/tmux.service[Unit] Descriptiontmux server for auto restore Afternetwork-online.target Wantsnetwork-online.target [Service] Typeforking ExecStart/usr/bin/tmux new-session -d -s auto-restore ExecStop/usr/bin/tmux kill-server Restarton-failure [Install] WantedBydefault.target说明几个配置的含义。Typeforking是因为 tmux 启动后进程会 fork 到后台systemd 需要知道它启动完成的状态。ExecStart里创建了一个名为auto-restore的临时会话这个会话本身不重要它存在的意义是让 tmux server 进程跑起来。一旦 server 起来tmux-continuum 的恢复逻辑就会检测到保存文件把这个会话关闭并恢复出历史会话。network-online.target的作用是确保网络已经就绪。如果你的服务器上保存了 ssh 会话而网络还没起来恢复时的 ssh 连接会失败。加上这个依赖能在多数场景下规避“开机自动恢复显示一堆 ssh 连接失败”的问题。然后启用并启动服务systemctl --user daemon-reload systemctl --user enable tmux.service systemctl --user start tmux.service如果你希望用户服务在系统启动时就能运行而不需要用户登录还需要执行sudo loginctl enable-linger your_username这条命令的作用是让用户的 systemd 实例在用户没有登录的情况下也保持运行这样服务才能做到真正的“开机自启动”。4.2 首次恢复的初始化脚本服务建好后第一次重启前需要确保 resurrect 已经保存过至少一份快照。建议在重启前先手动执行一次保存tmux run-shell ~/.tmux/plugins/tmux-resurrect/scripts/save.sh或者进入 tmux 按prefix Ctrl-s。保存之后检查一下~/.tmux/resurrect/目录ls -la ~/.tmux/resurrect/看到最新的tmux_resurrect_*.txt文件生成就说明保存成功。接下来还有一个需要手动处理的坑continuum 的自动恢复有一个安全机制它默认只会恢复最近 30 分钟内保存过会话的状态。如果你距离上次保存已经超过 30 分钟它会跳过自动恢复。这个机制是为了避免误恢复一个非常旧的状态但也让很多第一次配置的人踩了坑——明明保存过、服务也起来了为什么就是没恢复解决方式是直接修改~/.tmux.conf加一行set -g continuum-restore-threshold 30这个参数的单位是分钟30 表示只恢复 30 分钟内的快照。你可以改成 144024 小时或者更大的值甚至设成 0 表示不限制。具体看你自己的容忍度我建议设成 1440避免周一早上来发现上周五的会话被拒绝恢复。4.3 验证恢复结果的检查清单配置完成后做一个完整的演练是很值得的。我的验证流程是这样的第一步在 tmux 里随便开几个窗口其中两个窗口分别 cd 到不同项目目录一个窗口跑htop另一个窗口跑vim ~/.tmux.conf。按prefix Ctrl-s保存一次。第二步检查保存文件内容cat ~/.tmux/resurrect/tmux_resurrect_latest.txt确认里面包含new-session、new-window、cd这些指令。第三步模拟重启tmux kill-server sudo reboot或者不想真重启的话直接tmux kill-server后重新连 SSH手动执行tmux new-session -d观察是否自动恢复。第四步重启回来后执行tmux list-sessions tmux attach检查窗口数量、布局、当前目录、vim 是否重新打开文件、htop 是否自动启动。我自己的实测结果里除了 ssh 连接需要网络那位可能会有延迟其他本地进程基本秒回。窗口布局比如三个 pane 上下左右的分割也会精确还原包括每个 pane 的大小比例。5. 常见问题排查与避坑实录5.1 恢复后窗口丢失或目录不对这是最常见的问题。恢复出来的会话窗口数量少了或者窗口里 cd 的目录没变。先检查保存文件里是否完整记录了这些信息。窗口丢失大概率是上一次保存时窗口本来就不存在比如你后来新开的窗口是在保存之后才创建的。这不算 bug是保存时机的问题可以缩短保存间隔到 5 分钟。目录不还原则要确认两个点一是你用的 shell 是不是 bashresurrect 对 bash/zsh 支持最好二是排查是否有.bashrc或.zshrc里的逻辑改变了目录比如你cd时自动触发了一些cd别名跳转。恢复时命令会执行cd但如果你的 shell 配置里有cd后的钩子函数尤其是 oh-my-zsh 的一些插件可能会把目录又切走。另外一个容易被忽略的点是恢复时 tmux 会创建新的 pane默认 HOME 目录启动然后执行cd指令切回去。如果你的项目目录挂在别的分区或者 NFS 上启动 tmux 时该目录还没挂载好cd就会失败。这种情况建议在 systemd 服务里加Afterremote-fs.target确保远程文件系统已挂载。5.2 自动保存不生效装了 continuum 之后等半天看目录里的保存文件时间戳没有更新可以从几个方向排查。第一确认插件真的被 TPM 加载了。进入 tmux按prefix I重新安装然后prefix t如果配置了 display-message看输出。也可以用tmux list-commands | grep resurrect检查相关命令是否存在。第二检查配置是否拼写错误。continuum-save-interval这种变量名必须和插件源码里完全一致TPM 不会因为配置项不存在而报错所以很容易静默失败。直接在 tmux 里执行tmux show-options -g | grep continuum能显示出continuum-save-interval等变量才说明配置生效了。第三确认插件加载顺序。~/.tmux.conf里插件列表必须在run ~/.tmux/plugins/tpm/tpm之前且 TPM 初始化要在最后。如果你的~/.tmux.conf里后面还有source-file或者别的run-shell可能会导致顺序混乱。5.3 保存文件格式与版本兼容tmux-resurrect 保存的文件格式不是跨版本稳定的。拿 tmux 2.9 保存的文件去给 tmux 3.2 恢复可能某些窗口布局命令解析失败导致部分窗口创建不出来。这不是配置文件的问题是插件针对不同 tmux 版本生成不同格式的输出。解决方案是保持服务器 tmux 版本和插件版本都尽量新并且不要去跨版本迁移恢复文件。如果必须从旧机器迁移到新机器可以考虑用 Tmuxinator 这类更结构化、跨版本容错能力更强的配置文件方案重新定义工作区。5.4 嵌套 tmux 会话的注意事项还有一个场景值得提醒。如果你习惯在跳板机上嵌套使用 tmux——外层 tmux 跑在跳板机、内层 tmux 跑在目标机——那么两个 tmux 的插件都会工作但恢复逻辑可能会冲突。我遇到过一次外层 tmux 的 continuum 在保存时把内层 tmux 的程序名也记录成普通进程恢复时就试图在跳板机上重新拉起一个 tmux 窗口结果造成层层嵌套的会话混乱。解决办法有几个一是在外层 tmux 里关闭 resurrect 对 tmux 进程的恢复配置resurrect-processes时不要包含 tmux二是把内层 tmux 的快捷键前缀改为另一个键比如Ctrla避免嵌套时的键冲突三是干脆在跳板机不装 continuum只在目标机上装。最省心的还是第三种因为跳板机本身只是一个过路站它的会话状态没有恢复价值。手动在目标机保存的目标机恢复比穿透两层会话去恢复要可靠得多。5.5 排查流程速查表现象可能原因排查方法自动保存没执行配置项没生效/插件没加载tmux show-options -g | grep continuum保存了但没自动恢复快照超过恢复阈值查看continuum-restore-threshold调大或设为 0恢复后窗口少保存时机晚于窗口创建缩短保存间隔保存前确认快照包含新窗口恢复后目录不对shell 钩子或挂载时序问题检查 shell 配置systemd 服务加 Afterremote-fs.target恢复后 vim/ssh 没启动进程未加入白名单配置resurrect-processes加入 vim ssh系统重启后没恢复systemd 服务没启动systemctl --user status tmux.service查日志多说一句resurrect 和 continuum 都是通过保存文本快照来做恢复的它们不能替代真正的进程级快照比如 CRIU 或者虚拟机快照。如果你的场景需要在崩溃后完全还原进程内存状态那 tmux 持久化方案做不到。但绝大多数开发运维场景里我们需要的不是“进程复活”而是“工作环境重现”这个方案已经覆盖得足够好了。最后再分享一个小技巧每次执行完重要操作、重新调整了窗口布局之后我习惯手动按一下prefix Ctrl-s立刻保存。continuum 的自动保存是兜底手动保存是把确定性握在自己手里。两件事配合起来基本上可以做到“丢失现场”这件事在正常的运维工作中彻底消失。
返回列表