一直以來備份這種事就是多做沒事,少做就會出事情。有一套很棒的 gem 叫做Backup ,可以來完成一些本來也許要用 shell script 才能做的事情(我對 shell script 相當苦手!),而且語法還很好寫、很漂亮。
也許大家看到 gem 就覺得這是 Ruby 吧,其實這跟你寫不寫 Ruby 或 Rails 沒太大關係,這真的是個很方便的工具,推薦大家玩看看!
其實 Backup 這個 gem 不只能備份資料庫,不過我比較需要的是就是資料庫的備份,所以特別把它筆記下來,至於其它的用法可參考官方的使用說明。
安裝
安裝Backup:
> gem install backup
安裝完成後,執行 backup:
> backup generate
Generated configuration file in '/Users/eddie/Backup/config.rb'
它會在你帳號的根目錄底下建立一個Backup資料夾,並產生一個空的config.rb。另外你還可以給它一些參數,例如:
> backup generate --databases='mysql' --storages='s3' --compressors='gzip'
Generated configuration file in '/Users/eddie/Backup/config.rb'
它會幫忙產生好一些空的 block 讓你可以改來用:
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
#檔案:config.rb
Backup :: Model . new ( :eddie_blog , 'Eddie Blog DB' ) do
database MySQL do | db |
db . name = "eddie_blog" # 你要備份的資料庫
db . username = "eddie_blog_user" # 資料庫登入帳號
db . password = "eddie_blog_password" # 資料庫登入密碼
db . host = "localhost"
db . port = 3306
db . socket = "/tmp/mysql.sock"
db . skip_tables = [ 'skip' , 'these' , 'tables' ] # 哪些table不要備份的
db . only_tables = [ 'only' , 'these' 'tables' ] #只要備份指定的table
db . utility_path = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
db . additional_options = [ '--quick' , '--single-transaction' ]
end
store_with S3 do | s3 |
s3 . access_key_id = 'my_access_key_id'
s3 . secret_access_key = 'my_secret_access_key'
s3 . region = 'ap-northeast-1' # 因為我的S3 bucket是開在Tokyo,所以改這樣
s3 . bucket = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
s3 . path = '/databases/blog.eddie.com.tw' # 備份存放的路徑
s3 . keep = 20 # 要保留多少個檔案,超過的話舊的就會被清掉
end
compress_with Gzip do | compression |
compression . best = true
compression . fast = false
end
end
以上設定請依你的環境進行修改,語法應該相當簡單易懂,接著開始來進行備份:
> backup perform -t eddie_blog
[2011/05/24 05:24:27][message] Performing backup for Eddie Blog!
[2011/05/24 05:24:27][message] Backup::Database::MySQL started dumping and archiving "eddie_blog".
[2011/05/24 05:24:27][message] Backup started packaging everything to a single archive file.
[2011/05/24 05:24:27][message] Backup::Compressor::Gzip started compressing the archive.
[2011/05/24 05:24:37][message] Backup::Storage::S3 started transferring "2011.05.24.05.24.26.eddie_blog.tar.gz".
[2011/05/24 05:24:43][message] Backup started cleaning up the temporary files.
要注意的是,你在執行這段的時候可能會出現錯誤訊息,告訴你缺裝了什麼gem,它會很貼心的自動幫你安裝需要的gem,如果你權限不足,那就加個sudo吧。
這樣就完成了!
備份通知
除了備份到S3之外,還有別的像是FTP、SCP、RSync等方案,而且Backup這套gem還可以設定notifier,例如可以在備份完成後發Email通知或是自動丟到Twitter通知 (可參考 Notifiers 的說明)。不過我個人不喜歡收一堆的信件通知,也不想讓備份這種雜事去洗 twitter,剛好在 Storage 裡有個 Dropbox 可以用,備份完成我同時也會收到 dropbox 的通知,我要備份的檔案都不大,所以 dropbox 對我來說是個不錯的方案。
改寫如下:
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
#檔案:config.rb
Backup :: Model . new ( :eddie_blog , 'Eddie Blog DB' ) do
database MySQL do | db |
db . name = "eddie_blog" # 你要備份的資料庫
db . username = "eddie_blog_user" # 資料庫登入帳號
db . password = "eddie_blog_password" # 資料庫登入密碼
db . host = "localhost"
db . port = 3306
db . socket = "/tmp/mysql.sock"
db . skip_tables = [ 'skip' , 'these' , 'tables' ] # 哪些table不要備份的
db . only_tables = [ 'only' , 'these' 'tables' ] #只要備份指定的table
db . utility_path = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
db . additional_options = [ '--quick' , '--single-transaction' ]
end
store_with S3 do | s3 |
s3 . access_key_id = 'my_access_key_id'
s3 . secret_access_key = 'my_secret_access_key'
s3 . region = 'ap-northeast-1' # 因為我的S3 bucket是開在Tokyo,所以改這樣
s3 . bucket = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
s3 . path = '/databases/blog.eddie.com.tw' # 備份存放的路徑
s3 . keep = 20 # 要保留多少個檔案,超過的話舊的就會被清掉
end
store_with Dropbox do | db |
db . email = 'my@email.com' # dropbox登入帳號
db . password = 'my_dropbox_password' # dropbox登入密碼
db . api_key = 'my_api_key'
db . api_secret = 'my_api_secret'
db . timeout = 300
db . path = '/Backups/Databases'
db . keep = 20
end
compress_with Gzip do | compression |
compression . best = true
compression . fast = false
end
end
這裡用到的api跟secret可到dropbox的開發者網站 免費申請來用。寫好之後再執行一次備份,這次就除了備份到S3之外,也會另外備一份到Dropbox:
> backup perform -t eddie_blog
[2011/05/24 05:35:19][message] Performing backup for Eddie Blog!
[2011/05/24 05:35:19][message] Backup::Database::MySQL started dumping and archiving "eddie_blog".
[2011/05/24 05:35:19][message] Backup started packaging everything to a single archive file.
[2011/05/24 05:35:19][message] Backup::Compressor::Gzip started compressing the archive.
[2011/05/24 05:35:20][message] Backup::Storage::Dropbox started transferring "2011.05.24.05.35.18.eddie_blog.tar.gz".
[2011/05/24 05:35:30][message] Backup::Storage::S3 started transferring "2011.05.24.05.35.18.eddie_blog.tar.gz".
[2011/05/24 05:35:36][message] Backup started cleaning up the temporary files.
備份成功,而且還有個漂亮的提示視窗!
加入其它備份工作
那如果要再加其它的備份進來怎麼做? 就照著第一段的寫:
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#檔案:config.rb
# 原來的
Backup :: Model . new ( :eddie_blog , 'Eddie Blog DB' ) do
database MySQL do | db |
db . name = "eddie_blog" # 你要備份的資料庫
db . username = "eddie_blog_user" # 資料庫登入帳號
db . password = "eddie_blog_password" # 資料庫登入密碼
db . host = "localhost"
db . port = 3306
db . socket = "/tmp/mysql.sock"
db . skip_tables = [ 'skip' , 'these' , 'tables' ] # 哪些table不要備份的
db . only_tables = [ 'only' , 'these' 'tables' ] #只要備份指定的table
db . utility_path = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
db . additional_options = [ '--quick' , '--single-transaction' ]
end
store_with S3 do | s3 |
s3 . access_key_id = 'my_access_key_id'
s3 . secret_access_key = 'my_secret_access_key'
s3 . region = 'ap-northeast-1' # 因為我的S3 bucket是開在Tokyo,所以改這樣
s3 . bucket = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
s3 . path = '/databases/blog.eddie.com.tw' # 備份存放的路徑
s3 . keep = 20 # 要保留多少個檔案,超過的話舊的就會被清掉
end
store_with Dropbox do | db |
db . email = 'my@email.com' # dropbox登入帳號
db . password = 'my_password' # dropbox登入密碼
db . api_key = 'my_api_key'
db . api_secret = 'my_api_secret'
db . timeout = 300
db . path = '/Backups/Databases'
db . keep = 20
end
compress_with Gzip do | compression |
compression . best = true
compression . fast = false
end
end
# 另一個
Backup :: Model . new ( :my_superdb , 'Another Super DB' ) do
database MySQL do | db |
db . name = "my_superdb" # 你要備份的資料庫
db . username = "my_superdb_user" # 資料庫登入帳號
db . password = "my_superdb_password" # 資料庫登入密碼
db . host = "localhost"
db . port = 3306
db . socket = "/tmp/mysql.sock"
db . skip_tables = [ 'skip' , 'these' , 'tables' ] # 哪些table不要備份的
db . only_tables = [ 'only' , 'these' 'tables' ] #只要備份指定的table
db . utility_path = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
db . additional_options = [ '--quick' , '--single-transaction' ]
end
store_with S3 do | s3 |
s3 . access_key_id = 'my_access_key_id'
s3 . secret_access_key = 'my_secret_access_key'
s3 . region = 'ap-northeast-1' # 因為我的S3 bucket是開在Tokyo,所以改這樣
s3 . bucket = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
s3 . path = '/databases/blog.eddie.com.tw' # 備份存放的路徑
s3 . keep = 20 # 要保留多少個檔案,超過的話舊的就會被清掉
end
store_with Dropbox do | db |
db . email = 'my@email.com' # dropbox登入帳號
db . password = 'my_password' # dropbox登入密碼
db . api_key = 'my_api_key'
db . api_secret = 'my_api_secret'
db . timeout = 300
db . path = '/Backups/Databases'
db . keep = 20
end
compress_with Gzip do | compression |
compression . best = true
compression . fast = false
end
end
看到這裡我就覺得有點痛苦了,太多重複的東西,沒把它抽出來另外寫就整個很濕很難受(not DRY )。所以我把一些相關的設定抽出來寫在另外的檔案:
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
#檔案:backup-defaults.rb
Backup :: Configuration :: Storage :: S3 . defaults do | s3 |
s3 . access_key_id = 'my_access_key_id'
s3 . secret_access_key = 'my_secret_access_key'
s3 . region = 'ap-northeast-1' # 因為我的S3 bucket是開在Tokyo,所以改這樣
s3 . bucket = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
s3 . path = '/databases/blog.eddie.com.tw' # 備份存放的路徑
s3 . keep = 20 # 要保留多少個檔案,超過的話舊的就會被清掉
end
Backup :: Configuration :: Storage :: Dropbox . defaults do | dropbox |
dropbox . email = 'my@email.com' # dropbox登入帳號
dropbox . password = 'my_password' # dropbox登入密碼
dropbox . api_key = 'my_api_key'
dropbox . api_secret = 'my_api_secret'
dropbox . timeout = 300
dropbox . path = '/Backups/Databases'
dropbox . keep = 20
end
Backup :: Configuration :: Database :: MySQL . defaults do | mysql |
mysql . host = "localhost"
mysql . port = 3306
mysql . utility_path = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
mysql . additional_options = [ '--quick' , '--single-transaction' ]
end
Backup :: Configuration :: Compressor :: Gzip . defaults do | compressor |
compressor . best = true
compressor . fast = false
end
所以原來的設定檔就可以變成:
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
# 檔案:config.rb
require 'backup-defaults'
Backup :: Model . new ( :eddie_blog , 'Eddie Blog' ) do
compress_with Gzip
store_with Dropbox
store_with S3 do | s3 |
s3 . path = '/databases/blog.eddie.com.tw'
end
database MySQL do | db |
db . name = "my_superdb" # 你要備份的資料庫
db . username = "my_superdb_user" # 資料庫登入帳號
db . password = "my_superdb_password" # 資料庫登入密碼
end
end
Backup :: Model . new ( :my_superdb , 'Another Super DB' ) do
compress_with Gzip
store_with Dropbox
store_with S3 do | s3 |
s3 . path = '/databases/blog.eddie.com.tw'
end
database MySQL do | db |
db . name = "my_superdb" # 你要備份的資料庫
db . username = "my_superdb_user" # 資料庫登入帳號
db . password = "my_superdb_password" # 資料庫登入密碼
end
end
乾淨多了!
如果你不介意S3的存放路徑的話,也可以把s3.path寫回defaults值裡,程式碼可以更短。
自動備份
確定整個備份沒問題之後,再來要怎麼把它設定為自動備份? 如果你知道怎麼寫cronjob的話那就直接開起來寫。官網有建議另一套gem叫做whenever ,這是一套很棒的gem,能做滿多事情的,不過這裡我們只用它來簡單的執行一個指令而已。
安裝請參考whenever的github project。建一個config資料夾準備放設定檔(其實自己手動建也行,反正它幫你產生的也是空白的):
> wheneverize .
[add] writing `./config/schedule.rb'
[done] wheneverized!
編輯設定檔:
1
2
3
4
5
#檔案 config/schedule.rb
every 12 . hours do
command "backup perform -t eddie_blog"
end
更新到cronjob裡:
> whenever --update-crontab
[write] crontab file updated
看一下cronjob:
> crontab -e
# Begin Whenever generated tasks for: /home/eddie/Backup/config/schedule.rb
0 0,12 * * * /bin/bash -l -c 'backup perform -t eddie_blog'
它設定在每天的0點及12點的時候會進行備份的工作。
打完,收工!
結論
有人會懷疑像這種S3或Dropbox的備份安全性如何? 會不會被其它人或是雲端主機自己內部的員工看到你的檔案?
對我來說,我要備份的檔案都不會太機密,我要的只是萬一出問題,可以找得到檔案來快速還原,有真正重要的我會另外找方案處理。我想這些雲端主機的安全性應該是不會太差,但如果你信不過它,而且要備份的東西是非常極機密,萬一外流就有人會死的那種,那你也許可以試著把Storage的地方改成SCP或SFTP之類的,把檔案備到你的機器上囉。
最後,感謝神人們寫出這麼方便的gem,讓我睡覺可以睡得更安穩一些了!
參考資料:
補充:
2011.5.27 我用whenever的時候發現cronjob沒辦法正常執行,在/var/log/syslog裡會出現”failed with exit status 127″的錯誤訊息(OS: Ubuntu),似乎是找不到執行的執令的關係,不知道是哪邊環境沒設定好,不過我後來手動把crontab修改成:
0 0,12 * * * /bin/bash -l -c '/usr/local/bin/backup perform -t eddie_blog'
把backup改成完整路徑就ok了!