0% encontró este documento útil (0 votos)
117 vistas83 páginas

Arquitecturas Cloud para TIC

El documento describe varias arquitecturas de software modernas para aplicaciones en la nube, incluyendo arquitecturas en capas, dirigidas por eventos, de microservicios y basadas en espacios. Explica que estas arquitecturas buscan escalabilidad y despliegue de componentes de forma independiente para permitir el desarrollo ágil.

Cargado por

solrac1308
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Temas abordados

  • cloud computing,
  • OpenShift,
  • sistemas de mensajería,
  • servicios en la nube,
  • arquitectura basada en microke…,
  • patrones de arquitectura,
  • contenedores LXC,
  • sistemas de descubrimiento,
  • metodologías ágiles,
  • Ansible
0% encontró este documento útil (0 votos)
117 vistas83 páginas

Arquitecturas Cloud para TIC

El documento describe varias arquitecturas de software modernas para aplicaciones en la nube, incluyendo arquitecturas en capas, dirigidas por eventos, de microservicios y basadas en espacios. Explica que estas arquitecturas buscan escalabilidad y despliegue de componentes de forma independiente para permitir el desarrollo ágil.

Cargado por

solrac1308
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Temas abordados

  • cloud computing,
  • OpenShift,
  • sistemas de mensajería,
  • servicios en la nube,
  • arquitectura basada en microke…,
  • patrones de arquitectura,
  • contenedores LXC,
  • sistemas de descubrimiento,
  • metodologías ágiles,
  • Ansible

Arquitecturas software para la nube

Objetivos

De la asignatura
● Conocer los conceptos relacionados con el proceso de virtualización tanto de
software como de hardware y ponerlos en práctica.
● Justificar la necesidad de procesamiento virtual frente a real en el contexto de
una infraestructura TIC de una organización.
● Optimizar aplicaciones sobre plataformas virtuales.
● Conocer diferentes tecnologías relacionadas con la virtualización
(Computación Nube, Utility Computing, Software as a Service) e
implementaciones tales como Google AppSpot, OpenShift o Heroku.

Específicos
1. Comprender los ecosistemas de despliegue de aplicaciones modernos y su
relación con la nube.
2. Entender las diferentes arquitecturas de software modernas y su relación con
cloud computing​.
3. Aprender diseños que se puedan usar más adelante en aplicaciones
desplegables en la nube.

Introducción
Puedes echarle un vistazo a ​esta colección de transpas​ con información de un par
de presentaciones sobre arquitectura moderna de aplicaciones. Aunque centrado en
microservicios, ​este conjunto de patrones​ menciona también algunos de los que
veremos aquí.

El panorama informático del siglo XXI está lleno de posibilidades: múltiples lugares
donde ejecutar una aplicación, múltiples servicios que se pueden usar desde ella,
todos conectados a través de Internet, y metodologías específicas para desarrollar
aplicaciones. Se hace difícil concretar en una arquitectura específica, entendiendo
por arquitectura una visión de bloques de los diferentes componentes de una
aplicación, su situación física y lógica y la relación entre ellos. Resulta bastante
claro, sin embargo, que una arquitectura monolítica con un cliente ejecutando el
interfaz de usuario y un servidor, replicado o no pero ejecutando todos los
componentes de una aplicación, no resulta adecuada para las aplicaciones
modernas ni por sus características de escalado ni por el reparto de tareas y datos
entre diferentes partes de un equipo de desarrollo.

En este tema introductorio a la asignatura veremos diferentes arquitecturas de


software con ejemplos concretos, ejemplos que se podrán usar, siquiera de una
forma básica, para elegir una aplicación para la cual crear la arquitectura virtual.

Arquitecturas software
En gran parte, este apartado está sacado de ​Software Architecture Patterns, de
Mark Richards​, un ​ebook​ gratuito que te puedes descargar dando tu email, lo que
aconsejo vivamente que hagas.

En general, las arquitecturas software modernas tienen como objetivo común el


buscar la consistencia en la velocidad de respuesta al usuario. Otros elementos
comunes son el uso de recursos propios combinados con recursos de diferentes
empresas o ​vendors​, y que se basan en unas metodologías de desarrollo ágil e
integración y despliegue continuo. Veremos qué arquitecturas son las más comunes
en este contexto.

Arquitectura en capas
La ​arquitectura en capas​ o multilayer es el desarrollo natural de la arquitectura
cliente-servidor, creando 3 o más capas entre las que se suele incluir la capa de
presentación, la de aplicación, la de lógica de negocio y la de acceso a datos.
Muchos marcos de desarrollo modernos trabajan de esta forma y son relativamente
simples de desarrollar. El problema principal es que sólo permiten escalado dentro
de cada una de las capas, siendo al final alguna de ellas un cuello de botella.

Arquitectura dirigida por eventos


Este tipo de arquitectura​ representa un cambio fundamental con respecto a la
arquitectura tradicional. El elemento fundamental de esta arquitectura es la cola de
eventos, que se originan en el usuario, pero también de una parte a otra de la
arquitectura. A través de una cola de eventos, diferentes procesadores de eventos
van extrayendo eventos de la cola y procesándolos de forma asíncrona. El hecho de
que pueda haber una cantidad de procesadores de eventos indeterminada y que
puedan estar en cualquier sitio (nube privada o pública) hace que sea una
arquitectura que escala de forma fácil, aunque haya que monitorizar todo el sistema
de forma que se pueda recuperar fácilmente en caso de fallo. También permite
desplegar o rearrancar cada uno de los procesadores de forma independiente.

El principal problema es que es más difícil de testear y también su desarrollo se


hace un poco más complicado que en el caso anterior.

Algunos marcos de aplicaciones como Java Swing usan este tipo de arquitectura.

Arquitectura microkernel
Se trata de ​una arquitectura​ más o menos monolítica, con un núcleo central al que
se pueden añadir funcionalidades mediante plugins. Un tipo de arquitectura clásico
que se usa en CMSs como Joomla o en sistemas de aprendizaje como Moodle. El
problema principal es la escalabilidad, ya que el núcleo puede representar un cuello
de botella.

Arquitectura basada en microservicios.


Una de las arquitecturas más populares hoy en día, los ​microservicios​ se
caracterizan por poder usar tecnologías subyacentes que van desde la virtualización
completa en la nube hasta el uso de contenedores Docker en una sola máquina
virtual.

Lo principal en una arquitectura de microservicios es que se trata de unidades que


se van a desplegar de forma independiente, diferentes servicios que trabajarán de
forma totalmente independiente unos de otros. Un servicio es, en sí, un servicio tal
como una base de datos o puede ser un objeto o grupo de objetos que ofrezca
alguna funcionalidad que se pueda separar claramente del resto de la aplicación.

Dentro de este tipo de arquitectura, el núcleo de la aplicación puede ser un simple


API basado en REST, pero es mucho más común que sea un sistema con
mensajería centralizada, donde un sistema de paso de mensajes recibe peticiones
de servicio y las sirve a quienes lo han hecho de forma asíncrona. En este sentido,
si los servicios están separados y consideramos que los mensajes son eventos, la
arquitectura sería similar a la basada en eventos anterior, salvo que esta
arquitectura suele estar más acoplada que la basada en eventos.

Como la anterior, tiene la ventaja de poder desplegarse de forma independiente,


poder escalar de forma independiente y, por tanto, constituye uno de los patrones
de arquitectura más populares hoy en día.

Arquitectura basada en ​espacios


Entre todas las arquitecturas vistas, ​esta​ es la más antigua, usándose en
arquitecturas distribuidas casi desde principio de los 90, con lenguajes como Linda
introduciendo el concepto de ​espacio de tuplas​.

En esta arquitectura, un ​espacio​ es compartido por una serie de unidades de


procesamiento, que se comunican entre sí principalmente a través de ese espacio.
A diferencia de la arquitectura basada en eventos, ese espacio está
desestructurado, aunque internamente tiene un espacio de mensajería tal como las
arquitecturas mencionadas anteriormente. Sin embargo, es un ​patrón​ con una
implementación relativamente simple que puede servir para aplicaciones a pequeña
escala, aunque es más complicado de desarrollar que otros.

Resumen
Todos los patrones anteriores se usan en la actualidad junto con el patrón de
aplicación cliente-servidor, que sigue perviviendo, pero que causa todo tipo de
problemas de escalado, sobre todo, aparte de la dificultad de desarrollo si no se
hace con cuidado.

Todas estas arquitecturas, además, asumen un entorno de integración y despliegue


continuo, que a su vez necesita una metodología de desarrollo basada en tests, tal
como la que se verá más adelante.

Lo principal, en todo caso, es buscar la arquitectura más adecuada para una


aplicación en vez de aceptar como un hecho una arquitectura cliente servidor o,
peor, una arquitectura monolítica de un solo fichero que tiene toda la funcionalidad
de una aplicación. Una aplicación casi siempre va a necesitar usar APIs de otras,
siquiera para la autenticación, y decenas o posiblemente cientos de servicios
diferentes. Cómo se lleva a la práctica esto y qué tipo de implicaciones tiene en las
herramientas las veremos a continuación.

Buscar una aplicación de ejemplo, preferiblemente propia, y deducir qué patrón es el


que usa. ¿Qué habría que hacer para evolucionar a un patrón tipo microservicios?

Desplegando en la nube: algunas consideraciones


Casi todas las arquitecturas mencionadas anteriormente tienen características
comunes. Primero, usan sistemas de mensajería, interna o externamente. Segundo,
usan algún tipo de API, generalmente basada en REST. Tercero, están basadas en
diferentes componentes. Y, por último, usan y/o despliegan servicios en la nube. A
su vez, esto implica una serie de cosas que veremos a continuación.
Descubrimiento de servicios
Una de las características que a veces no se consideran ​a priori​ de estas
arquitecturas es su naturaleza dinámica. El hecho de que se añadan nuevos
proveedores, procesadores o microservicios implica que estos nuevos servicios
tienen que conocer la configuración del resto de la nube para coordinarse
correctamente y también para que los sistemas de escalado automático sepan con
qué nodos cuentan para ello. Estos sistemas de descubrimiento con, en una primera
aproximación, unos ficheros de configuración dinámicos y distribuidos.

En casi todos los casos en que se despliegue una arquitectura en la nube se tendrá
que hacer uso de este tipo de sistemas. Muchos ​orquestadores​ como Kubernetes
tienen los suyos propios, pero familiarizarse con otros como e​ tcd​ o c​ onsul​ puede
ayudar a entender su funcionamiento y, sobre todo, a usarlo en nuestras propias
arquitecturas que no usen orquestadores.

Hay muchos ​patrones de uso de estos sistemas de descubrimiento de servicios​,


pero en general se trata de tener, cuando se lanza un nuevo proveedor, un URL que
proporcione información y un cliente que sea capaz de procesarla.

Almacenes de datos: más allá del SQL


Aunque las bases de datos tradicionales, relacionales y basadas en SQL se adaptan
perfectamente a la nube, necesitan que se conozca de antemano la estructura de la
información y sus relaciones, se adaptan relativamente mal a trabajar con texto
desestructurado, y a menos que todos los datos almacenados tengan la misma
estructura, se desperdicia una cantidad considerable de espacio. Por eso en estas
arquitecturas modernas están complementadas por las bases de datos NoSQL, de
las que lo primero que hay que aprender es que no se trata de sistemas que usen
un lenguaje llamado ​NoSQL​ (aunque algunos lo están tratando de diseñar) sino de
base de datos muy diversas y que usan lenguajes propios o empotrados para
acceder a ellas. Hay diferentes tipos de almacenes de datos: clave-valor, basadas
en documentos, orientadas a columna o a grafos. Una aplicación moderna usará
una, o varias, desde MongoDB hasta Redis pasando por Cassandra o Elastic.

En algunos casos también se pueden usar los sistemas de descubrimiento


mencionados anteriormente, que esencialmente son almacenes clave-valor.

Lenguajes de programación: programación políglota


Nada fuerza a que todos los servicios o componentes tengan que usar el mismo
lenguaje. Los interfaces son genéricos, generalmente REST u otro tipo de servicios
web, pero si no lo son, hay herramientas como ​Thrift​ que permiten definir de forma
genérica APIs y compilarlas para un lenguaje determinado. Casi todas las
aplicaciones van a usar JavaScript o alguna de sus variantes (CoffeeScript,
TypeScript) en el interfaz de usuario, pero además habrá componentes que sea
mejor escribir en Scala y otros que sea mejor escribir en Ruby o en Perl. También
los servicios de despliegue y de construcción tendrán su propio lenguaje, Groovy
(en Gradle) o Python. En general, trabajar con un solo lenguaje limita mucho el tipo
de aplicaciones que puedes hacer, aunque si hay uno que pueda abarcarlo casi
todo, ese es JavaScript.

De aquí a allí, todo son nubes.


La nube, que parece algo genérico y que a veces se abstrae simplemente en
máquinas virtuales que se pagan por uso, son un panorama mucho más complejo,
que va desde servicios de mensajería hasta contenedores, pasando por todo tipo de
servicios en la nube: almacenamiento, redes virtuales, conversión de ficheros y todo
tipo de cosas. Los tres niveles clásicos de Infraestructura, Plataforma y Software
como servicio se combinan en una sola aplicación que puede usar una base de
datos (SaaS) o almacén de datos (almacenamiento como servicio) junto con un
sistema de mensajería almacenado en un PaaS y una serie de procesadores
almacenados en máquinas virtuales instanciadas en diferentes proveedores. Por
tanto, distinguir aplicaciones ​para PaaS​ de aplicaciones ​para IaaS​ es totalmente
absurdo y cualquier arquitectura sana incluirá un poco de cada una, a gusto del
arquitecto.

Los mensajeros están en las nubes


La mayoría de estos sistemas usan mensajería para comunicarse, o para estar al
corriente de eventos, o para gestionar toda la arquitectura. Este tipo de ​colas de
mensajes​ son quizás las que diferencian más a estas arquitecturas de las
monolíticas, que no las necesitan. Estos sistemas, también llamados buzones,
implementan unos sistemas de comunicación asíncrona y también sistemas de
publicación/suscripción, de forma que servicios pueden decidir a qué mensajes
contestan o de qué tipo de eventos van a recibir comunicación. Las arquitecturas
serverless​, un tipo de arquitectura basada en microservicios, tienen estos buzones
en su columna vertebral, usándose para transmitir de un servicio a otro los datos y
también para activar los microservicios cuando suceda un evento determinado.

Hay una serie de estándares de mensajería, pero muchas de las implementaciones


existentes se acogen a varios para que sea más simple la interoperabilidad. Y
aparte de las ​implementaciones libres​ que uno puede usar dentro de su
arquitectura, los proveedores de cloud tienen sus propias implementaciones tales
como ​Amazon SQS​ o ​Firebase de Google​.

Resumiendo: hay todo un mundo ahí fuera


Y es un mundo complejo, pero cuyo conocimiento os acercará mucho más a la
creación de aplicaciones sólidas, eficientes y escalables. Todo lo de arriba se
resume en que hay que continuar aprendiendo lenguajes, tecnologías, metodologías
y arquitecturas. Y no parar de hacerlo, para la nube o para lo que venga más
adelante.

En la aplicación que se ha usado como ejemplo en el ejercicio anterior, ¿podría usar


diferentes lenguajes? ¿Qué almacenes de datos serían los más convenientes?

Bibliografía y otros recursos


Algunos recursos a los que puedes acceder desde la ​Biblioteca de la UGR​:

● Pattern-oriented software architecture for dummies​, aunque es muy básico,


por lo menos explica en qué se pueden usar los patrones de software y cómo
aplicarlos en un momento determinado.
● Practical software architecture​ Una visión más práctica de diferentes
arquitecturas de software.
● Building microservices​ un recurso electrónico que explica cómo construir
microservicios desde cero, la fase de modelización a la de construcción del
mismo.

A dónde ir desde aquí


Este tema sirve como introducción para elegir el proyecto que se va a desarrollar
durante la asignatura. Habrá que realizar estos ejercicios para superar el ​primer hito
del proyecto​.

A continuación se puede echar un vistazo a los ​PaaS​, plataformas como servicio​,


que pueden resultar útiles en la misma. En este tema donde se explica cómo se
pueden desplegar aplicaciones para prototipo o para producción de forma
relativamente simple, o bien al tema dedicado a los ​contenedores​, que es el
siguiente en el temario de la asignatura.
Provisionamiento en infraestructuras
virtuales

Objetivos de la asignatura
● Diseñar, construir y analizar las prestaciones de un centro de proceso de
datos virtual.
● Documentar y mantener una plataforma virtual.
● Realizar tareas de administración en infraestructura virtual.

Objetivos específicos
1. Aprender lenguajes de configuración usados en infraestructuras virtuales.
2. Saber cómo aplicarlos en un caso determinado.
3. Conocer los sistemas de gestión de la configuración, provisionamiento y
monitorización más usados hoy en día.

Introducción
Los ​gestores de configuraciones​ trabajan sobre máquinas virtuales ya creadas y en
funcionamiento, describiendo la configuración que necesitan para ejecutar una
aplicación determinada. Proporcionan ​infraestructura como código​, permitiendo
describir los sistemas en los que se va a ejecutar código como si de un programa se
tratara, con todo lo que ello conlleva: control de código y testeo, por ejemplo.

Las ​recetas​ usadas por los gestores de configuraciones se ejecutan directamente si


ya tenemos tal máquina provisionada con al menos un sistema operativo, pero es
más eficiente e independiente de la configuración específica trabajar con ellos
desde programas de orquestación de máquinas virtuales como ​Vagrant

Hay muchos gestores de configuraciones: ​Chef​, ​Salt​, Ansible, ​mgmt​ (este último un
poco experimental) y Puppet, por ejemplo. Todos son libres, pero ​tienen
características específicas​ que hay que tener en cuenta a la hora de elegir uno u
otro. En el caso específico de ​sistemas operativos​, que es del que vamos a hablar
aquí, se trata de gestionar automáticamente todas las tareas de configuración de un
sistema, automatizando la edición de ficheros de configuración, instalación de
software y configuración del mismo, creación de usuarios y autenticación, de forma
que se pueda hacer de forma automática y masiva.

Para poder configurar máquinas virtuales específicas primero hay que instanciarlas.
Dejaremos para más adelante cómo hacerlo de forma independiente del proveedor
de tales servicios y veremos a continuación cómo preparar una máquina virtual para
ser provisionada.

En general, estas herramientas suelen usar un ​lenguaje de dominio específico​, con


una pequeña cantidad de órdenes relacionada con lo que hacen: establecer una
configuración determinada en un servidor. Este lenguaje puede consistir en
versiones reducidas de lenguajes como Ruby o en conjuntos de diccionarios
específicos de JSON o de YAML, dos lenguajes de serialización de estructuras de
datos.

Generalmente, también, se trata de ​lenguajes declarativos​ en el sentido que suelen


especificar la configuración que se quiere alcanzar, no qué acciones hay que llevar
a cabo para alcanzar esa configuración; la propia lógica de la herramienta suele
encargarse de ello. Por esa misma razón se usa el concepto de ​idempotencia​ si se
aplica dos veces una configuración, el resultado es el mismo.

En cualquier caso, la configuración más simple que se puede hacer es usar un


lenguaje de scripting como ​bash​ o algún otro que se sepa que va a estar instalado
en el ordenador a configurar; Perl y Python suelen ser buenos candidatos porque
están instalados en casi todas las configuraciones base. El problema que tiene,
sobre todo el primero, es la falta de idempotencia, la mala gestión de los errores, y,
sobre todo, la no idempotencia.

Configurando una instancia


Se puede crear una máquina virtual de muchas formas, usando el interfaz web o el
de línea de órdenes del proveedor de servicios correspondiente. Consulta el manual
de la misma sobre cómo hacerlo. Sin embargo, si quieres hacer el provisionamiento
desde ​fuera​, no puedes usar una imagen cualquiera. Esta imagen tendrá que tener:

● Un servidor ​ssh​ activado. Casi todas las imágenes de sistemas operativos en


