Skip to content

Latest commit

 

History

History
493 lines (479 loc) · 34.6 KB

docker - WIP.md

File metadata and controls

493 lines (479 loc) · 34.6 KB


Quede aca: https://docs.docker.com/develop/develop-images/baseimages/

Intro y referencia

Arquitectura

Docker se compone de 3 elementos

  • Un servidor / daemon que administra todo
    • dockerd
  • Un cliente que interactua con ese servidor y le dice que hacer
    • el CLI docker
    • Docker REST API
  • Docker objects que representan los elementos relevantes
    • Imagenes (un filesystem read-only)
    • Containers (un filesystem basado en una imagen, que ademas es writable y corre procesos)
    • Volumes
    • Networks

Ademas podes agrupar containers instanciados en diferentes daemons (osea probablemente en diferentes pcs) a esto se lo llama “service” y te permiten tener tener Computacion distribuida

Images, containers y layers

  • Las IMAGES son un Read-only filesystem que son el punto de partida para crear un container
  • Un CONTAINER es un conjunto de procesos corriendo en un filesystem que es una copia de una imagen cuando se creo el container

Podes ver los containers como instanciaciones de una imagen, cuando creas un container estas copando el filesystem de una imagen y colocandolo en un read/write filesystem que ademas tiene funcionando ciertos procesos (el container).

El sistema docker funciona con layers que consisten en

  • Una serie de imagenes, cada una sobreescribiendo y añadiendo cosas de la anterior generando un filesystem final
    • Ej una añade el SO, otra añade apache, otra añade wordpress
  • Un contanier que copia el filesistem final en un filesystem read/write y lo corre con ciertos procesos

enter image description here

Referencia de comandos utiles

  • docker info Muestra el estado del servicio docker (contenedores corriendo, pausados, detenidos, imagenes, version)
  • docker ps <-param> Lista los containers que estan en funcionamiento
    • -a Muestra containers que fueron iniciados y ahora estan detenidos
    • -q Solo muestra los containers id
  • docker run <-param> <imagen> <comando> Crea un container desde una imagen, si no la encuentra la descarga del repositorio
    • -param
      • –name nombre Le asigna un nombre al container que podes usar en lugar de su ID
      • -it Hace que el container sea interactivo (te conecta a su consola)
      • -d run in background (daemon)
      • -p <hostport>:<containerport> mapea un puerto a otro
    • imagen Nombre de la imagen desde la que se crea el container
    • comando El comando que correr apenas el container termine de crearse. overridea el comando default del dockerfile (CMD)
  • docker image ls Lista las imagenes disponibles en el OS para crear containers
  • docker container ls Lista containers
    • docker container ls --allmuestra containers funcionando
  • docker logs <id> Muestra el log de stdout de un container
  • docker container stop <id> Detiene el contenedor gracefully
  • docker container kill <id> detiene el contenedor no gracefully
  • docker pull <image name>:<TAG> descarga de dockerhub una imagen, las tags indican variaciones de una imagen
  • docker diff <id> muestra las diferencias entre el container y su base image
  • docker commit <id> crea una imagen del container
  • docker tag <imageId> <tuTag> coloca un nombre (tag) a una imagen
  • docker rm <id> Elimina un container
  • docker rmi <idImagen> Elimina una imagen
  • docker port <id> muestra el mapping de puertos para un contianer

Notas de instalacion

Acerca del uso de SUDO

Para evitar el uso de SUDO es critico agregar al usuario que va a administrar docker al grupo docker que se crea en el sistema al instalarlo.

IP de containers en windows

En windows no podes usar localhost para comunicarte con un container usando un puerto mapeado, tenes que usar la ip del container y el puerto mapeado, la obtenes con el comando docker-machine ip

Detener contenedor en windows

En windows CTRL-C no detiene el contenedor, tenes que detenerlo con docker container stop <Container NAME or ID

Operacion

Containers - WIP

Instanciacion

Una buena analogia de los containers es que son como objetos instanciados de una clase (la imagen). son iguales a la imagen al momento de crearse, pero luego a lo largo de su vida son modificados.

Para instanciar un container desde una imagen usas.
Si no tenes la imagen cargada entonces docker intenta bajarla de dockerhub

docker run <-param> <imagen> <comando>

  • -param
    • - -name nombre Le asigna un nombre al container que podes usar en lugar de su ID
    • -it Hace que el container sea interactivo (te conecta a su consola)
    • -d run in background (daemon)
    • -p <hostport>:<containerport> mapea un puerto a otro
  • imagen Nombre de la imagen desde la que se crea el container
  • comando El comando que correr apenas el container termine de crearse. overridea el comando default del dockerfile (CMD)

Nombrar

Los containers y las imagenes siempre tienen ids que se ven como un hash, pero ademas pueden tener:

  • nombres en el caso de los containers
  • tags en el caso de las imagenes

