contained_mr 0.2.0 → 0.3.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.
- 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
|