los servicios de nube lo tienen, pero a menudo también hay que activar los
puertos correspondientes (llamados ​endpoints​). Esto es imprescindible para
la conexión.
● Algún lenguaje de programación, el que sea necesario para el sistema de
provisionamiento que se use. Normalmente se trata de Python (Ansible) o
Perl (Rex), pero puede ser también otro lenguaje de ​scripting​ como Ruby.
● A veces hace falta instalar algún paquete adicional. Por ejemplo ​python-apt​ si
quieres usar directamente las órdenes correspondientes de Ansible en vez de
ejecutar otras órdenes. Chef tiene que estar instalado en el destino o ​target​,
por lo que en muchos casos los servicios de nube ofrecen imágenes con él
ya instalado.

Cada imagen suele tener un usuario con privilegios de administrador configurado.


Consultar en cada lugar cuál es, pero una vez hecho eso, el siguiente paso en el
provisionamiento es crear un par de clave pública/privada y copiar la pública al
objetivo. A partir de ahí ya se puede provisionar, empezando por Chef.

Usando Chef para provisionamiento


Chef​ es una herramienta que, en general, se usa en un servidor que se encarga no
sólo de gestionar la configuración, sino también las versiones. Empezar a usarlo ​es
complicado​. Sin embargo, como introducción a la gestión de configuraciones se
puede usar c​ hef-solo​, una versión aislada que permite trabajar en una máquina
desde la misma y que, por tanto, se puede usar como introducción y para probar
configuraciones.

Hay varios tutoriales que te permiten, con relativa rapidez, comenzar a trabajar con
Chef-solo en un servidor; ​este te proporciona una serie de ficheros que puedes usar
y ​este otro es más directo, dando una serie de órdenes​. En todo caso, se trata
básicamente tener acceso a un servidor o máquina virtual, instalar una serie de
aplicaciones en él y ejecutarlas sobre un fichero de configuración

En ​esta página​ indican como descargar Chef para todo tipo de distribuciones.
Vamos a usar principalmente ​chef-solo​, una herramienta que se tiene que ejecutar
desde el ordenador que queramos provisionar.

La forma que se aconseja usar es esta, pero se instala el programa en un lugar no


estándar, `/opt/chefdk/bin`. Habrá que añadirlo al `PATH` o tenerlo en cuenta a la
hora de ejecutarlo.
$ /opt/chefdk/bin/chef-solo --version
Chef: 13.4.19

Esta es la versión actual a fecha de octubre de 2017.

Instalar ​chef-solo​ en la máquina virtual que vayamos a usar


Una ​receta​ de Chef ​consiste en crear una serie de ficheros​: una ​lista de ejecución
que especifica qué es lo que se va a configurar; esta lista se incluye en un fichero
node.json​, o ​recetario​ (​cookbook​) que incluye una serie de ​recetas​ que configuran,
efectivamente, los recursos y, finalmente, un fichero de configuración que dice
dónde están los dos ficheros anteriores y cualquier otro recursos que haga falta.
Estos últimos dos ficheros están escritos en Ruby. La estructura de directorios se
puede generar ​directamente en las últimas versiones​ con

chef generate app first_cookbook

Vamos a empezar a escribir una recetilla del Chef. Generalmente, ​escribir una
receta es algo más complicado​, pero comenzaremos por una receta muy simple que
instale el imprescindible e​ macs​ y le asigne un nombre al nodo. Creamos el directorio
chef​ en algún sitio conveniente y dentro de ese directorio irán diferentes ficheros.

El fichero que contendrá efectivamente la receta se llamará ​default.rb

package 'emacs'
directory '/home/jmerelo/Documentos'
file "/home/jmerelo/Documentos/LEEME" do
owner "jmerelo"
group "jmerelo"
mode 00544
action :create
content "Directorio para documentos diversos"
end

El nombre del fichero indica que se trata de la receta por omisión, pero el nombre de
la receta viene determinado por el directorio en el que se meta, que podemos crear
de un tirón con

mkdir -p chef/cookbooks/emacs/recipes

Este fichero tiene tres partes: instala el paquete ​emacs​, crea un directorio para
documentos y dentro de él un fichero que explica, por si hubiera duda, de qué se
trata. Evidentemente, tanto caminos como nombres de usuario se deben cambiar a
los correspondientes en la máquina virtual que estemos configurando.

El siguiente fichero, ​node.json​, incluirá una referencia a esta receta


{
"run_list": [ "recipe[emacs]" ]
}

Este fichero hace referencia a un recetario, ​emacs​ y dado que no se especifica nada
más se ejecutará la receta por defecto.

Finalmente, el ​fichero de configuración ​solo.rb​ incluirá referencias a ambos.

file_cache_path "/home/jmerelo/chef"
cookbook_path "/home/jmerelo/chef/cookbooks"
json_attribs "/home/jmerelo/chef/node.json"

Una vez más, ​cambiando los caminos por los que correspondan​. Para ejecutarlo,

sudo chef-solo -c chef/solo.rb

(si se ejecuta desde el directorio raíz). Esta orden producirá una serie de mensajes
para cada una de las órdenes y, si todo va bien, tendremos este útil editor instalado.

Crear una receta para instalar ​nginx​, tu editor favorito y algún directorio y fichero que
uses de forma habitual.

Para usar ​chef-solo​ hay simplemente que instalar unos cuantos programas, pero en
gran parte ya está automatizado: ​aquí explica como usarlo en Ubuntu​, por ejemplo
basándose en ​este Gist (programas cortos en GitHub)​ que instala todas las
herramientas necesarias para comenzar a ejecutar chef.

Este ​curso en vídeo​ te enseña también a trabajar con Chef, aunque con la edad que
tiene es posible que esté un poco obsoleto.

De ninguna manera JSON es un lenguaje universal para gestión de configuraciones.


Prácticamente todo el resto de los sistemas de configuración usan ​YAML (​yet
another markup language​)​. Recientemente se ha ​publicado una introducción al tema
que será suficiente para el uso que le vamos a dar más adelante

Escribir en YAML la siguiente estructura de datos en JSON

{ "uno": "dos",
"tres": [ 4, 5, "Seis", { "siete": 8, "nueve": [ 10, 11 ] } ] }
Normalmente estas recetas van a estar bajo control de un sistema de gestión de
fuentes; de esta forma se pueden probar diferentes configuraciones, crear nuevas
versiones de la misma pero, sobre todo, tener claro en cada momento qué
configuración es la que se está ejecutando en producción, que será habitualmente la
que esté en una rama designada de la misma.

Gestionando configuraciones con Salt


Salt​ es otra herramienta de configuración que se ha hecho popular en los últimos
años. Aunque algo más complicada de configurar que Ansible, tiene como ventaja
que permite modularizar la configuración para poder adaptarla a sistemas de la
forma más conveniente. s​ alt-ssh​ está basado en Python y sólo requiere, en principio,
que este lenguaje esté instalado en el sistema que se va a provisionar.

La mejor forma de empezar es ​instalarlo desde Github​ aunque nosotros vamos a


trabajar con ​salt-ssh​, un sistema que no requiere ningún tipo de instalación en el
ordenador objetivo y que se puede usar, hasta cierto punto, como Ansible. Vamos a
usarlo para ​instalar los servicios necesarios para el bot de Telegram en Scala​ que
hemos visto en alguna otra ocasión.

Una vez instalado ​salt-ssh​, una de los principales problemas es que requiere una
cierta cantidad de configuración global. Generalmente vamos a usar un directorio tal
como ~ ​ /lib/salt/​ como directorio de trabajo y configuración; y este directorio va a
estar fuera del repositorio y en un directorio de superusuario por omisión. Se puede
usar el directorio local, pero en ese caso habrá que decir específicamente donde se
encuentra cada tipo de fichero. En el R ​ EADME.md​ del bot​ vienen las instrucciones
necesarias para crear ese fichero de provisionamiento y la configuración global.

Pero para configurar nuestro propio sistema, uno de los más importantes es el
fichero ​roster​:

app:
host: 104.196.9.185
user: one_user
sudo: True

Aquí no sólo se declara la dirección a la que vamos a conectar, sino también si hace
falta ser sudo o no. Con esto podemos ejecutar ya parte de la configuración que
vamos a ejecutar más adelante:
salt-ssh '*' --roster-file=./roster -r "sudo apt-get install python-apt" -c ~/lib/salt
--force-color

En este caso, se trata de ejecutar una orden para poder instalar ​python-apt​, un
módulo para poder ejecutar órdenes de instalación de paquetes directamente desde
Python.

Pero para efectivamente provisionar se usan ficheros ​salt-stack​. Por ejemplo, ​estos
ficheros instalan Java​ en la versión necesaria para poder instalar más adelante
Scala. Y Scala lo instalamos en ​este fichero

include:
- javasetup

scala:
pkg.installed

Este fichero se ejecutaría con

salt-ssh app state.apply scala --roster-file=./roster -c ~/lib/salt --force-color

Este programa, como Ansible, actúa de manera descriptiva. Indica que el paquete
scala tiene que estar instalado, pero para ello tiene que cumplir una serie de
prerrequisitos incluidos en el fichero j​ avasetup.sls​; el nombre de estado se tiene que
corresponder con el nombre del fichero en sí. Este a su vez requiere la instalación
de otra serie de paquetes, e incluye otro fichero. Lo bueno es que esos dos ficheros,
javasetup​ y j​ ava​, se pueden usar para todos los paquetes que usen esa máquina
virtual; para instalar Scala sólo hay que crear este último fichero.

Todos estos, por cierto, tienen que ejecutarse desde directorios específicos, al
menos la forma más simple de hacerlo es copiar todos los ficheros ​.sls​ a
~/lib/salt/states​ y hacerlo desde ahí.

En general, y por la forma que tiene un poco rígida de hacer las cosas, resulta algo
más complicado usarlo que Ansible o incluso Chef. Sin embargo y limitado a
salt-ssh​, una vez que la configuración está completa y si se tienen configuraciones
base, construir a partir de ellas es relativamente sencillo.

Provisionar una máquina virtual en algún entorno con los que trabajemos
habitualmente usando Salt.
Otros sistemas de gestión de configuración
Las principales alternativas a Chef son ​Ansible​, ​Salt​ y ​Puppet​. Todos ellos se
comparan en ​este artículo​, aunque los principales contendientes son ​Puppet y Chef,
sin que ninguno de los dos sea perfecto​.

De todas ellas, vamos a ​ver Ansible​ que parece ser uno de los que se está
desarrollando con más intensidad últimamente. ​Ansible es​ un sistema de gestión
remota de configuración que permite gestionar simultáneamente miles de sistemas
diferentes. Está basado en YAML para la descripción de los sistemas y escrito en
Python.

Se instala como un módulo de Python, usando por ejemplo la utilidad de instalación


de módulos ​pip​ (que habrá que instalar si no se tiene)

sudo pip install paramiko PyYAML jinja2 httplib2 ansible

sudo​ no hace falta si usas un gestor de entornos como ​pyenv

El resto de las utilidades son también necesarias y en realidad se instalan


automáticamente al instalar ansible. Estas utilidades se tienen que instalar ​en el
anfitrión​, no hace falta instalarlas en el invitado, que lo único que necesitará, en
principio, es tener activada la conexión por ssh y tener una cuenta válida y forma
válida de acceder a ella.

Cada máquina que se añada al control de Ansible se tiene que añadir a un ​fichero,
llamado inventario​, que contiene las diferentes máquinas controladas por el mismo.
Por ejemplo

$ echo "ansible-iv.cloudapp.net" > ~/ansible_hosts

se puede ejecutar desde el ​shell​ para meter (​echo​) una cadena con una dirección
(en este caso, una máquina virtual de Azure) en el fichero ​ansible_hosts​ situado en
mi directorio raíz. El lugar de ese fichero es arbitrario, por lo que habrá que avisar a
Ansible donde está usando una variable de entorno:

export ANSIBLE_HOSTS=~/ansible_hosts

Y, con un nodo, ya se puede comprobar si Ansible funciona con


$ ansible all -u jjmerelo -m ping

Esta orden hace un ​ping​, es decir, simplemente comprueba si la máquina es


accesible desde la máquina local. ​-u​ incluye el nombre del usuario (si es diferente
del de la máquina local); habrá que añadir -​ -ask-pass​ si no se ha configurado la
máquina remota para poder acceder a ella sin clave.

De forma básica, lo que hace Ansible es simplemente ejecutar comandos de forma


remota y simultáneamente. Para hacerlo, podemos usar el ​inventario para agrupar
los servidores​, por ejemplo

[azure]
iv-ansible.cloudapp.net

crearía un grupo ​azure​ (con un solo ordenador), en el cual podemos ejecutar


comandos de forma remota

$ ansible azure -u jjmerelo -a df

nos mostraría en todas las maquinas de azure la organización del sistema de


ficheros (que es lo que hace el comando ​df​). Una vez más, ​-u​ es opcional.

Esta orden usa un ​módulo​ de ansible y se puede ejecutar también de esta forma:

$ ansible azure -m shell ls

haciendo uso del módulo ​shell​. Hay muchos ​más módulos​ a los que se le pueden
enviar comandos del tipo "variable = valor". Por ejemplo, se puede trabajar con
servidores web o ​copiar ficheros​ o ​incluso desplegar aplicaciones directamente
usando el módulo ​git

Desplegar los fuentes de una aplicación cualquiera, propia o libre, que se encuentre
en un servidor git público en la máquina virtual Azure (o una máquina virtual local)
usando a​ nsible​.

Finalmente, el concepto similar a las recetas de Chef en Ansible son los ​playbooks​,
ficheros en YAML que le dicen a la máquina virtual qué es lo que hay que instalar en
tareas​, de la forma siguiente

---
- hosts: azure
sudo: yes
tasks:
- name: Update emacs
apt: pkg=emacs state=present

Esto se guarda en un fichero y se ​le llama, por ejemplo, emacs.yml​, y se ejecuta con

ansible-playbook ../../ejemplos/ansible/emacs.yml

(recordando siempre el temita del nombre de usuario), lo que dará, si todo ha ido
bien, un resultado como el siguiente

En el fichero YAML lo que se está expresando es un array asociativo con las claves
​ udo​ y t​ asks​. En el primero ponemos el bloque de servidores en el que vamos
hosts​, s
a actuar, en el segundo si hace falta hacer sudo o no y en el tercero las tareas que
vamos a ejecutar, en este caso una sola. El apartado de tareas es un vector de
hashes, cada uno de los cuales tiene en n ​ ame​ el nombre de la tarea, a título
informativo y en las otras claves lo que se va a hacer; a ​ pt​ indicará que hay que
instalar un paquete (​pkg​) llamado e​ macs​ y que hay que comprobar si está presente o
no (​state​). El que se trabaje con​estados​ y no de forma imperativa hace que los
playbooks​ sean ​idempotentes​, es decir, si se ejecutan varias veces darán el mismo
resultado que si se ejecutan una sola vez.

1. Desplegar la aplicación que se haya usado anteriormente con todos los


módulos necesarios usando un ​playbook​ de Ansible.

Bibliografía
Uno de los libros más interesantes es ​Infrastructure as Code, de Kief Morris​. Hace
un resumen muy acertado de todos los principios que rigen la gestión de
infraestructura virtual a través de código y un repaso de todas las herramientas
usadas en el mismo. También está disponible como ​recurso electrónico​ en la
biblioteca de la ​UGR​.

A donde ir desde aquí


Para empezar, hay que hacer ​el hito del proyecto correspondiente​.

Desde aquí puedes ir directamente al tema de ​orquestación de máquinas virtuales​,


donde se aprenderá a trabajar con configuraciones más complejas que usen varias
máquinas virtuales a la vez, pero en la asignatura continuaremos con ​la
automatización de tareas en la nube​ para aprender como trabajar con diferentes
proveedores cloud usando sus propias herramientas.

A partir de aquí se puede seguir aprendiendo sobre DevOps en ​el blog​ o ​en IBM​.
Libros como ​DevOps for Developers​ pueden ser también de ayuda. Esta
comparativa de sistemas de configuración​ te permite ver todos los que hay, ver la
última columna cuáles son los más recientemente actualizados y qué esperar de
cada uno de ellos. También en este ​gist explica las diferencias entre herramientas
en este área​, incluyendo también Puppet e incluso Docker. En ​presentaciones como
esta se habla de CAPS: Chef, Ansible, Puppet and Salt​ como una categoría en sí.
En ​este artículo​ también los comparan y en ​este último​ llevan a cabo la
configuración de un servidor simple usando los cuatro.

Automatización de tareas en la nube

Objetivos
1. Conocer las diferentes tecnologías y herramientas de virtualización tanto para
procesamiento, comunicación y almacenamiento.
2. Diseñar, construir y analizar las prestaciones de un centro de proceso de
datos virtual.
3. Documentar y mantener una plataforma virtual.
4. Realizar tareas de administración de infraestructuras virtuales.

Introducción
El objetivo de las plataformas de virtualización es, eventualmente, crear y gestionar
una máquina virtual completa que funcione de forma aislada del resto del sistema y
que permita trabajar con sistemas virtualizados de forma flexible, escalable y
adaptada a cualquier objetivo. Eventualmente, el objetivo de este este tema es
aprender a crear ​infraestructura como servicio tal como vimos en el primer tema​.
Para ello necesitamos configurar una serie de infraestructuras virtuales,
especialmente​almacenamiento como se vio en el tema anterior​.

Los programas que permiten crear infraestructuras virtuales completas se


denominan ​hipervisores​. Un hipervisor permite manejar las diferentes
infraestructuras desde línea de órdenes o mediante un programa, y a su vez se
habla de dos tipos de hipervisores: los de ​tipo I​ o ​bare metal​ que se ejecutan
directamente sobre el hardware (es decir, el sistema arranca con ellos) y los de ​tipo
II​ o ​alojados​, que se ejecutan dentro de un sistema operativo tradicional como un
módulo del núcleo o simplemente un programa. En muchos casos la diferencia no
está clara, porque hay hipervisores que son distribuciones de un sistema operativo
con módulos determinados y por lo tanto de Tipo II (si consideramos el módulo) o de
Tipo I (si consideramos el sistema operativo completo), y en todo caso la distinción
es más académica que funcional; en la práctica, en la mayoría de los casos nos
vamos a encontrar con hipervisores alojados que se ejecutan desde un sistema
operativo.

Para apoyar la virtualización, casi todos los procesadores actuales y especialmente


los de las líneas más populares basadas en la arquitectura x86 tienen una serie de
instrucciones que permiten usarla de manera segura y eficiente​. Esta arquitectura
tiene dos ramas: la Intel y la AMD, cada uno de los cuales tiene un conjunto de
instrucciones diferentes para llevarla a cabo. Aunque la mayoría de los
procesadores lo incluyen, los portátiles de gama baja y algunos ordenadores de
sobremesa antiguos no la incluyen, por lo que habrá que comprobar si nuestro
procesador lo hace. Si no lo hiciera, se habla de​paravirtualización​ en la que los
hipervisores tienen que ​interpretar​ cada imagen del sistema operativo que alojan
(llamado ​invitado​) y convertirla en instrucciones del que aloja (llamado ​anfitrión​ o
host​). La mayor parte de los hipervisores, como ​Xen​o ​KVM​ incluyen también la
capacidad de paravirtualizar ciertos sistemas operativos en caso de que los
anfitriones no tengan soporte; por ejemplo, KVM se ha asociado con ​QEMU​ que lo
usa en caso de que el procesador tenga soporte.

Por encima de los hipervisores están los sistemas de gestión de máquinas virtuales
en la nube que, aunque se puedan usar desde las mismas herramientas y con el
mismo cometido, en realidad abstraen el trabajo del hipervisor y permiten trabajar
con un API uniforme independientemente del hipervisor real que esté por debajo.
Estos sistemas de gestión de máquinas virtuales pueden ser libres, como
OpenStack u OpenNebula, o privativos, como los que se usan en la mayoría de los
sistemas de la nube. Todos, sin embargo, permiten realizar una serie de tareas que
veremos a continuación.

Pasos a dar para creación de una instancia en la nube


Aunque los proveedores de nube usan mecanismos diferentes, en realidad los
pasos a dar son muy parecidos, lo que evidentemente es un paso para que luego se
automatice con un interfaz uniforme. Para echar a andar una instancia, o máquina
virtual, en la nube, habrá que hacer los siguientes pasos