Donde sea que puedas usar un ID podes usar el nombre o el tag

Para nombrarlos usamos el parametro --name

docker run --name miNombre <imagen> 

Control de procesos

Podes controlar los containers que tenes usando

  • docker container stop <id>
    • Detiene el proceso
  • docker container start <id>
  • docker container pause<id>

Container logs

El stdout de un container va a los logs de docker, accesibles desde
docker logs <id>

Daemon

En produccion vas a querer mantener los containers funcionando como daemons usando docker run -d <imagen>

Conectarte con bash

Simplemente ejecutas un bash en el container y pedis que sea interactivo

docker exec -t -i container_name /bin/bash

Crear images/guardar container

Mejores practicas

  • Los containers deben ser stateless y deben poder ser detenidos, reiniciados o reinstanciados de su imagen en todo momento
  • Cada container debe tener un unico concern

Modo Interactivo

CRITICO: Los containers son efimeros, si el server se apaga su estado se pierde.

Para crear una imagen

  • Creas un container en base a una o varias imagenes
  • haces tus cambios
  • corres docker commit <idContainer> <tag> y docker guarda una nueva imagen basada en tu container (usando el id) con el nombre que coloques en TAG

Modo programatico - DockerFile

Resumen rapido para crear imagenes

Para crear una imagen de docker de forma programatica usamos un dockerfile junto con archivos que querramos copiar al contenedor.

Los necesitas a todos en la misma carpeta y a esta la llamaremos build context.

Cada comando instancia un container copiando el container del comando anterior. el container del comando anterior es borrado.

FROM <imagen base>
RUN <comandos ej: apt-get update>
RUN <comandos ej: apt-get install -y wget>

Tips: Conviene siempre colocar los comandos de tal forma que las cosas con menos cambios vayan arriba y las de mayor frecuencia de cambios vayan abajo. Esto permite aprovechar el build cache para hacer los builds mas rapidos.

En tu dockerfile probablemente quieras:

  • usar una imagen oficial de base usando FROM
  • Copiar archivos al contenedor usando COPY
  • instalar dependencias con algun package manager RUN usando algun package.json que copiaste usando copy
    • apt-get
    • npm
    • pip
  • Exponer puertos usando EXPOSE
  • Setear variables de entorno
    • usando ENV no recomendado
    • copiando archivos .sh a /etc/profile.d
# Use an official Python runtime as a parent image
FROM python:2.7-slim

# Set the working directory to /app WORKDIR /app

# Copias cosas del directorio del dockerfile al contenedor COPY . /app

# Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt

# Make port 80 available to the world outside this container EXPOSE 80

# Define environment variable ENV NAME World

# Run app.py when the container launches CMD ["python", "app.py"]

Finalmente usas el dockerfile asi:
Para crear tu imagen entonces haces lo siguiente
Docker build --tag <miTag>:<version> <pathHaciaElBuildContext>

Build context

Una vez que tenes un Dockerfile podes crear tu imagen. Docker build necesita dos cosas para crear una imagen

  • EL DockerFile que describe que hacer
  • Un Build context que es una carpeta con:
    • El dockerFile
    • Todos los archivos que podrias necesitar copiar al container con comandos como COPY, ADD, etc

EL build context agranda la imagen (no asi el container) ya que se copia todo el build context para tenerlo disponible a futuros layers que pudieran querer correr comandos como COPY. es por eso que te conviene mantenerlo chiquito
Para eso podes usar el archivo .dockerignore para ignorar los archivos que no queres que formen parte del build context

Build process

Para crear tu container entonces haces lo siguiente
Docker build --tag <miTag>:<version> <pathHaciaElBuildContext>

Una vez creado, podes ver el historial de containers intermedios y comandos corridos con

docker history <id imagen>

Build cache, cache busting y apt-get

Docker guarda un cache de las operaciones para hacer los build processes mas rapidos.

El cache funciona de la siguiente manera:

  • Si la base image es la misma, uso cache
  • linea por linea del dockerfile voy usando los layers en el cache a medida que veo que las instrucciones fueron las mismas
    • Para las instrucciones COPY y ADD hago un checksum de los archivos, si los checksums son iguales a los del cache entonces lo uso

NOTA IMPORTANTE SOBRE APT-GET: Notese que esto se cachea

RUN apt-get update  
RUN apt-get install -y nginx

Por lo tanto cambiar el apt-get install no genera que se vuelva a correr el apt-get update. Te conviene entonces poner todo junto en un comando

RUN apt-get update && apt-get install -y \
   aufs-tools \
  automake \
  ETC ETC...

