lunes, 30 de junio de 2014

Ansible y Digital Ocean

Justo hoy tenía prevista hacer una subida a producción del API de Teowaki con bastantes cambios de configuración que llevo probando un tiempo en local.

La subida incluye la actualización a Rails 4.1.2, y bastantes gemas, entre las que está Capistrano 3, que ha supuesto reescribir los ficheros de deploy. Para acabar de completarlo, tambien quiero actualizar a Ruby 2.1.2.

Está claro que esta subida tiene todos los números para que algo falle y dejar caído producción un rato...

Así que he optado por la opción de crear un nuevo servidor donde probar todos esos cambios y aprovecho para ir actualizando a Ubuntu Trusty. Una vez creado el servidor y comprobado que todo está bien, simplemente cambiaré el balanceador para que apunte al nuevo servidor y apagaré el servidor antiguo.

Aprovechando estos cambios le he dado un nuevo empujón a la configuración de ansible, creando los servidores desde los playbook en lugar de crearlos desde la consola de Digital Ocean, como hacía hasta ahora.

Continuando la entrada del mes pasado, donde conté como hacer Bootstrap con Ansible, aquí cuento como crear los servidores.

Según la documentación, el primer paso es instalar las dependencias, en este caso sólo dopy, un wrapper de la API de Digital Ocean en Python.
$ sudo pip install dopy
Para facilitar la configuración, nos bajamos los ficheros de inventorio
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/plugins/inventory/digital_ocean.py
$ chmod +x digital_ocean.py
$ wget https://raw.githubusercontent.com/ansible/ansible/devel/plugins/inventory/digital_ocean.ini
Creamos un par de variables de entorno con la API_KEY y el CLIENT_ID, que los consultamos en la configuración de Digital Ocean
export DO_API_KEY=MI_API_KEY
export DO_CLIENT_ID=MI_CLIENT_ID
Y chequeamos si la configuración es correcta listando los droplets que tenemos creados (con -h o --help puedes ver las opciones disponibles)
$./digital_ocean.py --droplets --pretty
Para crear un droplet, necesitamos 4 datos principales, la imagen que usaremos para crearlo, el tamaño, la region donde lo creamos y la clave SSH que usaremos para acceder. Todos esos datos tienen un ID único en Digital Ocean, que podemos consultar con el script anterior (con los parámetros images, sizes, regions y ssh-keys)
$ ./digital_ocean.py --sizes --pretty
{
  "sizes": [
    {
      "cost_per_hour": "0.00744", 
      "cost_per_month": "5.0", 
      "cpu": "1", 
      "disk": "20", 
      "id": "66", 
      "memory": "512", 
      "name": "512MB", 
      "slug": "512mb"
    }, 
...
Para simplificar la creación de nuevos droplets, he añadido en el fichero hosts una sección local (el droplet se crea desde localhost, no desde un servidor remoto)
[local]
localhost
Y un fichero de variables de grupo  
$ more group_vars/local.yml 
---
# SIZES:
#   "slug": "512mb", "name": "512MB", "id": "66", "disk": "20", "cpu": "1", "cost_per_month": "5.0"
#   "slug": "1gb", "name": "1GB", "id": "63", "disk": "30", "cpu": "1", "cost_per_month": "10.0"
#   "slug": "2gb", "name": "2GB", "id": "62", "disk": "40", "cpu": "2", "cost_per_month": "20.0",
do_size_512mb: 66
do_size_1gb: 63
do_size_2gb: 62
# IMAGES:
#   "slug": "ubuntu-14-04-x64", "distribution": "Ubuntu", "id": "3240036", "name": "Ubuntu_14.04_x64",
do_image_ubuntu_14_04_x64: 3240036
# REGIONS;
#   "slug": "nyc2", "id": "4", "name": "New_York_2",
do_region_nyc2: 4
# KEYS:
#   "id": "xxxx", "name": "diego"
do_diego_key:  xxxx

Y ya solo nos queda decidir la configuración y el nombre del servidor (SERVER_NAME en el playbook)
---
- hosts: local
  connection: local
  tasks:
  - name: Create new Digital Ocean Droplet
    digital_ocean: >
      state=present
      command=droplet
      name=SERVER_NAME
      size_id={{ do_size_1gb }}
      region_id={{ do_region_nyc2 }}
      image_id={{ do_image_ubuntu_14_04_x64 }}
      private_networking=yes
      ssh_key_ids={{ do_diego_key }}

Una vez creado, ejecutamos el bootstrap de mi post anterior y los roles del servidor.

Existen soluciones mucho más completas para provisionar los servidores como ansible-provisioner, pero en mi caso con hacer la creación manualmente modificando este playbook es más que suficiente.