1. Localizar la imagen que deseamos. Esta imagen contendrá el sistema


operativo, así como alguna utilidad adicional que nos permita trabajar
fácilmente con sistemas de aprovisionamiento o configurar alguna otra cosa.
Para ello, suelen tener imágenes o ​plantillas​ que se pueden usar de forma
directa, algunas de ellas ​oficiales​ o proporcionadas por los que publiquen el
sistema operativo. Antes de crear una instancia de la máquina virtual habrá
que buscar la forma de identificar estas imágenes para crear la instancia con
ellas.
2. Crear metadatos relativos a la instancia de la máquina virtual. Cosas como el
grupo de recursos​, que indica cómo va a escalar, el centro de datos en el que
se va a alojar, y alguna cosa adicional, como el tipo de instancia que se va a
usar.
3. Crear una forma automática de acceder a la máquina virtual, generalmente
mediante generación y copia a la instancia en funcionamiento de la clave
pública cuyo par está bajo tu control. Dependiendo del sistema, se tendrá que
hacer "a mano" o usará las claves ya disponibles.
4. Acceso a la propia máquina virtual en funcionamiento, principalmente la
consola y también a los registros, así como los metadatos.
Todas las plataformas suelen tener una utilidad de línea de órdenes, o varias, que
permite acceder al API de la misma una vez identificados ahí. Generalmente son
libres, así que también se pueden usar desde tu propio programa. A continuación
veremos como trabajar en alguna de ellas.

Trabajando con máquinas virtuales en la nube


Vamos a ver cómo funcionan algunas de estas utilidades de línea de órdenes,
empezando por el CLI de Azure clásico, luego el moderno, y más adelante el de
OpenStack

CLI de Azure (clásico)


Tendrás que instalártelo, previa instalación de node.js, que es en lo que está
basado. ​npm install -g azure-cli​ bastará.

Azure permite, ​tras la creación de almacenamiento virtual​, la creación de máquinas


virtuales, como es natural. Se puede crear una máquina virtual desde el panel de
control, pero también desde la ​línea de órdenes​. Primero hay que saber qué
imágenes hay disponibles:

azure vm image list

Por ejemplo, se puede escoger la imagen


b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu_DAILY_BUILD-trusty-14_04-LTS-amd6
4-server-20131221-en-us-30GB​ de una versión LTS de Ubuntu o de otra como la
b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-20131215-en-us-
30GB​ Con

azure vm image show


b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-20131215-en-us-
30GB

nos muestra detalles sobre la imagen; entre otras cosas dónde está disponible y
sobre si es Premium o no (en este caso no lo es). Con esta (o con otra) podemos
crear una máquina virtual

azure vm create peasomaquina


b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-20131215-en-us-
30GB peasousuario PeasoD2clav= --location "West Europe" --ssh

