ghost blog 自动备份
ghost-blog 自动备份
blog放在digitalocean上,说不定哪天忘记缴费就给停了,超出时间数据就都没了。
数据
先确定需要备份哪些数据,其实我们所需要备份的数据只有是数据库数据、主题(如果用的是默认数据库,就没有必要了)
$ cd ghost/content
$ du -h
200K ./data
8.0K ./apps
24K ./themes/casper/assets/fonts
12K ./themes/casper/assets/js
52K ./themes/casper/assets/css
280K ./themes/casper/assets
12K ./themes/casper/partials
336K ./themes/casper
24K ./themes/casper_ok2/assets/fonts
60K ./themes/casper_ok2/assets/js
116K ./themes/casper_ok2/assets/css
392K ./themes/casper_ok2/assets
12K ./themes/casper_ok2/partials
444K ./themes/casper_ok2
784K ./themes
8.0K ./images
1004K .
可以看到我目前blog里面一个content所有内容才1M不到(imags是空的,之前我有blog写过使用反向代理把dropbox作为图床)。 其中有两个主题,ghost默认主题casper就可以删了,仅备份casper_ok2
rm -rf ./themes/casper
打包
确认环境中已经安装了7z或者zip等压缩工具
//在content目录下
7z a content.7z ./
//或者
zip content.zip ./ -r
上传
我使用七牛的服务来备份数据库,七牛各种语言的官方文档
我使用golang方式,实际推荐qrsync命令行 首先先安装一下依赖库
go get-u qiniupkg.com/api.v7
做个测试,把本地content.7z上传到七牛服务器上,改名为qiniu_content.7z
package main
import (
"fmt"
"qiniupkg.com/api.v7/conf"
"qiniupkg.com/api.v7/kodo"
"qiniupkg.com/api.v7/kodocli"
)
var (
//设置上传到的空间
bucket = "----------"
)
//构造返回值字段
type PutRet struct {
Hash string `json:"hash"`
Key string `json:"key"`
}
func main() {
//初始化AK,SK
conf.ACCESS_KEY = "****************************************"
conf.SECRET_KEY = "++++++++++++++++++++++++++++++++++++++++"
//创建一个Client
c := kodo.New(0, nil)
//设置上传的策略
policy := &kodo.PutPolicy{
Scope: bucket,
//设置Token过期时间
Expires: 60,
}
//生成一个上传token
token := c.MakeUptoken(policy)
//构建一个uploader
zone := 0
uploader := kodocli.NewUploader(zone, nil)
var ret PutRet
//设置上传文件的路径
filepath := "./content.7z"
//调用PutFileWithoutKey方式上传,没有设置saveasKey以文件的hash命名
res := uploader.PutFile(nil, &ret, token, "qiniu_content.7z", filepath, nil)
//打印返回的信息
fmt.Println(ret)
//打印出错信息
if res != nil {
fmt.Println("io.Put failed:", res)
return
}
}
自动化定时
说到底,这东西还是得手动上传,太麻烦了。 那么来了一个自动化上传的。
time 库
先来测试time 库
//test_time.go
curtime := time.Now()
fmt.Printf("time.Now() is %v\n", curtime)
g1 := curtime.Format("2006-01-02")
g2 := curtime.Format("2008-01-03")
fmt.Printf("Format 2006-01-02, get the result is %v\n", g1)
fmt.Printf("Format 2008-03-12, get the result is %v\n", g2)
g3 := curtime.Format("15:04:05.99999999")
fmt.Printf("Format 15:04:05.99999999, get the result is %v\n", g3)
//stdout
time.Now() is 2016-11-01 17:24:04.516592119 +0800 CST
Format 2006-01-02, get the result is 2016-11-01
Format 2008-03-12, get the result is 1008-11-05
Format 15:04:05.99999999, get the result is 17:24:04.51659211
要想format正确,必须使用2006-01-02 15:04:05这个时间戳,golang的生日,其中含义如下
1 2 3 4 5 6 7
月 日 时 分 秒 年 时 区
exec 库
上传之前肯定需要压缩,需要调用7z或者zip(我这里只写了7z)
//cmd := exec.Command("7z a " + time.Now().Format("2006-01-02") + "_content.7z /home/ch/workspace/go/go_upload/m/")
cmd := exec.Command("7z", "a", time.Now().Format("2006-01-02")+"_content.7z", "/home/ch/workspace/go/go_upload/m/")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n\n\tall logout from exec:-------------------- start ----------------\n")
fmt.Printf("%s\n", out.String())
fmt.Printf("\tall logout from exec:-------------------- end ----------------\n\n")
注意注释的地方,如果Command里面的参数是整个命令字符串,将会报错 (/home/ch/workspace/go/go_upload/m/实际是存在的)
2016/11/01 17:38:22 fork/exec 7z a 2016-11-01_content.7z /home/ch/workspace/go/go_upload/m/: no such file or directory
exit status 1
定时
代码在文章结束处有下载 目前主函数的逻辑大致如下:
func main() {
curtime := time.Now()
filename := curtime.Format("2006-01-02") + "_content.7z"
cmd := exec.Command("7z", "a", filename, "./apps", "./themes", "./images", "./data")
err := cmd.Run()
if nil != err {
log.Fatal(err)
}
uploadToqiniu(filename)
}
现在想办法定时
crotab [-u user] file 或
crotab [-u usre] [-e | -l | -r]
-u user 设置某个用户的crontab服务
file 文件名,载入任务列表
-e 编辑crontab文件内容
-l 显示crontab文件内容
-r 删除crontab文件内容
-i 删除crontab时,需确认
比方这是我以前写的一个备份mongodb的
0 */2 * * * /usr/local/dbbackup/backup.sh 1> /usr/local/dbbackup/log/crontab_mongo_back.file &
0 0 * * * /usr/local/dbbackup/deletebackup.sh 1> /usr/local/dbbackup/log/crontab_mongo_delete.file &
前面5位,可以为0、 *、 */n(n为整数)、 n-m(n,m区间)、n,m(n,m两个时间点)
分别代表 分钟,小时,号数,月份,星期
例子:每天18 : 00至23 : 00之间每隔30分钟重启smb
0,30 18-23 * * * /etc/init.d/smb restart
例子:每周六、周日的1 : 10重启smb
10 1 * * 6,0 /etc/init.d/smb restart
逻辑问题
crontab设置如下,
//上面已经将 go程序 生成 可执行 backup
0 */12 * * * /home/chaihelen/backup 1> /home/chainhelen/backup.log &
实际情况是如果代码只是在content目录下,我们手动执行,是没有大问题 一旦设置定时器,执行的路径变成了/home/chainhelen/backup,那么程序里面的相对位置都是有问题的
路径
//比如/home/chainhelen/backup,backup是程序,现在在/home/目录下
//运行程序用了"./chainhelen/backup",那么file = "./chainhelen/backup"
file, _ := exec.LookPath(os.Args[0])
//获取绝对位置wpath = "/home/chainhelen/backup"
cpath, _ := filepath.Abs(file)
//父亲一级的路径,ppath = "/home/chainhelen"
ppath.Dir(cpath)
所以代码最终变成这样
func getExeDir() string {
file, _ := exec.LookPath(os.Args[0])
wpath, _ := filepath.Abs(file)
return path.Dir(wpath)
}
func main() {
curtime := time.Now()
filename := curtime.Format("2006-01-02") + "_content.7z"
absDir := getExeDir()
absfilename := path.Join(absDir, "./"+filename)
absapps := path.Join(absDir, "./apps")
absthemes := path.Join(absDir, "./themes")
absimages := path.Join(absDir, "./images")
absdata := path.Join(absDir, "./data")
cmd := exec.Command("7z", "a", absfilename, absapps, absthemes, absimages, absdata)
err := cmd.Run()
if nil != err {
log.Fatal(err)
}
uploadToqiniu(filename, absfilename)
rmcmd := exec.Command("rm", absfilename)
rmcmd.Run()
}