Docker
Qué ès?
Com les màquines virtuals serveix per aillar processos. A diferència de les MV's, no duplica recursos.
Es basa en dues tecnologies de Linux:
- cgroups: per limitar els recursos (memoria, cpu...) dels processos
- namespaces: Limita l'avast que pot veure el procés (altres processos, xarxa, sistema de fitxers...)
Docker afegeix una capa per sobre:
- Aporta una forma uniforme de definir i accedir a les aplicacions
Containers
Creem una container interactiu (-it) de docker, basant-nos en una imatge d'Ubuntu.
- Imagen: definición de entorno (ficheros)
- Se identifican por un nombre
- Se montan una encima de otra (podemos hacer una imagen de mysql encima de una de Ubuntu)
- Hay un repositorio de imagenes construidas de donde bajarlas
- También podemos crearla con un Dockerfile
- Container: instancia de una imagen
- Tienen un id
- Puede estar encendida o apagada
- Mantiene el estado entre encendidos si no la borramos
- Executa una comanda dintre d'un sistema de fitxers "copiat" de la imatge.
- Les modificacions que fem sobre el sistema de fitxers no afecten a altres instancies d'ubuntu que llencem.
- Els processos que llencem a dintre son visibles des de el sistema pare, pero no al reves.
- Quan termina el proces (en aquest cas es bash, per defecte) la imatge creada queda parada (no es destrueix)
- Des de fora podem parar el process amb
docker stop 451bccb8de1c
docker ps -a # L'opció -a es per veure tambe els que estan parats
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5fabbdd99c65 ubuntu "bash" 5 minutes ago Exited (0) 5 minutes ago heuristic_lamarr
Podem fer servir tant els id's com els noms per referir-nos a les instàncies.
docker log 5fabbdd99c65 # ens mostra la sortida estandard del procés principal, util sobretot si no l'hem executat interactiu o hem sortit
docker log -f 5fabbdd99c65 # 'follow' sigue monitorizando lo que sale despues
docker stop 5fabbdd99c65
docker start -i 5fabbdd99c65 # para arrancarlo otra vez
docker rm 5fabbdd99c65 # para borrarlo definitivamente del sistema
docker run -it ubuntu # volveria a arrancar uno diferente
docker exec -it 5fabbdd99c65 ls -alF
Imatges
Les imatges de partida les trobem a la web Docker Hub
Las images tienen tags (versiones)
Dockerfile
FROM scratch
ADD mytarball.tar.gz
RUN tar xvfz mytarball.tar.gz
COPY file /var/www/html
CMD ['/bin/bash']
FROM
es la imagen de base imagen:tagADD
copia del sistema de ficheros local a dentro del containerRUN
ejecuta comando dentro del containerENV
establece una variable de entorno para los comandos que se ejecuten (Sobrescribible desde fuera)CMD
fija el comando inicial, es una array estilo argv (elemento por parametro)- Sobreescribible por cli con el parametro del run
ENTRYPOINT
fija el comando para arrancar el comando inicialsh -c
- Sobreescribible por cli con
--entrypoint
- Sobreescribible por cli con
LABEL
metadata de la imagen mylabel="value"- maintaner, application...
WORKDIR
cambia al directorio para los siguientes comandos
Per a executar el Dockerfile
La imatge es dirà myapp
- Per obrir i mapejar ports -pOUTER:INNER
- Per esborrar el container en acabar el run,
--rm
-f mydockerfile
si el fichero no se llamaDockerfile
(por ejemplo para tener varios en un directorio)
Environ
Patron util usar las variables de entorno para modificar la configuración.
- Se usa ENV en el Dockfile para definir el valor por defecto
- Se sobrescribe en CLI
docker run
con la opcion-e VAR=value
- Los programas lo cogen usando
- Se usa en los ficheros copiados substituyendo ${VAR}
Cache
- Docker crea capas cada comando sacando el checksum como id.
- Se reaprovechan los pasos de anteriores ejecuciones.
- Interesa poner abajo los comandos que más a menudo vayan a alterar la imagen.
- Puede interesarnos juntar varias sentencias RUN con
&&
para reducir las capas
Multistage build
Útil per a no incloure a la imatge final tot el que cal per construir l'aplicació, com ara l'es eines de build i les dependencies de desenvolupament (composer, babel, cython...).
- Tamany: Ocupa menys
- Seguretat: Es redueix la superficie d'atac
Construim l'aplicació en una imatge i després copiem els fitxers construits a l'imatge final.
# Partim d'una imatge amb les eines de construcció
FROM composer AS dependencies
WORKDIR /app
# Copiem nomes els fitxers de dependències
COPY dependencies.json /app
RUN make dependencies
# Fins aquí la imatge no depén del nostre codi
COPY . /app
RUN make build
# Inclou l'aplicació muntada pero també les eines de construccio
# Creem una nova imatge neta
FROM ubuntu
# Copiem no des del host sino de la imatge inicial
COPY --from dependencies dist/ /var/www/html
RUN ...
Xarxa
Els ports d'escolta del container no son visibles a l'exterior per defecte. Tampoc colisionen, que això es bó.
Un Dockerfile defineix un port publicable amb EXPOSE
, pe EXPOSE 80
.
Això vol dir que podem mapejar aquest port a un port del host.
L'opció -P
mapeja tots els ports exposats a ports aleatoris del host.
Podem esbrinar els ports mapejats amb docker ps
.
Normalment volem exposar-ho a un port concret: run amb -p hostport:containerport
Si no ponem un OUTER:
, s'assignen ports aleatoris.
Els ports exposats s'hereten. Per exemple, les imatges oficials d'apache exposen el port 80. Els docker de que depenen de la imatge d'apache tambe el tindran.
Reinici automàtic
Recursos
--memory 4g
Mata el proceso cuando se pasa--cpus 1.5
Reserva como maximo ese slot de cpu (multiproceso)--cpu-shares N
peso para repartir la CPU si hay escasez (1024 defecto)
Volúmenes
Util para mantener el estado entre ejecuciones. Por ejemplo las bases de datos, uploads... Son directorios que se montan en las imagenes.
Tambien es muy util para montar el codigo en desarrollo para acelerar el flujo de desarrollo.
-v /data
crea una carpeta random-v volumename:/data
reusa un volumne por nombre-v ruta/:/data
reusa un volumne por path
docker volume ls # lista los volumentes
docker volume create name # crea un volumen especifico
docker inspect -f "{{json .Mounts}}" my-app # Lista los volumenes montados por el container my-app
Por defecto los guarda en un directorio de docker (var/lib/docker/). Podemos especificar el directorio que montamos.
Tambien se puede especificar el VOLUME en el Dockerfile. En ese caso, si no le especificamos nada, docker lo creara donde crea los dockers aunque no digamos nada en cli.
Publicacion
Crear una cuenta en docker hub. En settings linkad con la cuenta del repo (github, bitbucket...).
Composición de servicios
docker-compose
es una capa por encima de docker
que agrupa diversos contenedores en una aplicación
especificando los parametros con los que llamariamos a docker run
.
Se define en el
# docker-compose.yaml
version: 2.3
services:
myservice:
images: ubuntu # Esta imagen la toma tal cual
volumes: # Monta el directorio actual en /var/www/html
-${PWD}:/var/www/html
ports:
- 8000:80
myapp:
build: # Esta imagen es construida
context: ${PWD} # Path en el que se construira la imagen, tomamos PWD del entorno de usuario
dockerfile: Dockerfile # Fichero Dockerfile para construir la imagen
ports: # Expondra el puerto 80 interno como 8001 en localhost
- 8001:80
environment: # Pasara estas variables de entorno
- VAR=value
depends_on: # Esperara a que el otro servicio este levantado
- myservice
-d
para detach (background)
Ofrece comandos que se aplican a la vez a todos los containers definidos en el fichero:
docker-compose ps
Estado de los containersdocker-compose log
Salida combinada de los containersdocker-compose top
Top de procesos en los containersdocker-compose stop
Para los procesos pero sin eliminarlosdocker-compose down
Elimina los contenedoresdocker-compose build
Solo construye las imagenes
La imagen construida se llama por defecto, como el directorio y el nombre del servicio: myproject_myapp
.
myproject
por el directorio en el que está el yamlmyapp
por la etiqueta del servicio
Las siguientes ejecuciones reusan la imagen creada.
Hace falta forzar con docker-compose up --build
Componiendo para multiples entornos
No es buena idea hardcodear el environment en el compose
- Cambiar de tipo de entorno: devel, testing, prod...
- Secrets en el repositorio!
Networking en compose
Por defecto, con docker-compose
cada docker tiene su propia red.
Para que dos contenedores se vean, tienen que estar en la misma red.
Podemos crear redes y mover contenedores entre ellas.
En ese caso, el nombre de contenedor hace de servername.
Utilidades de red:
docker network ls
docker network create NETWORK
docker network connect NETWORK CONTAINER
docker network disconnect NETWORK CONTAINER
Los servicios en un docker-compose se levantan, por defecto, como hosts diferentes de la misma red local default
.
Los composers se ven entre ellos usando como dns el nombre del servicio.
Pero podemos crear estructuras de red mas complejas.
Todos los contenedores se ven entre ellos si comparten red.
Cada servicio define sus networks
cuando queremos no ponerlas en default
:
- Las aplicaciones solo se pueden ver si tienen una red en común.
- Esto permite aislar servicios para más seguridad
...
services:
myservice:
...
networks:
- mynetwork
networks:
mynetwork:
myexistingnetwork:
external: # creada fuera del docker-compose con docker-network
name: myothernetwork
Docker en producción?
- No es conveniente montar el docker compose con servicios que necesitan persistència.
- Con docker-compose no podemos distribuir servicios. Para hacerlo asi necesitariamos herramientas tipo Kubernetes.
- Si que puede ser útil montar el docker de la aplicación
Consejos
- Usar imagenes oficiales
- se ha trabajado en ellas para que ocupen lo menos posible
- al final lo importante es la version del servicio, no que OS hay por debajo
- perdemos la cache entre imagenes si son OS distinto
- Usar la latest
- Siempre tengo la ultima version
- links vs dependson:
- links deprecado, decia que container habla con cual, con networks + dependson
- ip's
- Las ips no tienen porque mantenerse, mejor usar los nombres
- docker no espera a que el servicio esta disponilbe, simplemente la maquina
- diseño aplicaciones capaces de resistir a caida de servicios
- scripts para esperar que el servicio este levantado