En esta clave tenemos que asignar un nombre de máquina (que se convertirá en un


nombre de dominio ​peasomaquina.cloudapp.net​, un nombre de usuario (como
peasousuario​) que será el superusuario de la máquina, una clave como P ​ easoD2clav=
que debe incluir mayúsculas, minúsculas, números y caracteres especiales (no
uséis esta, hombre), una localización que en nuestro caso, para producción, será
conveniente que sea ​West Europa​ pero que para probar podéis llevárosla a la
localización exótica que queráis y, finalmente, para poder acceder a ella mediante
ssh, la última opción, si no no tengo muy claro cómo se podrá acceder. Una vez
hecho esto, conviene que se cree un par clave pública/privada y se copie al mismo
para poder acceder fácilmente.

La máquina todavía no está funcionando. Con ​azure vm list​ nos muestra las
máquinas virtuales que tenemos y el nombre que se le ha asignado y finalmente con
azure vm start​ se arranca la máquina y podemos conectarnos con ella usando
ssh​Una de las primeras cosas que hay que hacer cuando se arranque es actualizar
el sistema para evitar problemas de seguridad. A partir de ahí, podemos instalar lo
que queramos. El arranque tarda cierto tiempo y dependerá de la disponibilidad de
recursos; evidentemente, mientras no esté arrancada no se puede usar, pero
conviene de todas formas apagarla con

azure vm shutdown maquina

cuando terminemos la sesión y no sea necesaria, sobre todo porque, dado que se
pagan por tiempo de uso, se puede incurrir en costes innecesarios o en agotamiento
de los recursos gratuitos proporcionados.

Crear una máquina virtual Ubuntu e instalar en ella un servidor ​nginx​ para poder
acceder mediante web.

Trabajar con estas máquinas virtuales como si se tratara de máquinas reales no


tiene mucho sentido. El uso de infraestructuras virtuales, precisamente, lo que
permite es automatizar la creación y provisionamiento de las mismas de forma que
se puedan crear y configurar máquinas en instantes y personalizarlas de forma
masiva. Veremos como hacerlo en el​siguiente tema​.

Usando el nuevo CLI de Azure


El ​nuevo CLI de Azure​ está basado en Python y se puede instalar siguiendo las
instrucciones arriba. Es bastante similar al anterior, pero hay muchas tareas que se
realizan mucho más fácilmente usando valores por omisión relativamente fáciles.
Además, devuelve los resultados en JSON por lo que es relativamente fácil trabajar
con ellos de forma automática.

Comencemos por crear un grupo de recursos

az resource group create -l westeurope -n CCGroupEU

Esto crea un grupo de recursos en Europa Occidental. Vamos a usarlo más


adelante para crear nuestras instancias.

az vm image list

Te devolverá el grupo de máquinas virtuales disponibles. Pero, como se ha dicho


antes, te lo devuelve en JSON, con lo que es conveniente filtrarlo. Para ello
usaremos el tremendamente útil j​ q​, ​un lenguaje de peticiones para JSON​ con cierta
similitud con los selectores CSS.

Si se añade ​--all​ te devolverá todas las imágenes disponibles, pero ahora mismo
hay un ​bug registrado​ porque no se pueden usar en realidad con la línea de
órdenes. Así que nos ceñiremos a las imágenes disponibles localmente.

az vm image list | jq '.[] | select( .offer | contains("buntu"))'

Esta te filtrará sólo aquellas imágenes que contengan ​buntu​ (no sabemos si va a
estar en mayúsculas o minúsculas), devolviendo algo así:

De esta imagen hay que usar dos IDs: ​urn​ y ​urnAlias​, que nos permitirán identificar la
imagen con la que vamos a trabajar a continuación:
az vm create -g CCGroupEU -n bobot --image UbuntuLTS

En este caso creamos la máquina virtual llamada ​bobot​ con una imagen de
UbuntuLTS y ​az​ usa una clave SSH pública de nuestro directorio y nuestro nombre
de usuario para copiarlo directamente a la instancia creada. Puede tardar un rato en
crear la instancia, eso sí, pero devolverá un JSON que incluirá metadatos y su IP
pública como p ​ ublicIpAddress​. Una vez hecho eso, te puedes conectar directamente
a ella con su dirección IP y ssh.

Una vez que se deje de usar, conviene pararla con

az vm stop -g CCGroupEU -n bobot

Si no, seguirá disminuyendo el crédito.

Crear una instancia de una máquina virtual Debian y provisionarla usando alguna de
las aplicaciones vistas en ​el tema sobre herramientas de aprovisionamiento

CLI de OpenStack
OpenStack​ es un sistema libre de gestión de nubes privadas que se ha hecho muy
popular incluso en revendedores de sistemas como IBM u otros, que en realidad
ofrecen este tipo de acceso. Se puede probar en alguna instalación disponible en la
Facultad o Escuela o bien en ​TryStack​ si te admiten.

Como arriba, hay también un ​sistema de línea de órdenes​, inicialmente uno para
cada uno de los subsistemas de OpenStack pero últimamente unificado a una sola
​ penstack​. Para trabajar con él, tras descargar el cliente, hay que ​configurar
orden, o
una serie de variables de entorno descargándose un fichero​, que él mismo pone las
API keys y demás. Una vez ejecutado ese ​script​ de configuración se puede, por
ejemplo, crear una clave para acceder a las instancias que se vayan a crear

nova keypair-add Try > openstack-key.pem

que crea una clave llamada ​Try​ y la guarda en un fichero de clave privada.

Sin embargo, al menos en esta instalación de prueba, es más fácil trabajar con el
interfaz gráfico que con la línea de órdenes. En ​este tutorial​ te explica cómo crear el
par de claves. Una vez creada

● Hay que añadir el puerto 22 de ssh al grupo de seguridad que se vaya a usar
● Se tiene que crear una red y una subred para conectar la instancia a la
subred.
● Se tiene que crear un ​router​ al cual se conecta la red.
● Finalmente, se crea la instancia. Esto sí se puede hacer desde línea de
órdenes

openstack server create --flavor m1.smaller --image Ubuntu16.04 \


--nic net-id=b96fdf8d-99ca-3333-5555-38ccd03a4a3c \
--security-group default --key-name Try bobot-x

● La orden anterior crea una instancia llamada ​bobot-x​ con una imagen de
Ubuntu (una de las que hay por defecto) y una red cuyo ID se extrae desde el
interfaz gráfico. El ​flavor​ es el tipo de instancia, esta es una de las que hay
disponibles.
● Una vez creada la instancia, se le asigna una IP flotante para poder acceder
desde fuera, teóricamente de esta forma, aunque da error (​Conflict​) al menos
a mi

openstack floating ip create b96fdf8d-99ca-3333-5555-38ccd03a4a3c

● Si eso falla, se puede asignar una IP flotante desde el interfaz gráfico, yendo
a la lista de instancias.

Conseguir una cuenta de prueba en OpenStack y crear una instancia a la que se


pueda acceder, provisionándola con algún ​script​ disponible.

A dónde ir desde aquí


Si no lo has visto, en el ​siguiente tema​ pondremos en práctica todos los conceptos
aprendidos en este tema y ​en el tema dedicado a almacenamiento​ para crear
configuraciones que sean fácilmente gestionables y adaptables a un fin
determinado. También se puede ir a ​el hito del proyecto​ para aplicarlo al mismo.

Si lo que necesitas es un sistema ligero de virtualización, puedes mirar cómo


virtualizar con ​contenedores​.
Gestión de infraestructuras virtuales

Objetivos de la asignatura
● Diseñar, construir y analizar las prestaciones de un centro de proceso de
datos virtual.
● Documentar y mantener una plataforma virtual.
● Realizar tareas de administración en infraestructura virtual.

Objetivos específicos
1. Aprender lenguajes de configuración usados en infraestructuras virtuales.
2. Saber cómo aplicarlos en un caso determinado.
3. Conocer los sistemas de orquestación de máquinas virtuales.

Introducción
Antes de poder provisionar una máquina o conjunto de máquinas virtuales, es
necesario poder crearlas a partir de imágenes de sistemas operativos, en un
remedo de ​arranque​ tal como se hace en una máquina real. En el espíritu DevOps,
tiene que haber una forma de hacerlo automáticamente y de forma reproducible con
código, usando este como infraestructura. También es deseable hacerlo de forma
que sea totalmente portable de una infraestructura a otra; en general, debe haber un
procedimiento para que las infraestructuras como servicio (IaaS) sean controladas
programáticamente, aparte de las otras dos formas que suelen ofrecer, a través de
la web y a través de los interfaces de línea de órdenes. Trabajar con la web no es
escalable y apenas es reproducible, porque sólo permite crear infraestructuras una
por una o por grupos si es que son similares; la segunda no es portable, porque
cada servicio ​cloud​ tiene sus propias órdenes como se ha visto en ​el tema de
automatizar servicios ​cloud​. Sólo el trabajar con código desde una herramienta que
permita trabajar con cualquier servicio cloud permite reproducir configuraciones de
un vendedor a otro sin ningún problema.

Estas herramientas se denominan herramientas de ​orquestación​ o de ​gestión de


configuraciones​, aunque a veces en esta denominación también incluyen a otras de
más bajo nivel como Ansible o Chef. En realidad hoy en día la única herramienta
existente y en uso amplio es Vagrant. Otras herramientas, como Vortex, parecen
abandonadas y otras como Juju o ​Cobbler​funcionan a otro nivel diferente,
trabajando para configurar desde cero ​bare metal​ o máquinas sin ningún sistema
operativo instalado que usan arranque remoto. ​Terraform​, por otro lado, es una
herramienta que sí permite tratar la infraestructura como código y se encuentra más
o menos en la misma área, aunque ​Vagrant se centra en gestionar entornos de
desarrollo y Terraform es para construir infraestructura​; en ese sentido, Vagrant es
de más alto nivel que Terraform, aunque se pueden usar de forma complementaria.

La ventaja de Vagrant es que puede trabajar de forma indistinta con máquinas


virtuales locales o remotas e incluso, en las últimas versiones, con contenedores,
simplemente cambiando los ​drivers​ con los que va a trabajar. Lo veremos a
continuación.

No vamos a dedicar demasiado tiempo a la creación y configuración de máquinas


virtuales específicas, aunque en el tema adicional de ​uso de sistemas en la nube​ se
explica como trabajar con máquinas virtuales con k​ vm​ y cómo definir, desde la línea
de órdenes, máquinas virtuales en sistemas en cloud como Azure.

Orquestación de máquinas virtuales


A un nivel superior al provisionamiento de máquinas virtuales está la configuración,
orquestación y gestión de las mismas, herramientas como ​Vagrant​ ayudan a
hacerlo, aunque también Puppet e incluso Juju pueden hacer muchas de las
funciones de Vagrant, salvo por el hecho de que no pueden trabajar directamente
con el hipervisor.

Realmente no hay muchas alternativas a Vagrant. En ​este hilo de 2016 de


YCombinator​ mencionan algunos, pero ninguno es lo suficientemente amplio o
aceptado para hacerle sombra. Únicamente sistemas basados en contenedores
pueden acercársele; por ejemplo Kubernetes. Pero ninguno para orquestación de
máquinas virtuales.

La ventaja de Vagrant es que permite gestionar el ciclo de vida completo de una


máquina virtual, desde la creación hasta su destrucción pasando por el
provisionamiento y la monitorización o conexión con la misma. Además, permite
trabajar con todo tipo de hipervisores y provisionadores tanto de contenedores,
como en cloud, como en local.

Sigue las ​instrucciones en la web​, para instalarte Vagrant, la versión 2.0.1 ha salido
en noviembre de 2017. Es una aplicación escrita en Ruby, por lo que tendrás que
tener una instalación preparada. Te aconsejamos que uses un gestor de versiones
como ​RVM​ o RBEnv para poder trabajar con él en espacio de usuario fácilmente.
Tendrás que tener ​algunas nociones de Ruby​ para trabajar con Vagrant, que no es
sino un DSL (Domain Specific Language) construido sobre él, al menos tendrás que
saber como instalar ​gemas​ (bibliotecas), que se usarán para los ​plugin​ de Vagrant y
también cómo trabajar con bucles y variables, que se usarán en el fichero de
definición de máquinas virtuales denominado V ​ agrantfile​.

Con Vagrant ​te puedes descargar directamente​ ​una máquina configurada de esta
lista​ o bien cualquier otra máquina configurada en el formato ​box​, que es el que uno
de los que usa Vagrant.

Trabajemos con la configuración por omisión añadiendo la siguiente ​box​:

vagrant box add centos7


https://github.com/vezzoni/vagrant-vboxes/releases/download/0.0.1/centos-7-x86_64.b
ox

Para conocer todos los comandos de vagrant, ​vagrant help​ o ​vagrant list-commands​.

En este caso usamos un subcomando de ​vagrant box​, que permite añadir nuevas
imágenes a nuestro gestor de máquinas virtuales; ​add​ añade uno nuevo en este
caso usando el gestor por omisión que es Virtual Box. El formato determinará en
qué tipo de hipervisor se puede ejecutar; en general, Vagrant usa VirtualBox, y los
.box​ se pueden importar directamente en esta aplicación; los formatos vienen
listados en la página anterior. Las ​boxes​ disponibles se pueden consultar en
Vagrantbox.es​; en esa dirección hay diferentes sistemas operativos en diferentes
formatos, aunque generalmente la mayoría son para VirtualBox.

A continuación

vagrant init centos7

creará un ​Vagrantfile​ en el directorio en el que te encuentres, por lo que es


conveniente que el directorio esté vacío. En este caso crea ​este fichero​ en el que
realmente sólo se configura el nombre de la máquina (​centos7​) pero que a base de
des-comentar otras líneas se puede llegar a configurar de forma más completa.

Para comenzar a usar la máquina virtual se usa

vagrant up
En ese momento Virtual Box arrancará la máquina y te podrás conectar a ella
usando

vagrant ssh

Si quieres conectar por ​ssh​ desde algún otro programa, por ejemplo, Ansible,
tendrás que fijarte en cómo se configura la conexión y que puerto se usa.
Generalmente, una máquina virtual va a usar el puerto 2222 de ssh y para que
accedas desde ​fuera​ de Vagrant tendrás además que copiar tu clave pública, lo que
puedes hacer copiando y pegándola desde un terminal o bien ​usando el propio
Vagrantfile​ añadiendo las dos líneas siguientes:

#Copy public key


ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
config.vm.provision 'shell', inline: "echo #{ssh_pub_key} >>
/home/vagrant/.ssh/authorized_keys", privileged: false

Una vez creada la máquina, para que use estas líneas y se provisione hay que
hacer

vagrant provision

Hay que seguir teniendo en cuenta que se usa el puerto 2222, o sea que para
conectarte a la máquina (o usar un provisionador de forma externa a Vagrant)
tendrás que hacerlo así:

ssh [email protected] -p 2222

Para suspender el estado de la máquina virtual y guardarlo se usa

vagrant suspend

Esto dejará la máquina virtual en el estado en el que esté, que se podrá recuperar
en la próxima sesión. Cuando quieras deshacerte de ella,

vagrant destroy
Instalar una máquina virtual Debian usando Vagrant y conectar con ella.

Trabajando con otro tipo de máquinas virtuales e


hipervisores.
En ​vagrantbox.es​ la mayor parte de las imágenes tienen formato VirtualBox, pero
algunas tienen formato ​Vagrant-lxc​ (para usar en el sistema de virtualización ligera
lxc​), VMWare, KVM, Parallels o l​ ibvirt​. Ninguno de estos formatos está instalado por
defecto en Vagrant y para trabajar con ellos habrá que instalar un plugin.

Instalemos el ​plugin de ​libvirt​, por ejemplo, siguiendo las instrucciones de su


repositorio; de hecho, para instalar libvirt habrá que seguir también las instrucciones
en el mismo repositorio, que incluyen instalar q ​ emu​. ​libVirt​ es una librería que
abstrae todas las funciones de virtualización, permitiendo trabajar de forma uniforme
con diferentes hipervisores tales como Xen e incluso el propio l​ xc​ mencionado
anteriormente. Una vez instalada esta biblioteca, no hay que preocuparse tanto por
el sistema de virtualización que tengamos debajo e incluso podemos trabajar con
ella de forma programática para crear y configurar máquinas virtuales sin tener que
recurrir a sistemas de orquestación o provisionamiento.

Simultáneamente a la instalación, podemos descargarnos esta máquina virtual que


está en ese formato.

vagrant box add viniciusfs/centos7 https://atlas.hashicorp.com/viniciusfs/boxes/centos7/

A continuación, la inicialización será de la misma forma

vagrant init viniciusfs/centos7

que, igual que en el caso anterior, crea un fichero ​Vagrantfile​ (y así te lo dice; este
fichero será parecido a ​este​ que ya sabemos que permite trabajar y llevar a cabo
cualquier configuración adicional.

Podemos añadirle también la clave pública propia si queremos usarlo "desde fuera",
tal como también se ha hecho antes.

Una vez hecho eso ya podemos inicializar la máquina y trabajar con ella (pero antes
voy a apagar la máquina Azure que tengo ejecutándose desde que empecé a contar
lo anterior)
sudo vagrant up --provider=libvirt

donde la principal diferencia es que le estamos indicando que queremos usar el


proveedor​ ​libvirt​, en vez de el que usa por omisión, Virtual Box. Dado que este
proveedor conecta con un daemon que se ejecuta en modo privilegiado, habrá que
usar s​ udo​ en este caso.

Puede que tengas ​un problema con libvirt​ con "dhcp_leases: undefined method" o
similar comprobad la versión que tenéis. Si estáis usando la 0.0.36 desinstaladla e
instalad la 0.0.35 como indican en el mismo issue. Alternativamente, también se
puede pasar de este ejemplo, que era simplemente una forma de ilustrar diferentes
proveedores aparte del que aparece por defecto.

Usando este sudo, puedes conectarte con la máquina usando

sudo vagrant ssh

Y todos los demás comandos, también con ​sudo​, por lo indicado anteriormente.

Instalar una máquina virtual ArchLinux o FreeBSD para KVM, otro hipervisor libre,
usando Vagrant y conectar con ella.

Provisionando máquinas virtuales.


Una vez creada la máquina virtual se puede entrar en ella, configurarla e instalar
todo lo necesario desde la línea de órdenes. Pero, por supuesto, sabiendo lo que
sabemos sobre provisionamiento por el tema correspondiente, Vagrant
permite​provisionarla de muchas maneras diferentes​. En general, Vagrant usará
opciones de configuración diferente dependiendo del provisionador, subirá un
fichero a un directorio temporal del mismo y lo ejecutará (tras ejecutar todo lo
necesario para el mismo).

La provisión tiene lugar cuando se ​alza​ una máquina virtual (con ​vagrant up​) o bien
explícitamente haciendo ​vagrant provision​. En cualquier caso se lee del Vagrantfile y
se llevan a cabo las acciones especificadas en el fichero de configuración.

En general, trabajar con un provisionador requiere especificar de cuál se trata y


luego dar una serie de órdenes específicas. Comenzaremos por el ​shell​, que es el
más simple y, en realidad, equivale a entrar en la máquina y dar las órdenes a
mano. Instalaremos, como hemos hecho en otras ocasiones, el utilísimo editor
emacs​usando este V​ agrantfile​:
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "centos7"
config.vm.provision "shell",
inline: "yum install -y python"
end

Recordemos que se trata de un programa en Ruby en el cual configuramos la


máquina virtual. La 4ª línea indica el nombre de la máquina con la que vamos a
trabajar (que puede ser la usada en el caso anterior); recordemos también que, por
omisión, se trabaja con VirtualBox (si se hiciera con algún otro tipo de hipervisor
habría que usar el ​plugin​ correspondiente e inicializar la máquina de alguna otra
forma). La parte en la que efectivamente se hace la provisión va justamente a
continuación. La orden c​ onfig.vm.provision​ indica que se va a usar el sistema de
provisión del s​ hell​, es decir, órdenes de la línea de comandos; se le pasa un hash en
Ruby (variable: valor, tal como en javascript, separados por comas) en el que la
clave i​ nline​ indica el comando que se va a ejecutar, en este caso y​ um​, el programa
para instalar paquetes en CentOS, y al que se le indica -​ y​ para que conteste ​Yes​ a
todas las preguntas sobre la instalación.

Este Vagrantfile no necesita nada especial para ejecutarse: se le llama directamente


cuando se ejecuta ​vagrant up​ o explícitamente cuando se llama con ​vagrant
provision​. Lo único que hará es instalar este programa bajándose todas sus
dependencias (y tardará un rato).

Crear un script para provisionar de forma básica una máquina


virtual para el proyecto que se esté llevando a cabo en la asignatura.

El provisionamiento por ​shell​ admite ​muchas más opciones​: se puede usar un


fichero externo o incluso alojado en un sitio web (por ejemplo, un Gist alojado en
Github). Por ejemplo, ​este para provisionar nginx y node​ (no leer hasta después de
hacer el ejercicio anterior).

El problema con los guiones de ​shell

y no sé por qué diablos pongo guiones si pongo shell, podía poner scripts de shell
directamente y todo el mundo me entendería, o guiones de la concha y nadie me
entendería
es que son específicos de un sistema operativo determinado. Por eso Vagrant
permite muchas otras formas de configuración, incluyendo casi todos los sistemas
de provisionamiento populares (Chef, Puppet, Ansible, Salt) y también Docker. La
ventaja de estos sistemas de más alto nivel es que permiten trabajar
independientemente del sistema operativo. Cada uno de ellos tendrá sus opciones
específicas, pero veamos cómo se haría lo anterior usando el provisionador
chef-solo​.

Para empezar, hay que provisionar la máquina virtual para que funcione con
chef-solo y hay que hacerlo desde shell o Ansible; ​este ejemplo​ que usa ​este fichero
shell​ puede provisionar, por ejemplo, una máquina CentOS.

Una vez preinstalado chef

Lo que también podíamos haber hecho con ​una máquina que ya lo tuviera instalado,
de las que hay muchas en ​vagrantbox.es​ y de hecho es la mejor opción porque
chef-solo​ no se puede instalar en la versión 6.5 de CentOS fácilmente por no tener
una versión actualizada de Ruby)

lo incluimos en el Vagrantfile, tal como ​este

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "centos7"

config.vm.provision "chef_solo" do |chef|


chef.add_recipe "emacs"
end

end

Este fichero usa un bloque de Ruby para pasarle variables y simplemente declara
que se va a usar la receta ​emacs​, que previamente tendremos que haber creado en
un subdirectorio cookbooks que descienda exactamente del mismo directorio y que
contenga simplemente p ​ ackage 'emacs'​ que tendrá que estar en un fichero

cookbooks/emacs/recipes/default.rb

Con todo esto se puede configurar emacs. Pero, la verdad, seguro que es más fácil
hacerlo en Ansible y/o en otro sistema operativo que no sea CentOS porque yo, por
lo pronto, no he logrado instalar chef-solo en ninguna de las máquinas
pre-configuradas de VagrantBoxes.

Configurar tu máquina virtual usando `vagrant` con el provisionador


ansible

Desde Vagrant se puede crear también una ​caja base​ con lo mínimo necesario para
poder funcionar, incluyendo el soporte para ssh y provisionadores como Chef o
Puppet. Se puede crear directamente en VirtualBox y usar v​ agrant package​ para
empaquetarla​ y usarla para su consumo posterior.

Configuración de sistemas distribuidos


Vagrant es sumamente útil cuando se trata de configurar varios sistemas a la vez,
por la posibilidad que tiene de trabajar con diferentes proveedores pero también por
tratarse de un programa en Ruby que puede simplemente guardar el estado común
e implantarlo en las máquinas virtuales que se vayan creando.

A la vez, sistemas operativos como CoreOS son interesantes precisamente por la


facilidad para configurarlos como sistemas distribuidos, que proviene de su diseño
para ser anfitriones de contenedores pero también a su uso de e​ tcd​, una base de
datos clave-valor distribuida que se usa en este caso principalmente para guardar
las configuraciones.

Veamos en el siguiente ejemplo cómo se puede ​configurar un sistema con varias


máquinas virtuales coordinadas usando CoreOS​ (originalmente estaba ​aquí​. Es un
fichero un tanto largo y complicado, pero veamos las partes más interesantes.
Primero, usa un fichero externo de configuración, c​ onfig.rb​. A pesar de su nombre,
no es un fichero de Chef, simplemente un fichero que se va a incluir en la
configuración de Vagrant que se llama así.

# Size of the CoreOS cluster created by Vagrant


$num_instances=3

# Used to fetch a new discovery token for a cluster of size $num_instances


$new_discovery_url="https://discovery.etcd.io/new?size=#{$num_instances}"

# Automatically replace the discovery token on 'vagrant up'

if File.exists?('user-data') && ARGV[0].eql?('up')


require 'open-uri'
require 'yaml'

token = open($new_discovery_url).read

data = YAML.load(IO.readlines('user-data')[1..-1].join)

if data.key? 'coreos' and data['coreos'].key? 'etcd'


data['coreos']['etcd']['discovery'] = token
end

if data.key? 'coreos' and data['coreos'].key? 'etcd2'


data['coreos']['etcd2']['discovery'] = token
end

# Fix for YAML.load() converting reboot-strategy from 'off' to `false`


if data.key? 'coreos' and data['coreos'].key? 'update' and data['coreos']['update'].key?
'reboot-strategy'
if data['coreos']['update']['reboot-strategy'] == false
data['coreos']['update']['reboot-strategy'] = 'off'
end
end

yaml = YAML.dump(data)
File.open('user-data', 'w') { |file| file.write("#cloud-config\n\n#{yaml}") }
end

Este fichero, después de definir el número de máquinas virtuales que tenemos,


busca un fichero llamado ​user-data​ que es privado porque contiene un ​token
obtenido de ​https://discovery.etcd.io​. Este token da acceso al registro de todas las
instancias de e​ tcd​ con las que vamos a trabajar. En ​la muestra​ indica qué es lo que
hay que hacer para obtenerla. Por lo demás, lo único que hay que cambiar es el
número de instancias que se desean.

El ​Vagrantfile​, por otro lado, realiza una serie de adaptaciones de la máquina virtual
usada y crea una red privada virtual que una a las tres máquinas, de forma que se
puedan comunicar de forma segura. Una vez ejecutado v​ agrant up​ se puede
acceder a las máquinas por ssh con el nombre definido (​core-0x​) pero también se
pueden usar para escalar aplicaciones basadas en contenedores en el ​cluster​ de
CoreOS​ creado, con el que puedes ejecutar ​múltiples copias de tu aplicación​ para
replicación o escalado automático.

Algunos ejemplos interesantes


La migración a la nube ha hecho que se creen ciertos sistemas operativos cuyo fin
sea servir de soporte exclusivamente a servicios en la misma. Uno de ellos es
Scotch Box​, que empaqueta una serie de herramientas de cliente servidor para
ejecutar una pila de desarrollo completa, o b​ osh-lite​ para ​BOSH, una herramienta de
gestión de sistemas distribuidos​. Otra posibilidad es ​una máquina virtual para
empezar con ciencia de datos​.

Pero una de las más interesantes que podemos usar es ​RancherOS​, otro sistema
operativo, como CoreOS, diseñado para ejecutar contenedores. En el repo dice que
ya no se apoya esa versión, pero se puede al menos ejecutar y probar si se desea.

También se puede probar ​NixOps​, que aunque está diseñado especialmente para
una versión del sistema operativo llamado NixOS, puede usarse también para
orquestar máquinas virtuales en una serie de entornos.

A donde ir desde aquí


Este es el último tema del curso, pero a partir de aquí se puede seguir aprendiendo
sobre DevOps en ​el blog​ o ​en IBM​. Libros como ​DevOps for Developers​ pueden ser
también de ayuda.

Contenedores y cómo usarlos

Objetivos

Cubre los siguientes objetivos de la asignatura


● Conocer las diferentes tecnologías y herramientas de virtualización tanto para
procesamiento, comunicación y almacenamiento.
● Instalar, configurar, evaluar y optimizar las prestaciones de un servidor virtual.
● Configurar los diferentes dispositivos físicos para acceso a los servidores
virtuales: acceso de usuarios, redes de comunicaciones o entrada/salida.
● Diseñar, implementar y construir un centro de procesamiento de datos virtual.
● Documentar y mantener una plataforma virtual.
● Optimizar aplicaciones sobre plataformas virtuales.
● Conocer diferentes tecnologías relacionadas con la virtualización
(Computación Nube, Utility Computing, Software as a Service) e
implementaciones tales como Google AppSpot, OpenShift o Heroku.
● Realizar tareas de administración en infraestructura virtual.

Objetivos específicos
1. Entender cómo las diferentes tecnologías de virtualización se integran en la
creación de contenedores.
2. Crear infraestructuras virtuales completas.
3. Comprender los pasos necesarios para la configuración automática de las
mismas.

Introducción a la virtualización ligera: ​contenedores


El aislamiento de grupos de procesos formando una ​jaula​ o ​contenedor​ ha sido una
característica de ciertos sistemas operativos descendientes de Unix desde los años
80, en forma del programa ​chroot​ (creado por Bill Joy, el que más adelante sería
uno de los padres de Java). La restricción de uso de recursos de las ​jaulas c​ hroot​,
que ya hemos visto, se limitaba a la protección del acceso a ciertos recursos del
sistema de archivos, aunque eran relativamente fáciles de superar; incluso así, fue
durante mucho tiempo la forma principal de configurar servidores de alojamiento
compartidos y sigue siendo una forma simple de crear virtualizaciones ​ligeras​. Las
jaulas BSD​ constituían un sistema más avanzado, implementando una​virtualización
a nivel de sistema operativo​ que creaba un entorno virtual prácticamente
indistinguible de una máquina real (o máquina virtual real). Estas ​jaulas​ no sólo
impiden el acceso a ciertas partes del sistema de ficheros, sino que también
restringían lo que los procesos podían hacer en relación con el resto del sistema.
Tiene como limitación, sin embargo, la obligación de ejecutar la misma versión del
núcleo del sistema.

En ​esta presentación​ explica como los espacios de nombres son la clave para la
creación de contenedores y cuáles son sus ventajas frente a otros métodos de
virtualización

El mundo Linux no tendría capacidades similares hasta bien entrados los años 90,
con ​vServers, OpenVZ y finalmente LXC​. Este último, ​LXC​, se basa en el concepto
de ​grupos de control o CGROUPS​, una capacidad del núcleo de Linux desde la
versión 2.6.24 que crea ​contenedores​ de procesos unificando diferentes
capacidades del sistema operativo que incluyen acceso a recursos, prioridades y
control de los procesos. Los procesos dentro de un contenedor están ​aislados​ de
forma que sólo pueden ​ver​ los procesos dentro del mismo, creando un entorno
mucho más seguro que las anteriores ​jaulas​. Estos ​CGROUPS han sido ya vistos
en otro tema​.

Dentro de la familia de sistemas operativos Solaris (cuya última versión libre se


denomina ​Illumos​, y tiene también otras versiones como ​SmartOS​, centradas
precisamente en el uso de contenedores) la tecnología correspondiente se
denomina​zonas​. La principal diferencia es el bajo ​overhead​ que le añaden al
sistema operativo y el hecho de que se les puedan asignar recursos específicos;
estas diferencias son muy leves al tratarse simplemente de otra implementación de
virtualización a nivel de sistema operativo.

Un contenedor es, igual que una jaula, una forma ​ligera​ de virtualización, en el
sentido que no requiere un hipervisor para funcionar ni, en principio, ninguno de los
mecanismos hardware necesarios para llevar a cabo virtualización. Tiene la
limitación de que la ​máquina invitada​ debe tener el mismo kernel y misma CPU que
la máquina anfitriona, pero si esto no es un problema, puede resultar una alternativa
útil y ligera a la misma. A diferencia de las jaulas, combina restricciones en el
acceso al sistema de ficheros con otras restricciones aprovechando espacios de
nombres y grupos de control. l​ xc​ una de las soluciones de creación de contenedores
más fácil de usar hoy en día en Linux, sobre todo si se quiere usar desde un
programa a nivel de librería. Evidentemente, desde que ha salido Docker no es la
más popular, aunque es una solución madura y estable.

Esta virtualización ​ligera​ tiene, entre otras ventajas, una ​huella​ escasa: un
ordenador normal puede admitir 10 veces más contenedores (o ​tápers​) que
máquinas virtuales; su tiempo de arranque es de unos segundos y, además, tienes
mayor control desde fuera (desde el anfitrión) del que se pueda tener usando
máquinas virtuales.

Usando ​lxc
No todas las versiones de los núcleos del sistema operativo pueden usar este tipo
de container; para empezar, dependerá de cómo esté compilado, pero también del
soporte que tenga el hardware. l​ xc-checkconfig​ permite comprobar si está preparado
para usar este tipo de tecnología y también si se ha configurado correctamente.
Parte de la configuración se refiere a la instalación de c​ groups​, que hemos visto
antes; el resto a los espacios de nombres y a capacidades ​misceláneas​relacionadas
con la red y el sistema de ficheros.
Hay que tener en cuenta que si no aparece alguno de esas capacidades como
activada, LXC no va a funcionar.

La instalación en Linux se hace usando el paquete en los repositorios. Por las


diferencias entre la versión uno y la 2, en algunos aparecerán paquetes ​lxc1​ y ​lxc2​.
Es mejor instalar este último, con el nombre que sea. Al menos en Ubuntu y
distribuciones derivadas de esta, como Mint, no debería de tener un gran problema.

Instalando LXC en Arch


Para instalar LXC en una distribución ArchLinux lo que haremos será ejecutar el
comando siguiente:

pacaur -S lxc arch-install-scripts

Así instalaremos LXC junto con otro módulo que se recomienda en la


documentación de Arch para este paquete​. Además hemos de señalar que, por
cuestiones de seguridad que también se comentan en esta documentación, los
namespaces​ de usuario estarán deshabilitados (como veremos al usar
lxc-checkconfig​) ya que no se puede arrancar servidores sin permisos de
superusuario.

Cuando queramos crear nuestro primer contenedor LXC usando las instrucciones
que se detallan en este tema es posible que nos dé un error diciéndonos que no
está disponible el comando d​ ebootstrap​, solamente tendremos que instalarlo con la
orden s​ udo pacman -S debootstrap​, y ya podremos crear contenedores LXC sin
problemas.

Instalando LXC en Debian


Para instalar LXC en Debian es necesario contar con una versión 8 (Jessie) o
superior. Instalar LXC 2.0 en Debian 8 requiere añadir los ​Jessie backports​ un
repositorio de paquetes inestables y/o de test, por ello contiene versiones de los
paquetes bastante más nuevas que los repositorios oficiales.

Una vez hecho esto podemos instalar LXC usando las ​instrucciones oficiales de
instalación en Debian​ con los comandos:

sudo apt-get install -t jessie-backports lxc libvirt0 linux-image-amd64

sudo apt-get install libpam-cgroup libpam-cgfs bridge-utils

Podemos comprobar que se ha instalado correctamente ejecutando

lxc-checkconfig

Lo que nos debería de dar todo enabled; también podemos comprobar la versión de
LXC instalada con

lxc-info --version

Instala LXC en tu versión de Linux favorita. Normalmente la versión en desarrollo,


disponible tanto en ​GitHub​ como en el ​sitio web​ está bastante más avanzada; para
evitar problemas sobre todo con las herramientas que vamos a ver más adelante,
conviene que te instales la última versión y si es posible una igual o mayor a la 2.0.

Creando contenedores con ​lxc


Si no hay ningún problema y todas están ​enabled​ se puede ​usar lxc con relativa
facilidad​ siempre que tengamos una distro como Ubuntu relativamente moderna:

sudo lxc-create -t ubuntu -n una-caja

crea un contenedor denominado ​una-caja​ e instala Ubuntu en él. La versión que


instala dependerá de la que venga con la instalación de ​lxc​, generalmente una de
larga duración y en mi caso la 14.04.4; además, aparte de la instalación mínima,
incluirá en el contenedor una serie de utilidades como v​ im​ o s​ sh​. Todo esto lo
indicará en la consola según se vaya instalando, lo que tardará un rato.

Alternativamente, se puede usar una imagen similar a la que se usa en ​EC2 de


Amazon​, donde se denomina AMI:

sudo lxc-create -t ubuntu-cloud -n nubecilla

que funciona de forma ligeramente diferente, porque se descarga un fichero ​.tar.gz


usando ​wget​ (y tarda también un rato). Las imágenes disponibles las podemos
consultar en ​esta web​, junto con los nombres que tienen. La opción -​ t​ es para las
plantillas existentes instaladas en el sistema, que podemos consultar en el directorio
/usr/share/lxc/templates​.

Entre ellas, varias de ​Alpine Linux​, una distribución ligera precisamente dirigida a su
uso dentro de contenedores. También hay una llamada ​plamo​, escrita en algún tipo
de letra oriental, posiblemente japonés. Se puede instalar, pero igual no se entiende
nada.

Podemos listar los contenedores que tenemos disponibles con ​lxc-ls​, aunque en
este momento cualquier contenedor debería estar en estado ​STOPPED​.

Para arrancar el contenedor y conectarse a él,

sudo lxc-start -n nubecilla

, donde ​-n​ es la opción para dar el nombre del contenedor que se va a iniciar.
Dependiendo del contenedor que se arranque, habrá una configuración inicial; en
este caso, se configuran una serie de cosas y eventualmente sale el login, que será
para todas las máquinas creadas de esta forma u ​ buntu​ (también clave). Lo que hace
esta orden es automatizar una serie de tareas tales como asignar los CGROUPS,
crear los namespaces que sean necesarios, y crear un puente de red tal como
hemos visto anteriormente. En general, creará un puente llamado ​lxcbr0​ y otro con
el prefijo ​veth​.

Te puedes conectar al contenedor desde otro terminal usando ​lxc-console

sudo lxc-console -n nubecilla

La salida te dejará "pegado" al terminal.

Instalar una distro tal como Alpine y conectarse a ella usando el nombre de usuario
y clave que indicará en su creación

Una vez arrancados los contenedores, si se lista desde fuera aparecerá de esta
forma:

$ sudo lxc-ls -f
NAME STATE IPV4 IPV6 AUTOSTART
-----------------------------------------------
nubecilla RUNNING 10.0.3.171 - NO
una-caja STOPPED - - NO

Y, dentro de la misma, tendremos una máquina virtual con esta pinta:


Para el usuario del contenedor aparecerá exactamente igual que cualquier otro
ordenador: será una máquina virtual que, salvo error o brecha de seguridad, no
tendrá acceso al anfitrión, que sí podrá tener acceso a los mismos y pararlos
cuando le resulte conveniente.

sudo lxc-stop -n nubecilla

Las ​órdenes que incluye el paquete​ permiten administrar las máquinas virtuales,
actualizarlas y explican cómo usar otras plantillas de las suministradas para crear
contenedores con otro tipo de sistemas, sean o no debianitas. Se pueden crear
sistemas basados en Fedora; también clonar contenedores existentes para que
vaya todo rápidamente.

La ​guía del usuario​ indica también cómo usarlo como usuario sin privilegios, lo que
mayormente te ahorra la molestia de introducir sudo y en su caso la clave cada vez.
Si lo vas a usar con cierta frecuencia, sobre todo en desarrollo, puede ser una mejor
opción.

Los contenedores son la implementación de una serie de tecnologías ​que tienen


soporte en el sistema operativo: espacios de nombres, CGroups y puentes de red​: y
como tales pueden ser configurados para usar sólo una cuota determinada de
recursos, por ejemplo ​la CPU​. Para ello se usan los ficheros de configuración de
cada una de las máquinas virtuales. Sin embargo, tanto para controlar como para
visualizar los tápers (que así vamos a llamar a los contenedores a partir de ahora)
es más fácil usar ​lxc-webpanel​, un centro de control por web que permite iniciar y
parar las máquinas virtuales, aparte de controlar los recursos asignados a cada una
de ellas y visualizarlos; la página principal te da una visión general de los
contenedores instalados y desde ella se pueden arrancar o parar.
Cada solución de virtualización tiene sus ventajas e inconvenientes. La principal
ventaja de este tipo de contenedores son el aislamiento de recursos y la posibilidad
de manejarlos, lo que hace que se use de forma habitual en proveedores de
infraestructuras virtuales. El hecho de que se virtualicen los recursos también
implica que haya una diferencia en las prestaciones, que puede ser apreciable en
ciertas circunstancias.

Configurando las aplicaciones en un táper


Una vez creados los tápers, son en casi todos los aspectos como una instalación
normal de un sistema operativo: se puede instalar lo que uno quiera. Sin embargo,
una de las ventajas de la infraestructura virtual es precisamente la (aparente)
configuración del ​hardware​ mediante ​software​: de la misma forma que se crea, inicia
y para desde el anfitrión una MV, se puede configurar para que ejecute unos
servicios y programas determinados.

A este tipo de aplicaciones y sistemas se les denomina ​SCM por ​software


configuration management​; a pesar de ese nombre, se dedican principalmente a
configurar hardware, no software. Un sistema de este estilo permite, por ejemplo,
crear un táper (o, para el caso, una máquina virtual, o muchas de ellas) y
automáticamente ​provisionarla​ con el software necesario para comportarse como un
PaaS​ o simplemente como una máquina de servicio al cliente.

En general, un SCM permite crear métodos para instalar una aplicación o servicio
determinado, expresando sus dependencias, los servicios que provee y cómo se
puede trabajar con ellos. Por ejemplo, una base de datos ofrece precisamente ese
servicio; un sistema de gestión de contenidos dependerá del lenguaje en el que esté
escrito; además, se pueden establecer ​relaciones​entre ellos para que el CMS use la
BD para almacenar sus tablas.

Hay ​decenas de sistemas CMS​, aunque hoy en día los hay que tienen cierta
popularidad, como Salt, Rex, Ansible, Chef, Juju y Puppet. Todos ellos tienen sus
ventajas e inconvenientes, pero para la configuración de tápers se puede usar
directamente ​Juju​, creado por Canonical especialmente para máquinas virtuales de
ubuntu que se ejecuten en la nube de Amazon. En este punto nos interesa también
porque se puede usar directamente con contenedores LXC, mientras que no todos
lo hacen.

En el caso de ​lxc​, una forma fácil de gestionar configuraciones es hacerlo con


Vagrant usando ​el driver para lxc​. Usando alguna de las ​cajas base​ de Atlas​, pero
requiere cierta cantidad de trabajo para construir las plantillas con Vagrant;
eventualmente las cajas se tienen que introducir en el directorio de plantillas de ​lxc
para que se puedan usar, o bien usar las de Atlas tales como esta

vagrant init fgrehm/wheezy64-lxc

e iniciarlas con

sudo vagrant up --provider=lxc

Con esto se puede provisionar o conectarse usando las herramientas habituales,


siempre que la imagen tenga soporte para ello. Por ejemplo, se puede conectar uno
a esta imagen de Debian con ​vagrant ssh

Provisionar un contenedor LXC usando Ansible o alguna otra herramienta de


configuración que ya se haya usado

Introducción a Docker
Docker es una herramienta que permite ​aislar​ aplicaciones, creando ​contenedores
que pueden almacenarse de forma permanente para permitir el despliegue de esas
mismas aplicaciones en la nube. Por lo tanto, en una primera aproximación, Docker
serían similares a otras aplicaciones tales como LXC/LXD o incluso las ​jaulas ​chroot​,
es decir, una forma de empaquetar una aplicación con todo lo necesario para que
opere de forma independiente del resto de las aplicaciones y se pueda, por tanto,
replicar, escalar, desplegar, arrancar y destruir de forma también independiente.

Una traducción más precisa de ​container​ sería ​táper​, es decir, un recipiente,


generalmente de plástico, usado en cocina. Si me refiero a un táper a continuación,
es simplemente por esta razón.

Docker​ es una herramienta de gestión de contenedores que permite no sólo


instalarlos, sino trabajar con el conjunto de ellos instalados (orquestación) y
exportarlos de forma que se puedan desplegar en diferentes servicios en la nube. La
tecnología de​Docker​ es relativamente reciente, habiendo sido publicada en marzo
de 2013; actualmente está sufriendo una gran expansión, lo que ha llevado al
desarrollo paralelo de sistemas operativos tales como ​CoreOS​, basado en Linux y
que permite despliegue masivo de servidores. Pero no adelantemos
acontecimientos.

Docker funciona mejor en Linux, fue creado para Linux y es donde tiene mejor
soporte a nivel de núcleo del sistema operativo. Desde la última versión de
Windows, la 10, funciona relativamente bien también en este sistema operativo. Si
no tienes esa versión no te molestes; en todo caso, también en Windows 10 puedes
usar el subsistema Linux (Ubuntu y últimamente OpenSuSE) para interactuar con
Docker. Finalmente, aunque es usable desde Mac, en realidad el sistema operativo
no tiene soporte para el mismo. Es mejor que en este caso se use una máquina
virtual local o en la nube.

Aunque en una primera aproximación Docker es, como hemos dicho arriba, similar a
otras aplicaciones de virtualización ​ligera​como ​lxc/lxd​, que lo precedieron en el
tiempo, sin embargo el enfoque de Docker ​es fundamentalmente diferente​ es
fundamentalmente diferente, aunque las tecnologías subyacentes de virtualización
por software son las mismas. La principal diferencia es que Docker hace énfasis en
la gestión centralizada de recursos y, en una línea que va desde la virtualización por
hardware hasta la generación de un ejecutable para su uso en cualquier otra
máquina, estaría mucho más cerca de ésta que de la primera, mientras que ​lxc/lxd
estarían más enfocados a empaquetar máquinas virtuales completas o casi. En la
práctica, muchas aplicaciones, como la creación de máquinas virtuales efímeras
para ejecución de aplicaciones, van a ser las mismas, pero los casos de uso son
también diferentes, con Docker tendiendo más hacia uso de contenedores de ​usar y
tirar​ y 'lxc/lxd' a una alternativa ligera al uso de máquinas virtuales completas.

En todo caso, Docker se ha convertido últimamente en una herramienta


fundamental para el diseño de arquitecturas de software escalables, sobre todo por
su combinación con otra serie de herramientas como Swarm o Kubernetes para
orquestar conjuntos de contenedores, dando también lugar a todo un ecosistema de
aplicaciones y servicios que permiten usarlo fácilmente e integrarlo dentro de los
entornos de desarrollo de software habituales, especialmente los denominados
DevOps​. Y sí conviene tener en cuenta que un contenedor Docker sería más
parecido al ejecutable de una aplicación (con todo lo necesario para que esta
funcione), que a una máquina virtual, por lo que es más preciso decir que
ejecutamos​ un táper (que utilizaremos a partir de ahora como sinónimo de
"contenedor Docker") que ​estamos ejecutando algo en un táper​, de la misma forma
que ejecutaríamos algo en una máquina virtual.

A continuación vamos a ver cómo podemos usar Docker como simples usuarios,
para ver a continuación como se puede diseñar una arquitectura usándolo,
empezando por el principio, como instalarlo.

Conviene que, en este momento o un poco más adelante, tengas preparad una
instalación de un hipervisor o gestor de máquinas virtuales tipo VirtualBox o similar.
Sea porque quieras tener una máquina virtual Linux específica para esto, o para
tener varias máquinas virtuales funcionando a la vez.

Instalación de Docker
Instalar ​docker​ es sencillo desde que se publicó la versión 1.0, especialmente en
distribuciones de Linux. Por ejemplo, para ​Ubuntu hay que dar de alta una serie de
repositorios​ y no funcionará con versiones más antiguas de la 12.04 (y en este caso
sólo si se instalan kernels posteriores). En las últimas versiones, de hecho, ya está
en los repositorios oficiales de Ubuntu y para instalarlo no hay más que hacer

sudo apt-get install docker-engine

aunque la versión en los repositorios oficiales suele ser más antigua que la que se
descargue de la web o los repositorios adicionales. Este paquete incluye varias
aplicaciones: un ​daemon​, d​ ockerd​, y un cliente de línea de órdenes, d
​ ocker​. La
instalación dejará este ​daemon​ ejecutándose y lo configurará para que se arranque
con el inicio del sistema. También una serie de ​imágenes​ genéricas con las que se
puede empezar a trabajar de forma más o menos inmediata.

Hay también ​diferentes opciones para instalar Docker en Windows​ o en ​un Mac​.

Otra posibilidad para trabajar con Docker es usar ​el anteriormente denominado
CoreOS, ahora Container Linux​. ​Container Linux​ es una distribución diseñada para
usar aplicaciones distribuidas, casi de forma exclusiva, en contenedores, y aparte de
una serie de características interesantes, como el uso de e​ tcd​ para configuración
distribuida, tiene un gestor de Docker instalado en la configuración base. Si es para
experimentar Docker sin afectar la instalación de nuestro propio ordenador, se
aconseja que se instale ​Container Linux en una máquina virtual​.

Con cualquiera de las formas que hayamos elegido para instalar Docker, vamos a
comenzar desde el principio. Veremos a continuación cómo empezar a ejecutar
Docker.

Comenzando a ejecutar Docker


Docker consiste, entre otras cosas, en un servicio que se encarga de gestionar los
contenedores y una herramienta de línea de ordenes que es la que vamos a usar,
en general, para trabajar con él.

Los paquetes de instalación estándar generalmente instalan Docker como servicio


para que comience a ejecutarse en el momento que arranque el sistema. Si no se
está ejecutando ya, se puede arrancar como un servicio

sudo dockerd &

La línea de órdenes de docker conectará con este daemon, que mantendrá el


estado de docker y demás. Cada una de las órdenes se ejecutará también como
superusuario, al tener que contactar con este ​daemon​ usando un socket protegido.

Estamos trabajando con docker como superusuario, que es la forma adecuada de


hacerlo. ​Puedes seguir estas instrucciones para hacerlo desde un usuario sin
privilegios.​ sin privilegios de administración.

Con una instalación estándar,

sudo status docker

debería responder si se está ejecutando o no. Si está parado,

sudo start docker

comenzará a ejecutarlo.

Una vez instalado, se puede ejecutar el clásico

sudo docker run hello-world


Generalmente, vamos a usar Docker usando su herramienta de la línea de órdenes,
docker​, que permite instalar contenedores y trabajar con ellos. El resultado de esta
orden será un mensaje que te muestra que Docker está funcionando. Sin embargo,
veamos por partes qué es lo que hace esta orden.

1. Usa ​sudo​ para ejecutar el cliente de línea de órdenes de Docker. Es más


seguro, porque te fuerza a dar la clave de administrador en cada terminal que
se ejecute. Puede configurarse docker para que lo pueda usar cualquier
usuario, aunque es menos seguro y no lo aconsejamos.
2. Busca una ​imagen​ de Docker llamada h ​ ello-world​. Una imagen es equivalente
a un ​disco de instalación​ que contiene los elementos que se van a aislar
dentro del contenedor. Al no encontrar esa imagen localmente, la descarga
del ​Hub de Docker​, el lugar donde se suben las imágenes de Docker y donde
puedes encontrar muchas más; más adelante se verán.

Unable to find image 'hello-world:latest' locally


latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest:
sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest

1. Crea un ​contenedor​ usando como base esa imagen, es decir, el equivalente


a ​arrancar​ un sistema usando como disco duro esa imagen.
2. Ejecuta un programa llamado h ​ ello​ situado ​dentro​ de esa imagen. Ese
programa simplemente muestra el mensaje que nos aparece. Este es un
programa que el autor ha configurado para que sea ejecutado cuando se
ejecute el comando r​ un​ sobre esa imagen. Este programa se está ejecutando
dentro​ del contenedor y, por tanto, aislado del resto del sistema.
3. Sale del contenedor y te deposita en la línea de órdenes. El contenedor deja
de ejecutarse. La imagen se queda almacenada localmente, para la próxima
vez que se vaya a ejecutar.

De los pasos anteriores habrás deducido que se ha descargado una imagen cuyo
nombre es ​hello world​ y se ha creado un contenedor, en principio sin nombre.
Puedes listar las imágenes que tienes con

sudo docker images


que, en principio, sólo listará una llamada ​hello-world​ en la segunda columna,
etiquetada IMAGES. Pero esto incluye sólo las imágenes en sí. Para listas los
contenedores que tienes,

sudo docker ps -a

listará los contenedores que efectivamente se han creado, por ejemplo:

CONTAINER ID IMAGE COMMAND CREATED STATUS


PORTS NAMES
1e9e7dfe46e3 hello-world "/hello" 3 seconds ago Exited (0) 2
seconds ago focused_poincare
ec9ba7a27e93 hello-world "/hello" About an hour ago Exited (0)
About an hour ago dreamy_goldstine

Vemos dos contenedores, con dos IDs de contenedor diferentes, ambas


correspondientes a la misma imagen, ​hello-world​. Cada vez que ejecutemos la
imagen crearemos un contenedor nuevo, por lo que conviene que recordemos
ejecutarlo, siempre que no vayamos a necesitarlo, con

sudo docker run --rm hello-world

que borrará el contenedor creado una vez ejecutada la orden. Así se mantiene el
número de contenedores bajo y sobre todo se guardan sólo y exclusivamente los
que se piensen mantener o trabajar más adelante. Esta orden pone también de
manifiesto la idea de ​contenedores de usar y tirar​. Una vez ejecutado el contenedor,
se dispone de la memoria y el disco que usa.

Como vemos, los contenedores pueden actuar como un ​ejecutable​, una forma de
distribuir aplicaciones de forma independiente de la versión de Linux y de forma
efímera. De hecho, como tal ejecutable, se le pueden pasar argumentos por línea de
órdenes

sudo sudo docker run --rm jjmerelo/docker-daleksay -f smiling-octopus Uso


argumentos, ea

que usa ​el contenedor ​daleksay​ para imprimir a un pulpo sonriente diciendo cosas.
Como vemos, se le pasa como argumentos ​-f smiling-octopus Uso argumentos, ea​ de
forma que el contenedor actúa, para casi todos los efectos, como el propio
programa al que aísla.
Buscar alguna demo interesante de Docker y ejecutarla localmente, o en su defecto,
ejecutar la imagen anterior y ver cómo funciona y los procesos que se llevan a cabo
la primera vez que se ejecuta y las siguientes ocasiones.

Trabajando ​dentro​ de contenedores.


Pero no sólo podemos descargar y ejecutar contenedores de forma efímera.
También se puede crear un contenedor y trabajar en él. Realmente, no es la forma
adecuada de trabajar, que debería ser reproducible y automática, pero se puede
usar para crear prototipos o para probar cosas sobre contenedores cuya creación se
automatizará a continuación. Comencemos por descargar la imagen.

sudo docker pull alpine

Esta orden descarga una imagen de ​Alpine Linux​ y la instala, haciéndola disponible
para que se creen, a partir de ella, contenedores. Como se ha visto antes, las
imágenes que hay disponibles en el sistema se listan con

sudo docker images

Si acabas de hacer el pull anterior, aparecerá esa y otras que hayas creado
anteriormente. También aparecerá el tamaño de la imagen, que es solamente de
unos 4 megabytes. Otras imágenes, como las de Ubuntu, tendrán alrededor de 200
MBs, por lo que siempre se aconseja que se use este tipo de imágenes, mucho más
ligeras, que hace que la descarga sea mucho más rápida.

Se pueden usar, sin embargo, las imágenes que sean más adecuadas para la tarea,
el prototipo o la prueba que se quiera realizar. Hay muchas imágenes creadas y se
pueden crear y compartir en el sitio web de Docker, al estilo de las librerías de
Python o los paquetes Debian. Se pueden ​buscar todas las imágenes de un tipo
determinado, como Ubuntu​ o ​buscar las imágenes más populares​. Estas imágenes
contienen no sólo sistemas operativos ​bare bones​, sino también otros con una
funcionalidad determinada. Por ejemplo, una de las imágenes más populares es la
de n​ ginx​, la de Redis o la de Busybox, un sustituto del ​shell​ que incluye también una
serie de utilidades externas y que se pueden usar como imagen base.

Comparar el tamaño de las imágenes de diferentes sistemas operativos base,


Fedora, CentOS y Alpine, por ejemplo.

Si usas otra imagen, se tendrá que descargar lo que tardará más o menos
dependiendo de la conexión; hay también otro factor que veremos más adelante.
Una vez bajada, se pueden empezar a ejecutar comandos. Lo bueno de ​docker​ es
que permite ejecutarlos directamente, y en esto tenemos que tener en cuenta que
se va a tratar de comandos ​aislados​ y que, en realidad, no tenemos una máquina
virtual ​diferente​.

Podemos ejecutar, por ejemplo, un listado de los directorios

sudo docker run --rm alpine ls

Tras el sudo, hace falta el comando docker; ​run​ es el comando de docker que
estamos usando, ​--rm​ hace que la máquina se borre una vez ejecutado el comando.
alpine​ es el nombre de la máquina, el mismo que le hayamos dado antes cuando
hemos hecho pull y finalmente l​ s​, el comando que estamos ejecutando. Este
comando arranca el contenedor, lo ejecuta y a continuación sale de él. Esta es una
de las ventajas de este tipo de virtualización: es tan rápido arrancar que se puede
usar para un simple comando y dejar de usarse a continuación, y de hecho hasta se
puede borrar el contenedor correspondiente.

Esta imagen de Alpine no contiene bash, pero si el shell básico llamado ​ash​ y que
está instalado en ​sh​, por lo que podremos ​meternos​ en la misma ejecutando

sudo docker run -it alpine sh

Dentro de ella podemos trabajar como un consola cualquiera, pero teniendo acceso
sólo a los recursos propios.

Trabajando con Alpine Linux


Alpine es una instalación peculiar y más bien mínima, pero es muy interesante para
usarla como base para nuestros propios contenedores, por su minimalismo.
Conviene ​consultar el wiki​ para ver las tareas que se pueden realizar en ella.

Una de las primeras cosas que habrá que hacer es actualizar la distribución. Alpine
usa ​apk​ como gestor de paquetes, y la instalación base no permite hacer gran cosa,
así que para empezar conviene hacer

apk update
apk upgrade
Para que actualice la lista de paquetes disponibles. Después, se pueden instalar
paquetes, por ejemplo

apk add git perl

Una vez añadido todo lo que queramos a la imagen, se puede almacenar o subir al
registro. En todo caso, ​apk search​ te permite buscar los ficheros y paquetes que
necesites para compilar o instalar algo. En algunos casos puede ser un poco más
complicado que para otras distros, pero merece la pena.

Tareas adicionales con contenedores Docker


La máquina instalada la podemos arrancar usando como ID el nombre de la imagen
de la que procede, pero cada táper tiene un id único que se puede ver con

sudo docker ps -a=false

siempre que se esté ejecutando, obteniendo algo así:

CONTAINER ID IMAGE COMMAND CREATED


STATUS PORTS NAMES
b76f70b6c5ce ubuntu:12.04 /bin/bash About an hour ago Up About an
hour sharp_brattain

El primer número es el ID de la máquina que podemos usar también para referirnos


a ella en otros comandos. También se puede usar

sudo docker images

Que, una vez más, devolverá algo así:

REPOSITORY TAG IMAGE ID CREATED VIRTUAL


SIZE
ubuntu latest 8dbd9e392a96 9 months ago 128 MB
ubuntu precise 8dbd9e392a96 9 months ago 128 MB
ubuntu 12.10 b750fe79269d 9 months ago 175.3 MB
ubuntu quantal b750fe79269d 9 months ago 175.3 MB
El ​IMAGE ID​ es el ID interno del contenedor, que se puede usar para trabajar en
una u otra máquina igual que antes hemos usado el nombre de la imagen:

sudo docker run b750fe79269d du

Cómo crear imágenes docker interactivamente.


En vez de ejecutar las cosas una a una podemos directamente ​ejecutar un shell​:

sudo docker run -i -t ubuntu /bin/bash

que ​indica​ que se está creando un seudo-terminal (​-t​) y se está ejecutando el


comando interactivamente (​-i​). A partir de ahí sale la línea de órdenes, con
privilegios de superusuario, y podemos trabajar con el contenedor e instalar lo que
se nos ocurra. Esto, claro está, si tenemos ese contenedor instalado y
ejecutándose.

Cuando se ejecuta ​bash​ se está haciendo precisamente eso, ejecutando un


intérprete de línea de órdenes o ​shell​ de forma aislada del resto de los recursos.
Hablar de ​conectarte​ a un contenedor, en este caso, no tendría mucho sentido, o al
menos tanto sentido como ​conectarse​ a un proceso que está ejecutándose. De
hecho, una segunda ejecución del mismo comando

sudo docker run -it ubuntu /bin/bash

(donde hemos abreviado las opciones ​-i​ y ​-t​ juntándolas) crearía, a partir de la
imagen de Ubuntu, un nuevo contenedor.

En cualquiera de los casos, cuando se ejecuta ​exit​ o ​Control-D​ para salir del
contenedor, este deja de ejecutarse. Ejecutar

sudo docker ps -l

mostrará que ese contenedor está ​exited​, es decir, que ha salido, pero también
mostrará en la primera columna el ID del mismo. ​Arrancarlo​ de nuevo no nos traerá
la línea de órdenes, pero sí se arrancará el entorno de ejecución; si queremos volver
a ejecutar algo como la línea de órdenes, tendremos que arrancarlo y a continuación
efectivamente ejecutar algo como el ​shell
sudo docker start 6dc8ddb51cd6 && sudo docker exec -it 6dc8ddb51cd6 sh

Sin embargo, en este caso simplemente salir del shell no dejará de ejecutar el
contenedor, por lo que habrá que pararlo

sudo docker stop 6dc8ddb51cd6

y, a continuación, si no se va a usar más el contenedor, borrarlo

sudo docker rm 6dc8ddb51cd6

Las imágenes que se han creado se pueden examinar con ​inspect​, lo que nos da
información sobre qué metadatos se le han asignado por omisión, incluyendo una
IP.

sudo docker inspect


ed747e1b64506ac40e585ba9412592b00719778fd1dc55dc9bc388bb22a943a8

te dirá toda la información sobre la misma, incluyendo qué es lo que está haciendo
en un momento determinado.

Para finalizar, se puede parar usando ​stop​.

Hasta ahora el uso de docker ​no es muy diferente de ​lxc​, pero lo interesante​ es que
se puede guardar el estado de un contenedor tal como está usando ​commit

sudo docker commit


8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c
nuevo-nombre

que guardará el estado del contenedor tal como está en ese momento,
convirtiéndolo en una nueva imagen, a la que podemos acceder si usamos

sudo docker images

Este ​commit​ es equivalente al que se hace en un repositorio; para enviarlo al


repositorio habrá que usar ​push​ (pero sólo si uno se ha dado de alta antes).
Crear a partir del contenedor anterior una imagen persistente con ​commit​.

El hacer ​commit​ de una imagen crea una capa adicional, identificada por un SHA
específico, en el sistema de ficheros de Docker. Por ejemplo, si trabajamos con una
imagen cualquiera y hacemos commit de esta forma

sudo docker commit 3465c7cef2ba jjmerelo/bbtest

creamos una nueva imagen, que vamos a llamar ​jjmerelo/bbtest​. Esta imagen
contendrá, sobre la capa original, la capa adicional que hemos creado. Este
comando devolverá un determinado SHA, de la forma:

sha256:d092d86c2bcde671ccb7bb66aca28a09d710e49c56ad8c1f6a4c674007d912f3

Para examinar las capas,

sudo jq '.'
/var/lib/docker/image/aufs/imagedb/content/sha256/d092d86c2bcde671ccb7bb66aca28
a09d710e49c56ad8c1f6a4c674007d912f3

nos mostrará un JSON bien formateado (por eso usamos ​jq​, una herramienta
imprescindible) que, en el elemento ​diff_ids​, nos mostrará las capas. Si repetimos
esta operación cada vez que hagamos un commit sobre una nueva imagen, nos
mostrará las capas adicionales que se van formando.

Examinar la estructura de capas que se forma al crear imágenes nuevas a partir de


contenedores que se hayan estado ejecutando.

Almacenamiento de datos y creación de volúmenes


Docker.
Ya hemos visto cómo se convierte un contenedor en imagen, al menos de forma
local, con ​commit​. Pero veamos exactamente qué es lo que sucede y cómo se lleva
a cabo.

Docker crea un ​sistema de ficheros superpuesto u ​overlay​. Este ​sistema de ficheros


superpuesto​ puede tener varias formas posibles, igual que en Linux hay varios tipos
de sistemas de ficheros posibles; Docker usa diferentes drivers (denominados
​ verlay2​) para estructurar la información dentro del contenedor pero
overlay​ u o
generalmente usa un sistema ​copy on write​que escribe en sistema de ficheros
anfitrión cada vez que se produce una modificación en el sistema de ficheros
superpuesto.

En general, salvo que haya algún problema crítico de prestaciones, es mejor usar el
driver que se use por defecto; dependerá de la implementación de Docker (CE o
EE) y de la versión del kernel. En ​esta página​ se indica como configurar el driver
que se va a usar.

Hay una forma de usar contenedores sólo para almacenar datos, sin que haya
ningún proceso que se ejecute en ellos usando los llamados ​volúmenes​. Se crea
usando ​volume create

sudo docker volume create log

Igual que un contenedor Docker es algo así como un proceso con esteroides, un
volumen de Docker es una especie de disco transportable, que almacena
información y que puedes llevar de un lado a otro. De la misma forma, la
arquitectura de las aplicaciones varía. No vamos a tener una aplicación monolítica
que escriba en el log, lo analice y lo lea, sino diferentes contenedores que
interaccionarán no directamente, sino a través de este contenedor de
almacenamiento.

Por ejemplo, podemos usar un volumen para montar el ​/app​ de diferentes sistemas
operativos, de forma que podamos probar una aplicación determinada en los
mismos. Hagamos

sudo docker volume create benchmark


sudo docker pull fedora
sudo docker run -it --rm -v benchmark:/app fedora /bin/bash

Una vez dentro, se puede crear un minibenchmark, que diga por ejemplo el número
de ficheros ​ls -R / | wc​ y se guarda en ​/app​. Una vez hecho eso, puedes ejecutar ese
programa en cualquier distro, de esta forma:

sudo docker run -it --rm -v benchmark:/app fedora sh /app/bm.sh


87631 81506 1240789
sudo docker run -it --rm -v benchmark:/app alpine sh /app/bm.sh
72284 67414 974042
sudo docker run -it --rm -v benchmark:/app busybox sh /app/bm.sh
72141 67339 972158
Incidentalmente, se observa que de las tres imágenes de contenedores, la que tiene
una mínima cantidad de ficheros es​busybox​. Alpine, como se ha comentado antes,
es una distribución también bastante ligera con casi 14000 ficheros/directorios
menos que fedora.

La utilidad para este tipo de aplicaciones es relativamente limitada; estos


volúmenes, en general, se crean para ser usados por otros contenedores con algún
tipo de aplicación, por tanto. Por ejemplo con ​este microservicio en Perl Dancer2​ de
la forma siguiente

sudo docker run -it --rm -v log:/log -p5000:5000 jjmerelo/p5hitos

Se tendrá que haber construido antes el contenedor Docker, claro.

En este caso, con ​-p​ le indicamos los puertos que tiene que usar; antes de : será el
puerto "externo" y tras él el puerto que usa internamente. El volumen se usa con ​-v
log:/log​; el primer parámetro es el nombre del volumen externo que estamos
usando, en el segundo el nombre del directorio o sistema de ficheros interno en el
que se ha montado.

La aplicación, efectivamente, tendrá que estar de alguna forma configurada para


que ese sea el directorio donde se vayan a escribir los logs; no hace falta crear el
directorio, pero sí que la configuración sea la correcta.

Lo que se hace en este caso es que ​log​ actúa como un volumen de datos,
efectivamente. Y ese volumen de datos es persistente, por lo que los datos que se
escriben ahí se pueden guardar o enviar; también se puede usar simultáneamente
por parte de otro contenedor, ​montándolo​ de esta forma:

sudo docker run -it --rm -v log:/log jjmerelo/checklog

Una vez más, el volumen de docker ​log​ se monta en el directorio ​/log​, un nombre
arbitrario, porque igual que los puntos de montaje del filesystem de Linux, puede ser
en uno cualquiera; el OverlayFS crea ese directorio y lo hace accesible a un
programa, en este caso ​un programa también dockerizado​ que pasa del formato en
texto plano de los logs de ​Dancer2​ a un formato JSON que puede ser almacenado
incluso en otro volumen si se desea.

Crear un volumen y usarlo, por ejemplo, para escribir la salida de un programa


determinado.
Contenedores "de datos"
El problema con los volúmenes es que son una construcción local y es difícil
desplegarlos. Para solucionar esto se pueden usar simples contenedores de datos,
contenedores cuya principal misión es llevar un conjunto de datos de un lugar a otro
de forma que puedan ser compartidos. Crear un contenedor de datos se puede
hacer de la forma siguiente:

FROM busybox

WORKDIR /data
VOLUME /data
COPY hitos.json .

Es decir, simplemente se copia un fichero que estará ​empaquetado​ dentro del


contenedor. Habrá que construirlo. A diferencia de los volúmenes de datos, estos
contenedores de datos sí hay que ejecutarlos. En realidad es igual lo que se esté
ejecutando, por lo que generalmente se ejecutan de esta forma:

sudo docker run -d -it --rm jjmerelo/datos sh

Esta orden escribe un número hexa en la consola, que habrá que tener en cuenta
por que es el ​CONTAINER ID​, lo que vamos a usar más adelante. Como se ve, se
ejecuta como ​daemon​ -​ d​ y se ejecuta simplemente s​ h​. En este contenedor se estará
ejecutando de forma continua ese proceso, lo que puede ser interesante a la hora
de monitorizarlo, pero lo interesante de él es que se va a usar para cargar ese
fichero de configuración desde diferentes contenedores, de esta forma:

sudo docker run -it --rm --volumes-from 8d1e385 jjmerelo/p5hitos

--volumes-from​ usa el ID que se haya asignado al contenedor ejecutándose, o bien


el nombre del mismo, que no es el tag con el que le hemos llamado, sino un nombre
generado aleatoriamente d ​ eeste_estilo​. En este caso no hemos añadido una
definición de volúmenes, por lo que el contenedor se ejecutará y tendrá en /​ data​ el
mismo contenido. Se puede montar también el contenedor en modo de sólo lectura:

sudo docker run -it --rm --volumes-from 8d1e385:ro jjmerelo/p5hitos


con la etiqueta ​ro​ añadida al final del ID del contenedor que se está usando.

Como se ve, se ejecutan varios pasos uno de los cuales implica "tomar" un ID e
usarlo más adelante en el montaje. No es difícil de resolver con un script del shell,
pero como es una necesidad habitual se han habilitado otras herramientas para
poder hacer esto de forma ágil: ​compose

Composición de servicios con ​docker compose


Docker compose​ tiene que instalarse, no forma parte del conjunto de herramientas
que se instalan por omisión. Su principal tarea es crear aplicaciones que usen
diferentes contenedores, entre los que se citan ​entornos de desarrollo, entornos de
prueba o en general despliegues que usen un solo nodo​. Para entornos que escalen
automáticamente, o entornos que se vayan a desplegar en la nube las herramientas
necesarias son muy diferentes.

docker-compose​ es una herramienta que parte de una descripción de las relaciones


entre diferentes contenedores y que construye y arranca los mismos, relacionando
los puertos y los volúmenes; por ejemplo, puede usarse para conectar un
contenedor con otro contenedor de datos, de la forma siguiente:

version: '2'

services:
config:
build: config
web:
build: .
ports:
- "80:5000"
volumes_from:
- config:ro

La especificación de la versión indica de qué versión del interfaz se trata. Hay hasta
una versión 3, con ​cambios sustanciales​. En este caso, esta versión permite crear
dos servicios, uno que denominamos c​ onfig​, que será el contenedor que tenga la
configuración en un fichero, y otro que se llama w ​ eb​. YAML se organiza como un
hash​ o diccionario, de forma que s​ ervices​tiene dos claves c​ onfig​ y w
​ eb​. Dentro de
cada una de las claves se especifica como se levantan esos servicios. En el primer
​ uild​ o construir el servicio a partir del Dockerfile, y se especifica el
caso se trata de b
directorio donde se encuentra; sólo puede haber un Dockerfile por directorio, así que
para construir varios servicios tendrán que tendrán que ponerse en directorios
diferentes, como en ​este caso​. El segundo servicio está en el mismo directorio que
el fichero, que tiene que llamarse d​ ocker-compose.yml​, pero en este estamos
indicando un mapeo de puertos, con el 5000 interno cambiando al 80 externo (que,
recordemos, es un puerto privilegiado) y usando v​ olumes_from​ para usar los
volúmenes, es decir, los datos, contenidos en el fichero correspondiente.

Para ejecutarlo,

docker-compose up

Esto construirá las imágenes de los servicios, si no existen, y les asignará un


nombre que tiene que ver con el nombre del servicio; también ejecutará el
programa, en este caso de w​ eb​. Evidentemente, d​ ocker-compose down​ parará la
máquina virtual.

Usar un miniframework REST para crear un servicio web y introducirlo en un


contenedor, y componerlo con un cliente REST que sea el que finalmente se ejecuta
y sirve como "frontend".

En ​este artículo​ se explica cómo se puede montar un entorno de desarrollo con


Python y Postgres usando Docker Compose. Montar entornos de desarrollo
independientemente del sistema operativo en el que se encuentre el usuario es,
precisamente, uno de los casos de uso de esta herramienta.

La ventaja de describir la infraestructura como código es que, entre otras cosas, se


puede introducir en un entorno de test tal como ​Travis​. Travis permite instalar
cualquier tipo de servicio y lanzar tests; estos tests se interpretan de forma que se
da un aprobado global a los tests o se indica cuales no han pasado.

Y en estos tests podemos usar ​docker-compose​ y lanzarlo:

services:
- docker
env:
- DOCKER_COMPOSE_VERSION=1.17.0

before_install:
- sudo rm /usr/local/bin/docker-compose
- curl -L
https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION
}/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
- docker-compose up

script:
- docker ps -a | grep -q web

Como se ve más o menos, este fichero de configuración en YAML reproduce


diferentes fases del test. Después de seleccionar la versión con la que vamos a
​ efore_install​ borramos (por si hubiera una versión anterior) e
trabajar, en la fase b
instalamos desde cero d ​ ocker-compose​, y finalmente hacemos un b ​ uild​ usando el
​ ocker-compose.yml​ que debe estar en el directorio principal; con u
fichero d ​ p​ además
levantamos el servicio. Si hay algún problema a la hora de construir el test parará; si
no lo hay, además, en la fase s​ cript​ que es la que efectivamente lleva a cabo los
tests se comprueba que haya un contenedor que incluya el nombre w ​ eb​ (el nombre
real será algo así como w ​ eb-algo-1​, pero siempre incluirá el nombre del servicio en
compose). Si es así, el test pasará, si no, el test fallará, con lo que podremos
comprobar offline si el código es correcto o no.

Estos tests se pueden hacer también con simples Dockerfile, y de hecho sería
conveniente combinar los tests de los servicios conjuntos con los tests de
Dockerfile. Cualquier infraestructura es código, y como tal si no está testeado está
roto.

Algunas buenas prácticas en el uso de virtualización


ligera
Una de las principales ventajas que tiene este tipo de virtualización, sea cual sea
como se implemente, es precisamente el hecho de que sea ​ligera​, y que por lo tanto
los contenedores se puedan crear, activar y desactivar rápidamente. Por eso,
aunque pueda parecer a priori que es otra forma de crear máquinas virtuales, su
ámbito de aplicación es totalmente diferente al de estas. Conviene seguir este tipo
de reglas, sacadas entre otros sitios de ​esta lista de buenas prácticas con Docker​ y
esta otra​.

Simplicidad
Los contenedores deben de ser lo más simples posible, y llevar esta regla desde el
principio al final. Idealmente, los contenedores ejecutarán un solo proceso. Esto
facilita el escalado mediante replicación, y permite también separar las tareas en
diferentes contenedores, que pueden ser desplegados o actualizados de forma
totalmente independiente. Esto también facilita el uso de contenedores estándar,
bien depurados y cuya configuración sea lo más segura posible.

Esto también implica una serie de cosas: usar la distribución más ligera que soporte
la configuración, por ejemplo. El usar una distribución ligera y adaptada a
contenedores como ​Alpine Linux​ o ​Atomic Host​ hará que se creen contenedores
mucho más ligeros y rápidos de cargar y que tengan toda la funcionalidad que se
necesita. También conviene eliminar toda aquella funcionalidad que no se necesite y
que se haya usado solamente para construir el contenedor, tales como
compiladores o ficheros intermedios.

Seguridad
Los contenedores docker se ejecutan de forma aislada del resto del sistema
operativo, pero eso no significa que no se pueda penetrar en ellos y llevar a cabo
diferentes actividades no deseadas. Es importante, por ejemplo, que siempre que
sea posible se ejecute la aplicación como un usuario no privilegiado.

Recomendaciones a la hora de construir un contenedor


Docker da una serie de recomendaciones a la hora de construir contenedores​. Para
hacerlo reproducible, se deben usar Dockerfile o el equivalente en otro tipo de
contenedores, y las órdenes que se deben usar y cómo usarlas constituye un acervo
que conviene conocer y usar.

Usando Dockerfiles
La infraestructura se debe crear usando código, y en Docker pasa exactamente
igual. Tiene un mecanismo llamado Dockerfiles que permite construir contenedores
o tápers de forma que lo que quede en control de versiones sea el código en sí, no
el contenedor, con el consiguiente ahorro de espacio. La ventaja además es que en
el Docker hub​ hay multitud de contenedores ya hechos, que se pueden usar
directamente. Veamos un ejemplo, como es habitual para el bot en Scala que
hemos venido usando.

FROM frolvlad/alpine-scala
MAINTAINER JJ Merelo <[email protected]>
WORKDIR /root
CMD ["/usr/local/bin/sbt"]

RUN apk update && apk upgrade


RUN apk add git
RUN apk add curl

RUN curl -sL "http://dl.bintray.com/sbt/native-packages/sbt/0.13.13/sbt-0.13.13.tgz" -o


/usr/local/sbt.tgz
RUN cd /usr/local && tar xvfz sbt.tgz
RUN mv /usr/local/sbt-launcher-packaging-0.13.13/bin/sbt-launch.jar /usr/local/bin
COPY sbt /usr/local/bin
RUN chmod 0755 /usr/local/bin/sbt
RUN /usr/local/bin/sbt

En la primera línea se establece cuál es el contenedor de origen que estamos


usando. Siempre es conveniente usar distros ligeras, y en este caso usamos la ya
conocida Alpine, que tiene ya una versión que incluye Scala. A continuación se pone
la dirección del mantenedor, servidor, y el directorio de trabajo W ​ ORKDIR​ en el que
se va a entrar cuando se ejecute algo en el contenedor. El siguiente comando ​CMD
indica qué se va a ejecutar en caso de que se ejecute el contenedor directamente;
se trata de s​ bt​, el Scala Build Tool. Como se ve, la estructura siempre es la misma:
órdenes en mayúsculas, al principio de la línea. La ​referencia de las mismas se
encuentra en la web de Docker​.

Las siguientes órdenes son todas ​apk​, el gestor de paquetes de Alpine. No tiene
tantos empaquetados como las distros más conocidas, pero sí los básicos; siempre
al principio habrá que actualizar los repos para que no haya problemas.

El resto son otras órdenes ​RUN​, que ejecutan directamente órdenes dentro del
contenedor, y que en este caso descargan un paquete, que es la forma como se
distribuye s​ bt​, y lo ponen como ejecutable. Hay que hacer una cosa adicional: copiar
mediante C​ OPY​ un par de ficheros locales, el .​ jar​ que contiene el programa y el
fichero de hitos que se va a usar para responder al usuario.

Para crear una imagen a partir de esto se usa

sudo docker build -t jjmerelo/bobot .

(o el nick que tengas en GitHub). El ​-t​ es, como es habitual, para asignar un ​tag​, en
este caso uno que se puede usar más adelante en el Docker Hub. Tardará un rato,
sobre todo por la descarga de unas cuantas bibliotecas por parte de sbt, lo que se
hace en la última línea. Una vez hecho esto, si funciona la construcción, se podrá
ejecutar con
sudo docker run --rm -t --env BOBOT_TOKEN=un:token:tocho jjmerelo/bobot

donde --env se usa para pasar la variable de entorno de Telegram que necesita el
bot para funcionar.

Si queremos simplemente examinar el contenedor, podemos entrar en él de la forma


habitual

sudo docker run -it jjmerelo/bot sh

para entrar directamente en la línea de órdenes. El repositorio está en ​bobot​, como


es habitual. En este caso usamos ​CMD​para ejecutar la orden, ya que el contenedor
no recibe ningún parámetro adicional.

Reproducir los contenedores creados anteriormente usando un ​Dockerfile​.

Se pueden construir contenedores más complejos. Una funcionalidad interesante de


los contenedores es la posibilidad de usarlos como ​sustitutos​ de una orden, de
forma que sea mucho más fácil trabajar con alguna configuración específica de una
aplicación o de un lenguaje de programación determinado.

Por ejemplo, ​esta, llamada ​alpine-perl6​ que se puede usar en lugar del intérprete de
Perl6 y usa como base la distro ligera Alpine:

FROM alpine:latest
MAINTAINER JJ Merelo <[email protected]>
WORKDIR /root
ENTRYPOINT ["perl6"]

#Basic setup
RUN apk update
RUN apk upgrade

#Add basic programs


RUN apk add gcc git linux-headers make musl-dev perl

#Download and install rakudo


RUN git clone https://github.com/tadzik/rakudobrew ~/.rakudobrew
RUN echo 'export PATH=~/.rakudobrew/bin:$PATH' >> /etc/profile
RUN echo 'eval "$(/root/.rakudobrew/bin/rakudobrew init -)"' >> /etc/profile
ENV PATH="/root/.rakudobrew/bin:${PATH}"
RUN rakudobrew init

#Build moar
RUN rakudobrew build moar

#Build other utilities


RUN rakudobrew build panda
RUN panda install Linenoise

#Mount point
RUN mkdir /app
VOLUME /app

Como ya hemos visto anteriormente, usa ​apk​, la orden de Alpine para instalar
paquetes e instala lo necesario para que eche a andar el gestor de intérpretes de
Perl6 llamado r​ akudobrew​. Este gestor tarda un buen rato, hasta minutos, en
construir el intérprete a través de diferentes fases de compilación, por eso este
contenedor sustituye eso por la simple descarga del mismo. Instala además alguna
utilidad relativamente común, pero lo que lo hace trabajar "como" el intérprete es la
orden E​ NTRYPOINT ["perl6"]​. E
​ NTRYPOINT​ se usa para señalar a qué orden se va a
concatenar el resto de los argumentos en la línea de órdenes, en este caso,
tratándose del intérprete de Perl 6, se comportará exactamente como él. Para que
esto funcione también se ha definido una variable de entorno en:

ENV PATH="/root/.rakudobrew/bin:${PATH}"

que añade al ​PATH​ el directorio donde se encuentra. Con estas dos características
se puede ejecutar el contenedor con:

sudo docker run -t jjmerelo/alpine-perl6 -e "say π - 4 * ([+] <1 -1> <</<<


(1,3,5,7,9...10000)) "

