> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# ghost blog 自动备份
- URL: https://blog.hacking.pub/2016/11/01/ghost-blog-zi-dong-bei-fen/
- Published: 2016-11-01T13:09:21.000Z
- Updated: 2016-11-01T13:11:17.000Z
- Author: chainhelen
- Tags: 备份, go语言, crontab, 七牛

## 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

```

##### 上传

我使用七牛的服务来备份数据库，[七牛各种语言的官方文档](http://developer.qiniu.com/resource/official.html?ref=blog.hacking.pub)

我使用golang方式，实际推荐[qrsync命令行](http://developer.qiniu.com/code/v6/tool/qrsync.html?ref=blog.hacking.pub)  
首先先安装一下依赖库

```
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 &

前面５位，可以为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()
}

```

[代码下载](http://image.hacking.pub/s/7y7cey6fz6ysz5k/backup.go?dl=0&ref=blog.hacking.pub)