建立linux回收站

一次删库跑路实例

前天中午,正在那啃鸡腿吃午饭,同事电话打过来告诉我服务没反应了。

作为一个尽职的运维人员,嘴里叼着鸡腿从食堂跑到工位开始排查问题。

三年老运维,排错就是快。一个 df 命令就看出来,是项目的数据库服务器磁盘满了。老毛病了。

(别问我为什么没有磁盘监控,问就是测试环境懒得加)

早就让领导申请添加磁盘了,但是申请还没批下来,有啥办法,手工删除吧。

1
2
3
4
5
cd /data/mysql
rm -fr mysqlbak/*
#这里删除全部文件是因为这个mysqlbak里,放的都是自动备份的mysql文件,并且已经做好他机备份了,本机备份可有可无。所以为了快速恢复业务,直接来了一个rm -fr *
#本来做到这里就完事了,重启下mysql就成了。
systemctl restart mysqld

然后,然后,就发现数据库起不来了,看日志发现mysql的安装路径丢了。这可吓得我连鸡腿都掉到了地上。

赶紧跑到路径下一看,我*,mysql目录果然空了。

history 一看,刚才啃鸡腿没注意,运行的是 rm -fr mysql/*

得,删库了,订机票跑路吧。


当然其实不至于跑路,有备库,有备份,测试环境,直接恢复就行了。但是不可避免的,被领导批了一顿。

好在是测试环境,不然我现在可能已经在看守所了。

建立回收站机制

众所周知,windows文件如果误删除了,可以通过在回收站中找到文件进行还原的。

但是作为服务器的linux系统(我们用的是rhel 7),在非桌面模式下是没有回收站的。一线运维经常要快速解决问题,有时候手快就忘了备份的事,忘了通配符不能乱用,忘了XXXXX。

所以一方面,要加强生产环境的安全操作意识,一方面也要加强环境的安全程度。

所以建立一个回收站就很重要。

之前也琢磨过类似的东西,无非就是建立一个回收目录,然后用 mv 命令替换 rm 命令。 但是网上找的脚本都不好用,而且服务器太多,每隔服务器登录上去修改又容易弄出其他问题。所以决定自己写一个脚本,通过ansible搞定。

脚本编写过程

说搞就搞。

其实本身脚本并没有特别复杂的地方。

  • 进来就三板斧,注释写好,定义时间变量和回收目录变量
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

#############################
#auther: fushisanlang
#version: v1
#mail: fushisanlang@yahoo.com
#use for replace rm command
##############################

####Values
trashDir=~/.tarsh/
  • 然后就是定义删除操作,本质上就是一个带提示的 mv 操作
1
2
3
4
5
6
7
8
####Delete operation
deleteOperation () {
#根据当前日期创建回收站
#将文件添加时间后缀并移动到回收站
ls -d ${@} | while read deleteFiles
do
done
}
  • 最后就是实际脚本操作内容
1
deleteOperation ${@}

但是测试的时候,模拟用户习惯,一般删除文件都会下意思 rm -rf 或者 rm -ri 等添加参数,但是上述脚本在删除的时候就会报错,因为没有 -fr 之类的文件。

  • 所以为了解决这个问题,就要在删除的时候把参数过滤掉
1
2
3
4
5
6
7
8
9
10
11
12
13
deleteOperation () {
while [[ -n ${1} ]]; do
case ${1} in
-*)
shift 1;;
*)
break;
esac
done
ls -d ${@} | while read deleteFiles
do
done
}

但是确实有些人就是习惯了 rm -ri 这种提醒,如果没有确认,可能有些人就是不习惯。所以还要让脚本认识自己支持的参数。

  • 所以首先要先自己把参数定义好,写成 help
1
2
3
4
5
6
7
8
9
####helpPrint
helpPrint () {
echo 'Usage: rm13 [OPTION]... FILE...'
echo 'remove (unlink) the FILE(s).'
echo ' -f ignore nonexistent files and arguments, never prompt'
echo ' -i prompt before removal'
echo ' -h display this help and exit'
exit 1
}
  • 然后定义一个用来提醒用户是否删除的显示
1
2
3
4
5
6
7
8
####alertPrint
alertPrint () {
echo 'Are you sure to delete?(y/n)'
read inputContent
if [[ ${inputContent} != y ]]; then
exit 1
fi
}
  • 最后把参数写进函数,让函数支持参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
####Delete operation
deleteOperation () {
while [[ -n ${1} ]]; do
case ${1} in
-f|-r|-rf|-fr )
shift 1;;
-i|-ir|-ir )
alertPrint; shift 1;;
-*)
helpPrint; shift 1;;
*)
break;
esac
done
ls -d ${@} | while read deleteFiles
do
done
}

这样基本上,删除的功能就完成了。

但是我们没有考虑还原的功能。

作为一个回收站,我们可能不要求直接一键还原,但是我们要记录下来一个文件是什么时候删除的,在哪里删除的。

本来最好的方式是直接通过文件名来定义删除路径,但是因为linux下,路径分隔符 / 写到文件名里很麻烦。

  • 所以决定添加一个日志功能
1
2
3
4
5
6
7
recordFile=${trashDir}recordFile

####Record file
recordFile() {
`pwd` >> ${recordFile}
echo ${@} >> ${recordFile}
}

这样就可以把删除情况写到日志里,如果以后有需要,可以回头查找。

这样脚本就大功告成了。

以下是脚本全貌。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash

#############################
#auther: fushisanlang
#version: v1
#mail: fushisanlang@yahoo.com
#use for replace rm command
##############################

####Values
trashDir=~/.tarsh/
recordFile=${trashDir}recordFile
####alertPrint
alertPrint () {
echo 'Are you sure to delete?(y/n)'
read inputContent
if [[ ${inputContent} != y ]]; then
exit 1
fi
}

####helpPrint
helpPrint () {
echo 'Usage: rm13 [OPTION]... FILE...'
echo 'remove (unlink) the FILE(s).'
echo ' -f ignore nonexistent files and arguments, never prompt'
echo ' -i prompt before removal'
echo ' -h display this help and exit'
exit 1
}

####Delete operation
deleteOperation () {
while [[ -n ${1} ]]; do
case ${1} in
-f|-r|-rf|-fr )
shift 1;;
-i|-ir|-ir )
alertPrint; shift 1;;
-*)
helpPrint; shift 1;;
*)
break;
esac
done
recordFile ${@}
ls -d ${@} | while read deleteFiles
do
done
}

####Record file
recordFile() {
`pwd` >> ${recordFile}
echo ${@} >> ${recordFile}
}


deleteOperation ${@}

自动化部署

上一篇文章,我们已经定义好了一个通过mv替代rm命令的脚本。但是如果要给所有服务器手工配置,就很麻烦了。
要登录服务器,上传脚本,修改环境变量。
所以要想一个简单的方法。

首先是脚本获取,我想的是放到一个url上,通过wget的方式获取。
https://download.fushisanlang.cn/rm13.sh

我这里用的是nginx代理的方式做的,nginx配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 80;
server_name download.fushisanlang.cn;
rewrite ^(.*) https://$server_name$1 redirect;
}

server {
listen 443;
server_name download.fushisanlang.cn;
ssl on;
ssl_certificate /usr/local/nginx/ca/download.crt;
ssl_certificate_key /usr/local/nginx/ca/download.key;

ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 ;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHERSA-AES128-SHA:RC4-SHA:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!DSS:!PKS;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;

root /usr/local/nginx/html/download;
index index.html;

}

其次是配置方法,如果挨个服务器执行命令,工作量很大,所以直接写成脚本通过curl调用。
url为 https://download.fushisanlang.cn/install-rm.sh
安装脚本

1
2
3
4
5
#!/bin/bash
wget 'https://download.fushisanlang.cn/rm13.sh' -O ~/.rm13
echo "alias rm='sh ~/.rm13'" >> ~/.bashrc
source ~/.bashrc
echo "enjoy your new rm command."

使用方法也很简单,直接

1
curl https://download.fushisanlang.cn/install-rm.sh | bash

就可以了。

最后就是ansible通过shell模块,让所有服务器执行上述命令就可以了。

反思

其实浩浩荡荡做了这么一个事,最重要的还是运维人员的安全操作意识。
操作前先备份,操作时双人确认。
等等等等。
还是希望大家规范操作,不要真等到删库跑路了才知道后悔。