Mas comandos de docker file

  • FROM:<imagen> Indica la imagen de base
  • LABEL:<key>=<value> el label para la imagen, algo human friendly, podes tener varios labels.
    • key deberia ser prefixeado por un dominio que controles (Best practice), puede tener puntos o guiones
    • Value puede ser cualquier cosa (dominio, etc)
  • WORKDIR:<PATH> establece el woring directory del container desde este punto en adelante (ej RUN o CMD arrancan con ese path)
  • USER: <username|UID> indica con que usuario se correra el servicio, es para no correr el servicio como root.
    • Precio a esto necesitas crear el usuario, ej: RUN useradd --no-log-init -r -g postgres postgres
  • CMD <comando> indica un comando default que correr al iniciar el container (de bash) .
    • Si se corre un comando al iniciar el container entonces overrideas al CMD. ej: docker run <-param> <imagen> <comando>
    • solo puede haber un CMD command en todo el docker file, si hay mas de uno entonces se corre el ultimo.
  • ENTRYPOING ["<command>","<option1>","<option2>"...]
    • Es como CMD
    • no se overridea al insertar un comando con docker run
    • ENTRYPOING ["wget","-o","-q"]
    • Lo que se coloque como comando en docker run <-param> <imagen> <comando> se appendea al final del comando seteado en entrypoint
  • EXPOSE <portNumber> expone un puerto del container al host.
  • ADD <path host> <path container> Permite copiar un archivo/directorio del host al container
  • COPY <path host> <path container> Igual que add
  • ENV NAME World define variables de entorno
  • WORKDIR <pathEnContainer> setea el working directory

Docker networking

Exponer puertos vs mapear puertos

Los contenedores de docker normalmente son cerrados y no pueden escuchar con ningun puerto salvo que lo expongas usando EXPOSE.

  • EXPONER: expone un puerto del contenedor para que el contenedor pueda escucharlo, solo otros contenedores pueden comunicarse con el puerto, no es accesible fuera del host.
  • MAPEAR: Mapea un puerto expuesto a un puerto del host. De esta manera el contenedor puede comunicarse con el mundo exterior usando la ip y un puerto del host.

Mapeo de puertos

Normalmente los containers pueden comunicarse con el mundo exterior usando la ip del host, pero el mundo exterior no puede comunicarse con ellos (ya que los paquetes los recibe el host).

Para solucionar esto usamos port mapping, que es tomar un puerto del host y mapearlo a un puerto del container.

POR EJEMPLO: podemos mapear el puerto 555555 del host al puerto 80 del container, para que los paquetes que lleguen al puerto 555555 del host sean redirigidos al container como si vinieran de su puerto 80

Para realizar el mapeo usamos

  • el flag --publish-all para autodetectar los puertos activos del container y mapearlos automaticamente a puertos random del host
    • docker run <-param> --publish-all <imagen> <comando>
  • el flag -p <puerto container>:<puerto host>

Ahora podemos ver los puertos mapeados por docker para ese container usando docker port <id>.

Comunicar containers entre si

Podes comunicar containers simplemente indicando

docker run <-param> --link <containerId>:<aliasEnNuevoContainer> <imagen> <comando>
Ahora si miras el host file de este container vas a encontrar una enrutacion con el host name aliasEnNuevoContainer y la IP interna del container containerId

Volumenes

CRITICO: Es importante tener en cuenta que los volumenes compartidos no se commitean como parte de una imagen

Entre host y container (permanente)

Para montar un directorio del host como un volumen en un container podes hacer:

  • docker run <-param> -v path/en/el/container:path/en/el/host<imagen> <comando>
  • Podes agregar permisos docker run <-param> -v path/en/el/container:path/en/el/host:rw- <imagen> <comando>

Esto te permite tener archivos pemanentes que no se eliminan si el container se detiene.

Entre containers

Podes compartir los volumenes de un container con otros, incluso si este no esta corriendo.

Para crear un volumen compartible usas el siguiente comando

  • docker run <-param> -v path/en/el/container <nombreDeVolumen> <imagen> <comando>

Para usar los volumenes de otro container

  • docker run <-param> -volumes-from <containerId> <imagen> <comando>

Docker compose - WIP

Docker compose te permite instanciar y configurar multiples contenedores desde un solo archivo denominado dockerCompose en el formato .yml

En lugar de ejecutar docker run <-param> -volumes-from <containerId> <imagen> <comando> manualmente para cada container declaras

version: 2 #Version de sintaxis de dockercompose
#Declaras una red de load balancers (opcional)
networks: 
  nombreDeLoadBalancer:
#Declaras servicios
services:
	# Cada servicio es un contenedor, en este caso 
	#"web" y "database" 
	web:
	  name: "nombre del contenedor" #como --name
	  image: "miwilfly:7" #imagen usada
	  ports: #el mapeo de puertos
	   - "80:8000"
	   - "21:300"
	  links: ·#aliases
	   - basedatos:miAliasVisibleParaWeb
	  volumes: 
	   - "<path en container>:<pathHost>"
	  deploy:
	    placement: #ej Solo correr en swarm manager
	      constraints: [node.role == manager]
	  command: <comando, equivalente a CMD>
	  networks: 
	    - nombreDeLoadBalancer
	basedatos:
		image:"mysql:lastest"
		ports:
		 - "3306:3306"

