夜雨聆风学习资料网

ARTICLE · 1013862

公共电脑维护利器:银河麒麟桌面系统「开机自动还原」部署方案(rsync 版)

公共电脑维护利器:银河麒麟桌面系统「开机自动还原」部署方案(rsync 版)

适用:银河麒麟桌面操作系统 V10(含 SP1、2207/2303 等版本,x86_64 与 ARM64 飞腾/鲲鹏版通用)方案:基于 rsync 快照还原的一键脚本 kylin-reset-rsync.sh场景:教室、图书馆、政务大厅、单位公共机等「多人共用、用完即净」的环境文章结尾附脚本内容,也可关注公众号发送“开机自动还原”获取下载链接


一、为什么公共电脑需要"开机自动还原"

公共电脑被多人随意使用时,常见的"被弄脏"方式包括:

  • 桌面、下载、文档目录堆满文件,删不掉、清不完;

  • 乱装软件、改乱系统设置、改坏桌面环境;

  • 误删系统文件导致无法开机。

传统人工清理费时费力且容易漏。更彻底的做法是开机自动还原:每次开机都把系统恢复到管理员预设的"出厂状态",用户的一切改动在重启后自动消失。本方案完全基于 rsync,开源、离线可用、不依赖网络,非常适合内网与信创环境。


二、方案原理

kylin-reset-rsync.sh 的核心思路:

  • ‎首次部署时,把当前干净的 ‎/home 打成一份模板快照,存放到指定的分区目录;

  • 之后每次开机(在用户登录之前),系统自动用 rsync 把 ‎/home 覆盖还原为模板状态,用户改动全部清除;

  • ‎系统层(/usr、/opt、/etc)保持真实可写:管理员可随时升级应用、安装软件、修改配置,且不会被还原。

为什么要"只还原 /home、不动系统层"?

  • 普通用户没有 root 权限,本就改不了系统层,真正会被弄脏的就是家目录、桌面;

  • 系统层保持可写,意味着管理员升级应用、装软件、改配置零阻碍,无需任何额外操作;

  • 只还原家目录,还原快、占用小、风险低。

这套架构尤其适合需要经常给公共机升级软件、调整配置的单位——还原不干扰维护,维护不破坏还原。


三、部署前检查清单(务必逐项完成)

1. 查看磁盘与分区布局

lsblk

重点关注:

  • 系统根分区在哪(MOUNTPOINT 为 ‎/ 的那行);

  • 是否有一块独立的数据盘或未使用的空闲分区(用于存放模板快照);

  • 是否有出厂自带的 timeshift 分区(挂载点形如 ‎/run/timeshift/backup)。

2. 是否需要提前安装 timeshift?(常见疑问,先回答)

不需要。

  • 本方案完全不依赖 timeshift,rsync 是系统自带命令;

  • 麒麟部分版本(如 V10 2207 出厂镜像)自带 timeshift 及其备份分区(通常是一个几百 G、挂载在 ‎/run/timeshift/backup 的分区)。如果你发现机器里有这样的分区,可以直接复用它的空间存放模板快照,不用额外腾地方;

  • 如果没有 timeshift 分区,也没关系:方案只需一个可写目录,可以放在任何有权限的挂载点上,或用一块空闲分区单独挂载。

一句话:有 timeshift 分区就顺手复用,没有也不用装,方案照常可用。

3. 准备模板存放空间

在磁盘上确认一个"有剩余空间、且不是系统根分区"的位置。推荐优先级:

  1. 出厂 timeshift 备份分区(如 ‎/run/timeshift/backup,空间大);

  2. 独立数据盘或未使用分区;

  3. 任何可写的大容量挂载点。

模板快照大小约等于家目录的实际占用,建议存放位置至少预留 10~20G 以上余量。

没有 timeshift 分区怎么办? 本方案只需一个可写目录,按机器情况三选一即可:

机器情况
做法
部署参数
有内置空闲数据分区/逻辑卷(如 LVM 的 ‎/data)
直接把模板放该分区(推荐)
‎--dev=/dev/mapper/卷组--逻辑卷 --mp=/data
只有一个系统根分区(单盘无空闲分区)
在根分区下建专用目录存放
‎--dir=/var/lib/reset-template
只有外接移动硬盘可用
用移动硬盘分区,但需每次开机都插着
‎--dev=/dev/sdX --mp=/挂载点

三者都不需要提前安装 timeshift。内置数据分区(如已挂载的 /data)最省心:不占系统盘、开机自动就位;移动硬盘存在"忘插导致还原失效"的风险,仅作备选。


四、部署步骤

