Awx es el equivalente gratuito de Ansible Tower, se trata de la última versión de Tower(inestable), aunque cabe decir que en las pruebas realizadas ha funcionado sin dar ningún problema. Con AWX dispondremos de varas funcionalidades como poder delegar ciertas tareas de administración, acceso a informes de ejecución de playbooks, programar tareas, crear workflows y automatizar tareas mediante su API.
Las principales caracterÃsticas de awx son:
- Delegar: AWX nos permite crear usuarios, grupos y equipos. Se pueden filtrar los accesos a los inventarios, credenciales y playbooks, esto nos permite que usuarios sin experiencia puedan ejecutar complejas tareas de automatización. Un desarrollador es capaz de montarse él mismo los servidores de desarrollo que necesite tan solo presionando un botón.
- Informes de ejecución de playbooks: AWX nos permite visualizar las ejecuciones de los playbooks en el dashboard viendo de este modo si algún playbook falló, cuando se ejecutaron y por quién fueron ejecutados. Podremos ver en un click porque falló la ejecución en un host en concreto.
- Programar tareas: Se pueden programar tareas para ejecutar ciertas acciones como ejecución de templates y pulls de repositorios git. Las tareas se pueden incluso programar para una fecha futura en concreto, pudiendo irnos de vacaciones y seguir desplegando servidores en nuestra ausencia sin intervención humana.
- Workflows: Los templates nos permiten mediante el workflow editor encadenar la ejecución de playbooks, pulls de repos y notificaciones.
- Automatización de tareas mediante API: Todo el sistema funciona en base a la API REST, la interfaz web en realidad hace llamadas a esta api, disponer de la api nos puede permitir integrar tareas en scripts, webs de terceros y demás.
Lo manera mas sencilla de desplegar AWX es mediante Docker, en este manual se va a explicar tanto para Gentoo como Ubuntu:
-
Gentoo:
emerge -av app-emulation/docker app-admin/ansible dev-python/pip
/etc/init.d/docker start
rc-update add docker default
pip install docker --user -
Ubuntu:
apt install docker.io git ansible python-pip -y
pip uninstall -y docker docker-py
pip install docker
Clonamos los repos del proyecto:
Toda la instalación de servicios se basa en docker, los CTs de Docker se instalan mediante un playbook de Ansible:
vi inventory
dockerhub_base=ansible
dockerhub_version=latest
awx_official=true
postgres_data_dir=/var/lib/pgdocker
docker_compose_dir=/var/lib/awxcompose
Ejecutamos el playbook:
Podemos ver los CTs:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b2070d0b4bed ansible/awx_task:latest "/tini -- /bin/sh -c…" 6 seconds ago Up 6 seconds 8052/tcp awx_task
0bcc0f360861 ansible/awx_web:latest "/tini -- /bin/sh -c…" 8 seconds ago Up 6 seconds 0.0.0.0:80->8052/tcp awx_web
27e3ad73bc76 postgres:9.6 "docker-entrypoint.s…" 12 seconds ago Up 9 seconds 5432/tcp awx_postgres
acbdeb1d0cc4 ansible/awx_rabbitmq:3.7.4 "docker-entrypoint.s…" 12 seconds ago Up 9 seconds 4369/tcp, 5671-5672/tcp, 15671-15672/tcp, 25672/tcp awx_rabbitmq
723a9595b52d memcached:alpine "docker-entrypoint.s…" 12 seconds ago Up 10 seconds 11211/tcp awx_memcached
Accedemos a la interfaz web con las credenciales por defecto admin/password:
http://ansible-awx.alfaexploit.com
admin
password
NOTA: Lo primero que debemos hacer es cambiar el password de admin
Una vez cambiado el password de admin, creamos una organización, cada organización puede entenderse como una empresa, cliente o sede:
Ahora crearemos un inventario nuevo indicando la organización creada, además añadiremos algunos hosts:
Añadimos los hosts en la pestaña hosts:
En mi caso los playbooks se encuentran en un repositorio de git, para poder tener acceso a ellos debemos crear unas credenciales de acceso a este:
Para acceder vÃa ssh a los hosts también debemos crear unas credenciales:
Creamos el repositorio indicando que utilice las credenciales anteriormente configuradas:
Ahora ya estamos en condiciones de crear nuestro primer job template que se encargará de ejecutar el playbook baseGentoo.yml
Para ejecutar el template debemos presionar sobre el icono del cohete:
Una funcionalidad muy interesante de AWX es que podemos programar la ejecución de los templates mediante schedules, por ejemplo en este caso programamos la ejecución de la actualización de los sistemas Gentoo:
En la siguiente captura de pantalla podemos ver los schedules que trae por defecto mas el nuestro:
NOTA: Los schedules pueden ser para ejecutar playbooks, hacer un pull del repo de git o incluso ejecutar tareas administrativas
AWX nos permite ser notificados cuando se ejecuta un playbook, ya sea cuando terminó con éxito o cuando falló.
Para probar las notificaciones vamos a crear una pequeña API web que nos avisará vÃa Telegram:
#!/bin/python
from apiAWX import Server
if __name__ == '__main__':
Server().start()
#!/bin/python
from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from SocketServer import ThreadingMixIn
import simplejson
import threading
import requests
import sys
def sendTelegram(msg):
apiKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
url = "https://api.telegram.org/bot{}/sendMessage".format(apiKey)
#print msg
userId = "YYYYYYYYYYYYYY"
data = {"chat_id":userId,"text":msg}
r = requests.post(url,json=data)
#print r
def processAWXRequest(requestObject):
#print type(requestObject)
contentLength = int(requestObject.headers.getheader('content-length', 0))
data = requestObject.rfile.read(contentLength)
data = simplejson.loads(data)
#print type(data)
#print data
if 'body' in data and data['body'] == 'Ansible Tower Test Notification 1 https://towerhost':
msg = 'AWX test message detected'
else:
playbookStart = data['started']
playbookFinish = data['finished']
playbookAffectedHosts = str(data['hosts'].keys())
playbookStatus = data['status']
playbookName = data['name']
#print playbookStart
#print playbookName
#print playbookAffectedHosts
#print playbookStatus
#print playbookFinish
msg = playbookStart + '\nPlaybook: ' + playbookName + '\nAffected Hosts: ' + playbookAffectedHosts + '\nStatus: ' + playbookStatus + '\n' + playbookFinish
sendTelegram(msg)
requestObject.send_response(200)
def handler_404(self):
self.send_response(404)
# Routes array
ROUTES = {
('GET', "/"): lambda self: self.wfile.write("AWX-API by kr0m"),
('POST', "/processAWXRequest"): (processAWXRequest),
}
# Class handling GET/POST requests
class Handler(BaseHTTPRequestHandler):
# Send request GET to function defined in ROUTES array
def do_GET(self):
ROUTES.get(('GET', self.path), handler_404)(self)
# Send request POST to function defined in ROUTES array
def do_POST(self):
ROUTES.get(('POST', self.path), handler_404)(self)
class MultiThreadedHTTPServer(ThreadingMixIn, HTTPServer):
pass
class Server(threading.Thread):
def run(self):
httpd = MultiThreadedHTTPServer(('', 5555), Handler)
httpd.serve_forever()
Podemos comprobar que las notificaciones funcionan mediante el test notification, campanita
Para que nuestra API arranque en el boot de Gentoo:
su kr0m -c "nohup python /home/kr0m/awxWebhook/main.py &"
NOTA: En Ubuntu serÃa crear una unidad de systemd
Arrancamos manualmente la API mediante el script de local.d:
Otra funcionalidad muy interesante sobretodo si el equipo de programadores va a ejecutar nuestros playbooks son las surveys, estas les permitirán desplegar equipos con parámetros especÃficos a su proyecto, por ejemplo el nombre del usuario que desplegará el código, el docRoot del proyecto, etc.
Un ejemplo de encuesta podrÃa ser si desea instalar el binario de symfony:
Al ejecutar el playbook nos aparecerá un formulario para rellenar los datos sobre la encuesta:
Otro aspecto a destacar de AWX es la capacidad de obtener listas de hosts en base a querys sobre datos del host, para ello en vez de crear inventarios crearemos smart inventories, se pueden utilizar varios filtros para ello:
exact: Exact match (default lookup if not specified).
iexact: Case-insensitive version of exact.
contains: Field contains value.
icontains: Case-insensitive version of contains.
startswith: Field starts with value.
istartswith: Case-insensitive version of startswith.
endswith: Field ends with value.
iendswith: Case-insensitive version of endswith.
regex: Field matches the given regular expression.
iregex: Case-insensitive version of regex.
gt: Greater than comparison.
gte: Greater than or equal to comparison.
lt: Less than comparison.
lte: Less than or equal to comparison.
isnull: Check whether the given field or related object is null; expects a boolean value.
in: Check whether the given field’s value is present in the list provided; expects a list of items.
Boolean values may be specified as True or 1 for true, False or 0 for false (both case-insensitive).
Mi preferido es mediante expresiones regulares, por ejemplo para sacar todos los hosts cuyo nombre empieza por test serÃa:
name.regex:^test.*
Podemos ver el nuevo smart inventory en la lista de smart inventories:
NOTA: A mi parecer la mejor estrategia es crear listas de inventarios en base a EMPRESA-TECH-PROYECTO y nombrar los servidores del mismo modo para luego poder crear smart inventories mas fácilmente.
El proceso de subida de playbooks a producción suele ser programación/testeo en local, cuando el código es suficientemente maduro se sube al repositorio y finalmente los desarrolladores ejecutan los playbooks a través de awx.
Para poder testear en local los playbooks sin subirlos al repo podemos asignar valores a las variables que asignarÃa AWX pero desde la cli:
Todas las funcionalidades de Ansible funcionan generando unos ficheros en python que serán copiados vÃa ssh al servidor para posteriormente ser ejecutados en este, acto seguido son eliminados del servidor de Ansible.
Si necesitamos debugear estos ficheros podemos habilitar la opción ANSIBLE_KEEP_REMOTE_FILES para conservarlos.
Luego tan solo debemos entrar en el CT de Docker para ver los ficheros:
cat /root/.ansible/tmp/ansible-tmp-1560498029.06-113770163193085/AnsiballZ_git.py
tail -f /var/log/tower/dispatcher.log
La instalación de AWX mediante docker NO instala el servidor web con soporte para SSL, pero mediante socat podemos hacer un frontend SSL y redirigir el tráfico al puerto de AWX:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c74cbb26f09e ansible/awx_web:latest "/tini -- /bin/sh -c…" 5 days ago Up 4 minutes 0.0.0.0:80->8052/tcp awx_web
Primero generamos el certificado a presentar:
openssl req -new -key cert.key -x509 -days 36530 -out cert.crt
Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:XXXX
Locality Name (eg, city) []:YYYY
Organization Name (eg, company) [Internet Widgits Pty Ltd]:IT
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:kr0m@alfaexploit.com
Arrancamos socat escuchando en el puerto 443, sirviendo el certificado y reenviando el tráfico a localhost:80
Para que esto sea automático creamos un script de arranque, en Gentoo serÃa del siguiente modo en Ubuntu mediante una unidad de systemd:
nohup socat -v openssl-listen:443,cert=/home/kr0m/cert.pem,verify=0,reuseaddr,fork tcp4:localhost:80 &
Le asignamos los permisos necesarios:
Lo arrancamos manualmente:
AWX nos permite realizar backups de la configuración mediante la herramienta ansible-tower-cli, la instalamos desde pip:
Hacemos que sea accesible desde el $PATH:
Ahora procedemos con la configuración de la tool, necesita que le indiquemos el host donde conectar, el user/pass y algunos parámetros extra:
tower-cli config verify_ssl false
tower-cli config username admin
tower-cli config password XXXXXX
tower-cli config verbose True
El backup mas básico es este, que dumpea toda la config en un fichero json:
Si solo queremos dumpear las credenciales:
Para exportar toda la configuración a excepción de los objetos de una organización:
Tan importante con dumpear es poder restaurar los datos extraÃdos.
El restore básico serÃa:
Para restaurar la config a un AWX remoto:
Para restaurarlo todo a excepción de los datos de una organización en concreto: