s3-mysql-backup 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +62 -0
- data/lib/s3-mysql-backup.rb +1 -0
- data/lib/s3_mysql_backup.rb +108 -0
- data/lib/s3utils.rb +51 -0
- metadata +65 -0
data/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# S3 MySQL Backup
|
|
2
|
+
|
|
3
|
+
Simple backup of a MySQL database to Amazon S3,
|
|
4
|
+
with email notification via Gmail.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## What does it do?
|
|
8
|
+
|
|
9
|
+
It makes a gzipped and timestamped local backup of the specified
|
|
10
|
+
database using mysqldump. The local backup is then copied to
|
|
11
|
+
Amazon S3, and the results are emailed to the specified recipient.
|
|
12
|
+
|
|
13
|
+
Local and S3 backups are retained at this schedule:
|
|
14
|
+
- keep 30 days complete
|
|
15
|
+
- keep 90 days weekly beyond that
|
|
16
|
+
- keep only monthly after that
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
Configure with a YAML file:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
|
|
25
|
+
# backup_dir where to store the local backups
|
|
26
|
+
backup_dir: ~/s3_mysql_backups
|
|
27
|
+
|
|
28
|
+
# s3_access_key_id your Amazon S3 access_key_id
|
|
29
|
+
# s3_secret_access_key your Amazon S3 secret_access_key
|
|
30
|
+
# s3_bucket your Amazon S3 bucket for the backups
|
|
31
|
+
s3_access_key_id: my-key
|
|
32
|
+
s3_secret_access_key: my-secret
|
|
33
|
+
s3_bucket: my-bucket
|
|
34
|
+
|
|
35
|
+
# dump_user the database user for mysqldump
|
|
36
|
+
# dump_pass the password for the dump user
|
|
37
|
+
dump_user: my-user
|
|
38
|
+
dump_pass: my-pass
|
|
39
|
+
|
|
40
|
+
# mail_to where to send the backup summary email
|
|
41
|
+
mail_to: recipient@example.com
|
|
42
|
+
|
|
43
|
+
# Gmail credentials
|
|
44
|
+
gmail_user: me@gmail.com
|
|
45
|
+
gmail_pass: gmail-password
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
gem install s3-mysql-backup
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
S3MysqlBackup.new('database_name', '/path/to/s3-mysql-backup-config.yml').run
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Credits
|
|
61
|
+
|
|
62
|
+
2008+ Seventh Compass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 's3_mysql_backup'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require "net/smtp"
|
|
2
|
+
require "time"
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
require File.dirname(__FILE__) + '/s3utils'
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
class S3MysqlBackup
|
|
10
|
+
|
|
11
|
+
def initialize(db_name, path_to_config)
|
|
12
|
+
@db_name = db_name
|
|
13
|
+
@path_to_config = path_to_config
|
|
14
|
+
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run
|
|
19
|
+
ensure_backup_dir_exists
|
|
20
|
+
|
|
21
|
+
@s3utils = S3Utils.new(config['s3_access_key_id'], config['s3_secret_access_key'], config['s3_bucket'])
|
|
22
|
+
|
|
23
|
+
@dumpfile = dump_db
|
|
24
|
+
remove_old_backups
|
|
25
|
+
mail_notification(@dumpfile)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
protected
|
|
30
|
+
|
|
31
|
+
# make the DB backup file
|
|
32
|
+
def dump_db
|
|
33
|
+
filename = Time.now.strftime("#{@backup_dir}/#{@db_name}.%Y%m%d.%H%M%S.sql.gz")
|
|
34
|
+
mysqldump = `which mysqldump`.to_s.strip
|
|
35
|
+
`#{mysqldump} --user=#{config['dump_user']} --password=#{config['dump_pass']} #{@db_name} | gzip > #{filename}`
|
|
36
|
+
@s3utils.store(filename)
|
|
37
|
+
filename
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ensure_backup_dir_exists
|
|
41
|
+
@backup_dir = File.expand_path(config['backup_dir'])
|
|
42
|
+
FileUtils.mkdir_p @backup_dir
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def human_size(num, unit='bytes')
|
|
46
|
+
units = %w(bytes KB MB GB TB PB EB ZB YB)
|
|
47
|
+
if num <= 1024
|
|
48
|
+
"#{"%0.2f"%num} #{unit}"
|
|
49
|
+
else
|
|
50
|
+
human_size(num/1024.0, units[units.index(unit)+1])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def mail_notification(filename)
|
|
55
|
+
stats = File.stat(filename)
|
|
56
|
+
subject = "sql backup: #{@db_name}: #{human_size(stats.size)}"
|
|
57
|
+
|
|
58
|
+
content = []
|
|
59
|
+
content << "From: #{config['gmail_user']}"
|
|
60
|
+
content << "To: #{config['mail_to']}"
|
|
61
|
+
content << "Subject: #{subject}"
|
|
62
|
+
content << "Date: #{Time.now.rfc2822}"
|
|
63
|
+
content << "\n#{File.basename(filename)}\n" # body
|
|
64
|
+
content = content.join("\n")
|
|
65
|
+
|
|
66
|
+
smtp = Net::SMTP.new("smtp.gmail.com", 587)
|
|
67
|
+
smtp.enable_starttls
|
|
68
|
+
smtp.start("smtp.gmail.com", config['gmail_user'], config['gmail_pass'], :login) do
|
|
69
|
+
smtp.send_message(content, config['gmail_user'], config['mail_to'])
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def config
|
|
74
|
+
@s3config ||= YAML::load_file(@path_to_config)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# remove old backups
|
|
78
|
+
# - keep 30 days complete
|
|
79
|
+
# - keep 90 days weekly beyond that
|
|
80
|
+
# - keep only monthly after that
|
|
81
|
+
def remove_old_backups
|
|
82
|
+
today = Date.today
|
|
83
|
+
weekly = (today - 30)
|
|
84
|
+
monthly = (today - 120)
|
|
85
|
+
|
|
86
|
+
Dir["#{config['backup_dir']}/*.sql.gz"].each do |name|
|
|
87
|
+
date = name.split('.')[1]
|
|
88
|
+
filedate = Date.strptime(date, '%Y%m%d')
|
|
89
|
+
|
|
90
|
+
if filedate < weekly && filedate >= monthly
|
|
91
|
+
# keep weeklies and also first of month
|
|
92
|
+
unless filedate.wday == 0 || filedate.day == 1
|
|
93
|
+
FileUtils.rm_f(name)
|
|
94
|
+
@s3utils.delete(name)
|
|
95
|
+
end
|
|
96
|
+
elsif filedate < monthly
|
|
97
|
+
# delete all old local files
|
|
98
|
+
FileUtils.rm_f(name)
|
|
99
|
+
|
|
100
|
+
# keep just first of month in S3
|
|
101
|
+
unless filedate.day == 1
|
|
102
|
+
@s3utils.delete(name)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end # Dir.each
|
|
106
|
+
end # remove_old_backups
|
|
107
|
+
|
|
108
|
+
end
|
data/lib/s3utils.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'aws/s3'
|
|
2
|
+
|
|
3
|
+
include AWS::S3
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# wrapper for S3 operations
|
|
7
|
+
#
|
|
8
|
+
class S3Utils
|
|
9
|
+
|
|
10
|
+
def initialize(access_key_id, secret_access_key, bucket)
|
|
11
|
+
@bucket = bucket
|
|
12
|
+
connect(access_key_id, secret_access_key)
|
|
13
|
+
ensure_bucket_exists
|
|
14
|
+
self
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def copy(from, to)
|
|
18
|
+
S3Object.copy(File.basename(from), File.basename(to), @bucket)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def store(file_path)
|
|
22
|
+
S3Object.store(File.basename(file_path), open(file_path), @bucket)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete(file_path)
|
|
26
|
+
S3Object.delete(File.basename(file_path), @bucket)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def list
|
|
30
|
+
Bucket.find(@bucket).objects.each do |obj|
|
|
31
|
+
puts "#{obj.bucket.name}/#{obj.key}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
protected
|
|
36
|
+
|
|
37
|
+
def connect(access_key_id, secret_access_key)
|
|
38
|
+
AWS::S3::Base.establish_connection!(
|
|
39
|
+
:access_key_id => access_key_id,
|
|
40
|
+
:secret_access_key => secret_access_key
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def ensure_bucket_exists
|
|
45
|
+
Bucket.find(@bucket)
|
|
46
|
+
|
|
47
|
+
rescue AWS::S3::NoSuchBucket
|
|
48
|
+
Bucket.create(@bucket)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: s3-mysql-backup
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jeff Emminger
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-04-07 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: aws-s3
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
30
|
+
description: A simple mysql backup to Amazon S3
|
|
31
|
+
email: jeff@7compass.com
|
|
32
|
+
executables: []
|
|
33
|
+
extensions: []
|
|
34
|
+
extra_rdoc_files: []
|
|
35
|
+
files:
|
|
36
|
+
- lib/s3-mysql-backup.rb
|
|
37
|
+
- lib/s3_mysql_backup.rb
|
|
38
|
+
- lib/s3utils.rb
|
|
39
|
+
- README.md
|
|
40
|
+
homepage: https://github.com/7compass/s3-mysql-backup
|
|
41
|
+
licenses: []
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options: []
|
|
44
|
+
require_paths:
|
|
45
|
+
- .
|
|
46
|
+
- lib
|
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
|
+
none: false
|
|
49
|
+
requirements:
|
|
50
|
+
- - ! '>='
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
none: false
|
|
55
|
+
requirements:
|
|
56
|
+
- - ! '>='
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0'
|
|
59
|
+
requirements: []
|
|
60
|
+
rubyforge_project:
|
|
61
|
+
rubygems_version: 1.8.24
|
|
62
|
+
signing_key:
|
|
63
|
+
specification_version: 3
|
|
64
|
+
summary: Simple mysql backup to S3
|
|
65
|
+
test_files: []
|