Docker parte 1: características y comandos básicos

Continuando la serie de contenedores, hoy vamos a centrarnos en el motor, en Docker.

En la parte 2 de este artículo veremos en formato vídeo la parte de instalación, puesta en marcha y 1 ejemplo real para la creación de un blog mediante una aplicación de WordPress y un MySQL, cada uno en un contenedor y comunicados por una red.

Centrándonos en esta parte, ¿cuáles son los conceptos de Docker y cómo funciona? De forma rápida, Docker es el sistema de contenedores Linux, el estándar de cómo creamos el contenedor, pero no gestiona nada.

Conceptos

Se compone de:

  • Docker Engine. Es el equivalente al Hipervisor en la virtualización
    • Necesario para poder construir imágenes y construir contenedores Docker
    • Se puede instalar en Linux (de forma nativa) pero también en Windows (10Pro) y en Mac (no nativo, virtualizado) con los mismos comandos y la misma experiencia
    • Para instalarlo recurrimos a la web oficial de Docker: http://www.docker.com > Community/Enterprise Edition
  • Imágenes Docker
    • Almacenan un estado de nuestra «VM»: binarios y librerías necesarios para que funcione
    • Se pueden generar mediante “recetas” llamadas Dockerfile. En un Dockerfile se puede escribir en forma de código/script todo lo que quiero que se haga para construir la imagen de mi contenedor
    • Utilizan un sistema de almacenamiento basado en capas, que permite generar imágenes basadas unas en otras y tener control de cambios. Es análogo a clonar una VM a partir de otra y luego añadir lo que necesite
    • Permiten ser compartidas mediante repositorios de imágenes
    • Existen imágenes predefinidas con binaros y librerías ya instalados (Ubuntu, CentOS, Alpine, Nginx, MySQL, Java)

Ejemplo:

Me creo el Dockerfile con este contenido:

FROM ubuntu nos vamos a basar en una imagen existente (ubuntu)

RUN apt-get update && apt-get install nginx va a ejecutar esos comandos a la hora de construir la imagen

Es 1 sola linea que ejecuta 2 comandos = 1 capa (para minimizar). Si quisiera instalar un nginx y un apache, pondría 1º el apache por cumplir con el orden alfabético y facilitar la búsqueda. Si hubiera hecho 2 RUN serian 2 capas

CMD nginx lo que se ejecute cuando arranque la imagen

  1. Ejecuto docker build -t test . (imagen con nombre=test)
  2. Me da la tag «latest»
  3. En mi Dockerhub tendré un usuario (USER) y un repositorio para subir las imágenes (REPOSITORY)
  4. Lo especifico cuando haga docker tag seguido del nombre de la TAG que le quiero dar
  5. Para hacer docker push primero tengo que logearme con docker login
  6. Ya podríamos ejecutar el contenedor Docker run -ti –rm –name test ubuntu
  • Contenedores
    • Un contenedor es una instancia de una imagen
    • Podemos crear tantos contenedores de una imagen como queramos
    • Podemos ejecutarlos, pausarlos, pararlos, destruirlos e incluso guardar cambios
    • Se basan en dos mecanismos para aislar procesos en un mismo sistema operativo. El aislamiento del namespace es el Kernel, que permite que cada proceso solamente sea capaz de ver su propio sistema “virtual”
    • Aunque estén aislados unos de otros, pueden comunicarse unos con otros mediante un driver
    • Un contenedor tiene asignado una cantidad de recursos del sistema. Son los Control Groups (CGroups) del Kernel
    • Cada contenedor va a tener una IP para él solo que va a coger de forma dinámica. No hay ningún problema por tener varios contenedores en el mismo puerto porque cada uno va a tener su IP.
    • ¿Cómo se distribuyen los recursos entre los contenedores? Se pueden compartir recursos de CPU, memoria, red y de I/O de disco. Se pueden asignar de forma absoluta o un porcentaje.
    • Por defecto, los contenedores no son persistentes, los datos son efímeros. Para que sean persistentes y no se pierdan al destruirse, habría que enganchar un «volumen de datos» a los contenedores mediante un plugin. Puede ser un directorio de nuestro sistema donde se guarden todos los datos, un sistema de red, una cabina…
  • Registry. Es el repositorio
    • Usaremos el registry para alojar nuestra imágenes, de forma pública o privada, tener control de versiones y poder integrarlas con nuestros sistemas
    • También usaremos el registry para hacer uso de imágenes de terceros
    • Podemos encontrar distintos tipos de registries: Públicos (Dockerhub, Quay) o Privados

Comandos

  • Comando para ejecutar un contenedor
