-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
549 lines (475 loc) · 26 KB
/
index.html
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Fabric - Brief introduction</title>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/sky.css" id="theme">
<!-- Theme used for syntax highlighting of code -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h2><a href="http://www.fabfile.org/">Fabric</a></h2>
<h4>"Breve introducción"</h4>
</section>
<section>
<p>Sobre mi: <a href="http://monobotsoft.es">Héctor Alvarez</a></p>
<small><p>twitter: <a href="https://twitter.com/monobotblog">@monobotblog</a></p></small>
<small><p>email: <a href="mailto:[email protected]">[email protected]</a></p></small>
<small>Backend developer en <a href="https://cabaana.com/" target="_blank">cabaana</a></small>
</section>
<section>
<h3>
<img height="90" style="vertical-align: middle;border:none;box-shadow:none;background:none" data-src="http://docs.fabfile.org/en/1.13/_static/logo.png" alt="Up arrow" style="transform: rotate(180deg); -webkit-transform: rotate(180deg);">
Fabric
</h3>
<ul>
<p class="fragment"><small>Fabric es un framework de alto nivel para la línea de comandos</small></p>
<div class="fragment">
<p><small>Permite tanto la ejecución de comandos en local como en remoto, en uno o varios servidores.<span class="fragment"> Antes de que lo pregunten, <b>SI</b> se integra perfectamente con <b>ssh</b></span></small></p>
<p><span class="fragment"><small>Para ello necesitas tener ssh configurado de tal forma que <a href="https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files">se conecte sin verificación</a>, así fabric no tiene que preguntar por credenciales</small></span></p>
</div>
<p class="fragment"><small>Debido a éstas características, lo hace muy sencillo y versátil para ejecutar comandos largos, grupos de comandos, tareas de mantenimiento, despliegues, etc...</small></p>
</ul>
</section>
<section>
<h3>¿Porqué en <b>Python</b> en vez de <b>bash</b> directamente?</h3>
<img class="fragment" style="vertical-align: middle;border:none;box-shadow:none;background:none" src="lib/images/bash_mess.png" alt="why automate" style="transform: rotate(180deg); -webkit-transform: rotate(180deg);">
</section>
<section>
<ul>
<p><small>Con Python puedes crear scripts con solo unas pocas líneas de código, y obviamente mucho mas legible.</small></p>
<p class="fragment"><small>Además tener un script es menos dado a errores que escribirlo a mano, y menos aburrido (y estresante) si me preguntas.</span></small></p>
<p class="fragment"><small>Al ser python tienes acceso a cientos de paquetes que te hacen la vida más fácil (como logging, os, etc...).</span></small></p>
<p class="fragment"><small>El mismo grupo de comandos puede ejecutarse en un grupo de servidores.</small></p>
<p class="fragment"><small>Nada te impide incrustarlo en el cron de tu máquina y hacer la tarea automáticamente.</small></p>
<pre class="fragment"><small><code class="hljs" data-trim contenteditable>
2 0 * * * ec2-user /var/.envs/cabaana/bin/fab -f /srv/django/cabaana/scripts/fab/db_backup.py dev remote_bu
</code></small></pre>
</ul>
</section>
<section>
<h3>¿Porqué automatizar?</h3>
<img style="vertical-align: middle;border:none;box-shadow:none;background:none" src="lib/images/tarea.jpg" alt="why automate" style="transform: rotate(180deg); -webkit-transform: rotate(180deg);">
</section>
<section>
<h3>Así que ...</h3>
<img style="vertical-align: middle;border:none;box-shadow:none;background:none" src="lib/images/automate.jpg" alt="why automate" style="transform: rotate(180deg); -webkit-transform: rotate(180deg);">
</section>
<section>
<a href="http://www.fabfile.org/installing.html"><h3>Instalación</h3></a>
<ul>
<div class="fragment">
<p>Con <a href="https://pypi.python.org/pypi/pip">pip</a> directamente, para python3</p>
<pre><code class="hljs" data-trim contenteditable>
$ pip install fabric3
</code></pre>
</div>
<div class="fragment">
<p>Y para python2</p>
<pre><code class="hljs" data-trim contenteditable>
$ pip install fabric
</code></pre>
</div>
<div class="fragment">
<p>O mediante repositorios (por ejemplo en Ubuntu)</p>
<pre><code class="hljs" data-trim contenteditable>
$ sudo apt-get install fabric
</code></pre>
</div>
</ul>
</section>
<section data-background="#FFFFB1">
<section>
<h3>Veamos código!!</h3>
<p>En fabric se requiere definir variables de entorno (para configurar el script) y funciones (que son las tareas a ejecutar)</p>
<p class="fragment">Ejemplo sencillo:</p>
</section>
<section>
<p><small>
<pre><code class="hljs" data-trim contenteditable>
# -*- coding: utf-8 -*-
from fabric.api import run, env
from datetime import date
from os import path
today = date.today()
env.user = 'user'
env.hosts = ['9.9.9.9', '99.99.99.99', ]
env.DB_ENDPOINT = 'eu-west-1.rds.amazonaws.com'
env.DB_NAME = 'development'
def get_filepath():
filename = '{date}.sql'.format(date=today.strftime('%Y%m%d'))
return path.join('/', 'home', 'user', 'dbBackUp', '01daily', filename)
def db_backup():
run('/usr/pgsql-9.5/bin/pg_dump -U dbuser -h {EP} {NM} > {fp}'
.format(
EP=env.DB_ENDPOINT,
NM=env.DB_NAME,
fp=get_filepath(),
)
)
</li>
</code></pre>
</small></p>
</section>
</section>
<section>
<section>
<h3>API</h3>
<p><small>hay muchos commandos fabric, para una referencia completa ver <a href="http://docs.fabfile.org/en/1.13/api/contrib/files.html">la documentación</a></small></p>
<p class="fragment"><small>Los más útiles/usados son:</small></p>
</section>
<section>
<h4>local</h4>
<p>ejecutar comandos en tu máquina local</p>
</section>
<section>
<h4>run</h4>
<p>ejecutar comandos en máquinas remotas</p>
<p><small>* requiere saber en qué máquinas se va a ejecutar el comando.</small></p>
<p><small>* requires clave.</small></p>
</section>
<section>
<h4>sudo</h4>
<p>Corre el comando con privilegios de superusuario</p>
<p><small>* requiere clave</small></p>
<p><small>* solo para acceso remoto</small></p>
<p><small>* en local simplemente precedes a fabric con sudo de consola y ya lo tienes</small></p>
</section>
<section>
<h4>cd / lcd</h4>
<p>Ejecuta los siguientes comandos dentro de directorios específicos</p>
</section>
<section>
<h4>get</h4>
<p>Copia ficheros remotos en local</p>
</section>
<section>
<h4>put</h4>
<p>Copia ficheros locales en remoto</p>
</section>
</section>
<section data-background="#FFFFB1">
<section>
<h4>Eres un aburrido, tanto hablar y no vemos código por ningún lado!!!</h4>
<img style="vertical-align: middle;border:none;box-shadow:none;background:none" src="lib/images/disculpa.jpg" alt="why automate" style="transform: rotate(180deg); -webkit-transform: rotate(180deg);">
<div class="fragment">
<p>Tienen razón, ¿Cómo trabajaríamos con esos comandos?</p>
</div>
</section>
<section>
<h4>Veamos local, run y cd en acción</h4>
<p><b>cd</b> se usa dentro de una cláusula <b>with</b>, veamos:</b></p>
</section>
<section>
<p>
<pre><code class="hljs" data-trim contenteditable>
def archive(foldername, filename):
# veremos una forma mas elegante de hacer ésto mas tarde
verb = run
if server_name == 'localhost':
verb = local
working_dir = '/home/monobot/sync/{foldername}'.format(
foldername=foldername
)
with cd(working_dir):
verb('mv {filename} archived/ -rf')
</code></pre>
</p>
</section>
<section>
<h4>¡¡EH!! ¡¡espera!!, hay argumentos en esa función, ¿Cómo es posible?</h4>
<p>Correcto, podemos pasar argumentos desde la línea de comandos asi:</p>
<pre><code class="hljs" data-trim contenteditable>
$ fab archive:downloads,myfile.zip
</code></pre>
<p><small>Entiendan que los argumentos se pasan como texto, en el caso de que fueran enteros o de otro tipo tenemos que convertirlos dentro del script.</small></p>
</section>
</section>
<section>
<section>
<h3>env</h3>
<p><small>Es una clase que hereda de dict</small></p>
<p><small><b>env</b> será leido por fabric para comprobar en cada momento su configuración</small></p>
<p><small>Es por tanto donde se definen las variables de entorno del script y configuras ciertos comportamientos específicos.</small></p>
<p><small>hay muchas <b>env variables</b>, como siempre se recomienda leerse la <a href="http://docs.fabfile.org/en/1.13/usage/env.html">lista completa</a></small></p>
<p><small><b>env</b> además nos sirve para poder guardar y acceder a las variables que nosotros necesitemos específicamente (como en el primer ejemplo).</small></p>
<p class="fragment"><small>las variables de entorno de <b>env</b> mas importantes son:</small></p>
</section>
<section>
<h4>user / sudo_user</h4>
<ul>
<p>user</p>
<p><small>Donde defines quien va a ejecutar el comando, si no se define será quien esté ejecutando el script de fabric.</small></p>
<div class="fragment">
<p>sudo_user</p>
<p><small>Obviamente el usuario de sudo cuando necesitemos privilegios de superusuario (y necesita existir para poder ser leido por el comando <b>sudo</b>).</small></p>
</div>
</ul>
</section>
<section>
<h4>host / hosts</h4>
<ul>
<p>host</p>
<p><small>En qué máquinas se va a ejecutar el comando (requerido por tanto por run).</small></p>
<div class="fragment"><p>hosts</p>
<p><small>Lista de máquinas donde los comandos remotos (con run por ejemplo) se van a ejecutar.</small></p></div>
</ul>
</section>
<section>
<h4>password / passwords</h4>
<ul>
<p>password</p>
<p><small>Donde se almacena la clave que se va a ejecutar ciertos comandos.</small></p>
<div class="fragment"><p>passwords</p>
<p><small>Si cada máquina remota requiere de una clave específica se deberá de configurar mediante éste diccionario.</small></p>
</div>
<div class="fragment">
<p><small><b>NUNCA</b> guarden las claves en un fichero ".py", en el caso de que se requiera meter claves es mejor guardarlas en el entorno de la máquina y leerlas desde allí.</small></p>
<pre>
<code class="hljs" data-trim contenteditable>
import os
env.password = os.environ['MI_CLAVE']
</code>
</pre>
</div>
</ul>
</section>
<section>
<h4>sudo_password / sudo_passwords</h4>
<ul>
<p>sudo_password</p>
<p><small>Si no existe intentará utilizar la misma de <b>password</b>.</small></p>
<div class="fragment"><p>sudo_passwords</p>
<p><small>Al igual por si necesitas claves específicas por servidor.</small></p></div>
</ul>
</section>
<section>
<h4>shell</h4>
<ul>
<p><small>para definir el shell que va a ejecutar el comando, por defecto es <b>bash</b>.</small></p>
</ul>
</section>
<section data-background="#FFFFB1">
<h4>En un momento determinado puede necesitar cambiar una variable en un momento determinado o para una tarea determinada, ese caso tenemos <b>settings</b></h4>
<p class="fragment"></p>
<pre><code class="hljs" data-trim contenteditable>
from fabric.api import settings, run
def changing_inside(path):
with settings(warn_only=True):
return run('ls -la')
</code></pre>
</p>
<p>Por tanto ese comportamiento son afectará lo que ejecutemos dentro del <b>with</b></p>
</section>
</section>
<section data-background="#FFFFB1">
<section>
<h4>Venga ya Héctor, muestranos algo de código!!!</h4>
<img class="fragment" style="vertical-align: middle;border:none;box-shadow:none;background:none" src="lib/images/enfado.jpg" alt="why automate" style="transform: rotate(180deg); -webkit-transform: rotate(180deg);">
<div class="fragment">
<h4>Roger! ¿Cómo manejaríamos varias configuraciones?:</h4>
<p><small>Normalmente las configuraciones se setean en tiempo de ejecución, algunas son globales para todo el módulo, otras son específicas para unos comandos determinados como vimos hace un momento, pero algunas pueden ser dependiendo del servidor al que estemos manejando</small></p>
<p><small>eg:</small></p>
</div>
</section>
<section>
<p>
<pre><code class="hljs" data-trim contenteditable>
from fabric.api import env, run, local
env.user = 'user'
env.foo = 'bar'
def localhost():
env.run = local
env.DB_ENDPOINT = 'eu-west-1.rds.amazonaws.com'
env.DB_NAME = 'development'
def testing():
hosts = ['8.8.8.8']
env.run = run
env.DB_ENDPOINT = 'eu-west-2.rds.amazonaws.com'
env.DB_NAME = 'testing'
</li>
</code></pre>
</p>
</section>
<section>
<p><small>Por tanto podemos ejecutar los mismos comandos en diferentes servidores con configuraciones específicas:</small></p>
<p class="fragment">
<pre><code class="hljs" data-trim contenteditable>
$ fab -f mi_fabfile.py development db_backup
</li>
</code></pre>
<pre><code class="hljs" data-trim contenteditable>
$ fab -f mi_fabfile.py testing db_backup
</li>
</code></pre>
</p>
</section>
</section>
<section>
<section>
<h3>¿Cómo se ejecuta <b>fabric</b>?</h3>
<p>Fabric va a buscar un fichero llamado <b>fabfile.py</b> o un módulo llamado <b>fabfile</b> en el directorio actual, o puedes especificar el fichero directamente.</p>
<p class="fragment">
<pre><code class="hljs" data-trim contenteditable>
$ fab development db_backup
</code></pre>
<pre><code class="hljs" data-trim contenteditable>
$ fab -f mi_fabfile.py development db_backup
</code></pre>
</p>
<p class="fragment">
Normalmente me gusta mas definirlo explícitamente, pero depende de cada uno.
</p>
</section>
<section>
<h3>¿Cómo enviar argumentos?</h3>
<p>Ya lo explicamos</p>
<p class="fragment">
<pre><code class="hljs" data-trim contenteditable>
$ fab big_task:first_arg,second_arg,third=foo,fourth=bar
</code></pre>
</p>
<p>Y también recuerdo que solo llegan como cadena de texto, debemos en su caso convertirlo después.</p>
</section>
<section>
<p><small>A propósito nada te impide:</small></p>
<p class="fragment">
<pre><code class="hljs" data-trim contenteditable>
$ fab -f mi_fabfile.py development db_backup production db_backup
</li>
</code></pre>
</p>
</section>
</section>
<section>
<section>
<h3>tasks (tareas)</h3>
<p>Dentro del fichero de fabric podemos especificar que funciones son o no tareas.</p>
<p>Ésto es algo relativamente nuevo en fabric, para compatibilidad se permite no definir explicitamente, pero se recomienda su uso, eso si una vez defines al menos una tarea el resto no se considera tarea.</p>
<p>Para ello usarmos un decorador.</p>
</section>
<section data-background="#FFFFB1">
<p>
<h4>¡PERO QUE PESADO!, ¿No puedes enseñar algo de código sin tener que pedirlo?</h4>
<pre><code class="hljs" data-trim contenteditable>
from fabric.decorators import task
@task
def foo():
run('foo')
@task(alias='bar')
def my_bar():
run('bar')
</code></pre>
</p>
</section>
<section data-background="#FFFFB1">
<p>
<h4>Las tareas también se pueden definir como clases que hereden de <b>Task</b></h4>
<p>En mi opinión, me parece matar moscas a cañonazos, lo interesante de Fabric es que sea simple y práctico, seguramente hay muchos casos de uso para ésto.</p>
<pre><code class="hljs" data-trim contenteditable>
from fabric.api import run
from fabric.tasks import Task
class MyTask(Task):
name = "my task"
def run(self, environment, domain="whatever.com"):
run("foo")
instance = MyTask()
</code></pre>
</p>
</section>
</section>
<section>
<section>
<h3>Y por último</h3>
<p>Como hacer un módulo completo</p>
<p>debes de crear una estructura como ésta:</p>
</section>
<section>
<p><small>
<pre><code class="hljs" data-trim contenteditable>
fabfile/
├── __init__.py
| from configuration import inv, development, localhost, production
| from foo import foo_task
| from bar import bar_task
|
├── configuration.py
| @task
| def localhost():
| inv.hosts = []
| @task
| def development():
| inv.hosts = []
| @task
| def production():
| inv.hosts = []
|
├── foo.py
| @task
| def foo_bar():
|
└── bar.py
@task
def foo_bar():
</code></pre>
</small></p>
</section>
</section>
<section>
<h3>¡¡Ahora vamos a ver algo de código en vivo!!</h3>
<p><small>Que no se note que tengo una chuletilla (por lo de los nervios y tal)</small></p>
</section>
<section>
<h3>Ejercicio para los créditos</h3>
<p><small>El script debe conectar a un servidor remoto, y guardar el arbol de directorios y ficheros del home ("~/"), en un fichero, el script debe finalmente descargar este fichero a nuestro ordenador local.</small></p>
<p><small>pistas: para el arbol de ficheros y directorios hay un comando de consola llamado "tree", para que la salida del comando se guarde en un fichero determinado puedes poner "tree > myfile.txt".</small></p>
<p><small>Enviar el script por correo a [email protected] antes de un mes, con nombre completo y DNI</small></p>
</section>
<section>
<h3>¡Gracias!</h3>
<p>La guia completa la tienen disponible en: <a href="https://monobot.github.io/fabric_pyday2017/">https://monobot.github.io/fabric_pyday2017/</a></p>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.js"></script>
<script>
// More info https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
transition: 'concave', // none/fade/slide/convex/concave/zoom
// More info https://github.com/hakimel/reveal.js#dependencies
dependencies: [
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'plugin/zoom-js/zoom.js', async: true },
{ src: 'plugin/notes/notes.js', async: true }
]
});
</script>
</body>
</html>