DockerHub

Dockerhub funciona como github pero para containers.

  • Push - podes publicar tus imagenes tagueandolas con el nombre de usuario de dockerhub, tu repositiorio y un tag para identificarlo (una version posiblemente)
    docker tag <imagen> username/repository:tag
    docker push username/repository:tag
    
  • Pull - Podes tomar imagenes tuyas o de otro repositorio haciando un pull con la sintaxis username/repository:tag
    docker run -p 4000:80 username/repository:tag
    

Distributed / load balancing

Conceptos

Distributed systems se basa principalmente en el concepto de services o de micro-services, es decir dividir el sistema en varias partes que sea stateless y corran en diferentes servidores. cada servicio va en uno o varios containers funcionando en paralelo

Nomenclatura:

  • SWARM WORKER nodo con un docker daemon que corre algunos o todos los tasks de uno o varios services. se comunica mediante DOCKER API
  • SWARM MANAGER computadora que dirige el swarm y le dice a cada node que task de cada servicio instanciar. se comunica mediante DOCKER API
  • SWARM: conjunto de nodes.
  • NODE: computadora que esta corriendo un docker daemon
  • STACK: Conjunto de services. configurado en dockerCompose
  • SERVICE: La descripcion de cuantos tasks de una misma imagen se deben mantener instanciados a lo largo del swarm en todo momento (ej 10 apache servers). configurado en dockerCompose.
  • TASK: un container
  • LOAD-BALANCER: El sistema que se encarga de distribuir los requests entre ciertos containers

Consideraciones previas

Necesitas que cada host tenga abiertos los puertos

  • Port 7946 TCP/UDP for container network discovery.
  • Port 4789 UDP for the container ingress network.

Usando docker compose y swarm

Generar el swarm

  • Para transformar este nodo en un swarm manager hacemos
    • docker swarm init
    • El comando devolvera un comando docker join --token <token> <ip> que podemos usar para unir workers al swarm
  • Para ver los nodos hacemos docker node ls
  • para apagar docker swarm haces docker swarm leave --force

Una vez creado el swarm todos tus comandos de docker los vas a realizar en el swarm manager y este se encargara de coordinar los workers

Configurar el stack

DockerCompose te permite configurar un stack indicando servicios que se componen de una cierta cantidad de contenedores instanciados de la misma imagen de ciertas caracteristicas y con ciertas limitaciones

version: "3"
##Listado de los servcios:
services:
  ##Servicio 1, llamado "web" y su descripcion
  web:
    image: username/repo:tag
    deploy:
      replicas: 5 #Correr 5 contenedores
      resources:
        limits:
          cpus: "0.1" # cada uno usa max 10% de 1 CPU core
          memory: 50M # cada uno usa max 50mb ram
      restart_policy:
        condition: on-failure # Reiniciar si hay error
    ports: #Mapeo de puertos
      - "4000:80"
    networks: #Web va a usar el load-balancer "webnet"
      - webnet
   #Aca colocarias otro servico
  otroServicio:
networks: #Defino el load-balancer network llamado "webnet"
  webnet:

Desplegar el stack en el swarm

  • Para desplegar un stack en el swarm hacemos
    docker stack deploy -c docker-compose.yml <nombreDelSwarm>
  • Para eliminar el stack haces docker stack rm getstartedlab

Ver servicios, tasks, nodes y stacks activos en swarm

Podes ver los servicios corriendo en un stack asi

docker stack services <nombreDelSwarm>

Para ver los tasks corriendo en un service, haces

docker service ps getstartedlab_web

Para ver los tasks corriendo en un stack

docker stack ps <stackname>

Para ver los nodos hacemos

docker node ls

Escalar y distribuir

Podes escalar el servicio cambiando la cantidad de replicas en el docker-compose y corriendo:

docker stack deploy -c docker-compose.yml <nombreStack>

No hay necesidad de hacer nada mas, los contenedores se distribuiran en todo el swarm y el load balancer va a distribuir la carga equitativamente.

Acceder

Podes acceder con la ip de cualquier worker o el swarm manager, el load balancer se va a hacer cargo de todo

shared files y volumes

Si necesitas compartir archivos entre los containers (ej archivos subidos por suers) tenes que tener un container en un solo nodo que tenga un volumen compartirdo con ese nodo

Ejemplo de docker-composer

redis:
    image: redis
    ports:
      - "6379:6379"
    volumes:
      - "/home/docker/data:/data"
    deploy: ##Solo existira en el swarm manager
      placement:
        constraints: [node.role == manager]