Docker cookbook. Subir una imagen a nuestro Docker Registry privado

Docker Logo

La versión de docker utilizada para ejecutar los comandos de este artículo ha sido docker 1.12.0 en un sistema Ubuntu 16.04.1 LTS

Cuando empezamos a trabajar con Docker una de las primeras cosas que utilizaremos es el Docker Resgitry proporcionado por la gente de Docker y publicado en Docker Hub.

Como sabemos es la imagen utilizada para crear un contenedor donde subiremos nuestras imágenes de nuestros proyectos de forma privada. Eso si, con algunas limitaciones en cuanto a seguridad etc.

Como toda imagen subida a Docker Hub podemos disponer de ella y arrancar nuestro contenedor con un simple comando:

$ docker run -d -p 5000:5000 --name registry registry:2

Los pasos a seguir una vez tenemos nuestra imagen son muy sencillos. Basta con etiquetarla indicando el host del Docker Registry donde tiene que subir:

$ docker tag 5f7ca7e96e33 my-registry:5000/my-image:0.0.1-SNAPSHOT
$ docker push my-registry:5000/my-image:0.0.1-SNAPSHOT
The push refers to a repository [my-registry.com:5000/my-image]
Get https://my-registry.com:5000/v1/_ping:http: server gave HTTP response to HTTPS client

En este momento es cuando nos encontramos con la primera limitación de este Registry. No soporta la seguridad por SSL, y por lo tanto debemos decir a nuestro docker daemon que debe poder comunicarse con my-registry.com de forma no segura.

La configuración que debemos hacer es muy sencilla. Indicar el host de nuestro Registry, reiniciar el daemon y volver a subir la imagen

$ echo '{ "insecure-registries":["my-registry.com:5000"] }' > /etc/docker/daemon.json
$ sudo systemctl restart docker
$ docker push  my-registry.com:5000/my-image:0.0.1-SNAPSHOT
The push refers to a repository [my-registry.com:5000/my-image]
0d84ae38f138: Pushed
c18156f94ccc: Pushed
...
...
0.0.1-SNAPSHOT: digest: sha256:fceb6ceb9277761036ec670c1e23f30a3667441f98c54b4253c94737abb0b418 size: 4482
Anuncios

Algunos de los comandos más utilizados en Docker

Docker Logo

Estos comandos son lanzados desde docker client y son válidos tanto para la versión de Linux como para la de Windows.

Recordemos no obstante que la instalación de docker para un sistema Linux y Windows es diferente. La diferencia principal es que en Linux el docker host es nuestra propia máquina mientras que en Windows el docker host es una máquina virtual de VirtualBox, gestionada a través del comando docker-machine.

Listado de todas las máquinas virtuales de VirtualBox donde tenemos docker

$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM
default   *        virtualbox   Running   tcp://192.168.99.100:2376

IP de una de las máquinas virtuales de VirtualBox donde tenemos docker

$ docker-machine ip default
192.168.99.100

Crear un contenedor docker con una imagen de nginx

$ docker run -dP nginx
23d0bc5a0f529d3d79e44cfdf0d1c01e32ab4fdd09e4113fa5fdab2a11d7a81b

Donde,

  • -d, la imagen se ejecutará como daemon
  • -P, mapeo automático de puertos entre el contenedor ejecutando la imagen y docker host (recordemos en windows el docker host es una máquina virtual de VirtualBox y en Linux es nuestra propia máquina)

Listado de los contenedores ejecutando algún proceso

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                           NAMES
23d0bc5a0f52        nginx               "nginx -g 'daemon off"   10 seconds ago      Up 8 seconds        0.0.0.0:32771->80/tcp, 0.0.0.0:32770->443/tcp   big_aryabhata

Listado de los mapeos de puertos de todos los contenedores

Es el mismo comando anterior.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                           NAMES
29409f8390b7        nginx               "nginx -g 'daemon off"   6 seconds ago       Up 6 seconds        0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp   tiny_noyce

Listado de los mapeos de puertos de un contenedor

Mapeo de todos los puertos

$ docker port 29409f8390b7
443/tcp -> 0.0.0.0:32768
80/tcp -> 0.0.0.0:32769

Mapeo de un puerto

$ docker port 29409f8390b7 443
0.0.0.0:32768

Listado de los procesos que se están ejecutando en un contenedor de docker

$ docker top 23d0bc5a0f52
PID                 USER                COMMAND
28802               root                nginx: master process nginx -g daemon off;
28809               104                 nginx: worker process