第 1 步:准备脚本并赋权

将脚本 kylin-reset-rsync.sh 拷贝到目标机(U 盘或内网共享均可):

chmod +x kylin-reset-rsync.sh

第 2 步:一键部署

# 参数说明: --dev 指定模板存放分区, --mp 指定该分区挂载点sudo bash kylin-reset-rsync.sh deploy --dev=/dev/sdX --mp=/挂载点

示例:机器上 timeshift 备份分区为 /dev/sda5、挂载在 /run/timeshift/backup,则执行

sudo bash kylin-reset-rsync.sh deploy --dev=/dev/sda5

(--mp 默认即为 /run/timeshift/backup,无需重复指定)

例如:我的情况是有数据分区,但是没有安装timeshift,所以我是参照“3.准备模板存放空间”中第一行执行的命令。由于该脚本是默认情况是安装了timeshift,且指定了备份分区/dev/nvme0n1p5 ,所以每次执行命令的后面都有指定备份分区和挂载位置,具体情况可看图片标红处。

首次部署脚本会自动完成:

  • 校验分区存在且非系统根分区(防呆);

  • 挂载目标分区;

  • 将当前 ‎/home 打成模板快照;

  • 生成开机还原服务并设置为开机自启;

  • 备份原 fstab / grub 配置。

第 3 步:重启生效

sudo reboot

重启后,每次开机都会自动把 /home 恢复为模板状态,用户改动全部还原。

日常维护(管理员)

# 升级应用 / 安装软件 / 修改配置 —— 直接做, 不受还原影响sudo apt update && sudo apt upgrade# 配置满意后, 重新固化模板(下次开机按新模板还原)sudo bash kylin-reset-rsync.sh update-template# 查看部署状态sudo bash kylin-reset-rsync.sh status# 需要彻底暂停还原做深度维护时(重启后生效)sudo bash kylin-reset-rsync.sh maintenance on# 维护完成恢复还原sudo bash kylin-reset-rsync.sh maintenance off# 卸载还原sudo bash kylin-reset-rsync.sh uninstall

五、注意事项(重要,请逐条阅读)

  1. ‎部署前备份重要数据:脚本会自动备份 fstab、grub 配置,但请确保系统与用户关键数据已有备份再操作。

  2. ‎模板分区严禁选系统根分区:模板快照如果放在系统分区自身,还原时会被自己覆盖,务必用独立分区或独立挂载点。

  3. ‎格式化是危险操作:若对数据分区执行 ‎mkfs.ext4 格式化,会清空该分区全部数据。执行前务必 ‎lsblk 核对设备名,确认不是系统盘再操作。

  4. ‎还原范围与系统升级:本方案默认只还原 ‎/home。系统层升级、装软件、改配置会保留(不会被还原),这是设计意图;如确需把 ‎/etc、‎/opt 等系统配置也纳入还原范围,可调整脚本中的还原范围配置,但建议谨慎——避免还原把管理员配置也一并清掉。

  5. ‎用户数据重启即失:部署后,任何用户在 ‎/home 下保存的文件、桌面文件、浏览器下载等,重启后都会消失。务必在公共机旁贴提示,并考虑提供一个公共共享目录(不参与还原)供用户临时保存文件,或引导用户用 U 盘保存。

  6. ‎root 权限管控:还原方案只在管理员有 root 权限的前提下生效。请确认普通用户没有 sudo 权限,否则用户可自行关闭还原服务、绕过管控。建议同时开启麒麟系统的账户权限管理。

  7. ‎fstab / grub 自动备份:脚本会在修改前备份 ‎/etc/fstab 与 ‎/etc/default/grub(带时间戳)。若机器无法开机,可在应急环境(live CD 或单用户模式)用备份文件恢复。

  8. ‎数据盘故障兜底:脚本对模板分区挂载使用了 ‎nofail 选项,即使还原分区损坏或缺失,系统也能正常开机,不会卡在启动阶段。

  9. ‎维护模式不要忘记关闭:‎maintenance on 只对下一次启动生效;维护完成后务必执行 ‎maintenance off,否则还原将一直暂停。

  10. ‎模板更新要及时:管理员升级系统、装新软件后,记得执行 ‎update-template 重新固化模板,否则下次开机仍会还原到旧模板,升级等于白做。

  11. ‎ARM 与 x86 通用性:本方案命令在两个架构的麒麟桌面系统上完全一致,无需区分。

  12. ‎空间预留:模板快照大小约等于家目录实际占用,请确保模板分区有足够剩余空间。


六、常见问题排查

