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).
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.
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.
Permite fragmentar distintos recursos:
-
''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.
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.
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.
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 --help
$ docker images ruby > para ver todas las imágenes
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
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
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
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 run -d -p 4000:80 nginx
$ docker stop [nombre del container]
$ docker start [nombre del container]
$ docker attach
Sirve para liberar memoria en disco. Puedo borrar uno o más containers.
$ docker rm [nombre de container] [ + nombre de container] [ + nombre...
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
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.
Borra todas las imágenes dangling.
$ docker system prune
> borra todo
Una vez que tengo taggeada mi imagen la puedo pushear. Solo puedo pushear las cosas para las que tenga acceso.
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
Se le puede pasar un id y devuelve un json con eso que le pasé.
$ docker inspect [nombre de container]:latest
Más info: