El Swarm (enjambre) de Docker

dockerswarm

En esta entrada vamos a introducir la funcionalidad Swarm que soporta el motor (engine) de Docker, esto nos permitirá desplegar contenedores en varias máquinas añadiendo una infraestructura clúster entre ellas, y así poder utilizar y compartir los recursos de varias máquinas con Docker como si fuera una única instancia.

La gestión de red y algunos recursos es transparente para el usuario, el Swarm se encarga de orquestar la comunicación entre contenedores, también se publican los puertos compartidos de los contenedores por defecto en todos los nodos, por lo que no tendremos de que preocuparnos, da la posibilidad de escalar servicios fácilmente y distribuirlos entre los nodos, etc.

Iniciar el Swarm

En el Swarm de Docker existen dos tipos de nodos, los manager y los worker, siendo el manager el que delega las tareas a los workers y no al revés, el manager también forma parte del Swarm. En un entorno en producción se recomienda tener mínimo unos tres managers para soportar la tolerancia a fallos.

Se inicia el swarm en el manager con el siguiente comando

$ docker swarm init

Para añadir un manager al Swarm se ejecuta el siguiente comando, hay que seguir las instrucciones en el nodo correspondiente

$ docker swarm join-token manager

Para añadir un worker se ejecuta y seguir las instrucciones

$ docker swarm join-token worker

Una vez añadidos los nodos podremos visualizarlos desde el manager ejecutando

$ docker node ls

Desplegar servicios

Para desplegar (deploy) servicios en el Swarm usaremos docker-compose y la configuración correspondiente que está disponible a partir de la versión 3, configuramos un servicio:

version: '3'

services:
  servicio:
    image: imagen_del_servicio
    deploy:
      mode: replicated
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.hostname==nodehostname]
    volumes:
      - ssh-entrypoint:/entrypoint:ro
    networks:
      netservice:

networks:
  netservice:

volumes:
  ssh-entrypoint:
    driver: vieux/sshfs
    driver_opts:
      sshcmd: user@host:/remote_shared_folder
      password: secret
      port: 2222
      sftp_server: "/usr/bin/sudo /usr/lib/openssh/sftp-server"
      allow_other: ""

Ejecutamos el stack que vamos a desplegar en el Swarm

$ docker stack up --with-registry-auth -c docker-compose.yml nombre_del_servicio

La opción –with-registry-auth se utiliza por si usamos imágenes de un registro de Docker propio, el Swarm buscará la imagen a desplegar en los registros que estemos conectados en los nodos y la bajará en cada uno de ellos. Pueden mezclarse arquitecturas para tener un conjunto de imágenes multiarch jugando con el manifest del repositorio, pero esa opción es todavía experimental.

Hemos creado el volumen compartido por sshfs con el driver vieux/sshfs para varias arquitecturas, por lo que cada nodo se conectará a la carpeta /remote_shared_folder del host que indiquemos con la correspondiente configuración por sshfs.

En el apartado node.hostname se indica una máquina concreta para desplegar el servicio, si se omite esta opción se replicará en la que elija el Swarm

Gestión del Swarm

Una vez desplegado el servicio puede verse ejecutando

$ docker service ls

Y si queremos ver uno en concreto

$ docker service ps nombre_del_servicio

Para ver logs de un servicio se ejecuta

$ docker service logs -f nombre_del_servicio

Eliminar un servicio ejecutando

$ docker stack rm nombre_del_servicio

Y eliminar un nodo del Swarm

$ docker node rm nodo_a_eliminar

Por último comentar que podemos usar otras herramientas para gestionar el Swarm como es Portainer, aunque existen otras muchas nos facilita información gráficamente del clúster.

Portainer

Esto es todo por el momento, volveremos pronto con Kubernetes (k8s), saludos

Integración Continua: Gitea + Drone + SonarQube

¿Qué es y para que se usa la integración continua?

Es una práctica de ingeniería de software que consiste en hacer integraciones automáticas de un proyecto lo más a menudo posible para así poder detectar fallos cuanto antes. Entendemos por integración la compilación y ejecución de pruebas de todo un proyecto.

Gitea + Drone + SonarQube

Se utiliza el software libre de Gitea para almacenar los repositorios de código y que conectaremos a Drone para realizar la IC

Drone permite ejecutar las pipelines que tiene configuradas el repositorio, cuenta con mucha variedad de plugins dando posibilidad de despliegues o comunicaciones

SonarQube provee de métricas y analiza la calidad y seguridad del código

Con este stack podremos montar un ciclo de IC que cubre las necesidades básicas, también destaca los pocos recursos que necesita esta configuración pudiendo desplegarse en máquinas pequeñas como una Raspberry

Docker

Usamos Docker para desplegar las aplicaciones en distintos contenedores

version: '2'

services:
  gitea:
    image: gitea/gitea
    restart: always
    container_name: gitea
    hostname: gitea
    volumes:
      - ./data:/data
    expose:
      - 3000
    depends_on:
      - db
    networks:
      mynet:

  db:
    image: mariadb:10
    container_name: gitea-db
    hostname: gitea-db
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=secretdb
      - MYSQL_DATABASE=gitea
      - MYSQL_USER=gitea
      - MYSQL_PASSWORD=secretpass
    volumes:
      - ./db/:/var/lib/mysql
    networks:
      mynet:

  drone:
    image: drone/drone
    restart: always
    container_name: drone
    hostname: drone
    environment:
      - DRONE_GITEA_SERVER=https://gitea.domain.com
      - DRONE_GITEA_CLIENT_ID=secretid
      - DRONE_GITEA_CLIENT_SECRET=secret
      - DRONE_RPC_SECRET=secret-rpc
      - DRONE_SERVER_HOST=drone.domain.com
      - DRONE_SERVER_PROTO=https
    volumes:
      - ./drone:/data
    expose:
      - 80
    networks:
      mynet:

  runner:
    image: drone/drone-runner-docker
    restart: always
    container_name: runner
    hostname: runner
    environment:
      - DRONE_RPC_PROTO=http
      - DRONE_RPC_HOST=drone
      - DRONE_RPC_SECRET=secret-rpc
      - DRONE_RUNNER_CAPACITY=2
      - DRONE_RUNNER_NAME=runner
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    expose:
      - 3000
    networks:
      mynet:

  sonarqube:
    image: sonarqube
    container_name: sonarqube
    hostname: sonarqube
    expose:
      - 9000
    volumes:
      - ./sonar/conf:/opt/sonarqube/conf
      - ./sonar/data:/opt/sonarqube/data
      - ./sonar/logs:/opt/sonarqube/logs
      - ./sonar/extensions:/opt/sonarqube/extensions
    networks:
      mynet:

networks:
  mynet:

Solo hay que crear la app en Gitea para conectarla a Drone y generar las claves de la configuración del runner de Drone. También habría que generar la token del plugin de sonar para cada proyecto de Drone que configuremos

Con esta receta de docker-compose se puede poner un proxy que ataque los distintos contenedores bajo el mismo dominio.com

Eso es todo, nos vemos en la próxima

Dockerizando una Raspberry Pi 4

En esta entrada vamos a instalar y preparar un ordenador Raspberry Pi 4 con la distribución Buster Debian GNU/Linux y arquitectura arm64 (aarch64) aprovechando al máximo el procesador que lleva de serie

Necesitamos la imagen de RaspiOS oficial con el software arm64 instalado, también necesitaremos una tarjeta microSD para grabarla, esto podemos hacerlo con un solo comando desde un terminal

$ curl -s https://downloads.raspberrypi.org/raspios_arm64/images/raspios_arm64-2020-05-28/2020-05-27-raspios-buster-arm64.zip | gzip -d - | sudo dd if=/dev/stdin of=/dev/sdX status=progress

Donde el dispositivo /dev/sdX será el de la memoria microSD

Una vez termina el comando tendremos la imagen grabada en la tarjeta, la conectamos a la Raspberry Pi 4 y arrancará el sistema por defecto

Abrimos un terminal desde la Raspberry y ejecutamos los siguientes comandos

$ echo 'deb https://download.docker.com/linux/debian buster stable' | sudo tee /etc/apt/sources.list.d/docker.list
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt update
$ sudo apt install -y docker-ce