现象
原因
处理
部署时报分区不存在
设备名写错
‎lsblk 核对后用 ‎--dev= 指定真实分区
部署后 /home 没有还原
还原服务未启用
‎systemctl is-enabled kylin-reset.service 确认,未启用则重新部署
模板更新后不生效
忘记执行 ‎update-template
固化后重启一次验证
公共机用户能删还原服务
用户有 sudo 权限
收紧账户权限,普通用户不给 sudo
还原分区损坏导致开机异常
分区故障
脚本已用 nofail 兜底,系统可正常进入;修复分区后重新部署
机器没有 timeshift 分区能否部署
完全可以
用内置数据分区(‎/data)、根分区专用目录(‎--dir=)或常插移动硬盘,见第三节

七、总结

  • ‎开机自动还原:基于 rsync 快照,兼容性好、单硬盘可用、不依赖网络;

  • ‎不需要提前安装 timeshift:有现成 timeshift 分区顺手复用,没有也不影响;

  • ‎只还原 /home、系统区真实可写:兼顾"防脏"与"管理员自由维护",升级装软件零阻碍;

  • ‎务必先 lsblk 核对磁盘、备份数据、管好普通用户权限、贴好"数据重启即失"提示。

把公共电脑交给"开机自动还原",一劳永逸,让管理员从"天天清理"里解放出来。


附:完整一键部署脚本(kylin-reset-rsync.sh)

