ironfan 3.1.3 → 3.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +6 -1
- data/Gemfile +5 -2
- data/Guardfile +9 -0
- data/README.md +21 -20
- data/VERSION +1 -1
- data/ironfan.gemspec +16 -9
- data/lib/ironfan/cloud.rb +1 -1
- data/lib/ironfan/dsl_object.rb +1 -1
- data/lib/ironfan/server_slice.rb +2 -2
- data/notes/Home.md +23 -9
- data/notes/INSTALL-cloud_setup.md +4 -4
- data/notes/INSTALL.md +58 -64
- data/notes/advanced-superpowers.md +16 -0
- data/notes/{aws_console_screenshot.jpg → aws_servers.jpg} +0 -0
- data/notes/aws_user_key.png +0 -0
- data/notes/core_concepts.md +189 -0
- data/notes/declaring_volumes.md +1 -1
- data/notes/design_notes-aspect_oriented_devops.md +36 -0
- data/notes/design_notes-cookbook_event_ordering.md +37 -0
- data/notes/design_notes-dsl_object.md +63 -9
- data/notes/homebase-layout.txt +102 -0
- data/notes/knife-cluster-commands.md +18 -0
- data/notes/opscode_org_key.png +0 -0
- data/notes/opscode_user_key.png +0 -0
- data/notes/philosophy.md +14 -0
- data/notes/{Silverware.md → silverware.md} +0 -0
- data/notes/style_guide.md +100 -51
- data/notes/tips_and_troubleshooting.md +9 -0
- data/notes/walkthrough-hadoop.md +168 -0
- data/notes/walkthrough-web.md +166 -0
- metadata +35 -36
- data/notes/Knife-Cluster-Commands.md +0 -8
- data/notes/ironfan_homebase_layout.md +0 -94
data/notes/style_guide.md
CHANGED
@@ -1,41 +1,82 @@
|
|
1
1
|
# Ironfan + Chef Style Guide
|
2
2
|
|
3
|
-
|
3
|
+
------------------------------------------------------------------------
|
4
4
|
|
5
|
-
|
5
|
+
### System+Component define Names
|
6
6
|
|
7
|
-
|
7
|
+
Name things uniformly for their system and component. For the ganglia master,
|
8
|
+
|
9
|
+
* attributes: `node[:ganglia][:master]`
|
10
|
+
* recipe: `ganglia::master`
|
11
|
+
* role: `ganglia_master`
|
12
|
+
* directories: `ganglia/master` (if specific to component), `ganglia` (if not).
|
13
|
+
- for example: `/var/log/ganglia/master`
|
14
|
+
|
15
|
+
### Component names
|
16
|
+
|
17
|
+
* `agent.rb`
|
18
|
+
* `worker.rb`
|
19
|
+
* `datanode.rb`
|
20
|
+
* `webnode.rb`
|
8
21
|
|
9
|
-
You should crisply separate cookbook-wide concerns from component concerns. The server's attributes live in `node[:redis][:server]`, it is installed by the `redis::server` cookbook, and so forth.
|
10
|
-
|
11
|
-
You should also separate system configuration from multi-system integration. Cookbooks should provide hooks that are neighborly but not exhibitionist, and otherwise mind their own business. The `hadoop_cluster` cookbook describes hadoop, the `pig` cookbook pig, and the `zookeeper` cookbook zookeeper. The job of tying those components together (copying zookeeper jars into the pig home dir, or the port+addr of hadoop daemons) should be isolated.
|
12
22
|
|
13
23
|
### Recipes
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
25
|
+
Recipes partition these things:
|
26
|
+
|
27
|
+
* shared functionality between components
|
28
|
+
* proper event order
|
29
|
+
* optional or platform-specific functionality
|
30
|
+
|
31
|
+
* Within the foo cookbook, name your recipes like this:
|
32
|
+
- `default.rb` -- information shared by anyone using foo, including support packages, users and directories.
|
33
|
+
- `user.rb` -- define daemon users. Called 'user' even if there is more than one. It's OK to move this into the default cookbook.
|
34
|
+
- `install_from_X.rb` -- install packages (`install_from_package`), versioned tarballs (`install_from_release`). It's OK to move this into `default.rb`.
|
35
|
+
- `deploy.rb` -- use this when doing sha-versioned deploys.
|
36
|
+
- `plugins.rb` -- install additional plugins or support code. If you have separate plugins, name them `git_plugin`, `rspec_plugin`, etc.
|
37
|
+
- `server.rb` -- define the foo server process. Similarly, `agent`, `worker`, etc -- see component naming above.
|
38
|
+
- `client.rb` -- install libraries to *use* the foo service.
|
39
|
+
- `config_files.rb` -- discover other components, write final configuration to disk
|
40
|
+
- `finalize.rb` -- final cleanup
|
41
|
+
|
42
|
+
* Do not repeat the cookbook name in a recipe title: `ganglia::master`, not `ganglia::ganglia_master`.
|
43
|
+
* Use only `[a-z0-9_]` for cookbook and component names. Do not use capital letters or hyphens.
|
44
|
+
* Keep names short and descriptive (preferably 15 characters or less, or it jacks with the Chef webui).
|
45
|
+
|
20
46
|
* Always include a `default.rb` recipe, even if it is blank.
|
21
|
-
* *DO NOT* install daemons
|
22
|
-
|
23
|
-
|
47
|
+
* *DO NOT* use the default cookbook to install daemons or do anything interesting at all, even if that's currently the only thing the recipe does. I want to be able to refer to the attributes in the apache cookbook without launching the apache service. Think of it like a C header file.
|
48
|
+
|
49
|
+
A `client` is also passive -- it lets me *use* the system without requiring that I run it. This means the client recipe should *never* launch a process (chef_client` and `nfs_client` components are allowed exceptions).
|
24
50
|
|
25
51
|
### Cookbook Dependencies
|
26
52
|
|
27
53
|
* Dependencies should be announced in metadata.rb, of course.
|
28
|
-
*
|
29
|
-
|
30
|
-
|
31
|
-
- *
|
54
|
+
* Explicitly `include_recipe` for system resources -- `runit`, `java`, `silverware`, `thrift` and `apt`.
|
55
|
+
- never
|
56
|
+
* *DO NOT* use `include_recipe` unless putting it in the role would be utterly un-interesting. You *want* the run to break unless it's explicitly included in the role.
|
57
|
+
- *yes*: `java`, `ruby`, `announces`, etc.
|
58
|
+
- *no*: `zookeeper::client`, `nfs::server`, or anything that will start a daemon
|
32
59
|
Remember: ordinary cookbooks describe systems, roles and integration cookbooks coordinate them.
|
33
60
|
* `include_recipe` statements should only appear in recipes that are entry points. Recipes that are not meant to be called directly should assume their dependencies have been met.
|
34
61
|
* If a recipe is meant to be the primary entrypoint, it *should* include default, and it should do so explicitly: `include_recipe 'foo::default'` (not just 'foo').
|
35
62
|
|
63
|
+
Crisply separate cookbook-wide concerns from component concerns.
|
64
|
+
|
65
|
+
Separate system configuration from multi-system integration. Cookbooks should provide hooks that are neighborly but not exhibitionist, and otherwise mind their own business.
|
66
|
+
|
36
67
|
### Templates
|
37
68
|
|
38
|
-
*
|
69
|
+
*DO NOT* refer to attributes directly on the node (`node[:foo]`). This prevents people from using those templates outside the cookbook. Instead:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
# in recipe
|
73
|
+
template 'fooconf.yml' do
|
74
|
+
variables :foo => node[:foo]
|
75
|
+
end
|
76
|
+
|
77
|
+
# in template
|
78
|
+
@node[:log_dir]
|
79
|
+
```
|
39
80
|
|
40
81
|
### Attributes
|
41
82
|
|
@@ -43,42 +84,44 @@ You should also separate system configuration from multi-system integration. Coo
|
|
43
84
|
* Attributes shared by all components sit at cookbook level, and are always named for the cookbook: `node[:hadoop][:log_dir]` (since it is shared by all its components).
|
44
85
|
* Component-specific attributes sit at component level (`node[:cookbook_name][:component_name]`): eg `node[:hadoop][:namenode][:service_state]`. Do not use a prefix (NO: `node[:hadoop][:namenode_handler_count]`)
|
45
86
|
|
87
|
+
* Refer to node attributes by symbol, never by method:
|
88
|
+
- `node[:ganglia][:log_dir]`, not `node.ganglia.log_dir` or `node['ganglia']['log_dir']
|
89
|
+
|
46
90
|
#### Attribute Files
|
47
91
|
|
48
|
-
* The main attribute file should be named `attributes/default.rb`.
|
92
|
+
* The main attribute file should be named `attributes/default.rb`. Do not name the file after the cookbook, or anything else.
|
49
93
|
* If there are a sizeable number of tunable attributes (hadoop, cassandra), place them in `attributes/tuneables.rb`.
|
50
|
-
* ?? Place integration attribute *hooks* in `attributes/integration.rb` ??
|
51
94
|
|
52
|
-
|
53
|
-
- If a component has only one log file, call it 'log_file': `node[:foo][:server][:log_file]` and in general do not use a prefix.
|
54
|
-
- If a component has more than one log_file, *always* use a prefix: `node[:foo][:server][:dashboard_log_file]` and `node[:foo][:server][:gc_log_file]`.
|
95
|
+
## Name Attributes for their aspects
|
55
96
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
## Attribute Names
|
97
|
+
Attributes should be named for their aspect: `port`, `log`, etc. Use generic names if there is only one attribute for an aspect, prefixed names if there are many:
|
98
|
+
- For a component that only opens one port: `node[:foo][:server][:port]`
|
99
|
+
- More than one port, use a prefix: `node[:foo][:server][:dash_port]` and `node[:foo][:server][:rpc_port]`.
|
60
100
|
|
61
|
-
|
101
|
+
Sometimes the conventions below are inappropriate. All we ask is in those cases that you *not* use the special magic name. For example, don't use `:port` and give it a comma-separated string; name it something else, like `:port_list`.
|
102
|
+
|
103
|
+
Here are specific conventions:
|
62
104
|
|
63
105
|
### File and Dir Aspects
|
64
106
|
|
65
|
-
A *file* is the full directory and basename for a file. A *dir* is a directory whose contents correspond to a single concern. A *
|
107
|
+
A *file* is the full directory and basename for a file. A *dir* is a directory whose contents correspond to a single concern. A *prefix* not intended to be used directly -- it will be decorated with suffixes to form dirs and files. A *basename* is only the leaf part of a file reference. Don't use the terms 'path' or 'filename'.
|
66
108
|
|
67
|
-
Ignore the temptation to make a one-true-home-for-my-system, or to fight the package maintainer's choices.
|
109
|
+
Ignore the temptation to make a one-true-home-for-my-system, or to fight the package maintainer's choices. (FIXME: Rewrite to encourage OS-correct naming schemas.)
|
110
|
+
- a sandbox holding dir, pid, log, ...
|
68
111
|
|
69
112
|
#### Application
|
70
113
|
|
114
|
+
* **prefix**: A container with directories bin, lib, share, src, to use according to convention
|
115
|
+
- default: `/usr/local`.
|
71
116
|
* **home_dir**: Logical location for the cookbook's system code.
|
72
|
-
- default: typically, leave it up to the package maintainer. Otherwise, `:
|
117
|
+
- default: typically, leave it up to the package maintainer. Otherwise, `:prefix/share/:cookbook` should be a symlink to the `install_dir` (see below).
|
73
118
|
- instead of: `xx_home` / `dir` alone / `install_dir`
|
74
|
-
* **prefix_root**: A container with directories bin, lib, share, src, to use according to convention
|
75
|
-
- default: `/usr/local`.
|
76
119
|
* **install_dir**: The cookbook's system code, in case the home dir is a pointer to potential alternates.
|
77
|
-
- default: `:
|
120
|
+
- default: `:prefix/share/:cookbook-:version` ( you don't need the directory after the cookbook runs, use `:prefix/share/:cookbook-:version` instead, eg `/usr/local/src/tokyo_tyrant-xx.xx`)
|
78
121
|
- Make `home_dir` a symlink to this directory (eg home_dir `/usr/local/share/elasticsearch` links to install_dir `/usr/local/share/elasticsearch-0.17.8`).
|
79
122
|
* **src_dir**: holds the compressed tarball, its expanded contents, and the compiled files when installing from source. Use this when you will run `make install` or equivalent and use the files elsewhere.
|
80
|
-
- default: `:
|
81
|
-
- do not: expand the tarball to `:
|
123
|
+
- default: `:prefix/src/:system_name-:version`, eg `/usr/local/src/pig-0.9.tar.gz`
|
124
|
+
- do not: expand the tarball to `:prefix/src/(whatever)` if it will actually be used from there; instead, use the `install_dir` convention described above. (As a guideline, I should be able to blow away `/usr/local/src` and everything still works).
|
82
125
|
* **deploy_dir**: deployed code that follows the capistrano convention. See more about deploy variables below.
|
83
126
|
- the `:deploy_dir/shared` directory holds common files
|
84
127
|
- releases are checked out to `:deploy_dir/releases/{sha}`
|
@@ -138,7 +181,7 @@ Ignore the temptation to make a one-true-home-for-my-system, or to fight the pac
|
|
138
181
|
* **release_url**: URL for the release.
|
139
182
|
- instead of: install_url, package_url, being careless about partial vs whole URLs
|
140
183
|
* **release_file**: Where to put the release.
|
141
|
-
- default: `:
|
184
|
+
- default: `:prefix/src/system_name-version.ext`, eg `/usr/local/src/elasticsearch-0.17.8.tar.bz2`.
|
142
185
|
- do not use `/tmp` -- let me decide when to blow it away (and make it easy to be idempotent).
|
143
186
|
- do not use a non-versioned URL or file name.
|
144
187
|
* **release_file_sha** or **release_file_md5** fingerprint
|
@@ -147,8 +190,7 @@ Ignore the temptation to make a one-true-home-for-my-system, or to fight the pac
|
|
147
190
|
|
148
191
|
* **plugins**: array of system-specific plugins
|
149
192
|
|
150
|
-
use `deploy_{}` for anything that would be true whatever SCM you're using; use
|
151
|
-
`git_{}` (and so forth) where specific to that repo.
|
193
|
+
use `deploy_{}` for anything that would be true whatever SCM you're using; use `git_{}` (and so forth) where specific to that repo.
|
152
194
|
|
153
195
|
* **deploy_env** production / staging / etc
|
154
196
|
* **deploy_strategy**
|
@@ -160,7 +202,7 @@ use `deploy_{}` for anything that would be true whatever SCM you're using; use
|
|
160
202
|
* **git_revision**: SHA or branch
|
161
203
|
- instead of: `deploy_revision`
|
162
204
|
|
163
|
-
* **apt/
|
205
|
+
* **apt/(repo_name)** Options for adding a cookbook's apt repo.
|
164
206
|
- Note that this is filed under *apt*, not the cookbook.
|
165
207
|
- Use the best name for the repo, which is not necessarily the cookbook's name: eg `apt/cloudera/{...}`, which is shared by hadoop, flume, pig, and so on.
|
166
208
|
- `apt/{repo_name}/url` -- eg `http://archive.cloudera.com/debian`
|
@@ -190,7 +232,7 @@ use `deploy_{}` for anything that would be true whatever SCM you're using; use
|
|
190
232
|
|
191
233
|
* **XX_heap_max**, **xx_heap_min**, **java_heap_eden**
|
192
234
|
* **java_home**
|
193
|
-
* AVOID **java_opts** if possible: assemble it in your recipe from intelligible attribute names.
|
235
|
+
* AVOID batch declaration of options (e.g. **java_opts**) if possible: assemble it in your recipe from intelligible attribute names.
|
194
236
|
|
195
237
|
### Nitpicks
|
196
238
|
|
@@ -214,19 +256,12 @@ If your app does any of the following,
|
|
214
256
|
* **exports** -- jars or libs that other programs may wish to incorporate
|
215
257
|
* **consumes** -- placed there by any call to `discover`.
|
216
258
|
|
217
|
-
### Dummy aspects
|
218
|
-
|
219
|
-
Integration cookbooks that announce as
|
220
|
-
|
221
|
-
* Elastic Load Balancers
|
222
|
-
|
223
|
-
|
224
259
|
## Clusters
|
225
260
|
|
226
261
|
* Describe physical configuration:
|
227
262
|
- machine size, number of instances per facet, etc
|
228
263
|
- external assets (elastic IP, ebs volumes)
|
229
|
-
* Describe high-level assembly of systems via roles: `hadoop_namenode`, `nfs_client`, `
|
264
|
+
* Describe high-level assembly of systems via roles: `hadoop_namenode`, `nfs_client`, `ganglia_agent`, etc.
|
230
265
|
* Describe important modifications, such as `ironfan::system_internals`, mounts ebs volumes, etc
|
231
266
|
* Describe override attributes:
|
232
267
|
- `heap size`, rvm versions, etc.
|
@@ -247,5 +282,19 @@ roles shouldn't assemble systems. The contents of the infochimps_chef/roles/plat
|
|
247
282
|
|
248
283
|
* Deprecated:
|
249
284
|
- Cluster and facet roles (`roles/gibbon_cluster.rb`, `roles/gibbon_namenode.rb`, etc) go away
|
250
|
-
-
|
285
|
+
- Roles should be service-oriented: `hadoop_master` considered harmful, you should explicitly enumerate the services
|
286
|
+
|
287
|
+
|
288
|
+
### Facets should be (nearly) identical
|
289
|
+
|
290
|
+
Within a facet, keep your servers almost entirely identical. For example, servers in a MySQL facet would their index to set shard order and to claim the right attached volumes. However, it would be a mistake to have one server within a facet be a master process and the rest be worker processes -- just define different facets for each.
|
291
|
+
|
292
|
+
### Pedantic Distinctions:
|
293
|
+
|
294
|
+
Separate the following terms:
|
295
|
+
|
296
|
+
* A *machine* is a concrete thing that runs your code -- it might be a VM or raw metal, but it has CPUs and fans and a finite lifetime. It has a unique name tied to its physical presence -- something like 'i-123abcd' or 'rack 4 server 7'.
|
297
|
+
* A *chef node* is the code object that, together with the chef-client process, configures a machine. In ironfan, the chef node is strictly slave to the server description and the measured attributes of the machine.
|
298
|
+
* A *server description* gives the high-level specification the machine should acheive. This includes the roles, recipes and attributes given to the chef node; the physical characteristics of the machine ('8 cores, 7GB ram, AWS cloud'); and its relation to the rest of the system (george cluster, webnode facet, index 3).
|
251
299
|
|
300
|
+
In particular, we try to be careful to always call a Chef node a 'chef node' (never just 'node'). Try processing graph nodes in a flume node feeding a node.js decorator on a cloud node define by a chef node. No(de) way.
|
@@ -1,5 +1,12 @@
|
|
1
1
|
## Tips and Notes
|
2
2
|
|
3
|
+
### Gems
|
4
|
+
|
5
|
+
knife cluster ssh bonobo-worker-2 'sudo gem update --system'
|
6
|
+
knife cluster ssh bonobo-worker-2 'sudo true ; for foo in /usr/lib/ruby/gems/1.9.2-p290/specifications/* ; do sudo sed -i.bak "s!000000000Z!!" $foo ; done'
|
7
|
+
knife cluster ssh bonobo-worker-2 'sudo true ; for foo in /usr/lib/ruby/site_ruby/*/rubygems/deprecate.rb ; do sudo sed -i.bak "s!@skip ||= false!true!" $foo ; done'
|
8
|
+
|
9
|
+
|
3
10
|
### EC2 Notes Instance attributes: `disable_api_termination` and `delete_on_termination`
|
4
11
|
|
5
12
|
To set `delete_on_termination` to 'true' after the fact, run the following (modify the instance and volume to suit):
|
@@ -81,3 +88,5 @@ Your service is probably installed but removed from runit's purview; check the `
|
|
81
88
|
* directory `/etc/sv/foo`, containing file `run` and dirs `log` and `supervise`
|
82
89
|
* `/etc/init.d/foo` is symlinked to `/usr/bin/sv`
|
83
90
|
* `/etc/servics/foo` is symlinked tp `/etc/sv/foo`
|
91
|
+
|
92
|
+
|
@@ -0,0 +1,168 @@
|
|
1
|
+
FIXME: Repurpose general structure to demonstrate a Hadoop cluster.
|
2
|
+
|
3
|
+
## Walkthrough: Hadoop Cluster
|
4
|
+
|
5
|
+
Here's a very simple cluster:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
Ironfan.cluster 'hadoop_demo' do
|
9
|
+
cloud(:ec2) do
|
10
|
+
flavor 't1.micro'
|
11
|
+
end
|
12
|
+
|
13
|
+
role :base_role
|
14
|
+
role :chef_client
|
15
|
+
role :ssh
|
16
|
+
|
17
|
+
# The database server
|
18
|
+
facet :dbnode do
|
19
|
+
instances 1
|
20
|
+
role :mysql_server
|
21
|
+
|
22
|
+
cloud do
|
23
|
+
flavor 'm1.large'
|
24
|
+
backing 'ebs'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# A throwaway facet for development.
|
29
|
+
facet :webnode do
|
30
|
+
instances 2
|
31
|
+
role :nginx_server
|
32
|
+
role :awesome_webapp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
This code defines a cluster named hadoop_demo. A cluster is a group of servers united around a common purpose, in this case to serve a scalable web application.
|
38
|
+
|
39
|
+
The hadoop_demo cluster has two 'facets' -- dbnode and webnode. A facet is a subgroup of interchangeable servers that provide a logical set of systems: in this case, the systems that store the website's data and those that render it.
|
40
|
+
|
41
|
+
The dbnode facet has one server, which will be named `hadoop_demo-dbnode-0`; the webnode facet has two servers, `hadoop_demo-webnode-0` and `hadoop_demo-webnode-1`.
|
42
|
+
|
43
|
+
Each server inherits the appropriate behaviors from its facet and cluster. All the servers in this cluster have the `base_role`, `chef_client` and `ssh` roles. The dbnode machines additionally house a MySQL server, while the webnodes have an nginx reverse proxy for the custom `hadoop_demo_webapp`.
|
44
|
+
|
45
|
+
As you can see, the dbnode facet asks for a different flavor of machine (`m1.large`) than the cluster default (`t1.micro`). Settings in the facet override those in the server, and settings in the server override those of its facet. You economically describe only what's significant about each machine.
|
46
|
+
|
47
|
+
### Cluster-level tools
|
48
|
+
|
49
|
+
```
|
50
|
+
$ knife cluster show hadoop_demo
|
51
|
+
|
52
|
+
+---------------------+-------+------------+-------------+--------------+---------------+-----------------+----------+--------------+------------+------------+
|
53
|
+
| Name | Chef? | InstanceID | State | Public IP | Private IP | Created At | Flavor | Image | AZ | SSH Key |
|
54
|
+
+---------------------+-------+------------+-------------+--------------+---------------+-----------------+----------+--------------+------------+------------+
|
55
|
+
| hadoop_demo-dbnode-0 | yes | i-43c60e20 | running | 107.22.6.104 | 10.88.112.201 | 20111029-204156 | t1.micro | ami-cef405a7 | us-east-1a | hadoop_demo |
|
56
|
+
| hadoop_demo-webnode-0 | yes | i-1233aef1 | running | 102.99.3.123 | 10.88.112.123 | 20111029-204156 | t1.micro | ami-cef405a7 | us-east-1a | hadoop_demo |
|
57
|
+
| hadoop_demo-webnode-1 | yes | i-0986423b | not running | | | | | | | |
|
58
|
+
+---------------------+-------+------------+-------------+--------------+---------------+-----------------+----------+--------------+------------+------------+
|
59
|
+
|
60
|
+
```
|
61
|
+
|
62
|
+
The commands available are:
|
63
|
+
|
64
|
+
* list -- lists known clusters
|
65
|
+
* show -- show the named servers
|
66
|
+
* launch -- launch server
|
67
|
+
* bootstrap
|
68
|
+
* sync
|
69
|
+
* ssh
|
70
|
+
* start/stop
|
71
|
+
* kill
|
72
|
+
* kick -- trigger a chef-client run on each named machine, tailing the logs until the run completes
|
73
|
+
|
74
|
+
|
75
|
+
### Advanced clusters remain simple
|
76
|
+
|
77
|
+
Let's say that app is truly awesome, and the features and demand increases. This cluster adds an [ElasticSearch server](http://elasticsearch.org) for searching, a haproxy loadbalancer, and spreads the webnodes across two availability zones.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
Ironfan.cluster 'hadoop_demo' do
|
81
|
+
cloud(:ec2) do
|
82
|
+
image_name "maverick"
|
83
|
+
flavor "t1.micro"
|
84
|
+
availability_zones ['us-east-1a']
|
85
|
+
end
|
86
|
+
|
87
|
+
# The database server
|
88
|
+
facet :dbnode do
|
89
|
+
instances 1
|
90
|
+
role :mysql_server
|
91
|
+
cloud do
|
92
|
+
flavor 'm1.large'
|
93
|
+
backing 'ebs'
|
94
|
+
end
|
95
|
+
|
96
|
+
volume(:data) do
|
97
|
+
size 20
|
98
|
+
keep true
|
99
|
+
device '/dev/sdi'
|
100
|
+
mount_point '/data'
|
101
|
+
snapshot_id 'snap-a10234f'
|
102
|
+
attachable :ebs
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
facet :webnode do
|
107
|
+
instances 6
|
108
|
+
cloud.availability_zones ['us-east-1a', 'us-east-1b']
|
109
|
+
|
110
|
+
role :nginx_server
|
111
|
+
role :awesome_webapp
|
112
|
+
role :elasticsearch_client
|
113
|
+
|
114
|
+
volume(:server_logs) do
|
115
|
+
size 5
|
116
|
+
keep true
|
117
|
+
device '/dev/sdi'
|
118
|
+
mount_point '/server_logs'
|
119
|
+
snapshot_id 'snap-d9c1edb1'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
facet :esnode do
|
124
|
+
instances 1
|
125
|
+
role "elasticsearch_data_esnode"
|
126
|
+
role "elasticsearch_http_esnode"
|
127
|
+
cloud.flavor "m1.large"
|
128
|
+
end
|
129
|
+
|
130
|
+
facet :loadbalancer do
|
131
|
+
instances 1
|
132
|
+
role "haproxy"
|
133
|
+
cloud.flavor "m1.xlarge"
|
134
|
+
elastic_ip "128.69.69.23"
|
135
|
+
end
|
136
|
+
|
137
|
+
cluster_role.override_attributes({
|
138
|
+
:elasticsearch => {
|
139
|
+
:version => '0.17.8',
|
140
|
+
},
|
141
|
+
})
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
The facets are described and scale independently. If you'd like to add more webnodes, just increase the instance count. If a machine misbehaves, just terminate it. Running `knife cluster launch hadoop_demo webnode` will note which machines are missing, and launch and configure them appropriately.
|
146
|
+
|
147
|
+
Ironfan speaks naturally to both Chef and your cloud provider. The esnode's `cluster_role.override_attributes` statement will be synchronized to the chef server, pinning the elasticsearch version across the server and clients. Your chef roles should focus on specific subsystems; the cluster file lets you see the architecture as a whole.
|
148
|
+
|
149
|
+
With these simple settings, if you have already [set up chef's knife to launch cloud servers](http://wiki.opscode.com/display/chef/Launch+Cloud+Instances+with+Knife), typing `knife cluster launch hadoop_demo --bootstrap` will (using Amazon EC2 as an example):
|
150
|
+
|
151
|
+
* Synchronize to the chef server:
|
152
|
+
- create chef roles on the server for the cluster and each facet.
|
153
|
+
- apply role directives (eg the homebase's `default_attributes` declaration).
|
154
|
+
- create a node for each machine
|
155
|
+
- apply the runlist to each node
|
156
|
+
* Set up security isolation:
|
157
|
+
- uses a keypair (login ssh key) isolated to that cluster
|
158
|
+
- Recognizes the `ssh` role, and add a security group `ssh` that by default opens port 22.
|
159
|
+
- Recognize the `nfs_server` role, and adds security groups `nfs_server` and `nfs_client`
|
160
|
+
- Authorizes the `nfs_server` to accept connections from all `nfs_client`s. Machines in other clusters that you mark as `nfs_client`s can connect to the NFS server, but are not automatically granted any other access to the machines in this cluster. Ironfan's opinionated behavior is about more than saving you effort -- tying this behavior to the chef role means you can't screw it up.
|
161
|
+
* Launches the machines in parallel:
|
162
|
+
- using the image name and the availability zone, it determines the appropriate region, image ID, and other implied behavior.
|
163
|
+
- passes a JSON-encoded user_data hash specifying the machine's chef `node_name` and client key. An appropriately-configured machine image will need no further bootstrapping -- it will connect to the chef server with the appropriate identity and proceed completely unattended.
|
164
|
+
* Syncronizes to the cloud provider:
|
165
|
+
- Applies EC2 tags to the machine, making your console intelligible: ![AWS Console screenshot](https://github.com/infochimps-labs/ironfan/raw/version_3/notes/aws_console_screenshot.jpg)
|
166
|
+
- Connects external (EBS) volumes, if any, to the correct mount point -- it uses (and applies) tags to the volumes, so they know which machine to adhere to. If you've manually added volumes, just make sure they're defined correctly in your cluster file and run `knife cluster sync {cluster_name}`; it will paint them with the correct tags.
|
167
|
+
- Associates an elastic IP, if any, to the machine
|
168
|
+
* Bootstraps the machine using knife bootstrap
|
@@ -0,0 +1,166 @@
|
|
1
|
+
## Walkthrough: Web Cluster
|
2
|
+
|
3
|
+
Here's a very simple cluster:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Ironfan.cluster 'web_demo' do
|
7
|
+
cloud(:ec2) do
|
8
|
+
flavor 't1.micro'
|
9
|
+
end
|
10
|
+
|
11
|
+
role :base_role
|
12
|
+
role :chef_client
|
13
|
+
role :ssh
|
14
|
+
|
15
|
+
# The database server
|
16
|
+
facet :dbnode do
|
17
|
+
instances 1
|
18
|
+
role :mysql_server
|
19
|
+
|
20
|
+
cloud do
|
21
|
+
flavor 'm1.large'
|
22
|
+
backing 'ebs'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# A throwaway facet for development.
|
27
|
+
facet :webnode do
|
28
|
+
instances 2
|
29
|
+
role :nginx_server
|
30
|
+
role :awesome_webapp
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
This code defines a cluster named web_demo. A cluster is a group of servers united around a common purpose, in this case to serve a scalable web application.
|
36
|
+
|
37
|
+
The web_demo cluster has two 'facets' -- dbnode and webnode. A facet is a subgroup of interchangeable servers that provide a logical set of systems: in this case, the systems that store the website's data and those that render it.
|
38
|
+
|
39
|
+
The dbnode facet has one server, which will be named `web_demo-dbnode-0`; the webnode facet has two servers, `web_demo-webnode-0` and `web_demo-webnode-1`.
|
40
|
+
|
41
|
+
Each server inherits the appropriate behaviors from its facet and cluster. All the servers in this cluster have the `base_role`, `chef_client` and `ssh` roles. The dbnode machines additionally house a MySQL server, while the webnodes have an nginx reverse proxy for the custom `web_demo_webapp`.
|
42
|
+
|
43
|
+
As you can see, the dbnode facet asks for a different flavor of machine (`m1.large`) than the cluster default (`t1.micro`). Settings in the facet override those in the server, and settings in the server override those of its facet. You economically describe only what's significant about each machine.
|
44
|
+
|
45
|
+
### Cluster-level tools
|
46
|
+
|
47
|
+
```
|
48
|
+
$ knife cluster show web_demo
|
49
|
+
|
50
|
+
+---------------------+-------+------------+-------------+--------------+---------------+-----------------+----------+--------------+------------+------------+
|
51
|
+
| Name | Chef? | InstanceID | State | Public IP | Private IP | Created At | Flavor | Image | AZ | SSH Key |
|
52
|
+
+---------------------+-------+------------+-------------+--------------+---------------+-----------------+----------+--------------+------------+------------+
|
53
|
+
| web_demo-dbnode-0 | yes | i-43c60e20 | running | 107.22.6.104 | 10.88.112.201 | 20111029-204156 | t1.micro | ami-cef405a7 | us-east-1a | web_demo |
|
54
|
+
| web_demo-webnode-0 | yes | i-1233aef1 | running | 102.99.3.123 | 10.88.112.123 | 20111029-204156 | t1.micro | ami-cef405a7 | us-east-1a | web_demo |
|
55
|
+
| web_demo-webnode-1 | yes | i-0986423b | not running | | | | | | | |
|
56
|
+
+---------------------+-------+------------+-------------+--------------+---------------+-----------------+----------+--------------+------------+------------+
|
57
|
+
|
58
|
+
```
|
59
|
+
|
60
|
+
The commands available are:
|
61
|
+
|
62
|
+
* list -- lists known clusters
|
63
|
+
* show -- show the named servers
|
64
|
+
* launch -- launch server
|
65
|
+
* bootstrap
|
66
|
+
* sync
|
67
|
+
* ssh
|
68
|
+
* start/stop
|
69
|
+
* kill
|
70
|
+
* kick -- trigger a chef-client run on each named machine, tailing the logs until the run completes
|
71
|
+
|
72
|
+
|
73
|
+
### Advanced clusters remain simple
|
74
|
+
|
75
|
+
Let's say that app is truly awesome, and the features and demand increases. This cluster adds an [ElasticSearch server](http://elasticsearch.org) for searching, a haproxy loadbalancer, and spreads the webnodes across two availability zones.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Ironfan.cluster 'web_demo' do
|
79
|
+
cloud(:ec2) do
|
80
|
+
image_name "maverick"
|
81
|
+
flavor "t1.micro"
|
82
|
+
availability_zones ['us-east-1a']
|
83
|
+
end
|
84
|
+
|
85
|
+
# The database server
|
86
|
+
facet :dbnode do
|
87
|
+
instances 1
|
88
|
+
role :mysql_server
|
89
|
+
cloud do
|
90
|
+
flavor 'm1.large'
|
91
|
+
backing 'ebs'
|
92
|
+
end
|
93
|
+
|
94
|
+
volume(:data) do
|
95
|
+
size 20
|
96
|
+
keep true
|
97
|
+
device '/dev/sdi'
|
98
|
+
mount_point '/data'
|
99
|
+
snapshot_id 'snap-a10234f'
|
100
|
+
attachable :ebs
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
facet :webnode do
|
105
|
+
instances 6
|
106
|
+
cloud.availability_zones ['us-east-1a', 'us-east-1b']
|
107
|
+
|
108
|
+
role :nginx_server
|
109
|
+
role :awesome_webapp
|
110
|
+
role :elasticsearch_client
|
111
|
+
|
112
|
+
volume(:server_logs) do
|
113
|
+
size 5
|
114
|
+
keep true
|
115
|
+
device '/dev/sdi'
|
116
|
+
mount_point '/server_logs'
|
117
|
+
snapshot_id 'snap-d9c1edb1'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
facet :esnode do
|
122
|
+
instances 1
|
123
|
+
role "elasticsearch_data_esnode"
|
124
|
+
role "elasticsearch_http_esnode"
|
125
|
+
cloud.flavor "m1.large"
|
126
|
+
end
|
127
|
+
|
128
|
+
facet :loadbalancer do
|
129
|
+
instances 1
|
130
|
+
role "haproxy"
|
131
|
+
cloud.flavor "m1.xlarge"
|
132
|
+
elastic_ip "128.69.69.23"
|
133
|
+
end
|
134
|
+
|
135
|
+
cluster_role.override_attributes({
|
136
|
+
:elasticsearch => {
|
137
|
+
:version => '0.17.8',
|
138
|
+
},
|
139
|
+
})
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
The facets are described and scale independently. If you'd like to add more webnodes, just increase the instance count. If a machine misbehaves, just terminate it. Running `knife cluster launch web_demo webnode` will note which machines are missing, and launch and configure them appropriately.
|
144
|
+
|
145
|
+
Ironfan speaks naturally to both Chef and your cloud provider. The esnode's `cluster_role.override_attributes` statement will be synchronized to the chef server, pinning the elasticsearch version across the server and clients. Your chef roles should focus on specific subsystems; the cluster file lets you see the architecture as a whole.
|
146
|
+
|
147
|
+
With these simple settings, if you have already [set up chef's knife to launch cloud servers](http://wiki.opscode.com/display/chef/Launch+Cloud+Instances+with+Knife), typing `knife cluster launch web_demo --bootstrap` will (using Amazon EC2 as an example):
|
148
|
+
|
149
|
+
* Synchronize to the chef server:
|
150
|
+
- create chef roles on the server for the cluster and each facet.
|
151
|
+
- apply role directives (eg the homebase's `default_attributes` declaration).
|
152
|
+
- create a node for each machine
|
153
|
+
- apply the runlist to each node
|
154
|
+
* Set up security isolation:
|
155
|
+
- uses a keypair (login ssh key) isolated to that cluster
|
156
|
+
- Recognizes the `ssh` role, and add a security group `ssh` that by default opens port 22.
|
157
|
+
- Recognize the `nfs_server` role, and adds security groups `nfs_server` and `nfs_client`
|
158
|
+
- Authorizes the `nfs_server` to accept connections from all `nfs_client`s. Machines in other clusters that you mark as `nfs_client`s can connect to the NFS server, but are not automatically granted any other access to the machines in this cluster. Ironfan's opinionated behavior is about more than saving you effort -- tying this behavior to the chef role means you can't screw it up.
|
159
|
+
* Launches the machines in parallel:
|
160
|
+
- using the image name and the availability zone, it determines the appropriate region, image ID, and other implied behavior.
|
161
|
+
- passes a JSON-encoded user_data hash specifying the machine's chef `node_name` and client key. An appropriately-configured machine image will need no further bootstrapping -- it will connect to the chef server with the appropriate identity and proceed completely unattended.
|
162
|
+
* Syncronizes to the cloud provider:
|
163
|
+
- Applies EC2 tags to the machine, making your console intelligible: ![AWS Console screenshot](https://github.com/infochimps-labs/ironfan/wiki/aws_servers.jpg)
|
164
|
+
- Connects external (EBS) volumes, if any, to the correct mount point -- it uses (and applies) tags to the volumes, so they know which machine to adhere to. If you've manually added volumes, just make sure they're defined correctly in your cluster file and run `knife cluster sync {cluster_name}`; it will paint them with the correct tags.
|
165
|
+
- Associates an elastic IP, if any, to the machine
|
166
|
+
* Bootstraps the machine using knife bootstrap
|