docker run [PARAMS] [IMAGE] [CMD]

Compuesto por los parámetros que le queremos pasar + la imagen del contenedor + comandos que queremos ejecutar dentro de la imagen.

Ejemplo:

Docker run -ti --rm --name test ubuntu

-ti = terminal interactivo

–rm = para que cuando el contenedor finalice, automáticamente borre los datos del disco. Útil para cuando estas probando y no te deje basura.

–name = para darle un nombre al contenedor

Cuando lo ejecuto ya estoy dentro y puedo ejecutar comandos linux (ls, mkdir…)

Para salir del contenedor ejecuto «e«

El parámetro -p PORT:PORT nos permite redireccionar un puerto del contenedor al host, para poder acceder de forma externa sin necesidad de hacerlo desde dentro.

Ejemplo:

Docker run -ti --rm --name test-nginx -p 8181:80 nginx

Instanciamos un contenedor a partir de la imagen nginx. Nginx lo que hace es ejecutar un servidor web que escucha en el puerto 80 y nosotros podemos acceder a ese servidor a través de nuestro PC con el puerto 8181 (localhost:8181)

  • Comando para ver información de nuestros contenedores creados: nº contenedores, estado: stoped/running/paused, nº imágenes, la versión de Docker q tenemos instalada, la versión del driver de storage, plugins instalados…
docker info
  • Comando que nos da info de los contenedores en ejecución
docker ps
docker ps -a

El parámetro -a lo que hace es darnos info de los que ya no se están ejecutando y el por qué (si hemos sido nosotros o por algún error)

  • Comando que nos da info interna de un contenedor
docker inspect [NOMBRE/ID DEL CONTENEDOR]
  • Comando que nos muestra por pantalla la salida estándar (stdout) y de errores (stderr) del contenedor. Se almacena en un fichero con formato JSON
docker logs [NOMBRE/ID]
  • Comando para ejecutar un comando directamente en un contenedor que esté en ejecución
docker exec -ti [NOMBRE/ID] /bin/bash
  • Comando para parar ejecución de los procesos (contenedor en ejecución pero con los procesos congelados)
docker pause [N/ID]
  • Comando para detener los procesos (se envía una señal SIGINT) y se pueden arrancar después con “start”
docker stop [N/ID]
  • Comando para destruir un contenedor que esté parado de nuestra máquina. Perderíamos todos nuestros datos.
docker rm [N/ID]
  • Comando para ver las imágenes que hay en nuestro sistema
docker images -all
  • Comando para borrar imágenes
docker rmi
  • Comando para construir imágenes
docker build -t [NOMBRE DEL DOCKERFILE] . 

y haremos uso del Dockerfile (receta que nos permitirá construir las imágenes de forma programática). Es un fichero de texto que podemos compartir y versionar. Nos permitirá repetir el proceso de construcción de las imágenes. Pongo el «.» al final para darle la ubicación actual del Dockerfile.

El Dockerfile contiene:

  • FROM: imagen base en la que queremos basarnos (la tengo que tener descargada)
  • RUN: ejecuta un comando durante la creación de la imagen
  • CMD: ejecuta un comando al ejecutarse el contenedor desde la imagen
  • COPY/ADD: copia una fichero dentro de la imagen al construirse

Dockerfile best practices:

  • Cada contenedor debe tener 1 solo objetivo (1 proceso por contenedor al ser posible)
  • Minimizar el nº de capas (RUN, COPY, ADD)
  • Minimizar el tamaño de las capas
  • Ordenar alfabéticamente argumentos multilinea
  • Utilizar imágenes oficiales cuando sea posible (FROM)
  • Todas las imágenes tienen que tener 1 tag. Cuando la creas te da un tag por defecto «latest».

Para darle una etiqueta:

docker tag [NOMBRE DE LA IMAGEN] [USER/REPOSITORY:TAG]
  • Compartir las imágenes que hemos generado con docker build. La idea es utilizar un registry de Docker. Podemos usar el Dockerhub (registry público gratuito)
docker push [USER/REPOSITORY:TAG]
  • Para que los contenedores se comuniquen unos con otros podemos crear una red
docker network create [NOMBRE DE LA RED]

Después, si lo quiero conectar a esa red, cuando lo creo le daría el siguiente comando desde el RUN o conectarlo después desde dentro del contenedor:

docker network connect [NOMBRE RED] [NOMBRE CONTENEDOR]

¡No te pierdas la parte 2 de este artículo para ver, en formato vídeo, la facilidad de uso y utilidad de los contenedores, con un ejemplo real!

Comparte este artículo

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *