contained_mr 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -0
- data/VERSION +1 -1
- data/contained_mr.gemspec +4 -4
- data/lib/contained_mr/job.rb +1 -1
- data/lib/contained_mr/job_logic.rb +44 -6
- data/lib/contained_mr/template_logic.rb +1 -1
- data/test/test_job_logic.rb +26 -6
- data/test/test_mock_runner.rb +6 -0
- data/test/test_runner_logic.rb +6 -1
- data/test/test_template_logic.rb +0 -2
- data/testdata/job.hello +8 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddef939463b55a174ccb6f44f90ad8f28f9f8bdc
|
4
|
+
data.tar.gz: b80c4c9a95e03b2fc61bd378f1e405a26a633513
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 195e87944d7a7d3d447d7526ee58459c091381a5c7c25f78ac7c1768fc9857f1f3106f8b011554f3d232e6b7dd36f47a24cf402bc861912751d3abfc2317f79a
|
7
|
+
data.tar.gz: e19354cfca2219c5d2765e268a2d8a93ffd243168b50bd46b87e8b023cfc976d74f52631926daa917f33a4674b9be75d254be1f745f2f900084bd9a1a9570b95
|
data/README.md
CHANGED
@@ -5,6 +5,35 @@
|
|
5
5
|
|
6
6
|
Map-Reduce where both the mappers and the reducer run inside Docker containers.
|
7
7
|
|
8
|
+
## Development Environment
|
9
|
+
|
10
|
+
`contained-mr` requires access to a Docker daemon. The easiest way to
|
11
|
+
bring up a development setup is to install
|
12
|
+
[Docker Machine](https://github.com/docker/machine) and
|
13
|
+
[VirtualBox](https://www.virtualbox.org/).
|
14
|
+
|
15
|
+
The commands below install the prerequisites on OSX using
|
16
|
+
[Homebrew](http://brew.sh/).
|
17
|
+
|
18
|
+
```bash
|
19
|
+
brew install brew-cask docker docker-machine
|
20
|
+
brew cask install virtualbox
|
21
|
+
```
|
22
|
+
|
23
|
+
Create a Docker VM. This is a one-time setup.
|
24
|
+
|
25
|
+
```bash
|
26
|
+
docker-machine create --driver virtualbox dev
|
27
|
+
```
|
28
|
+
|
29
|
+
Set up the local environment to point to the Docker daemon in the VM. This must
|
30
|
+
be executed in every shell where `contained-mr` is used.
|
31
|
+
|
32
|
+
```bash
|
33
|
+
eval "$(docker-machine env dev)"
|
34
|
+
```
|
35
|
+
|
36
|
+
|
8
37
|
## Contributing to contained_mr
|
9
38
|
|
10
39
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/contained_mr.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: contained_mr 0.
|
5
|
+
# stub: contained_mr 0.3.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "contained_mr"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.3.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Victor Costan"]
|
14
|
-
s.date = "2015-09
|
14
|
+
s.date = "2015-10-09"
|
15
15
|
s.description = "Plumbing for running mappers and reducers inside Docker containers"
|
16
16
|
s.email = "victor@costan.us"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -67,7 +67,7 @@ Gem::Specification.new do |s|
|
|
67
67
|
]
|
68
68
|
s.homepage = "http://github.com/pwnall/contained_mr"
|
69
69
|
s.licenses = ["MIT"]
|
70
|
-
s.rubygems_version = "2.4.5"
|
70
|
+
s.rubygems_version = "2.4.5.1"
|
71
71
|
s.summary = "Map-Reduce with Docker containers"
|
72
72
|
|
73
73
|
if s.respond_to? :specification_version then
|
data/lib/contained_mr/job.rb
CHANGED
@@ -55,13 +55,17 @@ module ContainedMr::JobLogic
|
|
55
55
|
{ "Name" => k.to_s, "Soft" => v, "Hard" => v }
|
56
56
|
end
|
57
57
|
|
58
|
+
env = @template.mapper_env i
|
59
|
+
env.push "affinity:image==#{mapper_image_tag}"
|
60
|
+
|
58
61
|
{
|
59
62
|
'name' => "#{@name_prefix}_mapper.#{@id}.#{i}",
|
60
|
-
'Image' =>
|
63
|
+
'Image' => mapper_image_tag,
|
61
64
|
'Hostname' => "#{i}.mapper", 'Domainname' => '',
|
62
65
|
'Labels' => { 'contained_mr.ctl' => @name_prefix },
|
63
|
-
'Env' =>
|
66
|
+
'Env' => env, 'Ulimits' => ulimits,
|
64
67
|
'NetworkDisabled' => true, 'ExposedPorts' => {},
|
68
|
+
'HostConfig' => container_host_config(@mapper_options),
|
65
69
|
}
|
66
70
|
end
|
67
71
|
|
@@ -71,15 +75,45 @@ module ContainedMr::JobLogic
|
|
71
75
|
{ "Name" => k.to_s, "Soft" => v, "Hard" => v }
|
72
76
|
end
|
73
77
|
|
78
|
+
env = @template.reducer_env
|
79
|
+
env.push "affinity:image==#{reducer_image_tag}"
|
80
|
+
|
74
81
|
{
|
75
82
|
'name' => "#{@name_prefix}_reducer.#{@id}",
|
76
|
-
'Image' =>
|
83
|
+
'Image' => reducer_image_tag,
|
77
84
|
'Hostname' => 'reducer', 'Domainname' => '',
|
78
85
|
'Labels' => { 'contained_mr.ctl' => @name_prefix },
|
79
|
-
'Env' =>
|
86
|
+
'Env' => env, 'Ulimits' => ulimits,
|
80
87
|
'NetworkDisabled' => true, 'ExposedPorts' => {},
|
88
|
+
'HostConfig' => container_host_config(@reducer_options),
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
# Computes the value of the HostConfig key in container creation params.
|
93
|
+
#
|
94
|
+
# @param {Hash<Symbol, Object>} job_section the "mapper" or "reducer" section
|
95
|
+
# in the options
|
96
|
+
# @return {Hash<String, Object>} a container's HostConfig params
|
97
|
+
def container_host_config(job_section)
|
98
|
+
ram_bytes = (job_section[:ram] * 1048576).to_i
|
99
|
+
if job_section[:swap] == 0
|
100
|
+
swap_bytes = -1
|
101
|
+
else
|
102
|
+
swap_bytes = (job_section[:swap] * 1048576).to_i + ram_bytes
|
103
|
+
end
|
104
|
+
|
105
|
+
# NOTE: The value below is 1 second, in microsecodns. This is the maximum
|
106
|
+
# value, and it minimizes scheduling overheads, at the expense of
|
107
|
+
# precision.
|
108
|
+
cpu_period = 1_000_000
|
109
|
+
|
110
|
+
{
|
111
|
+
'Memory' => ram_bytes, 'MemorySwap' => swap_bytes,
|
112
|
+
'CpuShares' => (job_section[:vcpus] * cpu_period).to_i,
|
113
|
+
'CpuPeriod' => cpu_period
|
81
114
|
}
|
82
115
|
end
|
116
|
+
private :container_host_config
|
83
117
|
|
84
118
|
# Reads in JSON options and sets defaults.
|
85
119
|
def parse_options(json_options)
|
@@ -87,9 +121,11 @@ module ContainedMr::JobLogic
|
|
87
121
|
mapper_ulimits = mapper['ulimits'] || {}
|
88
122
|
@mapper_options = {
|
89
123
|
wait_time: mapper['wait_time'] || 60,
|
124
|
+
vcpus: mapper['vcpus'] || 1, # logical processors
|
125
|
+
ram: mapper['ram'] || 512, # megabytes
|
126
|
+
swap: mapper['swap'] || 0, # megabytes
|
90
127
|
ulimits: {
|
91
128
|
cpu: mapper_ulimits['cpu'] || 60, # seconds
|
92
|
-
rss: mapper_ulimits['rss'] || 500_000, # pages
|
93
129
|
}
|
94
130
|
}
|
95
131
|
|
@@ -97,9 +133,11 @@ module ContainedMr::JobLogic
|
|
97
133
|
reducer_ulimits = reducer['ulimits'] || {}
|
98
134
|
@reducer_options = {
|
99
135
|
wait_time: reducer['wait_time'] || 60,
|
136
|
+
vcpus: reducer['vcpus'] || 1, # logical processors
|
137
|
+
ram: reducer['ram'] || 512, # megabytes
|
138
|
+
swap: reducer['swap'] || 0, # megabytes
|
100
139
|
ulimits: {
|
101
140
|
cpu: reducer_ulimits['cpu'] || 60,
|
102
|
-
rss: reducer_ulimits['rss'] || 500_000,
|
103
141
|
}
|
104
142
|
}
|
105
143
|
end
|
@@ -69,7 +69,7 @@ module ContainedMr::TemplateLogic
|
|
69
69
|
# @private common code from mapper_dockerfile and reducer_dockerfile
|
70
70
|
def job_dockerfile(job_definition, input_source)
|
71
71
|
<<DOCKER_END
|
72
|
-
FROM #{
|
72
|
+
FROM #{image_tag}
|
73
73
|
COPY #{input_source} #{job_definition['input'] || '/input'}
|
74
74
|
WORKDIR #{job_definition['chdir'] || '/'}
|
75
75
|
ENTRYPOINT #{JSON.dump(job_definition['cmd'] || ['/bin/sh'])}
|
data/test/test_job_logic.rb
CHANGED
@@ -9,6 +9,14 @@ class TestJobLogic < MiniTest::Test
|
|
9
9
|
JSON.load(File.read('testdata/job.hello'))
|
10
10
|
end
|
11
11
|
|
12
|
+
def test_mapper_image_tag
|
13
|
+
assert_equal 'contained_mrtests/mapper.testjob', @job.mapper_image_tag
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_reducer_image_tag
|
17
|
+
assert_equal 'contained_mrtests/reducer.testjob', @job.reducer_image_tag
|
18
|
+
end
|
19
|
+
|
12
20
|
def test_mapper_container_options
|
13
21
|
assert_equal @template, @job.template
|
14
22
|
assert_equal 'contained_mrtests', @job.name_prefix
|
@@ -19,16 +27,22 @@ class TestJobLogic < MiniTest::Test
|
|
19
27
|
|
20
28
|
golden = {
|
21
29
|
'name' => 'contained_mrtests_mapper.testjob.2',
|
22
|
-
'Image' =>
|
30
|
+
'Image' => 'contained_mrtests/mapper.testjob',
|
23
31
|
'Hostname' => '2.mapper',
|
24
32
|
'Domainname' => '',
|
25
33
|
'Labels' => { 'contained_mr.ctl' => 'contained_mrtests' },
|
26
|
-
'Env' => [ 'ITEM=2', 'ITEMS=3'
|
34
|
+
'Env' => [ 'ITEM=2', 'ITEMS=3',
|
35
|
+
'affinity:image==contained_mrtests/mapper.testjob' ],
|
27
36
|
'Ulimits' => [
|
28
37
|
{ 'Name' => 'cpu', 'Hard' => 3, 'Soft' => 3 },
|
29
|
-
{ 'Name' => 'rss', 'Hard' => 1000000, 'Soft' => 1000000 },
|
30
38
|
],
|
31
39
|
'NetworkDisabled' => true, 'ExposedPorts' => {},
|
40
|
+
'HostConfig' => {
|
41
|
+
'Memory' => 256.5 * 1024 * 1024,
|
42
|
+
'MemorySwap' => (256.5 + 64) * 1024 * 1024,
|
43
|
+
'CpuShares' => 1500000,
|
44
|
+
'CpuPeriod' => 1000000,
|
45
|
+
},
|
32
46
|
}
|
33
47
|
assert_equal golden, @job.mapper_container_options(2)
|
34
48
|
end
|
@@ -36,16 +50,22 @@ class TestJobLogic < MiniTest::Test
|
|
36
50
|
def test_reducer_container_options
|
37
51
|
golden = {
|
38
52
|
'name' => 'contained_mrtests_reducer.testjob',
|
39
|
-
'Image' =>
|
53
|
+
'Image' => 'contained_mrtests/reducer.testjob',
|
40
54
|
'Hostname' => 'reducer',
|
41
55
|
'Domainname' => '',
|
42
56
|
'Labels' => { 'contained_mr.ctl' => 'contained_mrtests' },
|
43
|
-
'Env' => [ 'ITEMS=3'
|
57
|
+
'Env' => [ 'ITEMS=3',
|
58
|
+
'affinity:image==contained_mrtests/reducer.testjob' ],
|
44
59
|
'Ulimits' => [
|
45
60
|
{ 'Name' => 'cpu', 'Hard' => 2, 'Soft' => 2 },
|
46
|
-
{ 'Name' => 'rss', 'Hard' => 100000, 'Soft' => 100000 },
|
47
61
|
],
|
48
62
|
'NetworkDisabled' => true, 'ExposedPorts' => {},
|
63
|
+
'HostConfig' => {
|
64
|
+
'Memory' => 768.5 * 1024 * 1024,
|
65
|
+
'MemorySwap' => -1,
|
66
|
+
'CpuShares' => 500000,
|
67
|
+
'CpuPeriod' => 1000000,
|
68
|
+
},
|
49
69
|
}
|
50
70
|
assert_equal golden, @job.reducer_container_options
|
51
71
|
end
|
data/test/test_mock_runner.rb
CHANGED
@@ -14,6 +14,12 @@ class TestMockRunner < MiniTest::Test
|
|
14
14
|
{ 'Name' => 'rss', 'Hard' => 1000000, 'Soft' => 1000000 },
|
15
15
|
],
|
16
16
|
'NetworkDisabled' => true, 'ExposedPorts' => {},
|
17
|
+
'HostConfig' => {
|
18
|
+
'Memory' => 256.5 * 1024 * 1024,
|
19
|
+
'MemorySwap' => (256.5 + 64) * 1024 * 1024,
|
20
|
+
'CpuShares' => 1500000,
|
21
|
+
'CpuPeriod' => 1000000,
|
22
|
+
},
|
17
23
|
}
|
18
24
|
@runner = ContainedMr::Mock::Runner.new @container_options, 2.5,
|
19
25
|
'/usr/mrd/map-output'
|
data/test/test_runner_logic.rb
CHANGED
@@ -11,9 +11,14 @@ class TestRunnerLogic < MiniTest::Test
|
|
11
11
|
'Env' => [ 'ITEM=2', 'ITEMS=3' ],
|
12
12
|
'Ulimits' => [
|
13
13
|
{ 'Name' => 'cpu', 'Hard' => 3, 'Soft' => 3 },
|
14
|
-
{ 'Name' => 'rss', 'Hard' => 1000000, 'Soft' => 1000000 },
|
15
14
|
],
|
16
15
|
'NetworkDisabled' => true, 'ExposedPorts' => {},
|
16
|
+
'HostConfig' => {
|
17
|
+
'Memory' => 256.5 * 1024 * 1024,
|
18
|
+
'MemorySwap' => (256.5 + 64) * 1024 * 1024,
|
19
|
+
'CpuShares' => 1500000,
|
20
|
+
'CpuPeriod' => 1000000,
|
21
|
+
},
|
17
22
|
}
|
18
23
|
@runner = ContainedMr::Mock::Runner.new @container_options, 2.5,
|
19
24
|
'/usr/mrd/map-output'
|
data/test/test_template_logic.rb
CHANGED
@@ -9,13 +9,11 @@ class TestTemplateLogic < MiniTest::Test
|
|
9
9
|
|
10
10
|
def test_mapper_dockerfile
|
11
11
|
golden = File.read 'testdata/Dockerfile.hello.mapper'
|
12
|
-
golden.sub! 'contained_mrtests/base.hello', 'mock-template-image-id'
|
13
12
|
assert_equal golden, @template.mapper_dockerfile, 'mapper Dockerfile'
|
14
13
|
end
|
15
14
|
|
16
15
|
def test_reducer_dockerfile
|
17
16
|
golden = File.read 'testdata/Dockerfile.hello.reducer'
|
18
|
-
golden.sub! 'contained_mrtests/base.hello', 'mock-template-image-id'
|
19
17
|
assert_equal golden, @template.reducer_dockerfile, 'reducer Dockerfile'
|
20
18
|
end
|
21
19
|
|
data/testdata/job.hello
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
{
|
2
2
|
"mapper": {
|
3
3
|
"wait_time": 2.5,
|
4
|
-
"
|
4
|
+
"vcpus": 1.5,
|
5
|
+
"ram": 256.5,
|
6
|
+
"swap": 64,
|
7
|
+
"ulimits": { "cpu": 3 }
|
5
8
|
},
|
6
9
|
"reducer": {
|
7
10
|
"wait_time": 2,
|
8
|
-
"
|
11
|
+
"vcpus": 0.5,
|
12
|
+
"ram": 768.5,
|
13
|
+
"swap": 0,
|
14
|
+
"ulimits": { "cpu": 2 }
|
9
15
|
}
|
10
16
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contained_mr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Costan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09
|
11
|
+
date: 2015-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docker-api
|
@@ -209,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
209
|
version: '0'
|
210
210
|
requirements: []
|
211
211
|
rubyforge_project:
|
212
|
-
rubygems_version: 2.4.5
|
212
|
+
rubygems_version: 2.4.5.1
|
213
213
|
signing_key:
|
214
214
|
specification_version: 4
|
215
215
|
summary: Map-Reduce with Docker containers
|