Cuando termine de instalar se necesita reiniciar el sistema para que todo funcione correctamente, y ya tenemos docker en nuestro ordenador arm64

$ docker -v
Docker version 19.03.12, build 48a6621

Kernel Version: 5.4.51-v8+
Operating System: Debian GNU/Linux 10 (buster)
OSType: linux
Architecture: aarch64
CPUs: 4
Total Memory: 7.633GiB
Name: raspberrypi

Hasta la próxima…

Domingos Negros – 0x06 – Docker en pequeñas dosis

Seguimos en la brecha. Este domingo una sesión especial, un taller para iniciarse en la secta Docker

El compañero @ale nos trae “Docker en pequeñas dosis” para que sea digerible y no enganche en la primera cita

¿Cuando será la sesión?

El Domingo 31 de Mayo a las 19:30h UTC+2

Siempre es a las 19:30 los domingos 🙂

¿Dónde será la sesión?

En el sitio de siempre: https://mumble.hatthieves.es
El streaming será en: https://p2p.hatthieves.es

¿He de pagar algo?

Son sesiones gratuitas. Eso no quiere decir que no cuesten trabajo y tiempo. Pero tu no tendrás que pagar nada

¿Puedo ir informándome sobre el contenido del taller?

Siempre puedes leer las entradas que tenemos publicadas sobre el tema

Te esperamos el domingo para una sesión más de Domingo Negro


Díselo a tu primo, saludos

Docker con IPv6

docker6

Aquí vamos a explicar como habilitar el protocolo IPv6 en Docker, esto permitirá comunicar nuestros contenedores con el mundo exterior por este protocolo aparte del conocido IPv4, funciona solamente en GNU/Linux

Para esto hay que tener configurado en el servidor anfitrión IPv6 en la interfaz WAN (la que llega a internet), en algunos proveedores puede venir activado por defecto pero no es la norma, para probar si tenemos IPv6 en el anfitrión haremos un ping6

$ ping6 2600::

Si responde correctamente las solicitudes ICMPv6 tendremos IPv6 en la máquina, sino hay que negociar con el proveedor como habilitarlo

En el archivo /etc/docker/daemon.json (si no existe lo creamos) hay que añadir lo siguiente como se indica en la documentación de Docker

{
  "ipv6": true,
  "fixed-cidr-v6": "2001:db8:1::/80"
}

El parámetro fixed-cidr-v6 indica la subred interna que usarán los contenedores, es decir, la subred de la interfaz bridge que normalmente identificamos con docker0, se configurará con la primera dirección de la red disponible

Se elige la máscara de red /80 que es menor que la que hay en la interfaz externa /64 para evitar problemas en el enrutado de los paquetes IPv6, lo recomendable es usar una máscara con un rango menor, si ponemos una máscara /64 o del mismo valor que la externa se tiene que usar algún demonio de enrutado como ndppd

Una vez se reinicia Docker puede verse configurada la interfaz interna de red bridge con IPv6 habilitado y la consiguiente red asignada con el comando

$ docker network inspect bridge

Para usar IPv6 en los contenedores habrá que enrutar el tráfico de vuelta hacia la interfaz externa haciendo masquerading con el comando ip6tables

$ ip6tables -t nat -A POSTROUTING -s 2001:db8:1::/80 -j MASQUERADE

Además hay que configurar el sysctl de la máquina para preparar el kernel con un entorno IPv6, añadimos las siguientes líneas al archivo /etc/sysctl.d/99-sysctl.conf modificando la interfaz de red de salida por la correspondiente (hay que reiniciar la máquina)

net.ipv6.conf.default.accept_ra=0
net.ipv6.conf.all.accept_ra=0
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
net.ipv6.bindv6only=0
net.ipv6.conf.default.disable_ipv6=0
net.ipv6.conf.all.disable_ipv6=0
net.ipv6.conf.default.proxy_ndp=0
net.ipv6.conf.all.proxy_ndp=0
net.ipv6.conf.ensXXX.proxy_ndp=1
net.ipv6.conf.ensXXX.accept_ra=2
net.ipv6.conf.ensXXX.forwarding=0

