redis-backup 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +0 -3
- data/README.md +19 -13
- data/bin/redis-backup +75 -107
- data/lib/redis-backup/options.rb +62 -0
- data/lib/redis-backup/utils.rb +52 -0
- data/lib/redis-backup/version.rb +1 -1
- data/redis-backup.gemspec +1 -0
- metadata +20 -2
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
redis-backup
|
2
2
|
============
|
3
3
|
|
4
|
-
a small ruby gem that allows automated redis-backups
|
4
|
+
a small ruby gem that allows automated redis RDB-file backups with support for compression and S3 uploading.
|
5
5
|
|
6
6
|
Installation
|
7
7
|
============
|
@@ -12,15 +12,21 @@ Usage
|
|
12
12
|
=====
|
13
13
|
|
14
14
|
redis-backup [options]
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
--s3-
|
15
|
+
Specific options:
|
16
|
+
-b, --backup-dir BACKUP_DIR Directory to write backups to
|
17
|
+
-c, --compress Compress backup as a zip file
|
18
|
+
-s, --redis-source REDIS_SOURCE Full path to redis dump.rdb file
|
19
|
+
-m REDIS_SAVE_METHOD, The method to call on redis for saving (save or bgsave or none)
|
20
|
+
--redis-save-method
|
21
|
+
-H, --redis-host REDIS_HOST Host for local redis
|
22
|
+
-p, --redis-port REDIS_PORT Port for local redis
|
23
|
+
-A S3_ACCESS_KEY_ID, Your access_key_id for AWS S3
|
24
|
+
--s3-access-key-id
|
25
|
+
-B, --s3-bucket S3_BUCKET Name of bucket to use on S3
|
26
|
+
-C, --s3-clean Cleanup intermediate backup files
|
27
|
+
-S S3_SECRET_ACCESS_KEY, Your secret_access_key for AWS S3
|
28
|
+
--s3-secret-access-key
|
29
|
+
|
30
|
+
Common options:
|
31
|
+
-h, --help Show this message
|
32
|
+
--version Show version
|
data/bin/redis-backup
CHANGED
@@ -3,125 +3,63 @@
|
|
3
3
|
|
4
4
|
#
|
5
5
|
# redis-backup
|
6
|
-
#
|
7
|
-
# Uses environment variables for running:
|
8
|
-
#
|
9
|
-
# - BACKUP_DIR: Directory to write backups to
|
10
|
-
# - REDIS_SAVE: 0 or 1. If "1", then perform, and wait for, a bgsave before copying backup
|
11
|
-
# - REDIS_SAVE_METHOD: If calling a save, the method to call on redis for saving (save or bgsave)
|
12
|
-
# - REDIS_HOST: Host for local redis
|
13
|
-
# - REDIS_PORT: Port to local redis
|
14
|
-
# - REDIS_SOURCE: Full path to redis dump.rdb file
|
15
|
-
# - S3_SAVE: 0 or 1. If "1", then use S3 to backup the file
|
16
|
-
# - S3_BUCKET: Name of bucket to use on S3
|
17
|
-
# - S3_ACCESS_KEY_ID: Your access_key_id for AWS S3
|
18
|
-
# - S3_SECRET_ACCESS_KEY: Your secret_access_key for AWS S3
|
19
|
-
#
|
20
6
|
|
21
7
|
require 'aws-sdk'
|
22
8
|
require 'date'
|
23
9
|
require 'fileutils'
|
24
|
-
require 'optparse'
|
25
10
|
require 'redis'
|
11
|
+
require 'zip/zip'
|
26
12
|
|
27
|
-
require File.expand_path('../../lib/redis-backup/
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
opts.on('-h', '--help', 'Usage information') do
|
44
|
-
puts opts
|
45
|
-
exit
|
46
|
-
end
|
47
|
-
opts.on('-b', '--backup-dir BACKUP_DIR', 'Directory to write backups to') { |dir| backup_dir = dir }
|
48
|
-
opts.on('-s', '--redis-source REDIS_SOURCE', 'Full path to redis dump.rdb file') { |source| redis_source = source }
|
49
|
-
opts.on('-m', '--redis-save-method REDIS_SAVE_METHOD', 'The method to call on redis for saving (save or bgsave or none)') { |method| redis_save_method = method }
|
50
|
-
opts.on('-H', '--redis-host REDIS_HOST', 'Host for local redis') { |host| redis_host = host }
|
51
|
-
opts.on('-p', '--redis-port REDIS_PORT', 'Port for local redis') { |port| redis_port = port.to_i }
|
52
|
-
opts.on('-B', '--s3-bucket S3_BUCKET', 'Name of bucket to use on S3') { |bucket| s3_bucket = bucket }
|
53
|
-
opts.on('-A', '--s3-access-key-id S3_ACCESS_KEY_ID', 'Your access_key_id for AWS S3') { |key| s3_access_key_id = key }
|
54
|
-
opts.on('-S', '--s3-secret-access-key S3_SECRET_ACCESS_KEY', 'Your secret_access_key for AWS S3') { |secret| s3_secret_access_key = secret }
|
55
|
-
end
|
56
|
-
optparse.parse!
|
57
|
-
|
58
|
-
BACKUP_DIR = backup_dir
|
59
|
-
REDIS_SAVE_METHOD = redis_save_method
|
60
|
-
REDIS_HOST = redis_host
|
61
|
-
REDIS_SOURCE = redis_source
|
62
|
-
REDIS_PORT = redis_port
|
63
|
-
S3_SAVE = !!s3_bucket && !!s3_access_key_id && !!s3_secret_access_key
|
64
|
-
S3_BUCKET = s3_bucket
|
65
|
-
S3_ACCESS_KEY_ID = s3_access_key_id
|
66
|
-
S3_SECRET_ACCESS_KEY = s3_secret_access_key
|
67
|
-
|
68
|
-
|
69
|
-
def colorize(text, color_code); RUBY_PLATFORM =~ /win32/ ? text : "#{color_code}#{text}\e[0m"; end
|
70
|
-
def red(text); colorize(text, "\e[31m"); end
|
71
|
-
def green(text); colorize(text, "\e[32m"); end
|
72
|
-
def yellow(text); colorize(text, "\e[33m"); end
|
73
|
-
def blue(text); colorize(text, "\e[34m"); end
|
74
|
-
def magenta(text); colorize(text, "\e[35m"); end
|
75
|
-
def cyan(text); colorize(text, "\e[36m"); end
|
76
|
-
def white(text); colorize(text, "\e[37m"); end
|
77
|
-
def bold(text); colorize(text, "\e[1m"); end
|
78
|
-
def grey(text); colorize(text, "\e[90m"); end
|
79
|
-
|
80
|
-
abort("Redis .rdb file does not exist: " + REDIS_SOURCE) unless File.exists? REDIS_SOURCE
|
81
|
-
|
82
|
-
FileUtils.mkdir_p BACKUP_DIR unless File.directory? BACKUP_DIR
|
83
|
-
|
84
|
-
abort("Backup directory does not exist: " + BACKUP_DIR) unless File.directory? BACKUP_DIR
|
85
|
-
abort("Backup directory is not writable: " + BACKUP_DIR) unless File.writable? BACKUP_DIR
|
86
|
-
|
87
|
-
if S3_SAVE
|
88
|
-
abort("No bucket specified") unless S3_BUCKET
|
89
|
-
abort("No access key specified in S3_ACCESS_KEY_ID environment variable") unless S3_ACCESS_KEY_ID
|
90
|
-
abort("No secret access key specified in S3_SECRET_ACCESS_KEY environment variable") unless S3_SECRET_ACCESS_KEY
|
13
|
+
require File.expand_path('../../lib/redis-backup/options', __FILE__)
|
14
|
+
require File.expand_path('../../lib/redis-backup/utils', __FILE__)
|
15
|
+
|
16
|
+
options = RedisBackup::Options.parse(ARGV)
|
17
|
+
|
18
|
+
abort("Redis .rdb file does not exist: " + options.redis_source) unless File.exists? options.redis_source
|
19
|
+
|
20
|
+
FileUtils.mkdir_p options.backup_dir unless File.directory? options.backup_dir
|
21
|
+
|
22
|
+
abort("Backup directory does not exist: " + options.backup_dir) unless File.directory? options.backup_dir
|
23
|
+
abort("Backup directory is not writable: " + options.backup_dir) unless File.writable? options.backup_dir
|
24
|
+
|
25
|
+
if options.s3_save
|
26
|
+
abort("No bucket specified") unless options.s3_bucket
|
27
|
+
abort("No access key specified in S3_ACCESS_KEY_ID environment variable") unless options.s3_access_key_id
|
28
|
+
abort("No secret access key specified in S3_SECRET_ACCESS_KEY environment variable") unless options.s3_secret_access_key
|
91
29
|
AWS.config({
|
92
|
-
access_key_id:
|
93
|
-
secret_access_key:
|
30
|
+
access_key_id: options.s3_access_key_id,
|
31
|
+
secret_access_key: options.s3_secret_access_key
|
94
32
|
})
|
95
33
|
s3 = AWS::S3.new
|
96
|
-
bucket = s3.buckets[
|
97
|
-
abort("Bucket does not exist: " +
|
34
|
+
bucket = s3.buckets[options.s3_bucket]
|
35
|
+
abort("Bucket does not exist: " + options.s3_bucket) unless bucket.exists?
|
98
36
|
begin
|
99
37
|
owner = bucket.owner
|
100
38
|
rescue AWS::S3::Errors::AccessDenied => e
|
101
|
-
abort("Access to the bucket is denied: " +
|
39
|
+
abort("Access to the bucket is denied: " + options.s3_bucket)
|
102
40
|
end
|
103
41
|
end
|
104
42
|
|
105
43
|
|
106
|
-
puts
|
44
|
+
puts RedisBackup::Utils.cyan('Starting save')
|
107
45
|
backup_start = Time.now
|
108
46
|
|
109
|
-
puts "--> Saving from %s to %s" % [
|
47
|
+
puts "--> Saving from %s to %s" % [ options.redis_source, options.backup_dir ]
|
110
48
|
|
111
|
-
if
|
112
|
-
puts "--> Performing background save " + bold(cyan('↴'))
|
49
|
+
if options.redis_save_method
|
50
|
+
puts "--> Performing background save " + RedisBackup::Utils.bold(RedisBackup::Utils.cyan('↴'))
|
113
51
|
bgsave_start = Time.now
|
114
52
|
|
115
|
-
mtime = File.mtime(
|
53
|
+
mtime = File.mtime(options.redis_source).strftime("%Y-%m-%d-%H-%M-%S")
|
116
54
|
puts " - Current backup.rdb mtime: " + mtime
|
117
55
|
|
118
|
-
puts " - Sending '%s' command" % (
|
56
|
+
puts " - Sending '%s' command" % (options.redis_save_method)
|
119
57
|
redis = Redis.new(
|
120
|
-
:host =>
|
121
|
-
:port =>
|
58
|
+
:host => options.redis_host,
|
59
|
+
:port => options.redis_port
|
122
60
|
)
|
123
61
|
|
124
|
-
if
|
62
|
+
if options.redis_save_method == "bgsave"
|
125
63
|
redis.bgsave
|
126
64
|
else
|
127
65
|
redis.save
|
@@ -129,36 +67,66 @@ if REDIS_SAVE_METHOD
|
|
129
67
|
|
130
68
|
puts " - Command sent, waiting"
|
131
69
|
|
132
|
-
while File.mtime(
|
70
|
+
while File.mtime(options.redis_source).strftime("%Y-%m-%d-%H-%M-%S") == mtime
|
133
71
|
sleep 0.0001
|
134
72
|
end
|
135
73
|
|
136
|
-
puts " - Save performed successfully, took %.2f seconds" % (Time.now -
|
74
|
+
puts " - Save performed successfully, took %.2f seconds" % (Time.now - bgsave_start)
|
137
75
|
end
|
138
76
|
|
139
|
-
|
140
|
-
|
77
|
+
backup_filename = "%s-dump.rdb" % [ DateTime.parse(`date`).strftime("%Y-%m-%d-%H-%M-%S") ]
|
78
|
+
backup_rdb_file = "%s/%s" % [ options.backup_dir, backup_filename ]
|
79
|
+
backup_file = backup_rdb_file
|
80
|
+
|
81
|
+
puts "--> Copying backup from %s to %s" % [ options.redis_source, backup_rdb_file ]
|
82
|
+
copy_start = Time.now
|
83
|
+
FileUtils.cp(options.redis_source, backup_rdb_file)
|
84
|
+
puts " - Copying completed, took %.2f" % (Time.now - copy_start)
|
85
|
+
puts " - RDB filesize is %s" % (RedisBackup::Utils.filesize(backup_rdb_file))
|
141
86
|
|
142
|
-
|
143
|
-
|
87
|
+
if options.compress
|
88
|
+
puts "--> Performing compression " + RedisBackup::Utils.bold(RedisBackup::Utils.cyan('↴'))
|
89
|
+
compress_start = Time.now
|
144
90
|
|
145
|
-
|
146
|
-
|
91
|
+
puts " - Compressing to %s.zip" % [ backup_rdb_file ]
|
92
|
+
|
93
|
+
Zip::ZipFile.open(backup_rdb_file + ".zip", Zip::ZipFile::CREATE) do |zipfile|
|
94
|
+
zipfile.add(backup_filename, backup_rdb_file)
|
95
|
+
end
|
96
|
+
|
97
|
+
if options.clean
|
98
|
+
puts " - Removing temporary rdb file"
|
99
|
+
File.delete(backup_rdb_file)
|
100
|
+
end
|
101
|
+
|
102
|
+
puts " - Compression performed successfully, took %.2f seconds" % (Time.now - compress_start)
|
103
|
+
|
104
|
+
backup_file = backup_rdb_file + ".zip"
|
105
|
+
puts " - Compression filesize is %s" % (RedisBackup::Utils.filesize(backup_file))
|
106
|
+
end
|
107
|
+
|
108
|
+
if options.s3_save
|
109
|
+
puts "--> Pushing file to S3 " + RedisBackup::Utils.bold(RedisBackup::Utils.cyan('↴'))
|
147
110
|
s3_start = Time.now
|
148
111
|
|
149
112
|
puts " - Connecting to S3"
|
150
113
|
s3 = AWS::S3.new
|
151
|
-
bucket = s3.buckets[
|
114
|
+
bucket = s3.buckets[options.s3_bucket]
|
152
115
|
|
153
116
|
puts " - Uploading the file, please wait..."
|
154
|
-
basename = File.basename(
|
117
|
+
basename = File.basename(backup_file)
|
155
118
|
object = bucket.objects[basename]
|
156
|
-
object.write(:file =>
|
157
|
-
request_uri = object.url_for(:read).request_uri.gsub(
|
119
|
+
object.write(:file => backup_file)
|
120
|
+
request_uri = object.url_for(:read).request_uri.gsub(options.s3_access_key_id, "YOUR_S3_ACCESS_KEY_ID")
|
121
|
+
|
122
|
+
if options.clean
|
123
|
+
puts " - Removing temporary backup file"
|
124
|
+
File.delete(backup_file)
|
125
|
+
end
|
158
126
|
|
159
127
|
puts " - Uploaded to: " + object.public_url.request_uri
|
160
|
-
puts " - Use this URL to download the file: https://s3.amazonaws.com/" +
|
128
|
+
puts " - Use this URL to download the file: https://s3.amazonaws.com/" + options.s3_bucket + request_uri
|
161
129
|
puts " - S3 sync performed successfully, took %.2f seconds" % (Time.now - s3_start)
|
162
130
|
end
|
163
131
|
|
164
|
-
puts '🚀 ' + green('Backup took %.2f seconds' % (Time.now - backup_start))
|
132
|
+
puts '🚀 ' + RedisBackup::Utils.green('Backup took %.2f seconds' % (Time.now - backup_start))
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding : utf-8 -*-
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
require File.expand_path('../version', __FILE__)
|
8
|
+
|
9
|
+
module RedisBackup
|
10
|
+
class Options
|
11
|
+
def self.parse(args)
|
12
|
+
options = OpenStruct.new
|
13
|
+
|
14
|
+
options.backup_dir = "/data/backup/redis"
|
15
|
+
options.redis_host = "localhost"
|
16
|
+
options.redis_port = 6379
|
17
|
+
options.redis_save_method = false
|
18
|
+
options.redis_source = "/var/lib/redis/dump.rdb"
|
19
|
+
options.s3_save = !!ENV.fetch("S3_SAVE", false)
|
20
|
+
options.s3_bucket = false
|
21
|
+
options.s3_access_key_id = false
|
22
|
+
options.s3_secret_access_key = false
|
23
|
+
|
24
|
+
optparse = OptionParser.new do |opts|
|
25
|
+
opts.version = "redis-backup " + RedisBackup::VERSION
|
26
|
+
|
27
|
+
opts.banner = "Usage: redis-backup [options]"
|
28
|
+
opts.separator ""
|
29
|
+
opts.separator "Specific options:"
|
30
|
+
|
31
|
+
opts.on('-b', '--backup-dir BACKUP_DIR', 'Directory to write backups to') { |dir| options.backup_dir = dir }
|
32
|
+
opts.on('-c', '--compress', 'Compress backup as a zip file') { |dir| options.compress = true }
|
33
|
+
opts.on('-s', '--redis-source REDIS_SOURCE', 'Full path to redis dump.rdb file') { |source| options.redis_source = source }
|
34
|
+
opts.on('-m', '--redis-save-method REDIS_SAVE_METHOD', 'The method to call on redis for saving (save or bgsave or none)') { |method| options.redis_save_method = method }
|
35
|
+
opts.on('-H', '--redis-host REDIS_HOST', 'Host for local redis') { |host| options.redis_host = host }
|
36
|
+
opts.on('-p', '--redis-port REDIS_PORT', 'Port for local redis') { |port| options.redis_port = port.to_i }
|
37
|
+
opts.on('-A', '--s3-access-key-id S3_ACCESS_KEY_ID', 'Your access_key_id for AWS S3') { |key| options.s3_access_key_id = key }
|
38
|
+
opts.on('-B', '--s3-bucket S3_BUCKET', 'Name of bucket to use on S3') { |bucket| options.s3_bucket = bucket }
|
39
|
+
opts.on('-C', '--s3-clean', 'Cleanup intermediate backup files') { |dir| options.clean = true }
|
40
|
+
opts.on('-S', '--s3-secret-access-key S3_SECRET_ACCESS_KEY', 'Your secret_access_key for AWS S3') { |secret| options.s3_secret_access_key = secret }
|
41
|
+
|
42
|
+
opts.separator ""
|
43
|
+
opts.separator "Common options:"
|
44
|
+
|
45
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
46
|
+
puts opts
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
# Another typical switch to print the version.
|
51
|
+
opts.on_tail("--version", "Show version") do
|
52
|
+
puts "redis-backup " + RedisBackup::VERSION
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
optparse.parse!(args)
|
57
|
+
|
58
|
+
options.s3_save = !!options.s3_bucket && !!options.s3_access_key_id && !!options.s3_secret_access_key
|
59
|
+
options
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding : utf-8 -*-
|
3
|
+
|
4
|
+
module RedisBackup
|
5
|
+
class Utils
|
6
|
+
def self.filesize(filename)
|
7
|
+
compressed_file_size = File.size(filename).to_f / 2**20
|
8
|
+
'%.2f MB' % compressed_file_size
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.colorize(text, color_code)
|
12
|
+
RUBY_PLATFORM =~ /win32/ ? text : "#{color_code}#{text}\e[0m";
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.red(text)
|
16
|
+
colorize(text, "\e[31m")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.green(text)
|
20
|
+
colorize(text, "\e[32m")
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.yellow(text)
|
24
|
+
colorize(text, "\e[33m")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.blue(text)
|
28
|
+
colorize(text, "\e[34m")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.magenta(text)
|
32
|
+
colorize(text, "\e[35m")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.cyan(text)
|
36
|
+
colorize(text, "\e[36m")
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.white(text)
|
40
|
+
colorize(text, "\e[37m")
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.bold(text)
|
44
|
+
colorize(text, "\e[1m")
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.grey(text)
|
48
|
+
colorize(text, "\e[90m")
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/lib/redis-backup/version.rb
CHANGED
data/redis-backup.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rubyzip
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
46
62
|
description: redis-backup is a simple backup script to automate the creation and storage
|
47
63
|
or redis dump.rdb files.
|
48
64
|
email:
|
@@ -59,6 +75,8 @@ files:
|
|
59
75
|
- Rakefile
|
60
76
|
- bin/redis-backup
|
61
77
|
- lib/redis-backup.rb
|
78
|
+
- lib/redis-backup/options.rb
|
79
|
+
- lib/redis-backup/utils.rb
|
62
80
|
- lib/redis-backup/version.rb
|
63
81
|
- redis-backup.gemspec
|
64
82
|
homepage: https://github.com/josegonzalez/redis-backup
|