Creación y aprovisionamiento¶
En esta sección vamos a crear una máquina virtual, que aprovisionaremos con todo lo necesario para poder ejecutar nuestra app.
Creación de la VM¶
Para crear la VM vamos a usar Vagrant, que nos permitirá tener un archivo de configuración para establecer la box que vamos a usar y el proveedor que ejecutará la VM (en este caso he elegido VirtualBox por ser gratis y por su portabilidad), además de otros ajustes. También podemos especificarle directamente el sistema de aprovisionamiento que vamos a usar, que en mi caso ha sido ansible, asi podemos tener todo lo necesario con el comando vagrant.
Para configurarlo todo, tan solo necesitamos crear un archivo con nombre Vagrantfile
,
que tiene el siguiente formato:
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
# Definimos el nombre de nuestra VM para Vagrant
config.vm.define "NotasIV"
# He elegido Ubuntu 18.04 LTS ya que es la última version
# estable y con mayor tiempo de soporte actualmente de Ubuntu
config.vm.box = "ubuntu/bionic64"
# Con esto evitamos que busque actualizaciones de la box automaticamente
# Mejor actualizar manualmente que en un posible descuido
config.vm.box_check_update = false
# Asociamos el acceso a la VM a través de 127.0.0.1, asociando el puerto 5000
# del anfitrion al puerto 5000 de la VM.
config.vm.network "forwarded_port", guest: 5000, host: 5000, host_ip: "127.0.0.1"
# Localmente he usado virtualbox
config.vm.provider "virtualbox" do |vb|
# Configuramos el nombre que queremos que tenga la VM dentro de virtualbox
# para que no nos ponga nombres raros vagrant
vb.name = "NotasIV"
# Aparte le definimos 1GB de RAM y 2 nucleos de CPU
vb.memory = "1024"
vb.cpus = 2
end
# Simplemente configuramos ansible y la ruta de nuestro playbook
# para el aprovisionamiento
config.vm.provision "ansible" do |ansible|
ansible.playbook = "provisioning/playbook.yml"
end
end
Note
Como SO base he seleccionado la última versión estable de Ubuntu, la 18.04 LTS, ya que no es muy pesada (307MB) y tiene las últimas actualizaciones de la distribución.
Una vez tenemos este archivo, con la herramienta de construcción simplemente ejecutamos:
$ make vm
Esto lo que hará será crearnos una máquina virtual con los ajustes que hayamos definido en el Vagrantfile
,
pero no aprovisionará la máquina. En concreto, devolverá el siguiente output:
Bringing machine 'NotasIV' up with 'virtualbox' provider...
==> NotasIV: Importing base box 'ubuntu/bionic64'...
==> NotasIV: Matching MAC address for NAT networking...
==> NotasIV: Setting the name of the VM: NotasIV
==> NotasIV: Clearing any previously set network interfaces...
==> NotasIV: Preparing network interfaces based on configuration...
NotasIV: Adapter 1: nat
==> NotasIV: Forwarding ports...
NotasIV: 5000 (guest) => 5000 (host) (adapter 1)
NotasIV: 22 (guest) => 2222 (host) (adapter 1)
==> NotasIV: Running 'pre-boot' VM customizations...
==> NotasIV: Booting VM...
==> NotasIV: Waiting for machine to boot. This may take a few minutes...
NotasIV: SSH address: 127.0.0.1:2222
NotasIV: SSH username: vagrant
NotasIV: SSH auth method: private key
NotasIV: Warning: Remote connection disconnect. Retrying...
NotasIV:
NotasIV: Vagrant insecure key detected. Vagrant will automatically replace
NotasIV: this with a newly generated keypair for better security.
NotasIV:
NotasIV: Inserting generated public key within guest...
NotasIV: Removing insecure key from the guest if it's present...
NotasIV: Key inserted! Disconnecting and reconnecting using new SSH key...
==> NotasIV: Machine booted and ready!
==> NotasIV: Checking for guest additions in VM...
NotasIV: Guest Additions Version: 5.2.34
NotasIV: VirtualBox Version: 6.0
==> NotasIV: Mounting shared folders...
NotasIV: /vagrant => /home/angel/GitHub/NotasIV
Para acceder a ella, podemos hacerlo con el siguiente comando:
$ vagrant ssh
Esto funciona porque cuando vagrant crea nuestra máquina, también crea un usuario llamado vagrant
, generando un
par de llaves SSH e insertando la pública en la máquina virtual y la privada en la ruta .vagrant/machines/NotasIV/virtualbox/private_key
,
que es de donde la obtiene a la hora de hacer ssh. De hecho el proceso de creación del par de llaves y la inserción de la pública se muestra
en parte de la salida de cuando levantamos la máquina:
NotasIV: Vagrant insecure key detected. Vagrant will automatically replace
NotasIV: this with a newly generated keypair for better security.
NotasIV:
NotasIV: Inserting generated public key within guest...
NotasIV: Removing insecure key from the guest if it's present...
NotasIV: Key inserted! Disconnecting and reconnecting using new SSH key...
Esto lo vamos a modificar en el aprovisionamiento, creando un usuario dentro de la máquina y asociandole el par de llaves que nosotros queramos.
Aprovisionamiento¶
Como se ha dicho anteriormente, para aprovisionar la máquina se ha usado ansible, y para decirle qué debe aprovisionar sobre la máquina concretamente
he creado un archivo playbook.yml
en el directorio provisioning
, que contiene lo siguiente:
---
# Como Vagrant nos crea un inventario, aqui podemos poner directamente el nombre que le dimos a la VM.
- hosts: NotasIV
tasks:
# Primero con apt vamos a varias dependencias, como pip, make y npm para usar pm2
- name: Instalar dependencias
become: true
apt:
name:
- git
- python3-pip
- nodejs
- npm
- make
state: present
# Esto ejecuta sudo apt update antes de instalar las dependencias, necesario
# para que encuentre el paquete python3-pip
update_cache: true
# Una vez tenemos npm ahora instalamos pm2 de forma global en el equipo para que
# cualquier usuario que creemos tenga acceso.
- name: Instalar pm2 globalmente
become: true
npm:
name: pm2
global: yes
# Instalamos pipenv para tener las dependencias del proyecto aisladas del resto
# de la VM
- name: Instalar pipenv
pip:
name: pipenv
# Me creo un usuario angel con una shell de bash. Por defecto le crea un home, no hace
# falta especificarselo
- name: Crear usuario angel
become: true
user:
name: angel
shell: /bin/bash
# Como queremos configurar este usuario por ssh para acceder a él desde el anfitrion,
# le mandamos la clave pública que queremos tener autorizada para ese usuario,
# especificandole la tura en el anfitrion
- name: Agregar clave publica para el usuario angel
become: true
authorized_key:
user: angel
state: present
key: "{{ lookup('file', '/home/angel/.ssh/id_rsa.pub') }}"
Note
Un detalle importante es que, como explico en el propio playbook al principio, Vagrant nos crea un inventario para ansible
en .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
con las maquinas que hayamos definido en el Vagrantfile
.
Como se definió una máquina de nombre NotasIV, podemos ponerla directamente en la clave hosts. Si tuvieramos mas máquinas podriamos
agruparlas en un grupo y especificar ese grupo, o simplemente usar el keyword all para ejecutar las tasks del playbook sobre todas las maquinas definidas
en el Vagrantfile. Si estuvieramos usando el comando ansible-playbook en lugar de vagrant, el inventario por defecto estaría en /etc/ansible/hosts
.
Una vez tenemos todo listo para aprovisionar la máquina, ejecutamos lo siguiente:
$ make provision
Lo cual generará un output como el siguiente al ejecutarlo por primera vez:
NotasIV: Running ansible-playbook...
PLAY [NotasIV] *****************************************************************
TASK [Gathering Facts] *********************************************************
ok: [NotasIV]
TASK [Instalar dependencias] ***************************************************
changed: [NotasIV]
TASK [Instalar pm2 globalmente] ************************************************
changed: [NotasIV]
TASK [Instalar pipenv] *********************************************************
changed: [NotasIV]
TASK [Crear usuario angel] *****************************************************
changed: [NotasIV]
TASK [Agregar clave publica para el usuario angel] *****************************
changed: [NotasIV]
PLAY RECAP *********************************************************************
NotasIV : ok=6 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Las tareas marcadas con changed viene a decir que esa tarea se ha realizao y ha cambiado el estado de la máquina. Si por el contrario pusiera ok, significaría que esa tarea ya ha sido ejecutada y tenemos el sistema con el estado requerido para esa tarea, por lo que no es necesario ejecutarla.
Veamos a grandes rasgos qué hace nuestro playbook:
- Usando el modulo apt de ansible, instala y actualiza las dependencias necesarias para crear el entorno necesario para ejecutar la app.
- Usando los modulos npm y pip, instalamos pm2 y pipenv, necesarias para tener control sobre la ejecución de nuestra app y las librerías necesarias.
- Creamos un usuario llamado angel con el módulo user, asignándole un shell de bash en lugar de sh que es el que viene por defecto.
- Al usuario le asignamos la llave pública del par que vamos a usar para conectarnos a la máquina con ese usuario.
Para conectarnos con ssh a la máquina usando el usuario angel que hemos creado en el aprovisionamiento, debemos hacerlo con el comando ssh en lugar de vagrant ssh. Como vagrant asocia el puerto 2222 a ssh en la máquina y además tiene asociado 127.0.0.1 como IP de acceso, tan solo debemos ejecutar:
$ ssh angel@localhost -p 2222
Note
Suponemos que tenemos la llave privada asociada a ese usuario en ~/.ssh
en nuestro anfitrión, de lo contrario deberiamos de especificarselo
al comando ssh con la opción -i.
Vagrant Cloud¶
Para subir mi box a Vagrant Cloud y que cualquiera pueda usarla simplemente nos creamos una cuenta y creamos un nuevo repositorio (realmente es muy parecido a Docker Hub). Una vez lo hayamos hecho, primero debemos exportar nuestra máquina. Para ello ejecutamos:
$ vagrant package --output NotasIV
Esto nos exportará la máquina en formato .box, y en nuestro repositorio especificaremos una versión y un proveedor, como en nuestro caso ha sido virtualbox pues lo seleccionamos y subimos la máquina.
Hint
La box se puede encontrar aquí