Solo queda instanciar un contenedor y comprobar que tenemos ping6 desde él

$ docker run -ti --rm debian:buster ping6 -c10 2600::
PING 2600::(2600::) 56 data bytes
64 bytes from 2600::: icmp_seq=1 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=2 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=3 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=4 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=5 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=6 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=7 ttl=52 time=141 ms
64 bytes from 2600::: icmp_seq=8 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=9 ttl=52 time=140 ms
64 bytes from 2600::: icmp_seq=10 ttl=52 time=140 ms

--- 2600:: ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 20ms
rtt min/avg/max/mdev = 139.906/140.204/141.457/0.475 ms

Y colorín colorado este post se ha terminado, esperamos os haya gustado, nos vemos en la próxima

Haraka Wildduck

duck

En esta entrada vamos a completar el taller que dimos hace poco de como montar un servidor de correo electrónico completo y autogestionado en NodeJS

Tenemos que bajar el repositorio git que montamos para desplegar la aplicación en contenedores Docker por lo que necesitaremos tener instalado docker y docker-compose

Una vez descargado, generamos los certificados seguros para la aplicación en la carpeta secure con el script start.sh también hay que modificar el dominio sobre el que vamos a tener el correo en el archivo .env, además el DNS del dominio tiene que tener un registro MX apuntando a la máquina donde se despliegue el servidor

Arrancamos los servicios con el siguiente comando

docker-compose up -d

Si queremos mantener la persistencia deberemos copiar las base de datos

docker cp mongo:/data/db ./mongodb && chown -R 999.999 ./mongodb
 docker cp redis:/data ./redis && chown -R 999.999 ./redis

Descomentar las lineas del archivo docker-compose.yml

docker-compose down && docker-compose up -d

Y tendremos un servidor de correo en NodeJS nativo funcionando bajo nuestro control, saludos

Montar una instancia Nextcloud en Docker

nextcloud

En esta entrada vamos a explicar brevemente como montar una instancia de Nextcloud en Docker, Nextcloud es un potente servicio de software libre de cloud (nube), sirve para almacenar, sincronizar y compartir archivos principalmente aunque con sus plugins se pueden añadir muchas funcionalidades, reemplaza al viejo proyecto ownCloud.

Para ello utilizaremos la imagen oficial de Nextcloud que podemos encontrar en el hub de Docker, esto nos facilitará el despliegue y es más fácil que mantener una imagen propia, podemos encontrar distintas versiones de la imagen aunque nos basaremos en la última latest, además contamos con las últimas actualizaciones de seguridad.

Usaremos el programa docker-compose para el despliegue de la instancia, con el que configuramos los contenedores necesarios con nuestras opciones, en vez de hacerlo directamente desde la línea de comandos.

version: '2'

services:
  nextcloud:
    image: nextcloud
    restart: always
    container_name: nextcloud
    hostname: nextcloud
    environment:
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=xxx
      - MYSQL_HOST=mariadb-nextcloud
    depends_on:
      - mariadb-nextcloud
    expose:
      - 80
    links:
      - mariadb-nextcloud
    volumes:
      - ./nextcloud/html/custom_apps:/var/www/html/custom_apps
      - ./nextcloud/html/config:/var/www/html/config
      - ./nextcloud/html/data:/var/www/html/data
      - ./nextcloud/html/themes:/var/www/html/themes
    networks:
      - cloudnet:

  mariadb-nextcloud:
    image: mariadb
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    restart: always
    container_name: mariadb-nextcloud
    hostname: mariadb-nextcloud
    volumes:
      - ./nextcloud/mysql:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=yyy
      - MYSQL_PASSWORD=xxx
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
    networks:
      - cloudnet:

networks:
  cloudnet:

Teniendo este archivo en una carpeta, con el comando docker-compose up -d nos bajaremos la última versión de las imágenes de Nextcloud y MariaDB desde el hub de Docker, y seguidamente se instanciarán ambos contenedores. Hemos preferido compartir los volúmenes de los contenedores con el sistema de archivos local para mantener la persistencia por si se elimina el contenedor, y configurar las imágenes con las variables de entorno necesarias para el correcto funcionamiento del servicio.