Si tuviéramos perl6 instalado en local, se podría escribir directamente

perl6 -e "say π - 4 * ([+] <1 -1> <</<< (1,3,5,7,9...10000)) "

o algún otro ​one-liner​ de Perl6​.


En caso de que se trate de un servicio o algún otro tipo de programa de ejecución
continua, se puede usar directamente ​CMD​. En este caso, ​ENTRYPOINT​ da más
flexibilidad e incluso de puede evitar usando

sudo docker run -it --entrypoint "sh -l -c" jjmerelo/alpine-perl6

que accederá directamente a la línea de órdenes, en este caso ​busybox​, que es el


shell​ que provee Alpine.

Por otro lado, otra característica que tiene este contenedor es que, a través de
VOLUME​, hemos creado un directorio sobre el que podemos ​montar​ un directorio
externo, tal como hacemos aquí:

sudo docker run --rm -t -v `pwd`:/app \


jjmerelo/alpine-perl6 /app/horadam.p6 100 3 7 0.25 0.33

En realidad, usando ​-v​ se puede montar cualquier directorio externo en cualquier


directorio interno. ​VOLUME​ únicamente ​marca​ un directorio específico para ese tipo
de labor, de forma que se pueda usar de forma genérica para interaccionar con el
contenedor a través de ficheros externos o para ​copiar​ (en realidad, simplemente
hacer accesibles) estos ficheros al contenedor. En el caso anterior, podíamos haber
sustituido /​ app​ en los dos lugares donde aparece por cualquier otro valor y habría
funcionado igualmente.