Salida estándar de uno de los contenedores de docker

Arrancamos un contenedor de docker a partir de la última imagen nginx y queremos ver los logs de la salida estandar de nginx en la consola. Lo mismo que hariamos con el comando tail -f.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                           NAMES
23d0bc5a0f52        nginx               "nginx -g 'daemon off"   10 seconds ago      Up 8 seconds        0.0.0.0:32771->80/tcp, 0.0.0.0:32770->443/tcp   big_aryabhata

$ docker logs -f 23d0bc5a0f52
192.168.99.1 - - [04/Dec/2015:10:39:31 +0000] "GET /favicon.ico HTTP/1.1" 404 168 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0" "-"

Obtener información detallada de un contenedor en ejecución

La información es devuelta en formato JSON. Esta información nos devuelve todos los detalles del contenedor, como el ID, la IP donde se encuentra disponible, la MAC address, el hostname, etc

$ docker inspect 23d0bc5a0f52

Y si queremos filtrar solo algún tipo de información, lo podemos hacer utilizando una pantilla del lenguaje go.

$ docker inspect -f "Host name: {{.Config.Hostname}}, Identificador: {{.ID}}" 23d0bc5a0f52

Parar un contenedor

$ docker stop 23d0bc5a0f52
23d0bc5a0f52

Arrancar un contededor

$ docker start 23d0bc5a0f52
23d0bc5a0f52

Borrar un contenedor

Una vez hemos terminado de utilizar un contenedor, lo podemos eliminar si ya no nos hace falta. Previamente lo tenemos que parar. Una vez lo hemos borrado, dejaremos de tenerlo en la lista de contenedores.

$ docker rm 23d0bc5a0f52
$ docker ps -a

Listado de las imágenes descargadas localmente

Todas esta las imágenes las hemos descargado desde Docker Hub.

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
nginx               latest              198a73cfd686        2 weeks ago         132.8 MB
ubuntu              14.04               e9ae3c220b23        3 weeks ago         187.9 MB
ubuntu              latest              e9ae3c220b23        3 weeks ago         187.9 MB
hello-world         latest              0a6ba66e537a        7 weeks ago         960 B

Descargar de una imagen

Cada vez que ejecutamos una imagen que no tenemos, la imagen se descarga y se crea el contenedor donde se ejecuta esa imagen. Pero en ocasiones queremos tener ya la imagen descargada para no tener que esperar a su descarga, en ese caso lo hacemos con docker pull.

$ docker pull elasticsearch:2.1.0

Y una vez la tenemos descargada podemos en cualquier momento crear el contenedor para esa imagen y ejecutarlo. En este caso vamos a ejecutar un contenedor de elasticsearch y verificar su estado.

$ docker run -dP elasticsearch:2.1.0
f13efab1bbbe1f09509e72e03ad9627e2649a46c9c94076b9707104f0c130177

$ docker port f13efab1bbbe
9200/tcp -> 0.0.0.0:32771
9300/tcp -> 0.0.0.0:32770

$ docker-machine ip default
192.168.99.100

$ curl http://192.168.99.100:32771/
{
  "name" : "Bennet du Paris",
  "cluster_name" : "elasticsearch",
  "version" : {
    "number" : "2.1.0",
    "build_hash" : "72cd1f1a3eee09505e036106146dc1949dc5dc87",
    "build_timestamp" : "2015-11-18T22:40:03Z",
    "build_snapshot" : false,
    "lucene_version" : "5.3.1"
  },
  "tagline" : "You Know, for Search"
}

Crear una nueva imagen

Cuando una imagen no se adapta a nuestras necesidades, podemos crear una nueva imagen a partir de esa imagen. Por ejemplo, en la imagen ubuntu:14.04 no tenemos los comandos curl, wget, etc. Podemos crear una nueva imagen a partir de la original añadiendo los comandos o herramientas que necesitamos.

Creamos un contenedor a partir de la imagen que vamos a modificar

$ docker run -it ubuntu:14.04 bash
root@c33a2339ca21:/#

Donde

  • -it, indicamos a docker que deje un terminal abierto (-t) para trabajar de forma interactiva (-i) con el contenedor

Instalamos los comandos o herramientas que necesitamos y salimos del contenedor

root@c33a2339ca21:/# apt-get update
root@c33a2339ca21:/# apt-get install curl wget
root@c33a2339ca21:/# exit

