Saltar al contenido principal

Antes de empezar

Repositorio

Los despliegues de Docker Swarm requieren unos ficheros yml similares a los de Docker Compose, inicialmente pueden ser muy simples, pero cuanto más grande es un servicio (o cuando tienes muchos), empiezan a complicarse.

Estos ficheros y resto de configuraciones se pueden descargar desde este repositorio o clonarlo directamente en uno de los nodos:

git clone https://gitlab.com/ReiIzumi/swarm-project.git
cd swarm-project/

He numerado los servicios por simplicidad y cada artículo hace referencia a su carpeta.

Explicación de servicios

Cada fichero tiene una explicación (en inglés) del porqué se requiere, la opción elegida para llevarlo a cabo y el paso a paso para su despliegue, también tienen comentarios en cada nueva sección o si se debe tener algo en cuenta.

Estos artículos intentan ser una explicación rápida y de simplificar el despliegue, en caso de requerir más información, aconsejo leer los comentarios de cada servicio.

Formato Swarm

Aunque con la información de estos artículos es suficiente para hacer el despliegue, recomiendo tener unas bases de Swarm.

Este es un ejemplo básico del que todos los demás nacen, no contempla todas las posibilidades (algunos ficheros tienen más de lo que hace este ejemplo), pero es una base para hacerse una idea.

version: '3.5'
services:
# Un stack de Swarm debe contener uno o más servicios
test:
# Si es posible, se debe escoger la versión para evitar que futuros despliegues
# fallen por incompatibilidades no conocidas
image: image:version
# Es recomendable que cada stack tenga su propia network para que los servicios
# puedan comunicarse entre ellos, también es posible que se requiera conectar a
# las redes de otros servicios (para acceder a los proxys por ejemplo)
networks:
- intranet
- agent
# Todas las webs deben estar publicadas por el proxy, pero algunos servicios
# puede que requieran publicar otros puertos
ports:
- 80:80
- 8888:8888/udp
# Siempre que un servicio requiera almacenar datos, se utilizará un volumen,
# debido a que es un clúster, requiere un shared volume, así que se deberá
# decidir previamente cómo se solucionará, si por GlusterFS, NFS u otro
volumes:
- volume-data:/opt/data
- /path/to/disk/data2:/opt/data2
# Los contenedores suelen requerir de configuraciones previas
environment:
- KEY1=VALUE1
- KEY2='VALUE 2'
# Swarm permite el uso de secrets, por defecto se asignan a /run/secrets/secret_name
secrets:
- secret_password
# Los configs son igual a los secrets, ambos son legibles desde cualquier nodo, así que no requieren volumen
configs:
- source: config-name
target: /path/in/container/file.yml
deploy:
# El modo común es desplegar en replicado y elegir el número de replicas o global
# para desplegarlo en todos los nodos
mode: replicated
replicas: 1
# Al actualizar, se inicia primero el nuevo antes de apagar el viejo para evitar
# cortes, algunos servicios pueden no funcionar bien con esto
update_config:
order: start-first
placement:
# En caso de tener que limitar dónde se desplegará, se elige mediante constraints
# predefinidos o añadiendo labels a los nodos
constraints:
- node.role == worker
- node.labels.label_name==label_value
# Si se conocen los límites, es mejor indicarlos
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.5'
memory: 500M
# Para que Traefik publique el servicio, se debe configurar en los labels
labels:
# Traefik leerá los labels si este está a true
traefik.enable: "true"
# Debido a que hay 2 Traefik y un servicio podría tener las redes de ambos,
# cada uno tiene un identificador para evitar que se inicie donde no se espera
traefik.intranet: "true"

# ¡Cada servicio debe tener un 'traefik.htt.services.service_name diferente!

# En modo stack, Traefik no puede leer el puerto, así que siempre hay que indicar
# qué puerto es el que debe ser publicado por este
traefik.http.services.service_name.loadbalancer.server.port: "80"
# URL que se aceptará para redirigir a ese servicio. No puede estar repetida
traefik.http.routers.service_name.rule: "Host(`url.domain.intranet`)"
# Datos fijos para activar el servicio siempre por HTTPS
traefik.http.routers.service_name.entrypoints: "websecure"
traefik.http.routers.service_name.tls: "true"
# Si el servicio tiene una función para confirmar que sigue vivo, es recomendable
# indicarlo, si este no responde en el tiempo asignado, docker desplegará un nuevo
# contenedor
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:8080/echo"]
interval: 60s
timeout: 5s
retries: 3
start_period: 2m
# Además del environment, algunos servicios requieren añadir parámetros en el comando
command: command --arg /path/files
# Los logs escritos por los servicios deberían almacenarse en ELK, a no ser que se
# consideren innecesarios por algún motivo. Si el servicio tiene la opción de enviarlos
# por sí mismo, sería la opción ideal, pero si no es así, Docker puede almacenar los logs
# que él recibe
# En las versiones actuales, configurar esto NO implica perder los logs normales
logging:
driver: "gelf"
options:
gelf-address: "udp://127.0.0.1:12201"
# Cada servicio debe tener un nombre diferente para diferenciarlos más fácilmente
tag: "service_name"

networks:
# Para las redes existentes, como las de proxy, únicamente hay que hacer referencia a ellas
intranet:
external: true
# Las nuevas redes deben ser de tipo 'overlay' para que permitan ser usadas por el clúster
# y encriptadas. También es recomendable prefijar una subnet para tener mayor control
agent:
driver: overlay
driver_opts:
encrypted: 'true'
ipam:
driver: default
config:
- subnet: 10.0.8.0/24

# Debido al uso de shared volumes, todos están referenciados con un absolute path
# Esta configuración es únicamente para volumenes locales, así que no será necesaria
volumes:
volume-data:

# Cada secret que se utilice, debe ser referenciado, igual que las network o los configs
# Este es un ejemplo de secret preexistente
secrets:
keycloak_user:
external: true

# Igual que los secrets, los configs deben estar definidos. Tanto los secrets como los config
# son read-only, así que, si deben cambiar con el tiempo, es preferible indicar una versión
# o día, para poderlos referenciar hacia un nuevo valor sin tener que modificar todo el yml
# Este es un ejemplo donde se define en tiempo de despliegue el nuevo config
configs:
config-name:
file: ./source_file.yml
name: config-name_v1

Configuraciones

Un resumen de secrets, volumenes, networks y otros se encuentra en:

  • Configuraciones: contiene el listado completo de secrets, configs, volumenes, URL (según la URL original de los ficheros que apunta a domain.intranet o domain.cat) y labels de los nodos.
  • Networks: cada servicio tiene una o más redes, para tener mayor control, cada una de ellas tiene un rango asignado y también se indica si puede adjuntarse o no otros contenedores manualmente.