También hemos relacionado los contenedores (links) y expuesto el puerto 80/tcp, ya que nuestro servicio está detrás de un proxy nginx, del contenedor Nextcloud para utilizarlo aunque pueden incluirse los que se necesiten.

Una vez se levanten los contenedores podremos acceder al servicio por el puerto correspondiente y comenzar a configurar la instancia con las opciones que necesitemos.

Esperamos que esta entrada os haya gustado y os sirva para futuros despliegues, saludos

Empezando con Docker

docker

¿Qué es Docker?

Docker es un proyecto de código abierto que automatiza el despliegue de aplicaciones dentro de contenedores de software, proporcionando una capa adicional de abstracción y automatización de virtualización de aplicaciones en múltiples sistemas operativos.

Docker utiliza características de aislamiento de recursos del kernel Linux, tales como cgroups y espacios de nombres (namespaces) para permitir que “contenedores” independientes se ejecuten dentro de una sola instancia de Linux, evitando la sobrecarga de iniciar y mantener máquinas virtuales.

¿Qué NO es Docker?

Docker no es como otros sistemas de virtualización completa de un sistema operativo, no emula el hardware necesario para arrancarlo, utiliza el de la máquina anfitriona compartiendo los recursos de la misma entre los contenedores.

En resumen Docker es una herramienta que nos da la capacidad de trabajar con contenedores (máquinas virtuales) que comparten un mismo Kernel.

Vamos a ver un poco las partes básicas de Docker para hacer funcionar un sistema, y luego explicaremos como podemos automatizarlo con docker-compose.

Contenedores

Es el espacio donde se almacenan los datos de la máquina virtual, contiene todos los archivos necesarios para funcionar independientemente del sistema anfitrión. Un contenedor es una instancia de una imagen.

Imagen

Es un prototipo de un contenedor, así como una clase es el prototipo de un objeto y este una instancia de la misma, un contenedor es una instancia de una imagen. Varios contenedores pueden compartir una imagen, pero una imagen solo se corresponde con un contenedor.

Volumen

Es un espacio de almacenamiento que comparte el contenedor con la máquina anfitriona para tener persistencia de los datos, los volúmenes pueden ser de varios tipos como los internos (que los gestiona Docker) o pueden usarse mayormente carpetas compartidas.

Red

Las interfaces de red pueden configurarse según se necesite, tenemos varias como host, que comparte con la máquina anfitriona, el bridge que genera un puente de red o null que no tiene red.

Automatizar Docker con docker-compose

Docker compose es una herramienta creada por Docker, que permite crear una pila de contenedores intercomunicados, partiendo de distintas imágenes.

Docker compose se basa en un fichero con extensión yml donde vamos a indicar que imagen queremos desplegar, cómo se va a configurar y de qué depende.

version: '3'
services:
  web:
    build: .
    ports:
    - "80:80"
    volumes:
    - ./html:/var/www/html
    networks:
      net:

networks:
  net:

En el anterior ejemplo vemos como se crea un contenedor que compila un Dockerfile de la carpeta local, que comparte el volumen de la carpeta local html y también crea la configuración de red.

Conclusión

Las principales ventajas de la virtualización basada en contenedores son:

  • Menos recursos, ahorro en costes. En una misma máquina pueden desplegarse más contenedores que máquinas virtuales tradicionales. Las exigencias en el proceso de inicio y espacio en disco son menores y más rápidas.
  • Gestión TI más fácil, aumento de la productividad TI. La creación de contenedores permite estandarizar los despliegues ya que son entornos repetibles para tareas de desarrollo, prueba y producción. La compatibilidad con todos los sistemas de implantación elimina un valioso tiempo de configuración. Son elementos totalmente portables. Con Docker la implementación se realiza en segundos.
  • Múltiples aplicaciones independientes en un mismo host. Cada aplicación se ejecuta en su contenedor o clúster de contenedores de forma independiente, sin entrar en conflicto con el resto de aplicaciones que aloje el host que ejecutarán mediante sus propios contenedores. Esto garantiza un entorno seguro y eficiente.

Cuando una aplicación ya no es necesaria, simplemente se elimina su contenedor sin dejar huella en el sistema donde se ejecutaba.