Creamos la nueva imagen

$ docker commit -m "comandos curl y wget" -a "me" c33a2339ca21 ubuntu14.04/utils:0.0.1
074f77447d43f92aa8006ba97b54a26a187782224392d4a5ea3798dcb860060c

Verificamos la nueva imagen recién creada

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu14.04/utils   0.0.1               074f77447d43        14 minutes ago      221.3 MB
myrepo/myapp        1.0                 f4702680b832        6 days ago          259.3 MB

Creamos el nuevo contenedor a partir de la nueva imagen

$ docker run -it ubuntu14.04/utils:0.0.1
root@866b066b913a:/# curl http://localhost
curl: (7) Failed to connect to localhost port 80: Connection refused
root@866b066b913a:/# exit

Una vez lo hemos creado, ya podemos volver a ejecutarlo cuando lo necesitemos.

Verificamos el contenedor recién creado

$ docker ps -a
CONTAINER ID        IMAGE                     COMMAND                  CREATED              STATUS
866b066b913a        ubuntu14.04/utils:0.0.1   "bash"                   About a minute ago   Exited (7) About a minute ago
c33a2339ca21        ubuntu:14.04              "bash"                   31 minutes ago       Exited (4) 19 minutes ago

Ejecutamos el contenedor recién creado y nos conectamos a el

$ docker start 866b066b913a
$ docker exec -it 866b066b913a bash
root@866b066b913a:/# curl http://localhost
curl: (7) Failed to connect to localhost port 80: Connection refused
root@866b066b913a:/# exit
$ docker stop 866b066b913a

Crear una nueva imagen utilizando el fichero Dockerfile

Lo más habitual es que terminemos creando una nueva imagen a partir de una existente utilizando un fichero dockerfile. Este fichero es el que utiliza el comando docker build para generar la nueva imagen. Es mucho mas versátil y sencillo crear imágenes de esta manera.

A continuación vamos a crear la misma imagen del paso anterior pero esta vez utilizando un fichero dockerfile. Es decir, una imagen basada en Ubuntu 14.04 con los comandos curl y wget instalados.

Creamos el fichero Dockerfile

$ mkdir -p mis_imagenes/utils
$ cd mis_imagenes/utils

$ touch Dockerfile
$ view Dockerfile

Y añadimos el siguiente contenido

FROM ubuntu:14.04
MAINTAINER me me@mydomain.com
RUN apt-get update && apt-get install -y curl wget

Como vemos estamos indicando la imagen de partida (FROM), el creador de la nueva imagen (MAINTAINER), y cada uno de los comandos (RUN) que queremos ejecutar en la nueva imagen.

Construimos la nueva imagen

Para ello utilizaremos el comando docker build, al que le indicaremos el

  • directorio donde se encuentra el fichero dockerfile
  • el nombre (repositorio y tag) de la nueva imagen

En cada uno de los pasos ejecutados por docker build se están generando imágenes intermedias. Todas estas imágenes serán eliminadas una vez tengamos creada la imagen final.

$ docker build -t ubuntu14.04/utils:0.0.2 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:14.04
 ---> e9ae3c220b23
Step 2 : MAINTAINER me me@mydomain.com
 ---> Running in ca92d89310f3
 ---> facc5c7efd6d
Removing intermediate container ca92d89310f3
Step 3 : RUN apt-get update && apt-get install -y curl wget
 ---> Running in a5c20645fa15
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://archive.ubuntu.com trusty-updates InRelease [64.4 kB]
Get:2 http://archive.ubuntu.com trusty-security InRelease [64.4 kB]
Hit http://archive.ubuntu.com trusty Release.gpg
Hit http://archive.ubuntu.com trusty Release
Get:3 http://archive.ubuntu.com trusty-updates/main Sources [311 kB]
...
...
...
Processing triggers for libc-bin (2.19-0ubuntu6.6) ...
Processing triggers for ca-certificates (20141019ubuntu0.14.04.1) ...
Updating certificates in /etc/ssl/certs... 173 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.
 ---> 2c87ba841537
Removing intermediate container a5c20645fa15
Successfully built 2c87ba841537

La imagen creada se corresponde con

  • el nombre (repositorio y tag) ubuntu14.04/utils:0.0.2 y
  • el ID 2c87ba841537.

Verificamos y arrancamos la nueva imagen recién creada

Como siempre para arrancar una imagen tenemos que crear el contenedor a partir de la misma.

Verificamos la imagen recién creada

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu14.04/utils   0.0.2               2c87ba841537        6 minutes ago       221.3 MB
ubuntu14.04/utils   0.0.1               074f77447d43        31 hours ago        221.3 MB
myrepo/myapp        1.0                 f4702680b832        8 days ago          259.3 MB
elasticsearch       2.1.0               14d91f1920dc        2 weeks ago         345.7 MB

Creamos el contenedor a partir de la imagen

$ docker run -it ubuntu14.04/utils:0.0.2 bash
root@27ebe51743fc:/# which curl  wget
/usr/bin/curl
/usr/bin/wget
root@27ebe51743fc:/# exit
exit

Utilizamos el contenedor

El contenedor basado en nuestra imagen se corresponde con

  • el ID 27ebe51743fc y con
  • el nombre elated_borg.
$ docker ps -a
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                      PORTS                                           NAMES
27ebe51743fc        ubuntu14.04/utils:0.0.2   "bash"                   45 seconds ago      Exited (0) 14 seconds ago                                                   elated_borg

$ docker start elated_borg
elated_borg

$ docker exec -it elated_borg bash
root@27ebe51743fc:/# which curl wget
/usr/bin/curl
/usr/bin/wget
root@27ebe51743fc:/# exit
exit

Borrar una imagen

Podemos borrar la imagen a partir de su identificado (ID) o a partir de su nombre (repositorio y tag).

Listamos las imagenes

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu14.04/utils   0.0.2               2c87ba841537        18 hours ago        221.3 MB
ubuntu14.04/utils   0.0.1               074f77447d43        2 days ago          221.3 MB

Borramos la imagen que no necesitamos

$ docker rmi ubuntu14.04/utils:0.0.1
Untagged: ubuntu14.04/utils:0.0.1
Deleted: 074f77447d43f92aa8006ba97b54a26a187782224392d4a5ea3798dcb860060c

Borramos todas las imagenes de un determinado repositorio

$ docker rmi ubuntu14.04/utils

Diferencias entre la versión de Windows y Linux disponible para Docker

Docker Logo

En este artículo no vamos a entrar en detalles sobre lo que es docker. Solo vamos a resumir cuales son las diferencias principales entre las versiones disponibles para las plataformas más habituales, Windows y Linux.

Componentes principales de docker

docker daemon. Es un proceso daemon con el que nos comunicamos a los contenedores de docker. Para comunicarnos con el proceso daemon utilizarmos el cliente de docker o docker client.

docker client. Es el cliente que utilizaremos para comunicarnos con el proceso daemon y asi comunicarnos con los contenedores de docker.

containers. Cada una de las instancias o maquinas virtuales que se están ejecutandose.

docker host. Representa la instancia de docker donde se ejecutan todos los anteriores componentes, y donde tendremos por lo tanto ejecutándose cada una de las instancias o contenedores de docker.

Versión para Linux

  • Docker daemon se ejecuta como daemon en la propia maquina linux.
  • Localhost representa nuestra maquina local, y es ademas el docker host, y donde se encuentran ejecutandose todos los componentes de docker.

    Por lo tanto para comunicarnos con un servicio ejecutándose en uno de los contenedores de docker, lo haremos directamente con localhost:8080 o 0.0.0.0:8080.

Docker Linux

Versión para Windows

  • Docker daemon no se ejecuta como daemon, ya que no puede ejecutarse de forma nativa en Windows. En su lugar tenemos docker-machine con el que crearemos una VM de VirtualBox. Esta VM es donde se ejecuta el daemon de docker y es la que representa el docker host

  • La VM de VirtualBox representa el docker host, y es donde se ejecutan todos los componentes de docker, a excepción del docker client.

  • El docker client se encuentra ejecutandose en Windows, y se comunica con el docker daemon a traves de la VM de VirtualBox.

    Por lo tanto para acceder a cualquier servicio ejecutándose en alguno de los contenedores de docker utilizaremos la dirección de la maquina de VirtualBox.

  • docker-machine es responsable de arrancar la VM de VirtualBox.

Además y para facilitarnos el uso de docker tenemos Docker Quickstart Terminal, que es

  • el responsable de crear la VM de VirtualBox si no existe,
  • de arrancar la VM,
  • de abrir un terminar
  • y conectar dicho terminal con el daemon de docker que se encuentra ejecutándose en la VM.

De esta forma en un solo click tenemos un terminal con el que ya podemos interaccionar con docker.

Docker Windows