Docker 101

Notas en español sobre Docker para principiantes.

5/22/2019
docker, beginners

Lightning talk por @waj

Conceptos principales

Virtual Machine

Simula ejecutar un servidor o varios sobre la máquina que estamos corriendo, ej. VirtualBox. Exite un 'hypervisor' que va a separar los componentes de la máquina. Vagrant automatizaba la creación de una imagen en base de una imagen que ya existía (la clonaba).

Docker VM

Containers

Comparten un sistema operativo. Docker solo corre en Linux. Hay una VM transparente (ej. docker-snap). Entre todos los containers comparten el OS. Tengo un sólo OS y creo compartimentos/containers que permiten aislar ejecuciones de código en distintos espacios.

Docker Container

Docker Architecture

Corre siempre en un Host, DOCKER_HOST. Hay un Deamon que administra los containers, que se crean en base a una imagen que contiene el file system y las cuales provienen de un Registry oficial que existe en Docker, aunque podemos tambien tener imágenes propias. Por último hay un Client a través del cual pueden mandarse comandos.

Docker hace uso de un conjunto de tecnologías que ya estaban en Linux. Lo que hace es orquestar eso. Así como existen otras tecnologías de containers para otros sistemas operativos.

Docker architecture

Namespaces

Permite fragmentar distintos recursos:

  • pid

  • ''net'' o Interfaces de red: para cada container Docker crea una interfaz virtual.

  • ipc

  • mnt: administra los archivos montados en el host file system.

  • uts: cada container tiene su propio nombre de host.

Controler Groups (cgroups)

A un proceso le reservo x cantidad de memoria. Esto ayuda a que si hay un proceso que excede la cant de memoria se mate antes de que lo supere, entre otras cosas.

Union file system

Es un concepto, en el que se representa un sistema de archivos en capas. Sobre el sistema de archivos puedo crear un nuevo layer donde puedo agregar o modificar o quitar archivos sin modificar el file system inicial. Esto se usa para las imágenes y el file system de cada container. Se representa el file system en capas.

Comandos

$ docker pull

Las imagenes provienen de una Registry. docker pull permite bajar una imagen de una registry > https://hub.docker.com (hay imágenes oficiales y otras no).

También hay tags que puedo elegir por versiones.

$ docker pull ruby > se usa por defecto el tag "latest"

$ docker images

$ docker images --help

$ docker images ruby > para ver todas las imágenes

$ docker run

El comando que se usa para crear un container de docker.

$ docker run --interactive --tty ruby
irb > puts 1
1
=> nil

$ docker run --interactive --tty bash
root@[host-name que coincide con el id del container]:/# ls
bin ... > file system "del container"

--tty es un local output y un local input. Algunos procesos lo necesitan par aque no se cierren, por ej. bash.

--interactive

$ docker ps

Es un comando para listar containers. Cuando la ejecución termina, los containers no se borran, para eso hay que agregar el --rm para no ir acumulando basura innecesariamente.

$ docker ps -a

$ docker container rm [id || nombre de container]

Ej, para tener un servidor web, hay una imagen que se llama nginx:

$ docker pull nginx

$ docker run -rm nginx

$ docker run -rm -p 4000:80 nginx > mappea un puerto específico a 80

$ docker run -rm -P nginx > asigna un puerto random

$ docker exec

Todo container de docker corre como mínimo un comando (el defualt o uno que una le haya asignado), pero se le pueden agregar más comandos. exec permite meterse en un container que está corriendo:

$ docker exec --help

$ docker exec [nombre de container] ls > para listar archivos

$ docker exec -it [nombre de container] bash
root@[host]:/# ls
root@[host]:/# ls /proc/
root@[host]:/# ps aux

USER       PID
root        1  
> PID=1 es proceso principal. Es el proceso que tiene que adoptar los procesos que terminan. Históricamente ese proceso se llamaba 'init'.
root@[host]:/# exit

$ docker logs

Cada container tiene logs que no son ni más ni menos que todos los procesos que pasan por el standard output.

$ docker logs [nombre del container]

$ docker logs --tail=1 (cant de lineas del historial)
$ docker logs --follow (para seguir los logs nuevos además del historial)
$ docker logs --tail=5 --follow

> también es posible ver logs de un container que no está corriendo más

Docker guarda el timestamp antes de cada linea:

$docker logs --timestamps [nombre del container]

$ docker stop/start

$ docker run -d -p 4000:80 nginx

$ docker stop [nombre del container]
$ docker start [nombre del container]
$ docker attach

$ docker rm

Sirve para liberar memoria en disco. Puedo borrar uno o más containers.

$ docker rm [nombre de container] [ + nombre de container] [ + nombre...

$ docker commit

Permite volver a mandar a una imagen mi working copy. Creo una imagen, la modifico y creo un layer para después crear una nueva imagen en base a ese layer con la modificación.

$ docker commit [nombre de container] [nombre de nueva imagen]

https://github.com/wagoodman/dive para ver las layers

$ docker tag

Puedo crear nuevos tags sobre imágenes que ya existen.

Ej: "a foo, tagueámelo como foo:1.0"

$docker tag foo foo:1.0

$ docker images foo
REPOSITORY                   TAG
foo                          1.0
foo                         latest

$ docker image rm foo:1.0

Mientras exista un tag, esa imagen no se borra y quedan "dangling" images.

$ docker image prune

Borra todas las imágenes dangling.

$ docker system prune > borra todo

$ docker push

Una vez que tengo taggeada mi imagen la puedo pushear. Solo puedo pushear las cosas para las que tenga acceso.

$ docker build

La forma "correcta" de crear imágenes nuevas (versus $docker commit).

Creo un ''DockerFile'':

FROM ruby (desde qué imagen quiero crear) || scratch (una imagen de la nada)

ADD foo.rb /foo.rb

CMD ruby foo.rb

RUN touch foo.txt > correr comando

RUN apt-get update && \
    apt-get install -y nodejs && \
    apt-get clean && \
    rm -f ...
$ docker build . 
> busca Dockerfile en el directorio actual y ejecuta paso a paso los comandos

$ docker build -t myruby
> creo una imagen con un tag 'myruby'

$ docker exec -rm myruby

Todo lo que va haciendo cuando se ejecuta el Dockerfile va quedando en layers. A veces es necesario, por ej, compilar un programa e instalar algo y compilarlo, y después quiero borrar eso que instalé. Para eso me sirven los staged builds.

Ej:

FROM golang

ADD foo.go
WORKDIR / > para cambiar el working dir que viene por default
RUN go build foo.go

Con staged build el container deja de pesar tanto:

FROM golang AS build

ADD foo.go
WORKDIR / > para cambiar working dir default
RUN go build foo.go

FROM scratch
COPY --from=build /foo /foo
( CMD ["/foo"] ) > el comando va entre []

---

$ dockker run -it -rm [nombre container] /foo
> Hello World

Se puede pisar el CMD que viene por default con la imagen definiendo un entry point. Todo lo que se agrega después en el comando se corre a continuación considerando el nuevo entry point.

FROM ruby
ENTRYPOINT ["ruby"]

---

$ docker exec -rm  [nombre de container] -e 'puts 1'
> 1

$ docker inspect

Se le puede pasar un id y devuelve un json con eso que le pasé.

$ docker inspect [nombre de container]:latest