redis-backup 0.0.2 → 0.0.3
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/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
|