Introducción
Docker es una tecnología de virtualización “ligera” cuyo elemento básico es la utilización de contenedores en vez de máquinas virtuales y cuyo objetivo principal es el despliegue de aplicaciones encapsuladas en dichos contenedores.
Docker está formado por varios componentes:
-
Docker Engine: Es un demonio que corre sobre cualquier distribución de Linux y que expone una API externa para la gestión de imágenes y contenedores. Con ella podemos crear imágnenes, subirlas y bajarla de un registro de docker y ejecutar y gestionar contenedores.
-
Docker Client: Es el cliente de línea de comandos (CLI) que nos permite gestionar el Docker Engine. El cliente docker se puede configurar para trabajar con con un Docker Engine local o remoto, permitiendo gestionar tanto nuestro entorno de desarrollo local, como nuestro entorno de producción.
-
Docker Registry: La finalidad de este componente es almacenar las imágenes generadas por el Docker Engine. Puede estar instalada en un servidor independiente y es un componente fundamental, ya que nos permite distribuir nuestras aplicaciones. Es un proyecto open source que puede ser instalado gratuitamente en cualquier servidor, pero, como hemos comentado, el proyecto nos ofrece Docker Hub.
Instalación docker
En debian vamos a instalar la versión de la comunidad:
# apt install docker.io
Si queremos usar el cliente de docker con un usuario sin privilegios:
usermod -aG docker usuario
Volvemos acceder con el usuario al sistema, y comprobamos que ya podemos usar el cliente docker con el usuario sin privilegios, por ejemplo, podemos comprobar la versión que hemos instalado:
$ docker --version
Docker version 18.09.1, build 4c52b90
Docker Hello-world
Vamos a comprobar que todo funciona creando nuestro primer contendor desde la imagen hello-world
:
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:95ddb6c31407e84e91a986b004aee40975cb0bda14b5949f6faac5d2deadb4b9
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Pero, ¿qué es lo que está sucediendo al ejecutar esa orden?:
-
Al ser la primera vez que ejecuto un contenedor basado en esa imagen, la imagen
hello-word
se descarga desde el repositorio que se encuentra en el registro que vayamos a utilizar, en nuestro caso DockerHub. -
Muestra el mensaje de bienvenida que es la consecuencia de crear y arrancar un contenedor basado en esa imagen.
Si listamos los contenedores que se están ejecutando:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Comprobamos que este contenedor no se está ejecutando. Un contenedor ejecuta un proceso y cuando termina la ejecución, el contenedor se para.
Para ver los contenedores que no se están ejecutando:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19124e55fbc2 hello-world "/hello" 19 minutes ago Exited (0) 19 minutes ago vigorous_easley
Para eliminar el contenedor podemos identificarlo con su id
:
$ docker rm 372ca4634d53
o con su nombre:
$ docker rm vigorous_easley
Docker run Ubuntu
Con el comando run
vamos a crear un contenedor donde vamos a ejecutar un comando, en este caso vamos a crear el contenedor a partir de una imagen ubuntu
. Si la tenemos ya en nuestro ordenador no será necesario la descarga.
docker run ubuntu /bin/echo 'Hello world'
Hello world
Comprobamos que el contenedor ha ejecutado el comando que hemos indicado y se parado:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
653204833ed3 ubuntu "/bin/echo 'Hello wo…" 6 seconds ago Exited (0) 5 seconds ago hungry_golick
Con el comando docker images
podemos visualizar las imágenes que ya tenemos descargadas en nuestro registro local:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nextcloud latest c6d390bc4501 4 weeks ago 800MB
jenkins/jenkins lts 2302e5864777 4 weeks ago 717MB
mariadb latest ade39f0469a3 4 weeks ago 407MB
ubuntu latest f63181f19b2f 4 weeks ago 72.9MB
gcr.io/k8s-minikube/kicbase v0.0.17 a9b1f16d8ece 4 weeks ago 985MB
debian latest e7d08cddf791 6 weeks ago 114MB
hello-world latest bf756fb1ae65 13 months ago 13.3kB
Contenedor interactivo
En este caso usamos la opción -i
para abrir una sesión interactiva, -t
nos permite crear un pseudo-terminal que nos va a permitir interaccionar con el contenedor, indicamos un nombre del contenedor con la opción --name
, y la imagen que vamos a utilizar para crearlo, en este caso ubuntu, y por último el comando que vamos a ejecutar, en este caso /bin/bash
, que lanzará una sesión bash en el contenedor:
$ docker run -it --name contenedor1 ubuntu /bin/bash
root@4027d65709aa:/#
El contenedor se para cuando salimos de él. Para volver a conectarnos a él:
$ docker start contendor1
contendor1
$ docker attach contenedor1
root@4027d65709aa:/#
Si el contenedor se está ejecutando podemos ejecutar comando en él con el subcomando exec
:
$ docker start contendor1
contendor1
$ docker exec contenedor1 ls -al
Con la orden docker restart
reiniciamos el contendor, lo paramos y lo iniciamos.
Para mostrar información de un contenedor ejecutamos docker inspect
:
docker inspect contenedor1
[
{
"Id": "4027d65709aa2c4d40a6959d371a2379d6ca3ac248a0ff7f765a6fefd8430f61",
"Created": "2021-02-24T11:06:44.535829615Z",
"Path": "/bin/bash",
"Args": [],
"State": {
...
Nos muestra mucha información, está en formato JSON
y nos da datos sobre aspectos como:
- El id del contenedor.
- Los puertos abiertos y sus redirecciones
- Los bind mounts y volúmenes usados.
- El tamaño del contenedor
- La configuración de red del contenedor.
- El ENTRYPOINT que es lo que se ejecuta al hacer docker run.
- El valor de las variables de entorno.
- Y muchas más cosas….
En realidad, todas las imágenes tienen definidas un proceso que se ejecuta, en concreto la imagen ubuntu
tiene definida por defecto el proceso bash, por lo que podríamos haber ejecutado:
$ docker run -it --name contenedor1 ubuntu
Contenedor demonio
En esta ocasión hemos utilizado la opción -d
del comando run
, para que la ejecución del comando en el contenedor se haga en segundo plano.
$ docker run -d --name contenedor2 ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
Si comprobamos si se está ejecutando:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31c8546fa4f2 ubuntu "/bin/sh -c 'while t…" 50 seconds ago Up 50 seconds contenedor2
Y si comprobamos el log:
docker logs contenedor2
hello world
hello world
hello world
hello world
hello world
hello world
hello world
Por último podemos parar el contenedor y borrarlo con las siguientes instrucciones:
$ docker stop contenedor2
$ docker rm contenedor2
Hay que tener en cuenta que un contenedor que esta ejecutándose no puede ser eliminado. Tendríamos que para el contenedor y posteriormente borrarlo. Otra opción es borrarlo a la fuerza:
$ docker rm -f contenedor2
Creando un contenedor con un servidor web
Tenemos muchas imágenes en el registro público docker hub, por ejemplo podemos crear un servidor web con nginx
:
$ docker run -d --name my-nginx -p 8080:80 nginx
Vemos que el contenedor se está ejecutando, además con la opción -p mapeamos un puerto del equipo donde tenemos instalado el docker, con un puerto del contenedor. Para probarlo accede desde un navegador a la ip del servidor con docker y al puerto 8080.
Para acceder al log del contenedor podemos ejecutar:
$ docker logs -f my-nginx
Con la opción logs -f
seguimos visualizando los logs en tiempo real.
Contenedores con variables de entorno
Más adelante veremos que al crear un contenedor que necesita alguna configuración específica, lo que vamos a hacer es crear variables de entorno en el contenedor, para que el proceso que inicializa el contenedor pueda realizar dicha configuración.
Para crear una variable de entorno al crear un contenedor usamos el flag -e
o --env
:
$ docker run -it --name prueba -e USUARIO=prueba ubuntu bash
root@2eb0a2f61c0b:/# echo $USUARIO
prueba
root@2eb0a2f61c0b:/#
En ocasiones es obligatorio el inicializar alguna variable de entorno para que el contenedor pueda ser ejecutado. si miramos la documentación en Docker Hub de la imagen mariadb
, observamos que podemos definir algunas variables de entorno para la creación del contenedor (por ejemplo: MYSQL_DATABASE,MYSQL_USER, MYSQL_PASSWORD
,…). Pero hay una que la tenemos que indicar de forma obligatoria, la contraseña del usuario root (MYSQL_ROOT_PASSWORD
), por lo tanto:
$ docker run --name some-mariadb -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mariadb
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c3effd891e3 mariadb "docker-entrypoint.s…" 8 seconds ago Up 7 seconds 3306/tcp some-mariadb
Podemos ver que se ha creado una variable de entorno:
$ docker exec -it some-mariadb env
...
MYSQL_ROOT_PASSWORD=asdasd
...
Y para acceder podemos ejecutar:
$ docker exec -it some-mariadb bash
root@84f8a67d9e54:/# mysql -u root -p"$MYSQL_ROOT_PASSWORD"
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.5.8-MariaDB-1:10.5.8+maria~focal mariadb.org binary distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>