0. 写在前面:为什么你需要“神器”而非“常用命令
在服务器运维的日常工作里,误删文件这种事并不算稀罕,但真正碰上的时候,心里多少还是会有点慌。处理这类情况,最关键的反应往往不在于“多快恢复”,而在于“怎么避免二次破坏”。这份记录,更像是一次带着回忆和经验的整理,希望在类似事故发生时,能帮人稍微沉住气。那一刻,终端里只剩下一个命令行,回车声像闷雷——rm -rf
、git clean -fdx
、或者一句手滑的 s3 rm
。心跳会加速,脑子会短路。先说一句:别着急敲更多命令。救数据,是有方法的;有序、谨慎、按步骤,成功率远比盲目操作高得多。下面把我多年现场救援的经验浓缩为一份落地可操作的恢复策略,所有命令都给出示例与模拟输出,便于演练与现场使用。
按这套「先保,后救,再验」的顺序走,能把伤害降到最低。
$ sudo systemctl stop myapp.service
# 或者:在应用层拉掉写流量
$ sudo lsof +L1
# 模拟输出:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
myapp 2345 root 22u REG 8,1 10485760 0 123456 /var/log/app.log (deleted)
$ sudo dd if=/dev/sda1 of=/tmp/disk.img bs=4M conv=noerror,sync status=progress
12345+0 records in
12345+0 records out
51200000000 bytes (51 GB) copied, 1200.00 s, 42.6 MB/s
# LVM snapshot 示例
$ sudo lvcreate --size 10G --snapshot --name root_snap /dev/vg0/root
Logical volume "root_snap" created
重要提示:在未创建镜像/快照前,尽量不要写磁盘(包括
apt
、yum
、日志大量滚动),也不要重启机器(某些删除后打开的文件会在进程退出时彻底释放)。
下面按常见场景给出可执行步骤与命令模拟输出。遇到实际情形,优先选最贴近你环境的方法。
很多情况下,文件被删除后,进程仍持有文件句柄。文件实际上还在磁盘上,只是目录项被移除了。
lsof +L1
列出被删除但仍打开的文件。
(上面快速备忘有示例。)$ sudo cp /proc/2345/fd/22 /tmp/recovered_app.log
# 模拟输出:无输出 -> 成功
$ ls -lh /tmp/recovered_app.log
-rw-r--r-- 1 root root 10M Aug 10 11:10 /tmp/recovered_app.log
$ sudo kill -USR1 2345 # 假设应用支持 reopen log
$ sudo systemctl restart myapp.service
快照是最安全、最高效的恢复途径。目标是从快照中挂载只读副本或克隆为新卷并从上面恢复文件。
LVM 示例:
$ sudo lvcreate --size 10G --snapshot --name root_snap /dev/vg0/root
Logical volume "root_snap" created
$ sudo mkdir /mnt/snap
$ sudo mount -o ro /dev/vg0/root_snap /mnt/snap
$ ls /mnt/snap/var/log | head -n 5
AWS EBS 快照思路(示例 CLI):
$ aws ec2 create-snapshot --volume-id vol-0123456789abcdef0 --description "pre-recover"
# 模拟返回:
{
"SnapshotId": "snap-0abcdef1234567890",
...
}
# 然后用该 snapshot 创建新卷、挂载到恢复主机进行文件恢复
Btrfs / ZFS:用 btrfs restore
或 ZFS 快照做回滚/克隆。命令因环境而异,原则一样:不要直接在原数据上修复,在快照或克隆上操作。
这类最常见也最棘手。优先步骤如下:
$ sudo systemctl stop myapp.service
$ sudo systemctl stop rsyslog
$ sudo dd if=/dev/sda1 of=/tmp/disk.img bs=4M conv=noerror,sync status=progress
debugfs
/ extundelete
操作(对 ext 文件系统):# 在恢复主机上(非原盘):
$ sudo extundelete /dev/loop0 --restore-directory /var/log
# 模拟输出:
Processing journal...
Examining inode table...
Restored /var/log/app.log to RECOVERED_FILES/var/log/app.log
extundelete
会把恢复出的文件放在 RECOVERED_FILES
目录下。注意:extundelete
要求目标分区在运行时为未挂载状态(或以只读方式挂载)。
xfsdump
/xfsrestore
备份恢复,或者使用 xfs_repair
(这是修复工具,不是 undelete),最稳妥的方法仍是基于镜像尝试数据恢复工具(testdisk/photorec),成功率参差不齐。数据库误删(误删行/误 drop 表)要求更谨慎:千万别在原库上直接导入全量备份覆盖。
恢复步骤示例(假设你有全量备份 + binlog):
$ gunzip < prod_full_20250810.sql.gz | mysql -uroot -p -h restore-host
# 模拟:无输出 -> 成功
$ mysqlbinlog --start-datetime="2025-08-10 09:00:00" --stop-datetime="2025-08-10 09:59:59" /var/lib/mysql/mysql-bin.000001 | mysql -uroot -p -h restore-host mydb
# 模拟:输出 SQL 回放行数统计
注意:若误删导致 binlog 上包含删除语句,回放前需要过滤掉 DELETE 语句(用
--rewrite
或手工编辑 SQL),以免再次误删。
容器化环境恢复思路依赖于持久化存储类型。
示例:把 PVC 挂到临时恢复 Pod:
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: recover-pod
spec:
containers:
- name: shell
image: ubuntu
command: ["/bin/bash","-c","sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
EOF
$ kubectl exec -it recover-pod -- bash
root@recover-pod:/# ls -la /data
在 Pod 中将重要文件复制到另一个安全存储(例如临时 PVC、NFS 或对象存储)。
对象存储的恢复策略取决于是否开启了版本控制或回收站:
VersionId
的历史版本。$ aws s3api list-object-versions --bucket my-bucket --prefix path/to/file
# 找到 VersionId 后:
$ aws s3api get-object --bucket my-bucket --key path/to/file --version-id <VERSIONID> /tmp/file
Git 通常可以自救,前提是本地存在 reflog 或远程有未删除的引用。
$ git reflog
# 找到删除前的 commit id
$ git checkout -b recover <COMMIT_ID>
# 或从远程 fetch 后恢复分支
如果整仓被删除,但远程仍有备份,可从远程 clone 回来。
这是最后的手段,针对文件头识别做恢复,常用于照片、日志之类。成功率不保证,但仍可试:
$ sudo apt-get install testdisk
$ sudo testdisk /tmp/disk.img
# 交互式界面进行分区/文件恢复
或使用 photorec
批量恢复文件类型(注意恢复目录占用空间大)。
恢复不是交差,必须验证。
sha256sum
对比源/备份。$ sha256sum /tmp/recovered_app.log
e3b0c44298fc1c149afbf4c8996fb924...
$ curl -I http://127.0.0.1:8080/health
HTTP/1.1 200 OK
恢复后必须做完整复盘,把经验固化为变更,避免复发。
建议记录并提交的内容包括:
把关键命令、恢复 playbook 写成 Runbook,加入 on-call 手册并做演练。
这些策略能把事故概率与恢复时间大幅拉低。
rm -rf
,引入安全工具(safe-rm、trash-cli、或内部统一删除工具)。# 停止写入
sudo systemctl stop myapp.service
# 查被删除但仍打开的文件
sudo lsof +L1
# 做镜像(只读备份)
sudoddif=/dev/sda1 of=/tmp/disk.img bs=4M conv=noerror,sync status=progress
# 创建 LVM snapshot
sudo lvcreate --size 10G --snapshot --name root_snap /dev/vg0/root
# 挂载镜像
sudomkdir /mnt/recover
sudo mount -o ro,loop /tmp/disk.img /mnt/recover
# extundelete(在恢复盘对未挂载分区运行)
sudo extundelete /dev/loop0 --restore-directory /var/log
# MySQL 恢复示例(在恢复实例上)
gunzip < prod_full.sql.gz | mysql -uroot -p
mysqlbinlog --start-datetime="2025-08-10 09:00:00" --stop-datetime="2025-08-10 09:59:59" /var/lib/mysql/mysql-bin.000001 | mysql -uroot -p mydb
# S3 恢复版本
aws s3api list-object-versions --bucket my-bucket --prefix path/to/file
aws s3api get-object --bucket my-bucket --key path/to/file --version-id <VERSIONID> out.file
误删发生时,情绪会争先登场,但真正有用的是节奏。先保全证据、先做只读镜像或快照、再恢复并验证——用耐心去换取数据的完整。把今天的流程写进明天的 Runbook,把今天学到的防御措施变成明天的常态。技术可以把错误变小,把恢复变快;组织可以把错误变成成长。
这里我先声明一下,日常生活中大家都叫我波哥,跟辈分没关系,主要是岁数大了.就一个代称而已. 我的00后小同事我喊都是带哥的.张哥,李哥的. 但是这个称呼呀,在线下参加一些活动时.金主爸爸也这么叫就显的不太合适. 比如上次某集团策划总监,公司开大会来一句:“今个咱高兴!有请IT运维技术圈的波哥讲两句“ 这个氛围配这个称呼在互联网这行来讲就有点对不齐! 每次遇到这个情况我就想这么接话: “遇到各位是缘分,承蒙厚爱,啥也别说了,都在酒里了.我干了,你们随意!” 所以以后咱们改叫老杨,即市井又低调.还挺亲切,我觉得挺好.
运维X档案系列文章:
企业级 Kubernetes 集群安全加固全攻略( 附带一键检查脚本)
老杨的关于AI的号