#!/usr/bin/env bash#===============================================================================# 文件名 : kylin-reset-rsync.sh# 适用   : 银河麒麟 Kylin V10 桌面 (ARM/x86 均适用)#          针对单硬盘 / 无独立数据盘 / 5.4 内核(无 overlayfs 模块)机器设计# 功能   : 公共电脑「开机自动还原」— rsync 快照还原版##   原理  : 首次部署把 /home 打成模板快照, 存放于指定分区目录(默认复用#           nvme0n1p5 的 /run/timeshift/backup 分区, 空间大且不占用系统盘);#           开机时(登录前)自动把 /home 恢复为模板状态, 秒~分钟级。#   系统区(/usr /opt /etc)保持真实可写: 管理员升级应用/装软件/改配置不受影响,#   这些操作属于期望保留项, 不会被还原。##   维护流程: 管理员正常升级/配置 → 执行 update-template 重新打快照 → 下次开机生效## 用法示例:#   sudo bash kylin-reset-rsync.sh deploy                 # 一键部署#   sudo bash kylin-reset-rsync.sh deploy --dev=/dev/nvme0n1p5 --mp=/run/timeshift/backup#   sudo bash kylin-reset-rsync.sh update-template        # 重新固化模板(管理员维护后)#   sudo bash kylin-reset-rsync.sh maintenance on         # 暂停还原(重启生效)#   sudo bash kylin-reset-rsync.sh maintenance off        # 恢复还原#   sudo bash kylin-reset-rsync.sh status                 # 查看状态#   sudo bash kylin-reset-rsync.sh uninstall              # 卸载还原#===============================================================================set -uSCRIPT_NAME="$(basename "$0")"#----------------------- 可配置项(支持 --dev / --mp / --dir 覆盖) --------------TPL_DEV="${TPL_DEV:-/dev/nvme0n1p5}"          # 模板快照存放分区TPL_MP="${TPL_MP:-/run/timeshift/backup}"     # 该分区挂载点TPL_DIR="${TPL_DIR:-$TPL_MP/reset-template}"  # 模板快照目录# 开机要还原的目录(用空格分隔, 相对根路径; 默认仅 /home)# 如需连系统配置一起还原可改为: "home etc opt usr/local root"RESTORE_LIST="${RESTORE_LIST:-home}"RUNNER="/usr/local/sbin/kylin-reset-run.sh"SVC="/etc/systemd/system/kylin-reset.service"GRUB_CFG="/etc/default/grub"#----------------------- 工具函数 ----------------------------------------------red(){ printf '\033[31m%s\033[0m\n' "$*"; }grn(){ printf '\033[33m%s\033[0m\n' "$*"; }ylw(){ printf '\033[33m%s\033[0m\n' "$*"; }log(){ echo "[$(date '+%F %T')] [kylin-reset] $*"; }die(){ red "错误: $*"; exit 1; }require_root(){ [ "$(id -u)" -eq 0 ] || die "请用 root 运行: sudo bash $SCRIPT_NAME ..."; }check_os(){  local pretty="unknown"  [ -r /etc/os-release ] && { . /etc/os-release; pretty="${PRETTY_NAME:-unknown}"; }  case "$pretty" in    *Kylin*|*kylin*|*银河麒麟*|*麒麟*) : ;;    *) ylw "警告: 未检测到麒麟系统特征, 可能存在兼容风险 (当前: $pretty)" ;;  esac  grn "系统: $pretty | 内核: $(uname -r)"}ensure_mount(){  grep -q " $TPL_MP " /proc/mounts || {    log "挂载 $TPL_DEV -> $TPL_MP"    mkdir -p "$TPL_MP"    mount "$TPL_DEV" "$TPL_MP" || die "挂载 $TPL_DEV 失败, 请核对分区/挂载点"  }}update_grub_cfg(){  if command -v update-grub >/dev/null 2>&1; then update-grub;  elif [ -f /boot/grub2/grub.cfg ]; then grub2-mkconfig -o /boot/grub2/grub.cfg;  else grub-mkconfig -o /boot/grub/grub.cfg; fi}snapshot(){  local d sub tgt  for d in $RESTORE_LIST; do    sub="${d##*/}"    mkdir -p "$TPL_DIR/$sub"    log "打快照: /$d -> $TPL_DIR/$sub"    rsync -a --delete "/$d/" "$TPL_DIR/$sub/"  done}#----------------------- deploy ------------------------------------------------cmd_deploy(){  require_root  check_os  if systemctl is-enabled kylin-reset.service >/dev/null 2>&1 || [ -f "$RUNNER" ]; then    ylw "检测到已部署过。更新模板请执行: bash $SCRIPT_NAME update-template; 重装请先 uninstall"    exit 0  fi  lsblk -o NAME "$TPL_DEV" >/dev/null 2>&1 || die "分区 $TPL_DEV 不存在, 请 lsblk 核对后用 --dev= 指定"  ensure_mount  log "首次部署: 制作 /home 模板快照..."  snapshot  grn "模板快照已生成: $TPL_DIR"  # 生成开机还原执行脚本  cat > "$RUNNER" <<EOF#!/bin/sh# kylin-reset-run.sh  (由部署脚本自动生成)if grep -qw maintenance=1 /proc/cmdline; then exit 0; fi   # 维护模式: 跳过还原TPL_MP="$TPL_MP"; TPL_DIR="$TPL_DIR"; TPL_DEV="$TPL_DEV"grep -q " \$TPL_MP " /proc/mounts || { mkdir -p "\$TPL_MP"; mount "\$TPL_DEV" "\$TPL_MP" 2>/dev/null || exit 0; }for d in $RESTORE_LIST; do  sub="\${d##*/}"  [ -d "\$TPL_DIR/\$sub" ] && rsync -a --delete "\$TPL_DIR/\$sub/" "/\$d/"doneEOF  chmod +x "$RUNNER"  # 生成开机服务  cat > "$SVC" <<EOF[Unit]Description=Public pc auto restore (kylin-reset)DefaultDependencies=noAfter=local-fs.targetBefore=display-manager.service getty@tty1.service[Service]Type=oneshotExecStart=$RUNNERRemainAfterExit=yes[Install]WantedBy=multi-user.targetEOF  systemctl daemon-reload  systemctl enable kylin-reset.service >/dev/null 2>&1  grn ""  grn "================ 部署完成 ================"  grn " 开机自动还原: $RESTORE_LIST (默认 /home)"  grn " 模板位置    : $TPL_DIR"  grn " 系统区/应用升级不受影响, 可正常安装与配置"  ylw " 下一步: sudo reboot 后 /home 即被开机还原管控"  ylw " 维护后固化模板: sudo bash $SCRIPT_NAME update-template"  grn "=========================================="}#----------------------- update-template ----------------------------------------cmd_update_template(){  require_root  [ -f "$RUNNER" ] || die "尚未部署, 请先执行 deploy"  ensure_mount  ylw ">>> 将把当前 /home 状态固化为模板(即成为下次开机还原的目标) <<<"  read -r -p "确认? 输入 yes 继续: " ans  [ "$ans" = "yes" ] || die "已取消"  snapshot  grn "模板已更新, 下次开机生效。"}#----------------------- maintenance --------------------------------------------cmd_maintenance(){  require_root  local mode="${1:-}"  case "$mode" in    on)      [ -f "$GRUB_CFG" ] || die "未找到 $GRUB_CFG"      cp "$GRUB_CFG" "$GRUB_CFG.bak.marvis-reset.$(date +%Y%m%d%H%M%S)"      grep -qw maintenance=1 "$GRUB_CFG" \        || sed -i 's/GRUB_CMDLINE_LINUX="\([^"]*\)"/GRUB_CMDLINE_LINUX="\1 maintenance=1"/' "$GRUB_CFG"      update_grub_cfg >/dev/null 2>&1      grn "已启用维护模式(重启后还原暂停)。维护完成: bash $SCRIPT_NAME maintenance off"      ;;    off)      [ -f "$GRUB_CFG" ] || die "未找到 $GRUB_CFG"      sed -i 's/ maintenance=1//g' "$GRUB_CFG"      update_grub_cfg >/dev/null 2>&1      grn "已关闭维护模式, 重启后恢复自动还原。"      ;;    *) die "用法: $SCRIPT_NAME maintenance on|off" ;;  esac}#----------------------- status ------------------------------------------------cmd_status(){  echo "================ 部署状态 ================"  echo "模板分区 : $TPL_DEV -> $TPL_MP"  lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT "$TPL_DEV" 2>/dev/null || echo "  未找到 $TPL_DEV"  echo "模板目录 : $TPL_DIR"  [ -d "$TPL_DIR/home" ] && du -sh "$TPL_DIR/home" 2>/dev/null || echo "  尚无模板快照"  echo "还原服务 : $(systemctl is-enabled kylin-reset.service 2>/dev/null || echo 未启用)"  grep -qw maintenance=1 /proc/cmdline && echo "运行模式 : [维护模式] 还原暂停" || echo "运行模式 : [公共模式] 开机自动还原"  echo "还原范围 : $RESTORE_LIST"  echo "=========================================="}#----------------------- uninstall ----------------------------------------------cmd_uninstall(){  require_root  ylw ">>> 即将卸载开机还原 <<<"  read -r -p "确认? 输入 yes 继续: " ans  [ "$ans" = "yes" ] || die "已取消"  systemctl disable kylin-reset.service 2>/dev/null || true  rm -f "$SVC" "$RUNNER"  systemctl daemon-reload  grn "已卸载还原。模板快照未删除: $TPL_DIR (如需清理: sudo rm -rf $TPL_DIR)"}#----------------------- main ---------------------------------------------------main(){  local cmd="" i=0  local opt_args=() positional=()  # 分离子命令 / 选项 / 位置参数(选项可写在子命令前或后)  for a in "$@"; do    if [ -z "$cmd" ]; then      case "$a" in        --*) opt_args+=("$a") ;;        *)   cmd="$a" ;;      esac    else      case "$a" in        --*) opt_args+=("$a") ;;        *)   positional+=("$a") ;;      esac    fi  done  # 解析选项(支持 --dev=x 与 --dev x 两种写法)  while [ $i -lt ${#opt_args[@]} ]; do    a="${opt_args[$i]}"    case "$a" in      --dev=*) TPL_DEV="${a#--dev=}" ;;      --mp=*)  TPL_MP="${a#--mp=}" ;;      --dir=*) TPL_DIR="${a#--dir=}" ;;      --dev)   [ $((i+1)) -lt ${#opt_args[@]} ] && { TPL_DEV="${opt_args[$((i+1))]}"; i=$((i+1)); } ;;      --mp)    [ $((i+1)) -lt ${#opt_args[@]} ] && { TPL_MP="${opt_args[$((i+1))]}"; i=$((i+1)); } ;;      --dir)   [ $((i+1)) -lt ${#opt_args[@]} ] && { TPL_DIR="${opt_args[$((i+1))]}"; i=$((i+1)); } ;;    esac    i=$((i+1))  done  case "$cmd" in    deploy|install)         cmd_deploy ;;    update-template|update) cmd_update_template ;;    maintenance)            cmd_maintenance "${positional[0]:-}" ;;    status)                 cmd_status ;;    uninstall|remove)       cmd_uninstall ;;    help|-h|--help|"")      usage_text ;;    *) red "未知命令: $cmd"; exit 1 ;;  esac}usage_text(){  cat <<'EOF'用法: sudo bash kylin-reset-rsync.sh <命令> [选项]命令:  deploy [--dev=分区] [--mp=挂载点] [--dir=目录]   一键部署开机还原  update-template                                 重新固化模板(管理员维护后)  maintenance on|off                              切换维护模式  status                                          查看状态  uninstall                                       卸载还原选项(可写在子命令前或后):  --dev=/dev/xxx   模板分区 (默认 /dev/nvme0n1p5)  --mp=/挂载点     该分区挂载点 (默认 /run/timeshift/backup)  --dir=/目录      模板目录 (默认 <挂载点>/reset-template)示例:  sudo bash kylin-reset-rsync.sh deploy --dev=/dev/sda5  sudo bash kylin-reset-rsync.sh --dev=/dev/sda5 deployEOF}main "$@"

相关学习资料