mongo-ec2-backup 0.0.4 → 0.0.5
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.markdown +21 -50
- data/Rakefile +3 -2
- data/bin/ec2_snapshot_restorer +132 -0
- metadata +4 -3
- data/lib/ec2_snapshot_restorer.rb +0 -34
data/README.markdown
CHANGED
@@ -2,67 +2,38 @@
|
|
2
2
|
|
3
3
|
Suite of tools to backup and manage snapshots of MongoDB data set to EC2 Snapshots.
|
4
4
|
|
5
|
-
## Lock and Snapshot:
|
5
|
+
## Lock and Snapshot: mongo_lock_and_snapshot.rb
|
6
6
|
|
7
7
|
### Usage
|
8
8
|
|
9
|
-
Snapshot a list of devices on a given instance on ec2.
|
9
|
+
Snapshot a list of devices on a given instance on ec2.
|
10
10
|
|
11
11
|
```shell
|
12
|
-
|
12
|
+
/mnt/lib/mongo-ec2-consistent-backup/bin# infra-ruby lock_and_snapshot -p /ebs/lvms/lvol0/ -a ACCESS_KEY -s SECRET_KEY -d /dev/sdg,/dev/sdh,/dev/sdi,/dev/sdj,/dev/sdk,/dev/sdl,/dev/sdm,/dev/sdn
|
13
13
|
```
|
14
14
|
|
15
|
-
* --
|
16
|
-
* --access-key-id, -a
|
17
|
-
* --secret-access-key, -s
|
18
|
-
* --devices, -d
|
19
|
-
* --
|
20
|
-
* --
|
21
|
-
* --limit, -l <i>: Cleanup old snapshots to keep only limit snapshots. Default values are stored in EC2VolumeSnapshoter::KIND
|
22
|
-
* --region: Region hosting the instances
|
23
|
-
* --help, -e: Show this message
|
15
|
+
* --path, -p : Data path to freeze
|
16
|
+
* --access-key-id, -a : Access Key Id for AWS
|
17
|
+
* --secret-access-key, -s : Secret Access Key for AWS
|
18
|
+
* --devices, -d : Devices to snapshot, comma separated
|
19
|
+
* --type, -t : Snapshot type, to choose among test,snapshot,daily,weekly,monthly,yearly (default: snapshot)
|
20
|
+
* --help, -h: Show this message
|
24
21
|
|
25
|
-
|
22
|
+
It freeze the path using ```xfs_freeze```and create a snapshot for all the disks passed in the command line. To make this work without too much trouble with a mongo that is in a replica set, you can shut down the replica before running the command. You can also use mongo fsync and lock but this will probably make your cluster a bit nervous about that. Shutting down ensure no mongos will try to use the frozen mongo.
|
26
23
|
|
27
|
-
|
24
|
+
### Usage with IAM
|
28
25
|
|
29
|
-
|
30
|
-
This information can be fetched out for use at a later point via the knife script provided in the ohai-raid package:
|
26
|
+
If you use IAM for your authentication in EC2, here is a probably up to date list of the permissions you need to grant:
|
31
27
|
|
32
28
|
```
|
33
|
-
|
34
|
-
|
29
|
+
"ec2:CreateSnapshot",
|
30
|
+
"ec2:DeleteSnapshot",
|
31
|
+
"ec2:DescribeSnapshots",
|
32
|
+
"ec2:CreateTags",
|
33
|
+
"ec2:DescribeTags",
|
34
|
+
"ec2:DescribeVolumes",
|
35
|
+
"ec2:DescribeInstances",
|
36
|
+
"ec2:AttachVolume",
|
37
|
+
"ec2:CreateVolume"
|
35
38
|
```
|
36
|
-
|
37
|
-
You can combine the two tools to automate daily backup of you MongoDB server:
|
38
|
-
|
39
|
-
```
|
40
|
-
./lock_and_snapshot.rb -a ACCESS_KEY_ID -s SECRET_ACCESS_KEY --hostname server01 --devices $(knife exec /path/to/scripts/show_raid_devices server01.fqdn.com /dev/md0) --type daily
|
41
|
-
```
|
42
|
-
|
43
|
-
### Tool Description
|
44
|
-
|
45
|
-
* Find instance id by resolving the hostname provided in the CLI and scanning the instances in EC2
|
46
|
-
* Lock Mongo by connecting via the hostname:port provided in the parameters
|
47
|
-
* Snapshot the disks, delete old backups
|
48
|
-
* Unlock Mongo
|
49
|
-
|
50
|
-
## MD inspection: ec2-consistent-backup.rb
|
51
|
-
|
52
|
-
### Usage
|
53
|
-
|
54
|
-
This script demonstrates the way it analyses Mongo DB Data path to extract the MD device and components associated
|
55
|
-
|
56
|
-
```shell
|
57
|
-
./ec2-consistent-backup -p 27017
|
58
|
-
```
|
59
|
-
|
60
|
-
### Tool description
|
61
|
-
|
62
|
-
* connect to mongo at port provided, retrieves dbpath
|
63
|
-
* find what mount this dbpath corresponds to
|
64
|
-
* use /proc/mdstat to find out which drive are corresponding to the dbpath mount disk
|
65
|
-
|
66
|
-
# API
|
67
|
-
|
68
39
|
Internal API documentation is at: http://rubydoc.info/github/octplane/mongo-ec2-consistent-backup/master/frames
|
data/Rakefile
CHANGED
@@ -3,12 +3,12 @@ require "rake/clean"
|
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'rake/rdoctask'
|
5
5
|
|
6
|
-
desc "Packages up
|
6
|
+
desc "Packages up the gem."
|
7
7
|
task :default => :package
|
8
8
|
|
9
9
|
spec = Gem::Specification.new do |s|
|
10
10
|
s.name = 'mongo-ec2-backup'
|
11
|
-
s.version = '0.0.
|
11
|
+
s.version = '0.0.5'
|
12
12
|
s.summary = 'Snapshot your mongodb in the EC2 cloud via XFS Freeze'
|
13
13
|
|
14
14
|
s.author = 'Pierre Baillet'
|
@@ -25,6 +25,7 @@ spec = Gem::Specification.new do |s|
|
|
25
25
|
s.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*'].to_a
|
26
26
|
|
27
27
|
s.executables << "mongo_lock_and_snapshot"
|
28
|
+
s.executables << "ec2_snapshot_restorer"
|
28
29
|
|
29
30
|
# Supress the warning about no rubyforge project
|
30
31
|
s.rubyforge_project = 'nowarning'
|
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
ENV['BUNDLE_GEMFILE'] = File.join(File.dirname(__FILE__), "..", "Gemfile")
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'trollop'
|
7
|
+
require 'fog'
|
8
|
+
require 'open-uri'
|
9
|
+
|
10
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
11
|
+
|
12
|
+
DEBUG=false
|
13
|
+
|
14
|
+
def log what
|
15
|
+
puts what if DEBUG
|
16
|
+
end
|
17
|
+
#
|
18
|
+
class SnapshotRestorer
|
19
|
+
attr_accessor :snaps
|
20
|
+
def initialize(aki, sak)
|
21
|
+
@compute = Fog::Compute.new({:provider => 'AWS', :aws_access_key_id => aki, :aws_secret_access_key => sak})
|
22
|
+
@snaps = []
|
23
|
+
@volumes = []
|
24
|
+
end
|
25
|
+
def find_snapshots(instance_id, kind = 'snapshot')
|
26
|
+
log "Looking for snapshots for #{instance_id}"
|
27
|
+
volume_map = []
|
28
|
+
snapshots = {}
|
29
|
+
|
30
|
+
tags = @compute.tags.all(:key => 'instance_id', :value => instance_id)
|
31
|
+
|
32
|
+
max_date = nil
|
33
|
+
tags.each do |tag|
|
34
|
+
snap = @compute.snapshots.get(tag.resource_id)
|
35
|
+
t = snap.tags
|
36
|
+
|
37
|
+
# Ignore in progress snapshots
|
38
|
+
if instance_id == t['instance_id'] &&
|
39
|
+
snap.state == 'completed' &&
|
40
|
+
t['kind'] == kind
|
41
|
+
max_date = t['date'] if !max_date || max_date < t['date']
|
42
|
+
log "#{snap.inspect} is valid"
|
43
|
+
snapshots[t['date']] ||= []
|
44
|
+
snapshots[t['date']] << snap
|
45
|
+
end
|
46
|
+
end
|
47
|
+
snapshots['LATEST'] = snapshots[max_date] if snapshots[max_date]
|
48
|
+
return snapshots
|
49
|
+
end
|
50
|
+
def prepare_volumes(dest_instance)
|
51
|
+
@snaps.each do | resource_id |
|
52
|
+
snap = @compute.snapshots.get(resource_id)
|
53
|
+
# Snap have the following tags
|
54
|
+
# application
|
55
|
+
# device
|
56
|
+
# instance_id
|
57
|
+
# date
|
58
|
+
# kind
|
59
|
+
|
60
|
+
t = snap.tags
|
61
|
+
volume = @compute.volumes.new :snapshot_id => snap.id, :size => snap.volume_size, :availability_zone => 'us-east-1c'
|
62
|
+
volume.save
|
63
|
+
volume.reload
|
64
|
+
@compute.create_tags(volume.id, { "application" => t['application'],
|
65
|
+
"sdevice" => t['device'],
|
66
|
+
"date" => t['date'],
|
67
|
+
"kind" => t['kind'],
|
68
|
+
"sinstance" => t['instance_id'],
|
69
|
+
"dinstance" => dest_instance})
|
70
|
+
|
71
|
+
@volumes << volume
|
72
|
+
end
|
73
|
+
def rattach_volumes(base_device = nil)
|
74
|
+
dest = base_device
|
75
|
+
if !dest
|
76
|
+
dest = @volumes.map{ |v| v.tags['sdevice']}.min
|
77
|
+
end
|
78
|
+
dest = dest.dup
|
79
|
+
|
80
|
+
@volumes.each do |vol|
|
81
|
+
vol.reload
|
82
|
+
puts "Attaching #{vol.id} to #{dest} on #{vol.tags['dinstance']}"
|
83
|
+
@compute.attach_volume(vol.tags['dinstance'], vol.id, dest)
|
84
|
+
dest.next!
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if __FILE__ == $0
|
91
|
+
require 'trollop'
|
92
|
+
require 'ec2_instance_identifier'
|
93
|
+
require 'pp'
|
94
|
+
opts = Trollop::options do
|
95
|
+
opt :hostname, "Hostname tag to use to find the instance", :type => :string, :required => true
|
96
|
+
opt :access_key_id, "Access Key Id for AWS", :type => :string, :required => true
|
97
|
+
opt :secret_access_key, "Secret Access Key for AWS", :type => :string, :required => true
|
98
|
+
opt :date, "Date to restore, use LATEST to take latest data", :type => :string
|
99
|
+
opt :type, "Snapshot type to restore, defaults to snapshot", :type => :string, :default => 'snapshot'
|
100
|
+
opt :target, "Creates volume ready for mounting on instance id. Use special value SELF to restore here", :type => :string
|
101
|
+
opt :first_device, "First device to attach to (default is to use source first device) /dev/sdx", :type => :string
|
102
|
+
end
|
103
|
+
|
104
|
+
finder = EC2InstanceIdentifier.new(opts[:access_key_id], opts[:secret_access_key])
|
105
|
+
instance_identifier = finder.get_instance(opts[:hostname]).id
|
106
|
+
s = SnapshotRestorer.new(opts[:access_key_id], opts[:secret_access_key])
|
107
|
+
|
108
|
+
# Find this instance snapshots
|
109
|
+
snaps = s.find_snapshots(instance_identifier, opts[:type])
|
110
|
+
|
111
|
+
if ! opts[:date] || !snaps.has_key?(opts[:date])
|
112
|
+
puts "We have found the following snapshot's dates:"
|
113
|
+
snaps.each do |k,v|
|
114
|
+
puts "- #{k} (#{v.length} volume snapshots)"
|
115
|
+
end
|
116
|
+
else
|
117
|
+
puts "Snapshot taken at #{opts[:date]}"
|
118
|
+
snaps[opts[:date]].each do |snapshot|
|
119
|
+
puts "- #{snapshot.id}, #{snapshot.volume_size}GB - #{snapshot.tags['device']}"
|
120
|
+
end
|
121
|
+
if opts[:target]
|
122
|
+
s.snaps = snaps[opts[:date]].map{ |s| s.id }
|
123
|
+
target = opts[:target]
|
124
|
+
target = open("http://169.254.169.254/latest/meta-data/instance-id").read if target == "SELF"
|
125
|
+
puts "Preparing volumes for instance #{target}"
|
126
|
+
s.prepare_volumes(target)
|
127
|
+
# Need to clone, because trollop freeze the variable
|
128
|
+
s.rattach_volumes(opts[:first_device])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo-ec2-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
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: 2012-
|
12
|
+
date: 2012-09-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|
@@ -79,13 +79,14 @@ description:
|
|
79
79
|
email: oct@fotopedia.com
|
80
80
|
executables:
|
81
81
|
- mongo_lock_and_snapshot
|
82
|
+
- ec2_snapshot_restorer
|
82
83
|
extensions: []
|
83
84
|
extra_rdoc_files: []
|
84
85
|
files:
|
85
86
|
- lib/ec2-consistent-backup.rb
|
86
87
|
- lib/ec2_instance_identifier.rb
|
87
|
-
- lib/ec2_snapshot_restorer.rb
|
88
88
|
- lib/ec2_volume_snapshoter.rb
|
89
|
+
- bin/ec2_snapshot_restorer
|
89
90
|
- bin/mongo_lock_and_snapshot
|
90
91
|
- Gemfile
|
91
92
|
- Gemfile.lock
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'fog'
|
2
|
-
require 'open-uri'
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
class SnapshotRestorer
|
7
|
-
def initialize(aki, sak, snap_ids)
|
8
|
-
@compute = Fog::Compute.new({:provider => 'AWS', :aws_access_key_id => aki, :aws_secret_access_key => sak})
|
9
|
-
@snaps = snap_ids
|
10
|
-
@volumes = []
|
11
|
-
end
|
12
|
-
def restore()
|
13
|
-
@snaps.each do | resource_id |
|
14
|
-
snap = @compute.snapshots.get(resource_id)
|
15
|
-
# Snap have the following tags
|
16
|
-
# application
|
17
|
-
# device
|
18
|
-
# instance_id
|
19
|
-
# date
|
20
|
-
# kind
|
21
|
-
|
22
|
-
t = snap.tags
|
23
|
-
volume = @compute.volumes.new :snapshot_id => snap.id, :size => snap.volume_size, :availability_zone => 'us-east-1c'
|
24
|
-
@compute.tags.create(:resource_id => volume.id, :key =>"application", :value => NAME_PREFIX)
|
25
|
-
@compute.tags.create(:resource_id => volume.id, :key =>"device", :value => device)
|
26
|
-
@compute.tags.create(:resource_id => volume.id, :key =>"date", :value => ts)
|
27
|
-
@compute.tags.create(:resource_id => volume.id, :key =>"kind", :value => kind)
|
28
|
-
volume.save
|
29
|
-
@volumes << volume
|
30
|
-
end
|
31
|
-
end
|
32
|
-
def connect(instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").read)
|
33
|
-
end
|
34
|
-
end
|