_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

Gestiona tus despliegues sin perder la cabeza

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

Ignacio López Flores - @ignaciolflores

Founder & CEO @ Introbay

ignacio@introbay.com

Qué es Docker

A alto nivel: una VM ligera

  • Propio espacio de procesos
  • Propia interfaz de red
  • Puede ejecutar órdenes como root
  • Puede tener su propio /sbin/init
  • Contenedor == Máquina

A bajo nivel: CHROOT con esteroides

  • Puede no tener su propio /sbin/init
  • Contenedor = procesos aislados
  • Comparte el kernel con el host
  • Contenedor == Aplicación

VMs vs Contenedores

Los contenedores están aislados, pero comparten el kernel

... lo que aporta los beneficios de las VM pero con mucha menos sobrecarga, más portabilidad y eficiencia

Dockerfile

Ejemplo práctico

  • Compilación de una app
  • Ejecución de una app

Dockerfile

Dockerfile para la compilación
FROM golang:1.7.3
WORKDIR /go/src/mi-app/
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
docker build -t usuario/app:build .
docker create --name extract usuario/app:build
docker cp extract:/go/src/mi-app/app ./app
docker rm -f extract

Dockerfile

Dockerfile ejecución
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]

docker build --no-cache -t usuario/app:latest .
rm ./app

Dockerfile

Dockerfile.build
FROM golang:1.7.3
WORKDIR /go/src/mi-app/
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
Dockerfile
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]

Dockerfile

build.sh
#!/bin/sh
echo Building usuario/app:build
docker build -t usuario/app:build . -f Dockerfile.build
docker create --name extract usuario/app:build
docker cp extract:/go/src/mi-app/app ./app
docker rm -f extract

echo Building usuario/app:latest
docker build --no-cache -t usuario/app:latest .
rm ./app

Multi-stage builds

  • Podemos usar varias sentencias FROM en un Dockerfile
  • Cada sentencia FROM puede usar una imagen base diferente
  • Cada sentencia FROM comienza un nuevo ciclo de construcción
  • Podemos copiar los artifacts generados en ciclos posteriores

Multi-stage builds

Dockerfile
FROM golang:1.7.3
WORKDIR /go/src/mi-app/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/mi-app/app .
CMD ["./app"]
docker build --no-cache -t usuario/app:latest .

Multi-stage builds

Nombrar fases
FROM golang:1.7.3 as builder
WORKDIR /go/src/mi-app/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/mi-app/app .
CMD ["./app"]

Multi-stage builds

Definir fase objetivo

Podemos parar la construcción en la fase que necesitemos
docker build --target builder -t usuario/app:latest .

Puede resultar útil para

  • Depuración de una fase específica
  • Utilizar una fase de depuración en la que se habiliten ciertas herramientas
  • Utilizar una fase de test en la que añadamos datos a la aplicación

El sistema de capas

Al generar una imagen, Docker crea una capa por cada instrucción que escribimos en nuestro Dockerfile.

El sistema de capas

Con el fin de minimizar el número de capas, concatenamos instrucciones usando && y otras técnicas.

El sistema de capas

Además, cuando creamos un contenedor, docker le asigna una "thin-layer", que no es más que una capa encima de todas las demás y que tiene la peculiaridad de ser de lectura-escritura.

El sistema de capas

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py

El sistema de capas

El sistema de capas

Capas compartidas

Volúmenes

La información que se escribe en la "thin-layer" de un contenedor se destruye con el propio contenedor.

¿Qué pasa si quiero que mis datos sean persistentes?

Volúmenes

Existen tres tipos de almacenamientos

Volúmenes

Volumes se almacenan en una parte del sistema de ficheros gestionada por Docker.

El resto de procesos no deberían modificar estos datos.

Volúmenes

Bind mounts pueden estar almacenados en cualquier parte del sistema.

Otros procesos podrían modificar estos datos.

Volúmenes

tmpfs mounts se almacenan en la memoria del anfitrión solamente, y nunca se escriben el en sistema de ficheros.

Volúmenes

Volúmenes

Desarrollando con Docker

Desarrollando con Docker

  • La fase de desarrollo necesita herramientas extra que no se usarán en producción
  • Multi-stage
  • Los desarrolladores necesitarán compilar dentro del contenedor
  • Evitar tener que generar contínuamente las imágenes

Desarrollando con Docker

  • docker exec -it mi_app bash
  • Volumes
  • Permisos
  • En la fase (stage) de desarrollo, es útil añadir scripts de ayuda

Desarrollando con Docker

  • Usando docker-compose podemos definir stacks
  • Podemos empaquetar un directorio con docker-compose.yml + ficheros con datos para desarrollo y compartirlo

Desarrollando con Docker

app:
  build: ./build/app/.
  links:
    - db:db
  volumes:
    - ./files/drupal:/var/www
    - /etc/localtime:/etc/localtime:ro
    - $HOME/.ssh/id_rsa:/root/.ssh/id_rsa # WARNING: only for dev
    - $HOME/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub # WARNING: only for dev
    - $HOME/.ssh/known_hosts:/root/.ssh/known_hosts # WARNING: only for dev
    - ~/.gitconfig:/root/.gitconfig # WARNING: only for dev
  ports:
    - 80:80
  mem_limit: 1024m
db:
  image: mariadb:10.2
  env_file:
    - ./mysql.env
  volumes:
    - ./files/db:/var/lib/mysql
  mem_limit: 748m
  ports:
    - "3306:3306"

Docker en Producción

Diferencias con el entorno de desarrollo
  • Sólo tiene lo necesario
  • No hay herramientas para desarrollar
  • El objetivo es que la imagen resultante sea lo más ligera posible
  • El código fuente (o el ejecutable) están dentro de la imagen

Docker en Producción

Docker Registry
  • Se puede usar el servicio de Docker u otros
  • Podemos montar el nuestro usando una imagen oficial
  • Almacena nuestras imágenes
  • Etiquetar las imágenes puede resultar útil

Docker en Producción

Balanceo de carga y vhosts
  • Existen imágenes que nos facilitan la vida (jwilder/nginx-proxy)
  • Hacen uso de /var/run/docker.sock

Docker en Producción

Let's Encrypt
  • Existen imágenes que nos facilitan la vida (jrcs/letsencrypt-nginx-proxy-companion)
  • Se acopla al balanceador

Integración contínua

  • Construcción automática de las imágenes
  • Etiquetado automático de las imágenes
  • Gestión de las actualizaciones en nuevos despliegues
  • Test
  • Rollback

Integración contínua

Gestión de las actualizaciones en nuevos despliegues
Backup
image: mi/app:nuevaversion
docker-compose stop app
docker-compose rm -f app
docker-compose up -d app
docker-compose exec app sh /code/scripts/update.sh

Integración contínua

Test
  • Añadir una fase que incorpore herramientas de testing
  • Usar hooks en nuestro gestor de git que lancen procesos automáticos
  • Usar el resultado para saber si podemos mezclar
  • Construir un entorno a partir de una rama
  • Construir un entorno cuando hay una mezcla

Gracias