En este caso, además, usamos ​--rm​ para borrar el contenedor una vez se haya
usado y ​-t​ en vez de ​-it​ para indicar que sólo estamos interesados en que se asigne
un terminal y la salida del mismo, no vamos a interaccionar con él.

Provisión de contenedores docker con herramientas


estándar
docker​tiene capacidades de provisionamiento similares a otros ​sistemas (tales como
Vagrant​ usando ​Dockerfiles​. Por ejemplo, ​se puede crear fácilmente un Dockerfile
para instalar node.js con el módulo express​.

Gestionando contenedores remotos


Docker es una aplicación cliente-servidor que se ejecuta localmente. Gestionar
contenedores remotos implicaría, generalmente, trabajar con ejecutores remotos
tipo Ansible lo que, en caso de que haya que trabajar con muchos contenedores,
generaría todo tipo de inconvenientes. Para eso está ​docker-machine​, que en
general sirve para trabajar con gestores de contenedores en la nube o con
hipervisores locales, aunque sólo funciona con unos pocos, y generalmente
privativos.

Docker machine se descarga desde Docker​ y su funcionamiento es similar a otras


herramientas como Vagrant. En general, tras crear y gestionar un sistema en la
nube, o bien instalar un ​daemon​ que se pueda controlar localmente, crea un entorno
​ ocker​ con estos entornos
en la línea de órdenes que permite usar el cliente d
remotos.

Vamos a trabajar con VirtualBox localmente. Ejecutando

sudo docker-machine create --driver=virtualbox maquinilla

se le indica a ​docker-machine​ que vamos a crear una máquina llamada ​maquinilla​ y


que vamos a usar el driver de VirtualBox. Esta orden, en realidad, trabaja sobre
VirtualBox instalando una imagen llamada b ​ oot2docker​, una versión de sistema
operativo un poco destripada que arranca directamente en Docker. Como también
suele suceder en gestores de este estilo, se crea un par clave pública-privada que
nos va a servir más adelante para trabajar con esa máquina.

Con ​ls​ listamos las máquinas virtuales que hemos gestionado, así como alguna
información adicional:

$ sudo docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
maquinilla - virtualbox Running tcp://192.168.99.104:2376 v1.12.5
vbox-test - virtualbox Running tcp://192.168.99.100:2376 v1.12.5

Aquí hay dos máquinas, cada una con una dirección IP virtual que vamos a usar
para conectarnos a ellas directamente o desde nuestro cliente docker. Por ejemplo,
hacer ​ssh

$ sudo docker-machine ssh maquinilla


## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\_______/
_ _ ____ _ _
| |__ ___ ___ | |_|___ \ __| | ___ ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__| < __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 1.12.5, build HEAD : fc49b1e - Fri Dec 16 12:44:49 UTC 2016
Docker version 1.12.5, build 7392c3b

Como vemos, estamos en ​Boot2Docker​, un Linux ligero, con el servicio de Docker


incluido, que vamos a poder usar para desplegar y demás.

Si queremos usarlo más en serio, desde nuestra línea de órdenes, tenemos que
ejecutar

sudo docker-machine env maquinilla

Que devolverá algo así:

export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.104:2376"
export DOCKER_CERT_PATH="/home/jmerelo/.docker/machine/machines/maquinilla"
export DOCKER_MACHINE_NAME="maquinilla"
# Run this command to configure your shell:
# eval $(docker-machine env maquinilla)

Si estamos ejecutando desde superusuario, habrá que ejecutar

eval $(sudo docker-machine env maquinilla)

Esa orden exporta las variables anteriores, que le indicarán a docker qué tiene que
usar en ese ​shell​ explícitamente. Cada nuevo shell tendrá también que exportar
esas variables para poder usar la máquina virtual. Las órdenes docker que se
ejecuten a continuación se ejecutarán en esa máquina; por ejemplo,

sudo -E docker pull jjmerelo/alpine-perl6


descargará dentro de la máquina virtual esa imagen y se ejecutará dentro de ella
cualquier orden. En este caso, -E sirve para que las variables de entorno del shell
local, que hemos establecido anteriormente, se transporten al nuevo shell.
Efectivamente, desde el nuevo shell podemos comprobar que existen

REPOSITORY TAG IMAGE ID CREATED SIZE


jjmerelo/alpine-perl6 latest 837fc8bf9307 10
hours ago 491.5 MB

De la misma forma podemos operar con servidores en la nube, con sólo usar los
drivers correspondientes.

Crear con docker-machine una máquina virtual local que permita desplegar
contenedores y ejecutar en él contenedores creados con antelación.

Limpieza de contenedores
Una vez que se lleva trabajando con Docker un rato, puede que nos encontremos
con diferentes contenedores en diferente estado de construcción, así como las
imágenes a partir de las cuales se han creado esos contenedores. Todo eso ocupa
una cierta cantidad de espacio, y conviene de vez en cuando liberarlo para que no
acaben llenando el disco duro de la máquina que se use para desarrollo. Antes de
llegar a eso, conviene recordar la opción -​ -rm​ para ejecutar órdenes dentro del
contenedor, que limpia automáticamente el contenedor y lo elimina cuando se sale
del mismo:

sudo docker run --rm -t -v


/home/jmerelo/Code/forks/perl6/perl6-Math-Sequences:/test
jjmerelo/test-perl6 /test/t

Otros gestores de contenedores


La infraestructura basada en contenedores ha tenido tanto éxito que han surgido
diferentes tipos y también iniciativas de estandarización. El principal competidor este
área es r​ kt​, que además es nativo en el sistema operativo basado en contenedores
CoreOS y que puede usar de forma nativa el formato de contenedores de Docker,
aparte de tener el suyo propio llamado APPC y ​su propio lenguaje para provisionar
contenedores​. A diferencia de Docker, se pueden firmar y verificar imágenes, para
evitar su manipulación externa, y, al estar basado en estándares, puede usar
herramientas de orquestación como Kubernetes u otras.

Por otro lado, la ​Open Container Initiative​ está todavía en una fase muy preliminar.
Aunque contiene especificaciones tanto apara ejecutarlos como para especificar
imágenes, por lo pronto no hay muchas implementaciones de referencia que se
puedan usar. Si acaba cuajando puede hacer que el campo de los contenedores
evite monopolios, así que habrá que estar atentos al mismo. Hay ​trabajo en curso
para comprobar imágenes, por ejemplo.

A dónde ir desde aquí


Primero, hay que ​llevar a cabo el hito del proyecto correspondiente a este tema​.

Si te interesa, puedes consultar cómo se ​virtualiza el almacenamiento​ que, en


general, es independiente de la generación de una máquina virtual. También puedes
ir directamente al ​tema de uso de sistemas​ en el que se trabajará con sistemas de
virtualización completa.

Práctica 0: Uso correcto de Git y GitHub

Descripción
En este hito 0 del proyecto se trata de poner a punto las herramientas que se van a
usar para comunicar los objetivos, los ejercicios y las prácticas durante el resto del
curso. Durante el mismo, se busca también que se interioricen una serie de buenas
​ it​. Para ello, se creará un
prácticas a la hora de trabajar con repositorios de g
repositorio que se usará durante el resto de la asignatura para mostrar el avance el
proyecto de despliegue de una aplicación en diferentes hitos. El repositorio
contendrá

● Fichero con el nombre, formato y extensión convencional, que explique de


qué va a ir el proyecto, en qué va a estar basado y algunas referencias
relacionadas con el mismo, por ejemplo, si se va a usar una práctica de otra
asignatura o el trabajo fin de grado ya terminado o simplemente un proyecto
personal que se considere interesante.
No estamos ​recomendando​ que se use ninguno de ellos. De hecho, preferimos que
no​ se haga. En todo caso, eso es tema para el siguiente hito.

● Licencia que se va a usar en el proyecto.


● Otra serie de ficheros de uso habitual en repositorios.
● Documentación sobre la realización de esta práctica. En este momento no va
a hacer falta, pero conviene que se cree una página web usando los
diferentes mecanismos que ofrece GitHub y se enlace desde el fichero donde
se explique el proyecto.

Estas buenas prácticas se comprobarán a lo largo del resto de los proyectos. Si no


se siguen correctamente el hito del proyecto correspondiente será calificado a la
baja.

Prerrequisitos
Tras darse de alta en el grupo de Telegram de la asignatura y dar al profesor la
cuenta de Google para ver las notas, enviar también por Telegram el nick de GitHub
y cumplimentar los objetivos de la primera sesión.

Explicación
Primero, hay que configurar correctamente el entorno, lo que incluye

● Descarga de ​git​ para usarlo desde línea de órdenes.


● Creación de par de claves y subida de clave pública a GitHub.
● Configuración correcta del nombre y correo electrónico para que aparezca en
los ​commits​.
● Edición del perfil de GitHub para que aparezca una imagen en vez del avatar
por omisión, nombre completo y ciudad, así como universidad.
● Incrementar la seguridad de nuestra cuenta en GitHub activando el segundo
factor de autenticación.

Usar un repositorio de forma correcta no sólo permite organizar el trabajo de forma


más eficiente, sino que también contribuye a que sea más fácil colaborar con él y a
la creación de buenos hábitos de trabajo colaborativo. Hay una serie de​buenas
prácticas​, que incluyen, pero no se limitan, a

● Usar o bien el sistema de control de versiones que se incluya en el entorno


de desarrollo, y con esto quiero decir, por ejemplo, que si se suele usar
Emacs o NetBeans, suelen tener un sistema de control de versiones
integrado, o bien la línea de órdenes, lo que se recomienda. No usar ​nunca​ el
cliente gráfico de GitHub ni, salvo en caso de urgencia (considerándose como
tal que tengas que hacer algo urgentemente y no tengas acceso a tu propio
ordenador, sino sólo a un navegador y recuerdes tu contraseña de GitHub
(no es que estemos recomendando meter contraseñas de nada en
ordenadores ajenos (vale, ya paro. Que no lo hagáis y ya está))), editar
usando el editor de GitHub.
● Trabajar siempre con hitos (​milestones​) y órdenes de trabajo (​issues​) en el
repositorio en GitHub. En este caso, el hito final será la entrega de la práctica
y las órdenes de trabajo las diferentes tareas necesarias para terminar el hito.
● Hacer commits que abarquen una sola funcionalidad o tarea, pero sólo si la
funcionalidad es correcta (no tiene errores sintácticos, por ejemplo). Hacer
commits a menudo.
● Hacer commits descriptivos que indiquen en qué han avanzado la tarea, no
solamente una referencia a la tarea o "cambios en el fichero tal", siendo tal el
fichero que se ha cambiado y que fácilmente se puede ver en el commit.
● Todo ​commit​ debe corresponder a una tarea que se haya establecido en el
repositorio propio, toda tarea se cierra con un commit (simplemente
incluyendo ​closes #[tarea]​, por ejemplo c​ loses #1​ si es el primer ​issue​ o tarea.
Para referenciar una tarea, simplemente se pone el número de la tarea, por
ejemplo

Avanza la tarea #1

● No incluir en el repositorio ningún fichero que pueda ser generado a partir del
mismo, incluir un procedimiento para generar tales ficheros. Por ejemplo,
ningún fichero compilado a partir de otros, o un PDF generado a partir de los
ficheros LaTeX, o los ficheros generados por los entornos virtuales de ciertos
lenguajes. Esos ficheros, además, se tendrán que incluir en .​ gitignore​ para
que no aparezcan como "no seguidos" cuando se haga g ​ it status​.
● No incluir en el repositorio ningún código que no sea propio, incluir en el
mismo el procedimiento para incluir ese código en la compilación o
instalación, generalmente en forma de fichero de requisitos. Si el código
sobre el que se va a trabajar es directamente de otro repositorio, hacer un
fork​ del mismo, no copiar los ficheros. La estructura de un repositorio siempre
tiene que respetarse, y la mejor forma de atribuir correctamente los cambios
es trabajar sobre el repositorio original modificado.
● Usar desde el principio un fichero .​ gitignore​ para evitar añadir
accidentalmente ficheros que no deban estar en el repositorio, como ficheros
de respaldo o ficheros generados en compilación o construcción.
● No incluir ficheros binarios en el repositorio, aunque se necesiten en el
proyecto. Para ello están los ​releases​.
● Si se va a usar algún proyecto anterior, hacer un ​fork​ del mismo, no copiar los
ficheros y subirlos como contribución propia. Las contribuciones, siempre que
sea posible, deben estar firmadas por la persona que las haya creado, por
eso ​no se deben​ copiar simplemente los ficheros, sino ​forkear​ los repositorios
correspondientes.
● Siempre comprobar, antes de hacer un ​pull request​, que se está trabajando
sobre la última copia del fichero para evitar conflictos que imposibiliten que se
lleve a cabo la fusión por parte de la persona encargada del mismo.

Práctica 2: Provisionamiento de
máquinas virtuales

Descripción
Se trata de escribir un fichero que despliegue la infraestructura virtual para los
servicios que se van a usar en el proyecto de la asignatura.

Prerrequisitos
Haber alcanzado el 80% de los objetivos de las ocho primeras semanas de la
asignatura.

Explicación
El primer paso será crear una instancia de una máquina virtual sea localmente o en
algún sistema cloud y dotarla de un sistema operativo básico, aunque para algunos
sistemas de aprovisionamiento como Chef habrá que usar imágenes específicas
que tengan un cliente de c​ hef-solo​ ya instalado.

Una vez que se tiene una instancia de una máquina virtual creada y funcionando, lo
siguiente es dotarla de la infraestructura necesaria para ejecutar nuestro programa.
A priori puede que no sepamos en concreto qué es lo que vamos a usar, pero al
menos tendremos una idea de las aplicaciones y bibliotecas que se van a usar,
aparte de la infraestructura básica que necesitará cualquier aplicación, tal como
instalación de git, creación de un usuario y de los directorios que se van a usar
desde nuestra aplicación.
Para configurar esta máquina virtual se usarán scripts de Chef, Ansible, Salt, Rex,
Puppet o el programa de gestión de configuraciones que se desee. Como es una
continuación del hito anterior, se tendrán que desplegar la infraestructura necesaria
para que funcionen tales programas.

Con que se cree y configure un sólo sistema de provisionamiento (como Ansible o


Chef) es suficiente, pero si se hace con varios sistemas contará como puntos
adicionales.

Los ficheros de aprovisionamiento se pondrán en un directorio aparte del directorio


principal, preferiblemente llamado ​provision​. En caso de necesitar varios ficheros, se
aconseja que se cree un directorio adicional, dentro de este, con el nombre del
sistema de provisionamiento correspondiente.

Finalmente, los ficheros tienen que funcionar correctamente y crear toda la


infraestructura desde 0. Conviene que se elija una imagen de un sistema operativo
original lo más básica posible. Imágenes poco básicas ocupan más especio y
consumen más recursos.

En resumen, hay que seguir los siguientes pasos

1. Decidir qué infraestructura va a usar la aplicación que eventualmente


desplegaremos, desde el sistema operativo hasta el lenguaje, o al menos una
parte de ella.
2. Elegir un sistema local (tipo Xen o KVM) o remoto (Azure u otro) que permita
crear máquinas virtuales. Crear una máquina virtual en el mismo y añadirle la
imagen que se vaya a usar.
3. Crear una ​receta​ en el sistema de provisionamiento elegido y provisionarlo.
Indicar claramente qué sistema de gestión de configuraciones se está usando
en la entrega.

Hito 3º: Automatización de la creación de


máquinas virtuales

Descripción
En este hito se trata de usar los clientes de línea de órdenes de los servicios en la
nube para crear instancias de máquinas virtuales en la nube, que posteriormente se
puede provisionar.
Prerrequisitos
Haber alcanzado el 60% de los objetivos del tema correspondiente.

Explicación
Generalmente el proceso de provisionamiento y despliegue comienza con la
descripción a alto nivel de la infraestructura virtual que se vaya a usar, incluyendo la
creación de las máquinas virtuales y la descripción de las mismas. Usando
herramientas de orquestación como se verán más adelante se puede trabajar de
forma independiente del proveedor de nube; sin embargo, en algunos casos hay
características específicas de un proveedor que no están disponibles en
herramientas de propósito general.

Todos los proveedores de cloud usan un API para acceder al mismo; estas
herramientas CLI se pueden usar desde tal API para crear servicios, monitorizarlos
y manejarlos. En esta práctica usaremos las herramientas libres para hacer este tipo
de tareas de creación de nodos completos de máquinas virtuales y su posterior
provisionamiento con las herramientas creadas en el hito anterior.

Estas herramientas permite también elegir la ​imagen​ que se va a usar, de forma que
se puede automatizar la creación, desde cero, usando shell scripts o usando el CLI
como una biblioteca que se puede incluir desde diferentes lenguajes de
programación, con preferencia, claro, de aquél en el que está escrito la propia
biblioteca.

Aunque en el material de la asignatura hay dos ejemplos, Azure y OpenStack, se


puede usar también Amazon si se desea. De hecho, se aconseja que se creen
scripts de aprovisionamiento para el mismo tipo de máquina virtual en los dos.

Entrega de la práctica
Subir los fuentes a GitHub y ​editar este fichero enlazando el último commit​ en el que
se indique claramente el nombre del alumno y la dirección donde se ha subido el
fuente. Para aceptarse el PR, tendrá que pasar los tests, como es natural.

El script de aprovisionamiento tendrá que llamarse ​acopio.sh​ y estará en el directorio


principal. La máquina virtual se tendrá que mantener encendida al menos hasta que
se corrija, y se tendrá que incluir la dirección en el README.md de esta forma
Despliegue: mv.servicioclo.ud

Se hará constar el nombre del DNS de la máquina o su IP, no el URL porque en


principio no se pide que se despliegue ningún servicio web.

Se recuerda que el ​README.md​ es la descripción del proyecto; por eso, deberá


incluir una descripción general del proyecto, las instrucciones para configurar la
máquina virtual objetivo y ejecutar el provisionamiento desde nuestro ordenador,
incluyendo lo que hay que hacer para instalarlo en nuestra máquina local. En la
documentación, que estará en una rama aparte o donde se haya elegido, se tendrá
que mostrar explicar las diferentes decisiones que se han tomado como la imagen
de sistema operativo elegida y el sistema cloud que se ha usado, qué tipo de
facilidades específicas del sistema se han elegido y mostrar mediante pantallazos
que efectivamente se ha podido hacer provisionamiento de alguna máquina virtual o
incluso de varias y que están ejecutando los servicios desplegados. Las
explicaciones deben ser lo más claras posibles, de forma que más adelante un
compañero pueda corregirlo.

La explicación del proyecto deberá incluir los criterios usados para elegir la imagen
base. Esta documentación se incluirá en ficheros MarkDown en el sitio que se haya
elegido para la misma, preferiblemente un subdirectorio d ​ ocs​ que se haya publicado
desde GitHub.

Hito 4º: Orquestación de máquinas


virtuales

Descripción
En este hito se trata de usar ​Vagrant​ para provisionar una o preferiblemente varias
máquinas virtuales usando un proveedor de servicios cloud.

Prerrequisitos
Haber alcanzado el 60% de los objetivos de las sesiones correspondientes.

Explicación
El proceso de provisionamiento y despliegue comienza generalmente con la
descripción a alto nivel de la infraestructura virtual que se vaya a usar, incluyendo la
creación de las máquinas virtuales y la descripción de las mismas, incluyendo el
aprovisionamiento correspondiente.

Esto se puede hacer usando los SDK o interfaces de línea de órdenes del proveedor
de nube, pero en esta práctica usaremos Vagrant para hacer este tipo de tarea.
Vagrant es un sistema de orquestación de máquinas virtuales que permite usar
diferentes tipos de provisionamiento para configurar las máquinas, así como la
conexión entre las mismas. Vagrant también permite hacer cosas como copiar
ficheros o crear usuarios, que aunque se pueden hacer en el momento del
provisionamiento, puede que sea más conveniente hacerlo en el momento anterior
al provisionamiento porque puede haber algún elemento que tenga que estar
presente antes de que los provisionadores comiencen a funcionar. Finalmente,
permite también elegir la ​imagen​ que se va a usar, de forma que se puede
automatizar la creación, desde cero; esta imagen es independiente del proveedor,
siempre que use alguno de los formatos estándar y también ​provisonarla​ usando
uno de los muchos sistemas de provisionamiento que existen, tales como Chef o
Ansible.

Salvo que las imágenes pueden formato diferente, en principio debería ser posible
cambiar simplemente el ​proveedor​ de Vagrant y poder desplegar en diferentes
entornos, aunque la infraestructura básica puede también tener alguna variación. En
todo caso y como en casos anteriores, se pide al estudiante que use Vagrant en
varios entornos, aunque uno de ellos puede ser un hipervisor local y otro en la nube.
Uno de ellos será siempre el proveedor en la nube de alguno al que el estudiante
tenga acceso.

También se solicita que se elabore en el ​README.md​ del proyecto la razón por la


que han elegido una infraestructura determinada y si se usa una o varias máquinas
virtuales en la nube. Cuanto más complejo sea la arquitectura, más se valorará.

Entrega de la práctica
Subir los fuentes a GitHub y ​editar este fichero enlazando el último commit​ en el que
se indique claramente el nombre del alumno y la dirección donde se ha subido el
fuente. El ​commit​ se referirá al número de ​issue​ que se haya propuesto en el
repositorio propio. Si se va a hacer en coordinación con otros proyectos se puede
indicar en el mismo R ​ EADME.md​.
El ​Vagrantfile​ y todos los ficheros que precise estarán en un subdirectorio llamado
orquestacion​. La IP a la que se ha desplegado, que se comprobará igual que en el
hito anterior, estará en una línea de este tipo

Despliegue Vagrant: mv.servicioclo.ud

donde ​mv.servicioclo.ud​ será el nombre o la IP donde se ha hecho el despliegue


mediante Vagrant.

Se recuerda que el ​README.md​ es la descripción del proyecto; por eso, deberá


incluir las instrucciones para configurar la máquina virtual objetivo y ejecutar el
provisionamiento desde nuestro ordenador, incluyendo lo que hay que hacer para
instalarlo en nuestra máquina local. En la documentación (en una rama aparte o
donde se haya elegido) se tendrá que mostrar que efectivamente se ha podido
hacer provisionamiento de alguna máquina virtual o incluso de varias y que están
ejecutando los servicios desplegados.

Quinto hito: Contenedores para


despliegue en la nube.

Descripción
Antes de desplegarse a producción, las aplicaciones tienen que probarse en un
entorno aislado. Generalmente se denomina ​staging​ a esta etapa en el desarrollo y
despliegue de la aplicación, y se usa algún tipo de entorno, similar en todo menos
en los usuarios al entorno de despliegue definitivo.

Los contenedores también se pueden usar, desde la puesta en producción de


Docker, para desplegar aplicaciones de forma ​idempotente​, es decir, de forma que
la configuración sea exactamente la misma local y en la nube; todas las plataformas
tienen sistemas que permiten desplegar en la nube directamente contenedores en
forma de Dockerfile o de contenedores en Docker Hub ya creados y probados.

En esta práctica se trata de diseñar, usando Docker y describiendo la infraestructura


mediante un ​Dockerfile​, un contenedor o contenedores con el que se puedan hacer
pruebas fácilmente en esta fase la aplicación que se está diseñando.

En una arquitectura de microservicios se usa también Docker para desplegar en


contenedores separados cada uno de los microservicios, y poder publicar la
aplicación fácilmente con todos los contenedores, de una forma más o menos
independiente de la máquina subyacente, aunque no de la arquitectura. Esto
también significa que, en este punto, tendrá que haber una aplicación que
efectivamente devuelva, al menos, un estado, forma de JSON. Esto será lo que se
compruebe para ver que la aplicación de ha desplegado de forma correcta.

Prerrequisitos
Haber alcanzado el 60% de los objetivos del material correspondiente de la
asignatura. En el caso de que no se haya hecho, no se calificará este hito del
proyecto. Haber superado el hito anterior.

Explicación
El principal objetivo de esta práctica es familiarizarse con este tipo de infraestructura
virtual que se usa generalmente para dar un acceso limitado a una aplicación o un
servicio tal como un servidor web o a un usuario, que pueda acceder por ejemplo
sólo para depositar ficheros. Además de usarse para entorno de prueba, se puede
usar también como entorno de producción, en caso necesario, por ejemplo,
poniendo la aplicación en un contenedor de forma que se pueda desplegar con
seguridad en cualquier entorno IaaS o PaaS. De hecho, muchos PaaS usan ​docker
(o algún tipo de infraestructura similar, como l​ xc​) para crear contenedores con los
que se ejecutan las aplicaciones.

El objetivo secundario es el que el alumno tenga instaladas las herramientas


necesarias para trabajar con Docker; también en qué casos conviene usarlas por
motivos de seguridad o de conveniencia. Estas herramientas se añadirán a la
panoplia de un administrador que al terminar la asignatura tendría que tener el
alumno.

Lo importante es que la creación de ese entorno de pruebas sea ​reproducible​. No


bastará mostrar que el entorno funciona, sino que habrá que crear una serie de
scripts tales que, en una instalación determinada sin el contenedor o jaula, se pueda
crear fácilmente ese entorno ​y​ reproducir la aplicación que se va a probar.
Generalmente, si se usa un solo contenedor es suficiente con un Dockerfile. Si se
usan varios, habrá que orquestarlos usando la aplicación correspondiente.

El énfasis de esta práctica es en la creación y uso de este entorno de pruebas, por


lo que también se valorará cómo se han diseñado esas pruebas y lo realista que es
ese entorno. Por supuesto, también se busca que el alumno empiece a usar
sistemas de despliegue reales en su aplicación, usando ​git​, claves, integración
continua y el resto de los sistemas que se usan en el ciclo de vida de un aplicación
moderna.

No se exigirá que se haga ningún fichero de despliegue adicional, pero se valorará


que se usen las herramientas de construcción para hacer un despliegue y
eventualmente arranque de la aplicación en el contenedor. También que se incluya
el despliegue en una herramienta en la nube tal como Azure de forma automática.

Entrega de la práctica
Subir los fuentes a GitHub y hacer un ​pull request​ al documento de entregas como
es habitual. El documento tendrá que incluir el nombre del proyecto y un enlace a un
repositorio de contenedores docker o máquina virtual Azure o Amazon donde se
haya desplegado (o cualquier otro sistema). En caso de que el proyecto no sea
visible, una captura de pantalla, que como todas las capturas tendrá que ir en la
rama de documentación, es suficiente.

El URL del servicio web desplegado en un contenedor se pondrá en una sola línea
de esta forma

Contenedor: https://dirección.url

El URL de DockerHub podrá estar en cualquier lugar del fichero README.md. Si


hay algún despliegue adicional, simplemente también mencionadlo en cualquier
lugar del fichero.

Como en la práctica anterior, esta dirección tendrá que tener instalado un servicio
web que devuelva ​status: OK​, pero en este caso, en la ruta ​status​, no en el directorio
principal; es decir, se tendrá que desplegar, como mínimo, el servicio web del hito
anterior del proyecto.

Sexto hito: composición de servicios

Descripción
Cuando una aplicación no cabe en un solo contenedor por la existencia de varios
tier​, o simplemente nodos que sirven para almacenar datos, es necesario usar
Docker compose para describir de forma repetible la forma como se van a conectar
tales contenedores.
En esta práctica se trata de diseñar, usando Docker compose y describiendo la
infraestructura mediante un fichero ​docker-compose.yml​, un servicio que incluya
varios contenedores, incluyendo uno cuyo contenido exclusivo sea almacenar datos.

Prerrequisitos
Haber alcanzado el 60% de los objetivos del material correspondiente de la
asignatura. En el caso de que no se haya hecho, no se calificará este hito del
proyecto. Haber superado el hito anterior.

Explicación
Docker-compose es una herramienta que, si bien no se usa en producción de forma
habitual, sí es muy útil en entornos de desarrollo o de prueba. También se puede
usar en producción, siempre que no se requieran escalado de cada uno de los
nodos u algún tipo de orquestación complicada, para lo que habría que usar
Kubernetes.

Lo importante es que la creación de ese entorno de pruebas sea ​reproducible​. El


énfasis de esta práctica es en la creación y uso de este entorno de pruebas, por lo
que también se valorará cómo se han diseñado esas pruebas y lo realista que es
ese entorno, es decir, la existencia de un programa real que implemente un servicio
web de forma que se pueda monitorizar desde fuera si extá funcionando o no.

Para comprobar que funciona correctamente, se tendrá que desplegar usando


Vagrant o alguna herramienta específica de un proveedor de nube, copiando todos
los ficheros necesarios, desplegándolo y arrancando los servicios usando Docker
compose.

Entrega de la práctica
Subir los fuentes a GitHub y hacer un ​pull request​ al documento de entregas como
es habitual. El documento tendrá que incluir el nombre del proyecto y un enlace a un
repositorio de contenedores docker o máquina virtual Azure o Amazon donde se
haya desplegado (o cualquier otro sistema). En caso de que el proyecto no sea
visible, una captura de pantalla, que como todas las capturas tendrá que ir en la
rama de documentación, es suficiente.

El URL del servicio web desplegado en un contenedor se pondrá en una sola línea
de esta forma
Hito6: http://dirección.url

El fichero ​docker-compose.yml​ debe estar en el directorio principal del proyecto.

Como en la práctica anterior, esta dirección tendrá que tener instalado un servicio
web que devuelva ​status: OK​, pero en este caso, en la ruta ​status​, no en el directorio
principal; es decir, se tendrá que desplegar, como mínimo, el servicio web del hito
anterior del proyecto. Esto tendrá que implementarse como un servicio REST, y se
tendrá que devolver con el tipo MIME a ​ pplication/JSON​.

Common questions

Con tecnología de IA

Hypervisors are virtualization platforms that create and manage virtual machines, allowing multiple operating systems to share a single physical host . They are classified as Type I, running directly on hardware, or Type II, running on top of an existing OS . In contrast, containers are lightweight and isolate applications within the same OS kernel rather than virtualizing entire machines, which makes them more efficient but less isolative than VMs .

Docker's 'copy on write' file system allows changes to be written on top of existing files without altering the original, thus conserving storage and making processes more efficient . This enables multiple containers to share common files while maintaining unique changes, facilitating quick and efficient container launches and management .

Configuration management tools like Chef and Ansible provide infrastructure as code, allowing systems to be described as programs with control over code and testing . They help automate configuration tasks such as software installation, configuration file editing, and user authentication. These tools enable efficient, reproducible, and scalable management of virtual infrastructures .

Using containerized applications allows for more efficient resource utilization, simplified application deployment, and consistent environments across development and production . Containers provide modularity and portability, reducing infrastructure dependency and facilitating CI/CD implementation, potentially leading to faster innovation and reduced operational costs .

Docker volumes allow data to be decoupled from containers, acting like transportable disks that can be moved and used with different containers . Unlike traditional storage where applications manage logs directly, Docker volumes store data independent of container lifecycle, enabling easier data sharing and persistence across different containers .

Microservices architecture allows for building applications as a collection of small, independently deployable services . PaaS platforms support this architecture by providing a scalable environment to deploy and manage these services efficiently, simplifying the deployment process and handling infrastructure concerns . This combination improves the agility and scalability of software development and deployment .

'Infrastructure as code' refers to managing and provisioning computing infrastructure through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools . This approach supports cloud automation by making infrastructure management more consistent, scalable, and repeatable, facilitating deployment across multiple cloud providers without manual intervention .

Orchestration tools coordinate and manage complex configurations across multiple VM clusters, automating tasks such as deployment, scaling, and management . They enable consistent policy application, resource optimization, and minimize manual interventions, thus enhancing overall operational efficiency and reliability .

Understanding the differences among these tools is crucial because each has unique features, strengths, and weaknesses that impact performance, scalability, and ease of use . Choosing the right tool depends on specific project requirements, existing infrastructure, and team expertise, which can significantly affect operational efficiency and automation success .

Docker containers offer greater deployment flexibility as they are lightweight, allowing faster startup and less resource consumption compared to VMs . They enable easy scalability and integration with CI/CD pipelines. VMs, on the other hand, provide better isolation and compatibility with varying operating systems but are heavier and slower to deploy and scale .

También podría gustarte