-
Notifications
You must be signed in to change notification settings - Fork 208
/
12-step-12.nutsh
346 lines (256 loc) · 10.8 KB
/
12-step-12.nutsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
lesson_name("Migrating to roles!")
make_and_go_ws
clear_ws
run(`cp -r /tutorials/files/step-12/* .`)
"`Migrating to roles!`"
"Now that our playbook is done, let's refactor everything! We'll replace our plays with roles."
"Roles are just a new way of organizing files but bring interesting features. I won't go into great lengths here, since they're listed in Ansible's documentation ( `http://docs.ansible.com/ansible/latest/playbooks.html` ), but my favorite is probably roles dependencies: role B can depend on another role A. Thus, when applying role B, role A will automatically be applied too."
"`Roles structures`"
"Roles add a bit of \"magic\" to Ansible: they assume a specific file organization. While there is a suggested layout regarding roles, you can organize things the way you want using includes. However, role's conventions help building modular playbooks, and housekeeping will be much simpler. Rubyists would call this \"convention over configuration\"."
"Please press the \"`Enter`\" key to see the file layout!"
prompt {
if command == "" {
expect (" ")
break
}
}
"The file layout for roles looks like this:"
print("\t
roles\n\t
|\n\t
|_some_role\n\t
|\n\t
|_defaults\n\t
| |\n\t
| |_main.yml\n\t
| |_...\n\t
|\n\t
|_files\n\t
| |\n\t
| |_file1\n\t
| |_...\n\t
|\n\t
|_handlers\n\t
| |\n\t
| |_main.yml\n\t
| |_some_other_file.yml\n\t
| |_ ...\n\t
|\n\t
|_meta\n\t
| |\n\t
| |_main.yml\n\t
| |_some_other_file.yml\n\t
| |_ ...\n\t
|\n\t
|_tasks\n\t
| |\n\t
| |_main.yml\n\t
| |_some_other_file.yml\n\t
| |_ ...\n\t
|\n\t
|_templates\n\t
| |\n\t
| |_template1.j2\n\t
| |_...\n\t
|\n\t
|_vars\n\t
|\n\t
|_main.yml\n\t
|_some_other_file.yml\n\t
|_ ...\n\t
")
"Quite simple. The files named `main.yml` are not mandatory. However, when they exist, roles will add them to the play automatically. You can use this file to include other tasks, handlers, ... in the play. We'll see that in a minute."
"Please press the \"`Enter`\" key to continue!"
prompt {
if command == "" {
expect (" ")
break
}
}
"Note that there is also a `vars` and a `meta` directory. `vars` is used when you want to put a bunch of variables regarding the roles. However, I don't like setting vars in roles (or plays) directly. I think variables belong to configuration, while plays are the structure. In other words, I see plays and roles as a factory, and data as inputs to this factory. So I really prefer to have \"data\" (e.g. variables) outside roles and play. This way, I can share my roles more easily, without worrying about exposing too much about my servers. But that's just a personal preference. Ansible just lets you do it the way you want."
"But you have some vars that you hardly want to change. For instance, if you have a role for nginx that pulls the .deb package from a PPA, you might want to add the PPA address in `vars/main.yml`. It is something that you can configure, but that will be mostly static 99% of the time. Using `vars` will let you pull out this information out of your role, making it more generic. But really, this is a matter of taste."
"However, for real vars (e.g. things you would like to use in a configuration file generated by a template), you can set defaults for roles, and this is a recommended practice. Using sane defaults ensures your role always work. For instance, you could set the number of pre-forked servers for your apache server. The best place to put the defaults is... you guessed it, the `defaults` directory."
"The `meta` directory is where you can add dependencies, and it's really a neat feature. We'll see that later."
"Note that roles sit in the `roles` directory, which is also cool since it will reduce top level ansible playbook clutter. But you can configure Ansible to use an alternate directory to store role (see roles_path variable in ansible.cfg: `http://docs.ansible.com/ansible/latest/intro_configuration.html#roles-path`). This way you can setup a 'central place' for all your roles, and use them in all your playbooks."
"Please press the \"`Enter`\" key to continue!"
prompt {
if command == "" {
expect (" ")
break
}
}
"`Creating the Apache role`"
"Ok, now that we know the required layout, we can create our apache role from our apache playbook."
"The steps required are really simple:"
print("\t
- create the roles directory and apache role layout\n\t
- extract the apache handler into roles/apache/handlers/main.yml\n\t
- move the apache configuration file awesome-app into roles/apache/files/\n\t
- create a role playbook\n\t
", "cyan")
"Lets start!"
"`Creating the role layout`"
"First start with creating required folder structure for `apache` role. Run the following command:"
"*mkdir -p roles/apache/{tasks,handlers,files}*"
prompt {
if success && command == "mkdir -p roles/apache/{tasks,handlers,files}" {
expect ("mkdir -p roles/apache/{tasks,handlers,files}")
""
break
}
}
"Now we need to copy the tasks from `apache.yml` as `roles/apache/tasks/main.yml`"
"First check the `apache_tasks.yml` file that I prepared for you"
"*cat apache_tasks.yml*"
prompt {
if success && command == "cat apache_tasks.yml" {
expect ("cat apache_tasks.yml")
""
"The file is not fully reproduced, but it is exactly the content of `apache.yml` between `tasks:` and `handlers:`."
break
}
}
"Now move this file to the correct place with correct name:"
"*mv apache_tasks.yml roles/apache/tasks/main.yml*"
prompt {
if success && command == "mv apache_tasks.yml roles/apache/tasks/main.yml" {
expect ("mv apache_tasks.yml roles/apache/tasks/main.yml")
""
"Note that we also have to remove references to `files/` and `templates/` directories in tasks. Since we're using the roles structure, Ansible will look for them in the right directories."
"Ok, let's move on!"
break
}
}
"Please press the \"`Enter`\" key to continue!"
prompt {
if command == "" {
expect (" ")
break
}
}
"`Extracting the handler`"
"I've extracted the handlers part into file `apache_handlers.yaml`. Check its content:"
"*cat apache_handlers.yml*"
prompt {
if success && command == "cat apache_handlers.yml" {
expect ("cat apache_handlers.yml")
""
break
}
}
"Now move this file as roles/apache/handlers/main.yml"
"*mv apache_handlers.yml roles/apache/handlers/main.yml*"
prompt {
if success && command == "mv apache_handlers.yml roles/apache/handlers/main.yml" {
expect ("mv apache_handlers.yml roles/apache/handlers/main.yml")
""
break
}
}
"`Moving the configuration file`"
"As simple as:"
"*mv files/awesome-app roles/apache/files/*"
prompt {
if success && command == "mv files/awesome-app roles/apache/files/" {
expect ("mv files/awesome-app roles/apache/files/")
""
break
}
}
"We no longer need `files` folder at root, delete it:"
"*rm -rf files*"
prompt {
if success && command =~ "rm -rf files\." {
expect ("rm -rf files")
""
break
}
}
"At this point, the apache role is fully working, but we need a way to invoke it."
"`Create a role playbook`"
"I've just created a top level playbook that we'll use to map hosts and host groups to roles and called it `site.yml`, since our goal is to have our site-wide configuration in it."
"Check the top level playbook by running:"
"*cat site.yml*"
prompt {
if success && command == "cat site.yml" {
expect ("cat site.yml")
""
"That wasn't too hard."
break
}
}
"Now let's create the haproxy role."
"We could create the haproxy role directory structure using mkdir as we did for apache role:"
print("\t
mkdir -p step-12/roles/haproxy/{tasks,handlers,templates}\n\t
")
"But, we have another option which is using `ansible-galaxy`. Let's do it so:"
"*ansible-galaxy --offline init roles/haproxy*"
prompt {
if success && command == "ansible-galaxy --offline init roles/haproxy" {
expect ("ansible-galaxy --offline init roles/haproxy")
""
"You can check the directory structure with `ls -la roles/haproxy`."
break
}
}
"Now move the haproxy_tasks.yml to the correct place with correct name:"
"*mv haproxy_tasks.yml roles/haproxy/tasks/main.yml*"
prompt {
if success && command == "mv haproxy_tasks.yml roles/haproxy/tasks/main.yml" {
expect ("mv haproxy_tasks.yml roles/haproxy/tasks/main.yml")
""
break
}
}
"Move `haproxy_handlers.yml` to roles/haproxy/handlers/main.yml"
"*mv haproxy_handlers.yml roles/haproxy/handlers/main.yml*"
prompt {
if success && command == "mv haproxy_handlers.yml roles/haproxy/handlers/main.yml" {
expect ("mv haproxy_handlers.yml roles/haproxy/handlers/main.yml")
""
"Almost done!"
break
}
}
"Finally move `templates folder` (which only contains the haproxy template file) to the correct place:"
"*mv templates roles/haproxy/templates*"
prompt {
if success && command == "mv templates roles/haproxy/templates" {
expect ("mv templates roles/haproxy/templates")
""
break
}
}
"Great, `haproxy` role is ready as well!"
"Now we can run our new playbook. Run as follows:"
"*ansible-playbook -i hosts site.yml*"
prompt {
if success && command == "ansible-playbook -i hosts site.yml" {
expect ("ansible-playbook -i hosts site.yml")
""
"Perfect!"
say("Now head to `http://127.0.0.1:" + run (`echo $HOSTPORT_BASE`) + "` and see the result. Your cluster is deployed!")
say("You can even peek at HAProxy's statistics at `http://127.0.0.1:" + run (`echo $HOSTPORT_BASE`) + "/haproxy?stats`")
say("! Remember our hosts are docker containers and `port 80 of host0.example.org` is exposed to `port " + run (`echo $HOSTPORT_BASE`) + " of your local machine`")
break
}
}
"You may have noticed that running all roles in site.yml can take a long time. What if you only wanted to push changes to web? This is also easy, with the limit flag. Run the following:"
"*ansible-playbook -i hosts -l web site.yml*"
prompt {
if success && command == "ansible-playbook -i hosts -l web site.yml" {
expect ("ansible-playbook -i hosts -l web site.yml")
""
"This concludes our migration to roles. It was quite easy, and adds a bunch of features to our playbook that we can use later."
break
}
}
"This is the end of this lesson!"
"Please press the \"`Enter`\" key to continue!"
prompt {
if command == "" {
expect (" ")
break
}
}