0% encontró este documento útil (0 votos)
33 vistas123 páginas

Docker Documentation

El documento proporciona una guía sobre el uso de Docker, incluyendo la creación y gestión de contenedores a partir de imágenes, comandos para listar y manejar contenedores, así como la configuración de redes y puertos. Se explican comandos específicos como 'docker run', 'docker ps', y 'docker exec', además de cómo trabajar con variables de entorno y crear redes personalizadas. También se abordan aspectos de Docker Hub y la gestión de imágenes y contenedores, incluyendo la eliminación y el uso de logs.

Cargado por

Sebastian Mora
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
33 vistas123 páginas

Docker Documentation

El documento proporciona una guía sobre el uso de Docker, incluyendo la creación y gestión de contenedores a partir de imágenes, comandos para listar y manejar contenedores, así como la configuración de redes y puertos. Se explican comandos específicos como 'docker run', 'docker ps', y 'docker exec', además de cómo trabajar con variables de entorno y crear redes personalizadas. También se abordan aspectos de Docker Hub y la gestión de imágenes y contenedores, incluyendo la eliminación y el uso de logs.

Cargado por

Sebastian Mora
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 123

01-primer-contenedor.

md 2024-11-22

Contenedor
Se crea a partir de una imagen
Las imagenes se pueden descargar de DockerHub, se conecta automaticamente.

Saber que hace el comando docker run

docker run --help

Agregar nombre de la imagen y crear contenedor, le agregaremos como nombre "hello world"

docker run hello-world

Listar imagenes de docker

docker images

Listar los contenedores (en ejecución)

docker ps

Listar todos los contenedores

docker ps -a

Docker Hub
Sitio Web del repositorio

Podemos empezar a utilizar imagenes publicas que ha subido la comunidad.

Suscripciones a Docker
Precios

Limitaciones a nivel de cuenta. Se puede trabajar de manera gratuita pero tendra sus limitaciones.

1 / 123
01-primer-contenedor.md 2024-11-22

Crear otros contenedores


Descargar imagen, ejemplo ubuntu

docker pull ubuntu

Solo descarga la imagen, no crea contenedor.

Creo el contenedor

docker run ubuntu

Comando Docker ps
docker ps -l: el último utilizado
docker ps -n 4: los últimos 4 utilizados
docker ps --help: ayuda con el comando
docker ps -a -q: saca solo el id de todos los contenedores, tambien funciona docker ps -aq
docker ps -af "name=gallant_antonelli": filtrar de todos los contenedores los que tengan el nombre
"gallant_antonelli"

Nombre a Contenedores

docker run --name ubuntu2 ubuntu

Se le pone el nombre ubuntu2 a la imagen ubuntu creando el contenedor ubuntu2, para tener un acceso
personalizado y no generado por defecto por docker.

Modo Interactivo
Se ingresa directo al contenedor y no lo termina, esto es muy útil en el caso de SO como Ubuntu, Fedora, etc

docker run -it ubuntu3 ubuntu

En algunos sistemas operativos es necesario agregar bash al final, esto debido a que sino no puede acceder a
la imagen. Para salir del contenedor se puede realizar de dos formas:

dentro del contenedor: ejecutar exit


desde la consola: ejecutar docker stop nombredelcontenedor

2 / 123
01-primer-contenedor.md 2024-11-22

Se arrancaría con docker start nombredelcontenedor, se puede empezar y parar con el id (solo se
necesitan las 4 primeras letras)

Si quieres volver a ingresar al modo interactivo necesitas:

docker start -i ubuntu4

No es necesario el t, ya que el entiende que es interactivo.

Contenedores modo background


Que el contenedor se este ejecutando, pero que no tenga que estar en su terminal o algun bash, de esta
manera poder interactuar con él desde una consola.

docker run -d nginx

con el comando d detatched

De esta manera el contenedor se queda en ejecución y no me hemos entrado en ningun entorno interactivo.
No todos los contenedores pueden funcionar en modo background, si el contenedor no esta preparado para
ejecutarse en modo background este se terminará.

Una forma de ponerlos en modo background es utilizarlos en modo interactivo: docker run --name
fedora4 -d -it fedora, de esta manera se logra iniciar el contenedor y este no se termina.

Tags o Etiquetas
Etiquetas para dar algún sentido a la imagen, normalmente se le coloca la versión.

docker run --name apache2 -d httpd:alpine

De esta manera se descarga la versión especifica, en este caso de alpine.

Borrar imagenes de contenedores

docker rm 4780

En este caso 4780 se refiere a los 4 primeros números del id del contenedor a borrar

Nota: Tambien pueden borrarse con el nombre del contenedor.

Para una imagen:

3 / 123
01-primer-contenedor.md 2024-11-22

docker rmi id_imagen

No se pueden borrar las imagenes que esten contenidas dentro de un contenedor. Primero se debe
borrar su contenedor asociado

Si se quiere borrar la imagen forzadamente:

docker rmi -f id_imagen

Comando Exec
Ejecución de comandos con los contenedores. Enfocado a contenedores interactivos y en background.

docker exec ubuntu10 date

Me retorna la fecha

docker exec ubuntu10 uname -a

Me retorna las caracteristicas de la máquina

docker exec -it ubuntu10 bash

Accedo directamente al contenedor.

Comando Attach
Asociar la entrada o la salida para poder acceder de manera facil

docker attach nombre_contenedor

Si el contenedor es de ubuntu y tiene asociada una bash, nos permitira asociarnos a la bash (la salida).

Docker Logs y Docker Kill


Ejemplo: ejecución en modo background y ejecuta una shell

docker run -d ubuntu sh -c "while true;do date;done"

4 / 123
01-primer-contenedor.md 2024-11-22

Con el docker logs puedo ver que esta sacando por pantalla el contenedor, ya que se ejecuta en modo
background pero no vemos nada, con el comando podemos ver lo que esta sacando. Mostrando todo lo que
esta haciendo

docker logs id_contenedor(4 primeros datos)

Para poder mostrar los datos resumidos en los ultimos 10, podemos usar

docker logs id_contenedor --tail 10

Con docker kill podemos matar un contenedor.

docker kill id_contenedor

Docker Top

Permite averiguar cual es el proceso que mas esta consumiendo dentro del contenedor.

docker top id_contenedor

Docker stats

Permite ver información del contenedor. Nombre, uso de CPU, uso de memoria, limite, i/o red.

docker stats id_contenedor

Docker Inspect
Recuperar información de una imagen o un contenedor a nivel de sus propiedades

docker inspect id_contenedor > container.txt

Debido a que sale mucha información y queda mas comodo guardarlo en un archivo para visualizarlo
con mas calma

Docker system
Cuenta con 4 opciones:

df: Show docker disk usage

5 / 123
01-primer-contenedor.md 2024-11-22

events: Get real time events from the server


info: Display system-wide information
prune: Remove unused data

docker system info #información del sistema


docker system df #Cuantos imagenes/contenedores tenemos activos y su tamaño
docker system events #información de eventos de forma recurrente, similar al logs
docker system prune #borrar contenedores parados, redes sin utilizar, imagenes sin
uso y toda la cache sin uso.

Docker cp
Como podemos copiar ficheros/archivos entre un contenedor y el sistema operativo.

Local al Contenedor

docker cp ejemplo.txt ubuntu10:/tmp


#cp archivo contenedor/destino

#Revisar archivo
docker exec -it ubuntu10 bash
cd /tmp
ls -l ejemplo.txt
cat ejemplo.txt # visualizar archivo

Contenedor a Local

Del contenedor al SO: Estando en el SO...

docker cp ubunuto10:/tmp/ejemplo.txt .
# . : ubicación actual
# Revisar archivo
ls -l eje*
cat ejemplo.txt

Variables
Pasando argumentos a los contenedores

docker run --name ubuntu1 -d -it -e V1=10 ubuntu

Recomendación: Variables en mayusculas 'V1=10', pareja clave-valor.

6 / 123
01-primer-contenedor.md 2024-11-22

Verificar

docker exec -it ubuntu1 bash


# Para visualizar la varia
env #variables de entorno del usuario
printenv #variables de entorno
echo $NOMBRE_VARIABLE #mostrar valor

Multiples variables

docker run -itd -rm --name=ubuntazo -e V1=10 -e V2=holas -e V3="texto largo"


ubuntu

-rm = cuando termine el contenedor se elimine automaticamente

Ejemplo: MariaDB

MariaDB es un entorno que nos pide variables para funcionar, en este caso si no se las enviamos nos saltara
un error.

docker run -d --name basedatos mariadb

Se ejecuta pero se cierra debido a que no tiene los parámetros para poder funcionar correctamente. Si
revisamos los logs me dice lo siguiente:

Para solucionarlo agregaremos como nos indica MARIADB_ROOT_PASSWORD

docker run -d --name basedatos -e MARIADB_ROOT_PASSWORD=contra mariadb

De esta manera ya estaría corriendo el contenedor, para verificar:

docker exec -it basedatos bash


printenv #ver variables, en este caso se puede ver MARIADB_ROOT_PASSWORD
# Acceso al motor de base de datos
mariadb -u root -p # intro y pide la contraseña

7 / 123
01-primer-contenedor.md 2024-11-22

Ejemplo: Crear una base de datos llamada db1 con el usuario usu1

docker run -d --name basedatos -e MARIADB_ROOT_PASSWORD=contra -e


MARIADB_DATABASE=db1 -e MARIADB_USER=usu1 -e MARIADB_PASSWORD=contra1 mariadb
docker exec -it basedatos mariadb -u root -p #intro para ingresar contraseña
# Para ingresar con el usuario creado
docker exec -it basedatos mariadb -u usu1 -p

La contraseña del usuario siempre debe estar para crear un usuario

En MariaDB revisión de los datos:

show databases;
select user from mysql.user;

Bases de datos:

Usuarios:

Enviar variables dentro de un archivo Cuando son muchas variables...

-Archivo con las variables

MYSQL_ROOT_PASSWORD=contra
MYSQL_USER=usu1
8 / 123
01-primer-contenedor.md 2024-11-22

MYSQL_DATABASE=db1
MYSQL_PASSWORD=contra1

Comando de ejecución usando la bandera --env-file y colocando la dirección del archivo

docker run --name mysql10 -d --env-file D:\Cursos\Docker\07-Variables\mysql-


properties.txt mysql
# Ingreso a mysql con el usuario creado
docker exec -it mysql10 mysql -u usu1 -p
# Ingreso con root
docker exec -it mysql10 mysql -u root -p

Verificación de resultados

show databases; -- Cualquier usuario


select user from mysql.user; -- Desde usuario root

Bases de datos:

Usuarios (desde root):

Redes

9 / 123
01-primer-contenedor.md 2024-11-22

Docker implementa sus propias redes virtuales para dar soporte a los distintos contenedores que queremos
lanzar

Introducción
Trabajar con puertos
Creación de redes
Enlazar contenedores

Intro a los puertos en docker


Los puertos en docker son privados. Deben hacerse públicos y mapearlos con un puerto del host.

Trabajando con puertos

docker run --name apache1 -d -p 9000:80 httpd


# Verificar
docker ps

Ingresando al navegador en localhost:9000 podemos ver lo siguiente:

Configurandolo

docker exec -it apache1 bash


cd htdocs/
cat index.html #Contiene el It works! que es muestra en el navegador
# Creando mi primera página
nano index.html # Modifico el archivo

Archivo nuevo:

<html>
<body>
<h1>Hello world</h1>
<p>web page created with apache container using docker</p>
<p>Instructor: Apasoft Training</p>
</body>
</html>

10 / 123
01-primer-contenedor.md 2024-11-22

Guardo con Ctrl+O confirmo y luego Ctrl+X y me salgo. Vuelvo a cargar la página en el navegador y tengo:

Pagina web

Vamos a poner una página web ya diseñada para presentarla en el navegador desde un contenedor de
apache. Primero movemos la información a la ubicación del apache.

docker cp D:\Cursos\Docker\08-Redes\speed apache1:/usr/local/apache2/htdocs

Contiene los siguientes archivos

docker exec -it apache1 bash


cd htdocs/
ls

Me sale lo siguiente:

about.html
contact.html
css
gallery.html
images
index.html
js
testimonial.html

Una vez se cargue la página del navegador se tendra la nueva página web:

11 / 123
01-primer-contenedor.md 2024-11-22

Puerto automatico Si deseo que docker se encargue de colocar el puerto, necesito usar el siguiente
comando

docker run --name nginx2 -d -P nginx

De esta forma el puerto se colocará automaticamente.

Inspeccionar puerto de escucha

docker inspect --format='{{.Config.ExposedPorts}}' mariadb

Lo que nos arroja: 'map[3306/tcp:{}]' Comprobando que es el puerto 3306 por lo que ahora si podriamos
usar el comando y asignarle un puerto o que este se asigne automaticamente.

Redes en Docker
Docker mantiene una arquitectura por debajo que permite gestionar las distintas redes que podemos usar
dentro de docker para conectarlas a un contenedor.

Con el comando docker network podemos obtener información sobre las redes y objetos que tenemos.

Listar redes

docker network ls

Obtenemos lo siguiente:

12 / 123
01-primer-contenedor.md 2024-11-22

redes bridge: redes privadas de la maquina que se puedan conectar a traves de la red fisica al exterior.
Red default sino le decimos lo contrario.
redes host: todos los contenedores de esa red solo pueden dialogar con el host principal, no se pueden
ver entre si. Para contenedores autonomos e independientes
redes none: con un driver null, es decir, un contenedor sin red.

docker inspect contenedor_id

podemos ver las IPAddress del contenedor y vemos que es de tipo Bridge

Inspeccionar una red

docker network inspect bridge

bridge es el nombre de la red

Podemos ver los contenedores asociados, sus subredes y algunas opciones asociadas a la red.

Comprobar conexión entre contenedores

Debido a que le metodo bridge los mantiene conectados dentro de la misma red, podemos observar las redes
de los contenedores.

docker run --name nginx2 -d -P nginx


docker run --name nginx3 -d -P nginx
# 172.17.0.2 y 172.17.0.3 respectivamente

13 / 123
01-primer-contenedor.md 2024-11-22

docker exec -it nginx2 bash


apt-get update #actualización
apt-get install -y iputils-ping #instalar ping
ping 172.17.0.3 #hacemos ping al contenedor de nginx3

Obteniendo lo siguiente:

Comprobando los puertos de las imágenes y contenedores

Comprobar puertos:

docker port nombre_contenedor

Sino tenemos clara la información de la imagen respecto a puertos o algunos detalles extras, podemos
acceder a su detalle mediante:

docker inspect imagen_x

Ejemplo con la imagen nginx me sale lo siguiente:

14 / 123
01-primer-contenedor.md 2024-11-22

Donde podemos ver el parámetro ExposedPorts mostrandonos el puerto que tiene la imagen.

Crear una red


Cuando creamos una red nuestra de tipo bridge tiene mas capacidades que la versión bridge por defecto.

Para acceder a la ayuda del comando:

docker network create --help

creación:

docker network create red1


#Visualizar la red
docker network ls
#Ver subredes
docker inspect red1

Docker recomienda que creemos nuestras propias redes, ya que los contenedores de la red van a ver sus
puertos de manera automatica entre si.

creación 2: con rango de subredes

docker network create --subnet=192.168.0.0/16 red2

Tambien se debe poner el gateway...

Asociar un contenedor a una red


Se asocia la bandera network especificando el nombre de la red

docker run -it --name ubuntazo --network red1 ubuntu

Revisamos su red

15 / 123
01-primer-contenedor.md 2024-11-22

docker inspect ubuntazo

y en el parametro Networks nos debería aparecer la red1 asociada.

Ejemplo 2: Otro enlace a la misma red

docker run -d --name nginx4 --network red1 nginx

De igual forma se enlaza a la misma red y la pone la siguiente subred, es decir, el contenedor ubuntazo tiene
la red 172.18.0.2 y el contenedor nginx4 tiene la red 172.18.0.3.

Desde el contenedor ubuntazo hacemos un ping al contenedor nginx4 y obtenemos lo siguiente:

Por lo cual se comprueba su conexión.

Asociar mediante network

Tambien podemos asociar los contenedores a la red mediante el comando docker network.

docker network connect red2 ubuntazo

Lo que hace es conectar el contenedor ubuntazo a la red2 ya creada. Por lo cual ahora si inspeccionamos el
contenedor vemos que tiene 2 redes, la red1 y la red2. Lo que puede conllevar a un trabajo con multiples
redes.

Para desconectarlo de una red:

docker network disconnect red2 ubuntazo

Ahora solo queda conectado a la red1 el contenedor ubuntazo.

Cambiar propiedades de la red

16 / 123
01-primer-contenedor.md 2024-11-22

Asociar la red a una dirección IP establecida:

subnet: rango de IPs voy a utilizar


gateway: compuerta de la red (primer subred)
ip-range: desde que IP va a dar redes a los contenedores

docker network create --subnet=172.28.0.0/16 --gateway=172.28.0.1 --ip-


range=172.28.10.0/24 net4

Inspeccionando la red nos muestra lo siguiente:

Que son las caracteristicas que preestablecimos. Ahora lo asociamos a un contenedor

docker run -dit --name=ubuntua --network=net4 ubuntu


# Verificamos el contenedor
docker inspect ubuntua

Obteniendo lo siguiente:

Arrancando desde la IP 172.28.10.0, la cual fue la que prestablecimos como el inicio de las subredes con el
parametro --ip-range. Ahora si vuelvo a crear otro contenedor y enlazarlo a la red le pondra la siguiente
dirección IP 172.28.10.1.

docker run -dit --name=ubuntub --network=net4 ubuntu


# Verificamos el contenedor
docker inspect ubuntub

Mostrando lo siguiente

Todo esto dentro del ambito de las redes de Docker.

Enlazar contenedores
17 / 123
01-primer-contenedor.md 2024-11-22

Con los contenedores bridge y usando --link

con docker ps -aq obtengo los id's de todos los contenedores y si usamos

docker rm `docker ps -aq`

puedo eliminar cada contenedor, pasandole la salida del otro comando.

Ejemplo:

docker run -it --rm --name b1 busybox


docker run -it --rm --name b2 busybox

Luego con el comando cat /etc/hosts en cualquiera de las consolas de los contenedores podemos
observar el IP y con el comando ping dirección_IP verificamos su conexión.

Ahora si creamos otro contenedor podemos conectarlo directamente a uno ya creado

docker run -it --rm --name b3 --link b1:maquina3 busybox

de tal manera que nos conectamos a b1 y le asignamos el alias maquina3 de tal manera que obtenemos lo
siguiente con el comando para revisar su IP

donde tenemos su IP propia sino tambien el enlace contra la maquina definida b1. Ahora si hacemos un ping
obtenemos:

18 / 123
01-primer-contenedor.md 2024-11-22

Tambien se puede ping b1

Sin embargo si en la máquina b1 tratamos de hacer ping b1 ya no funciona debido a que este tipo de enlace
es unidireccional por tal motivo deberíamos poner toda la dirección ip

Enlazar contenedores mediante redes personalizadas

# servidor
docker run -d --name mysql_server -p 3100:3306 -e MYSQL_ROOT_PASSORD=contra --
network red1 mysql
# cliente
docker run -d --name mysql_client -p 3101:3306 -e MYSQL_ROOT_PASSORD=contra --
network red1 mysql
# Verificar
docker inspect network red1

prueba... Docker crea una especie de DNS para que los contenedores se localicen entre si a traves del nombre.

docker exec -it mysql_client bash


mysql -u root -p -h mysql_server

obteniendo:

de tal manera que no es necesario usar la dirección del host sino directamente el nombre en una red
personalizada con los contenedores
19 / 123
01-primer-contenedor.md 2024-11-22

Ejemplo con wordpress y mysql

Se realizara la creación de un contenedor de mysql y a este le conectaremos un wordpress que creara su base
de datos en el mysql. Luego nos conectaremos mediante el navegador al wordpress (internamente usa
apache).

docker run -d --name mysql_server -p 3100:3306 -e MYSQL_ROOT_PASSWORD=contra -e


MYSQL
_USER=wp -e MYSQL_PASSWORD=contra -e MYSQL_DATABASE=wpdb --network red1 mysql
# verificar
docker exec -it mysql_server mysql -u root -p

vemos la creación de la base de datos wpdb. Ahora crearemos el wordpress

docker run -d --name wp1 -p 9000:80 --netword red1 -e


WORDPRESS_DB_HOST=mysql_server -e WORDPRESS_DB_NAME=wpdb -e WORDPRESS_DB_USER=wp -
e WORDPRESS_DB_PASSWORD=contra wordpress

Si verificamos en los registros con docker logs wp1 evidenciamos su funcionamiento y luego nos queda
verificar en el navegador:

20 / 123
01-primer-contenedor.md 2024-11-22

El cual nos redirige a una página para la instalación del wordpress. Una vez instalado podemos revisar
rapidamente dentro del navegador iniciando sesión con las credenciales o mediante la consola:

revisando las tablas creadas en la instalación por wordpress

por temas prácticos no instalare el wordpress

Borrar una red


Ver ayuda del comando docker network rm --help, las redes deben estar desasociadas de los
contenedores para poder eliminarse

docker network rm network_name

Redes de tipo host

21 / 123
01-primer-contenedor.md 2024-11-22

Redes que nos permiten que un contenedor funcione al mismo nivel de la maquina en la que se encuentra. Es
una red que no esta aislada de la maquina real, sino que hace parte de la misma.

Solo funcionan en Linux

Solo se puede tener una instancia de las redes de tipo host

docker run -dit --name ubuntu1 --network host ubuntu

pero cuando inspeccionamos la red obtenemos:

No tiene IPv4 ni IPv6, lo que indica que el contenedor esta compartiendo el mismo namespace, en el mismo
ambito de red de la maquina.

docker run -dit --name apache1 --network host httpd

Con el comando anterior revisando tampoco nos ofrece una IP, pero si revisamos directamente el puerto que
ofrece apache nos muestra que funciona.

Volúmenes
Almacenamiento en Docker

22 / 123
01-primer-contenedor.md 2024-11-22

Se tiene 3 tipos d almacenamiento:

Volúmenes: permite persistir información dentro de una zona gestionada por docker, dentro de una
docker area dentro de un fichero del computador
bind mount: similar a un enlace directo a un directorio, montar un directorio real que apunte a uno del
contenedor
tmpfs mounts/named Pipes: montajes en memoria, cuando no necesitamos persistir la información,
es decir, si el contenedor muere, tambien se va la información. dos tipos: tmpfs (Linux) y named pipes
(Windows)

En Linux los volumenes se almacenan en: /var/lib/docker/volumes, en Windows es dependiente de varias


razones como la versión, linux containers, wsl, etc.

¿Cómo creamos un volumen?

docker run -it -v /datos --name ubuntu1 ubuntu bash


# si realizamos
ls -l

observamos el directorio del volumen /datos. Este estará enlazado al directorio en la ruta del volumen.
Podemos comprobar esto revisando el nombre del volumen

Información de volúmenes

Listar los volumenes

docker volume ls

Información de un volumen

docker volume inspect id_o_nombre_volumen

23 / 123
01-primer-contenedor.md 2024-11-22

Con el nombre del volumen

Si nos dirigimos en windows en la ruta \\wsl$ y accedemos a docker-desktop-


data>data>docker>volumes evidenciamos

verificamos el volumen dentro de windows con su identificación. Para acceder a esta carpeta compartida de
wsl es necesario acceder manualmente ya que no se muestra normalmente.

Crear volúmen

docker volume create vol1

Y si ahora inspeccionamos los volumenes que tenemos

docker run -it --name ubuntu7 -v vol1:/dir1 ubuntu bash

Creamos un contenedor con la imagen de ubuntu y le asociamos el volumen 1 al directorio 1.

Los volumenes pueden ser compartidos por los contenedores

Compartir múltiples volúmenes

docker run -it -v /datos --name ubuntu4 ubuntu bash


# Creamos un archivo en el volumen

24 / 123
01-primer-contenedor.md 2024-11-22

cd datos/
touch prueba.txt

Y si queremos asociar un contenedor al volumen del contenedor ya creado

docker run -it --name ubuntu5 --volumes-from ubuntu4 ubuntu bash

y si revisamos el volumen

cd datos/
ls

y observamos el archivo prueba.txt, y podemos asociar al mismo volumen pero referenciando un volumen
enlazado y no el original

docker run -it --name ubuntu6 --volumes-from ubuntu5 ubuntu bash

En este caso referenciamos a ubuntu5 que esta referenciando los volumenes de ubuntu4 por tal motivo
podemos observar el volumen con sus datos dentro en el directorio datos/ en la consola de ubuntu6.

Asi borremos los contenedores el volumen seguira existiendo.

Si borramos todos los contenedores asociados al volumen, el volumen quedaria disaciado y no podriamos
utilizarlo, tendria que coger la información del volumen y traspasarla al nuevo volumen para evitar este
inconveniente.

Mientras exista un contenedor apuntando al volumen, este estará activo.

Eliminar volumen

docker volume rm id_o_nombre

Bind mounts
Montaje en el cual este enlazado a un directorio local en el equipo por fuera de docker.

docker run -it -v D:\Cursos\Docker\09-Volumenes\directorio_bind_mount:/dir1 --name


ubuntu8 ubuntu

En este caso enlazamos la ruta del directorio con un directorio dentro del contenedor. Ahora si en el equipo
creamos un archivo dentro del directorio directorio_bind_mount.

25 / 123
01-primer-contenedor.md 2024-11-22

Los directorios sino existen los crea (se deben tener permisos de creación)

y luego revisamos la información en el contenedor

ls #revisamos si existe el directorio /dir1


cd dir1/
ls #revisamos si existe el archivo prueba.txt
cat prueba.txt

Donde podemos evidenciar la información puesta en el archivo prueba.txt visto desde el contenedor.

No se pueden visualizar este tipo de volumenes mediante el comando docker volume ls

Si inspeccionamos el contenedor en la opción de mounts tenemos:

Que nos dice que es de tipo bind, su fuente, su destino, lectura-escritura y otras propiedades mas.

TMPFS mounts
Crea almacenamiento en memoria mientras creo y uso el contenedor, si se borra el contenedor se borra la
información del contenedor. Pero si yo lo detengo y lo vuelvo a arrancar la información sigue estando, debido
a una capa de escritura que contiene el contenedor. Esto lo podemos probar en la siguiente imagen

26 / 123
01-primer-contenedor.md 2024-11-22

Podemos observar la información intacta

Ahora si comprobamos el mismo proceso con un almacenamiento de tipo TMPFS

docker run -it -d --name ubuntu2 --tmpfs /dir1 ubuntu


docker exec -it ubuntu2 bash
cd dir1/
touch borrar.txt
cd ..
mkdir dir2/
touch prueba.txt
cd ..
exit

En el cual creamos un directorio dir1/ de tipo tmpfs y otro dir2/ sin ser tmpfs, en ambos creamos archivos
de texto, ahora si paramos y volvemos a arrancar el contenedor tenemos lo siguiente

docker stop ubuntu2


docker start ubuntu2
docker exec -it ubuntu2 bash
ls #verificar los rirectorios

27 / 123
01-primer-contenedor.md 2024-11-22

Observamos que los archivos dentro de dir2/ aun continuan existiendo pero los archivos dentro de dir1/ ya
no se encuentran. Han sido borrados.

Imágenes
Una imágen en Docker son una serie de capas de tipo lectura.

Un sistema de ficheros de arranque (bootfs) readonly


Un sistema de ficheros de root (rootfs) readonly
debian, ubuntu, nanoserver windows, etc
Montaje de las capas de la imagen (readonly)
capa superior de lectura/escritura (contenedor)

Algunas imagenes traen algunas configuraciones y caracteristicas por defecto, a manera de ejemplo usaremos
la imagen de ubuntu

docker run -it --name ubuntu1 ubuntu bash


#Si ejecutamos wget
wget http://wwww.google.com
#Nos marca comando desconocido, necesitamos instalar las funcionalidades
apt-get update
apt-get install wget
#Si ejecutamos el comando
wget http://www.google.com
#Nos retorna un html

Podemos ver las diferencias de la imagen con sus modificaciones con el comando diff

docker diff ubuntu1

Nos retorna algo como lo siguiente:

28 / 123
01-primer-contenedor.md 2024-11-22

Donde las que son A nos indican una adición, C una modificación y D una eliminación.

Crear una imagen manualmente

docker commit ubuntu1 mi_ubuntu

mi_ubuntu puede tener una etiqueta y esta basada en el contenedor ubuntu1, comprobamos con
docker images

docker run -it mi_ubuntu bash

Recordando que mi_ubuntu parte de ubuntu1 y ubuntu1 fue modificado para poder usar wget, con esto
mismo podriamos verificar su funcionalidad

donde evidenciamos que ya cuenta con el comando wget

Capas dentro de las imagenes


Las imagenes del contenedor apuntan a las imagenes originales no copiando sus datos sino leyendolos, y
estos sobre todo este conjunto tienen una capa de escritura y lectura con la que se trabaja.

docker images #ver imagenes


docker image ls #ver imagenes
docker image inspect ubuntu #inspeccionar alguna capa

Algunas de ellas tienen mas capas que otras, como por ejemplo owncloud/server tiene lo siguiente:
29 / 123
01-primer-contenedor.md 2024-11-22

donde tiene diferentes capas con una encriptación en concreto, por lo cual las imagenes parten de mas
imagenes en las cuales se van apilando y creando una serie de capas para tener una imagen mas compleja
que me permita trabajar con las caracteristicas que deseo.

docker run -it -d --name ubuntu3 ubuntu


docker ps -s #tamaño de los contenedores

si revisamos el contenedor tenemos

evidenciamos 0 bytes de escritura ya que no lo hemos modificado y 78.1 Megabytes de lectura de la imagen
original, esto quiere decir que si creamos varias imagenes de ubuntu estos contenedores creados leeran de la
imagen que ya esta instalada en el sistema y no generaran mas peso innecesario en la maquina.

Si modificamos el contenedor ubuntu3 con apt-get update y revisamos ahora su tamaño

Lo que indica que ya tiene 42 MB en su capa de escritura y sigue leyendo de la capa de las imagenes, sin
embargo acá ya suma el base con el modificado. Tambien podemos mirar el total de los contenedores con -a

¿Donde se guardan las imágenes en Linux?

cd /var/lib/docker
ls -l

30 / 123
01-primer-contenedor.md 2024-11-22

#directorio image

¿Donde se guardan las imágenes en Windows?

Cuando se trabaja con wsl se encuentran en:

Donde el overlay2 es el driver de almacenamiento por defecto con los ficheros que forman parte de todos
los contenedores, tambien en una carpeta anterior tenemos containers

donde tenemos los contenedores. Sin embargo, se pierde un control de los cambios y no es personalizable, es
decir, que permita crear imagenes desde cero y no apartir de otros contenedores

Dockerfile
Un fichero de instrucciones que le indica a Docker como queremos construir una imagen.

FROM ubuntu

La imagen padre puede ser scratch (imagen ligera sin sistema), o alguna imagen en concreto

RUN apt-get update


RUN apt-get install -y python3

31 / 123
01-primer-contenedor.md 2024-11-22

En este caso se le coloca la bandera -y debido a que no permite la interactividad con el usuario, entonces por
defecto ya le especificamos el Si para que continue con la instalación de python3 en este caso.

Ahora para correr la imagen nos movemos a la ubicación y ejecutamos docker build

docker build -t imagen_python .

-t indica una etiqueta que le agregaremos a la imagen, el . indica que es en la ubicación actual.

Se va retornando la serie de pasos que va ejecutando. Ahora si revisamos las imagenes con docker images

Revisamos que si construyo la imagen. Esto luego deberiamos subirlo a nuestro dockerhub para poder
utilizarlo desde la nube, aunque deberiamos cambiar el nombre segun la nomenclatura especifica. Ejecutamos
la imagen para ver si cuenta con python3

docker run -it imagen_python python3


print('hola mundo')

Comando history

Nos indica los cambios que se han ejecutado en una imagen en concreto, si lo usamos con la imagen recien
creada obtenemos

32 / 123
01-primer-contenedor.md 2024-11-22

Donde vemos que el toma la imagen de ubuntu que ya habia sido descargada con anterioridad y sobre esa
agrega lo que le pedimos de modificaciones para crear la imagen deseada.

RUN echo 1.0 >> /etc/version && apt-get install -y git \


&& apt-get install -y iputils-ping

&& creara una sola capa del conjunto de las dos instrucciones, crea un echo 1.0 en un dichero /etc/version
sino existe lo crea, y luego instala una serie de funcionalidades, la \ indica que continuara en la siguiente
linea, para evitar lineas tan extensas. Ahora lo ejecutamos...

docker build -t imagen_python:v1 .


docker run -it imagen_python:v1 bash
# comprobación de los comandos
cat etc/version #nos muestra el 1.0
git
ping

Comando CMD

Nos permite indicar el comando por defecto del contenedor, luego de arrancado el contenedor, el CMD se va a
ejecutar para indicarle el comando por defecto. Solo puede haber un CMD dentro de un Dockerfile, es
decir, pueden existir varias lineas con el CMD pero solo va a valer el último.

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
CMD echo "Welcome to this container"

docker build -t image:v1 .


docker run -it --rm image:v1

lo cual retorna Welcome to this container y se cierra. Si realizamos el siguiente cambio CMD
["echo","Welcome to this contaner"] reemplazando toda la linea de CMD por la anterior permite
ejecutar con exec y no como shell, por lo cual es recomendado ya que no depende de una shell.
33 / 123
01-primer-contenedor.md 2024-11-22

docker build -t image:v1


docker run -it --rm image:v1

Obteniendo el mismo resultado

Si realizamos el cambio del CMD por CMD /bin/bash y denuevo ejecutamos todo otra vez al final obtenemos

lo que indica que cuando va a ejecutar esa linea primero llama a /bin/bash para ejecutar un bash y luego lo
vuelve a ejecutar bin/bash, lo cual no es correcto del todo ya que esta llamando una bash desde una shell

Se recomienda usar el comando JSON para llamar a nuestro comando de la siguiente forma

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
CMD ["/bin/bash"]

Retornando lo siguiente:

Comando ENTRYPOINT

Ejecuta un comando cuando arrancamos el contenedor, a diferencia del CMD nos permite ejecutar este
comando siempre, mientras que con el comando CMD esto no pasa.

A modo de ejemplo si ejecutamos el Dockerfile anterior y agregamos un comando como ls al comienzo


este se sale, debido a que estamos sustituyendo el comando anterior bash por ls.

34 / 123
01-primer-contenedor.md 2024-11-22

Si realizamos el cambio por ENTRYPOINT

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
ENTRYPOINT ["/bin/bash"]

y ejecutamos

Nos retorna un error, este añade el comando que le ponemos y al final no puede ejecutar el comando. Sin
embargo, este tema de añadir los comandos nos permite añadir argumentos enviados desde la consola al
momento de ejecutarlo

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
ENTRYPOINT ["df"]

y si ejecutamos añadiendo -h

Lo que indica que se ejecuto el comando df -h y no solo el df pero acá al momento de añadirse el comando
se formó un comando que si entiende la shell por lo que no retorno error.

35 / 123
01-primer-contenedor.md 2024-11-22

ENTRYPOINT y CMD

Un entrypoint y un cmd.pero se pueden poner juntos? Si. Ejemplo, un contenedor que cuando se ejecute
haga algo de manera obligatoria y cosas por defecto que pondriamos en el cmd.

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
ENTRYPOINT ["ping","-c","3"]
CMD ["localhost"]

El anterior comando dice que enviara 3 paquetes con el comando ping y en el CMD los parametros
predeterminados que se le pasaran al comando ENTRYPOINT, ejecutamos y obtenemos

Donde vemos que si queremos le podemos colocar o no la dirección a la cual hacer ping usando el
parametro de CMD de manera opcional.

Sobreescribir entrypoint

docker run --rm --entrypoint date image:v2

Obteniendo lo siguiente:

36 / 123
01-primer-contenedor.md 2024-11-22

Comando WORKDIR

Nos permite determinar el directorio de trabajo para otras directivas/comandos como e.j. RUN, ENTRYPOINT,
COPY, ADD, etc

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
RUN mkdir /datos
WORKDIR /datos
RUN touch f1.txt
RUN mkdir /datos1
WORKDIR /datos1
RUN touch f2.txt
ENTRYPOINT ["/bin/bash"]

Donde se crean los archivos con relación al directorio seleccionado

WORKDIR se puede usar multiples veces

Si ejecutamos el Dockerfile anterior obtenemos lo siguiente

Donde nos posiciona directamente en /datos1, ademas de tambien crear /datos y ambas con sus archivos
respectivos.

COMANDO COPY-ADD

Copiar contenido de la maquina host al contenedor, COPY fuente destino

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
##WORKDIR##
RUN mkdir /datos
WORKDIR /datos
RUN touch f1.txt
RUN mkdir /datos1
WORKDIR /datos1
RUN touch f2.txt
37 / 123
01-primer-contenedor.md 2024-11-22

##COPY##
COPY index.html .
COPY app.log /datos

##ENTRYPOINT##
ENTRYPOINT ["/bin/bash"]

Donde obtenemos lo siguiente

Vemos que se han movido correctamente los archivos al destino seleccionado, en este caso el . hace
referencia a /datos1 ya que es el ultimo sitio que se posiciona debido al WORKDIR.

ADD

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
##WORKDIR##
RUN mkdir /datos
WORKDIR /datos
RUN touch f1.txt
RUN mkdir /datos1
WORKDIR /datos1
RUN touch f2.txt

##COPY##
COPY index.html .
COPY app.log /datos

##ADD##
ADD docs docs
ADD f* /datos/
ADD f.tar .

38 / 123
01-primer-contenedor.md 2024-11-22

##ENTRYPOINT##
ENTRYPOINT ["/bin/bash"]

ADD y COPY son bastante similares, una diferencia es que ADD permite el paso de ficheros .tar (ficheros
empaquetados), este contiene archivos de f1 a f5. Tambien permite extraer contenido de URL.

Donde si lo ejecutamos obtenemos lo siguiente

En este caso el trata al archivo .tar como un directorio y por lo demas realiza los movimientos de los
archivos.

MANEJO DE VARIABLES

Usando el comando ENV seguido de la definición de la variable, ademas de poderlos usar internamente
definidos en el Dockerfile. Añadiendo lo siguiente antes del comando ENTRYPOINT al anterior archivo
Dockerfile

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir && mkdir $dir1

Lo ejecutamos y obtenemos

39 / 123
01-primer-contenedor.md 2024-11-22

Donde vemos los directorios /data y /data1, Ademas de poderlo revisar dentro del contenedor

Nos las podemos cambiar una vez creado el contenedor

Reemplazando el ENV por lo siguiente:

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir && mkdir $dir1
ENV TEXTO="Ejemplo de texto"

CMD echo $TEXTO

Ejecutamos y obtenemos:

Pero si queremos modificar la variable directamente desde el arranque del contenedor debemos usar la
bandera -e

docker run --rm -e TEXTO="Esto es un ejemplo 2" image:v6

Obteniendo

Pero si queremos cambiar dir1

40 / 123
01-primer-contenedor.md 2024-11-22

docker run -it --rm -e dir1=/ejemplazo image:v6 bash

Obtenemos

No realiza cambios ya que ya se ha ejecutado el paso de ejecutar el directorio (sigue creando el directorio
/data1), sin embargo si modifica el valor de la variable cuando revisamos en el ambiente del contenedor.

Se cambian las variables pero ya cuando he arrancado el contenedor

Si modifico el archivo con lo siguiente

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir
ENV TEXTO="Ejemplo de texto"

CMD mkdir $dir1; ls -l /


# Para no cerrar el contenedor y que muestre la información

Ya que cuento con la creación del directorio en el CMD ya en la parte escribible del contenedor y no antes. Y le
envío como argumento un valor a dir1

docker build -t image:v6 .


docker run --rm -e dir1=/ejemplazo image:v6

Obtenemos que el directorio /ejemplazo si se creo y el directorio anterior /data1 no. Ya que cuando se
efectuo el comando CMD recurrio al argumento enviado por consola.

41 / 123
01-primer-contenedor.md 2024-11-22

Y como lo podría poner en un script y lanzarlo con el CMD o el ENTRYPOINT

Creo el siguiente script en un archivo crear_dir.sh

mkdir $dir1
ls -l /

Y en el Dockerfile modificamos lo siguiente

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir
ENV TEXTO="Ejemplo de texto"

ADD crear_dir.sh .
# permisos
RUN chmod +x /datos1/crear_dir.sh
# ejecución
CMD /datos1/crear_dir.sh

Y si ahora lo ejecutamos,funciona de la misma forma:

42 / 123
01-primer-contenedor.md 2024-11-22

Ahora si le pasamos un parametro

docker run --rm -e dir1=/textos image:v7

Evidenciando que se creo el directorio /textos y nos muestra la salida de acuerdo a la ejecución del script

43 / 123
01-primer-contenedor.md 2024-11-22

Comando ARG

Directiva que sirve para poner variables, con la diferencia de que no es necesario poner un valor para las
variables dentro del Dockerfile

##WORKDIR##
RUN mkdir /datos
WORKDIR /datos
RUN touch f1.txt
RUN mkdir /datos1
WORKDIR /datos1
RUN touch f2.txt

##COPY##
COPY index.html .
COPY app.log /datos

##ADD##
ADD docs docs
ADD f* /datos/
ADD f.tar .

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir && mkdir $dir1
ENV TEXTO="Ejemplo de texto"

44 / 123
01-primer-contenedor.md 2024-11-22

##ARG##
ARG dir2
RUN mkdir $dir2

##ENTRYPOINT##
ENTRYPOINT ["/bin/bash"]

Obligatorio pasar el parametro dir2 sino retorna un error al momento de construir la imagen

docker build -t image:v6 --build-arg dir2=/nueva_carpeta .


docker run -it --rm image:v6

Obtenemos lo siguiente

Ejemplo: creando un usuario

Creamos un script con adduser $user_docker donde ejecutara el comando adduser y recibira la variable
user_docker

##ARG##
ARG dir2
RUN mkdir $dir2
ARG user
ENV user_docker $user
ADD add_user.sh /datos1/
RUN chmod +x /datos1/add_user.sh
RUN /datos1/add_user.sh

Notar que ARG no enlaza entre el contenedor y el dockerfile, por lo que no importa si la declaramos como
user o como user_docker como se define en el script, de esta forma le enviamos al comando ENV para
enlazarla con la variable del script.

docker build -t image:v8 --build-arg dir2=/ejemplo2 --build-arg user=sebas .


docker run -it --rm image:v8
cat /etc/passwd # revisar usuarios
cd /home/
ls

45 / 123
01-primer-contenedor.md 2024-11-22

Revisando obtenemos el usuario creado y un directorio del usuario en /home

Comando EXPOSE

Permite la exposición de puertos para poder usarse publicamente (se debe seguir usando la opción -p), de tal
manera que sirve de guia al momento de construir el Dockerfile El script entrypoint.sh queda:

apachectl start
/bin/bash

Y el Dockerfile queda:

RUN touch f1.txt


RUN mkdir /datos1
WORKDIR /datos1
RUN touch f2.txt

##COPY##
COPY index.html .
COPY app.log /datos

##ADD##
ADD docs docs
46 / 123
01-primer-contenedor.md 2024-11-22

ADD f* /datos/
ADD f.tar .

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir && mkdir $dir1
ENV TEXTO="Ejemplo de texto"

##EXPOSE##
RUN apt-get install -y apache2
EXPOSE 80
ADD entrypoint.sh /datos1/

##CMD##
RUN chmod +x /datos1/entrypoint.sh
CMD /datos1/entrypoint.sh

Ejecutamos

docker build -t image:v9 .


docker run -it --rm -p 8080:80 image:v9

y Obtenemos:

Nos menciona una advertencia de la IP del servidor por lo que se recomienda usar un servidor propio, sin
embargo, para probar nos sirve, ahora revisamos en la maquina en un navegador el localhost con el puerto
8080 predefinido en el Dockerfile, donde nos damos cuenta que si funcionó el enlace de los puertos con la
aplicación del contenedor

47 / 123
01-primer-contenedor.md 2024-11-22

Comando VOLUME

Crear volumenes con el Dockerfile

FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN echo 1.0 >> /etc/version && apt-get install -y git \
&& apt-get install -y iputils-ping
##WORKDIR##
RUN mkdir /datos
WORKDIR /datos
RUN touch f1.txt
RUN mkdir /datos1
WORKDIR /datos1
RUN touch f2.txt

##COPY##
COPY index.html .
COPY app.log /datos

##ADD##
ADD docs docs
ADD f* /datos/
ADD f.tar .

##ENV##
ENV dir=/data dir1=/data1
RUN mkdir $dir && mkdir $dir1
ENV TEXTO="Ejemplo de texto"

##EXPOSE##
RUN apt-get install -y apache2
EXPOSE 80
48 / 123
01-primer-contenedor.md 2024-11-22

ADD entrypoint.sh /datos1/

##VOLUME##
ADD paginas /var/www/html
VOLUME ["/var/www/html"]

##CMD##
RUN chmod +x /datos1/entrypoint.sh
CMD /datos1/entrypoint.sh

Donde movemos un directorio a una dirección en el contenedor y luego en esa dirección creo un volumen.
Ahora si ejecutamos el Dockerfile y creamos un contenedor basandonos en su imagen.

docker build -t image:v10 .


docker run -it --rm -p 8080:80 --name contenedor_1 image:v10

Y revisamos su funcionamiento

Observamos que ahora muestra el contenido del directorio que le pasamos /paginas en vez del por defecto
de apache debido a que movimos y reemplazamos el contenido que muestra apache con el contenido de
/paginas en la ruta /var/www/html. Y si revisamos los volumenes notamos un nuevo volumen. Tambien
podemos comprobar directamente el contenido del contenedor por medio de \\wsl.localhost\docker-
desktop-
data\data\docker\volumes\8e7e69962e8e690a45d1ebbfb2e7f96d30ae0e5d37fda9ef0c17d514bb5fc49
1\_data en el caso de usar wsl en Windows.

49 / 123
01-primer-contenedor.md 2024-11-22

Ahora si creamos otro contenedor y enlazamos al mismo volumen.

docker run -it --rm -p 9080:80 --volumes-from contenedor_1 --name contene


dor_2 image:v10

Revisando en el navegador

Observando el mismo contenido desde otro puerto, ahora si revisamos los volumenes, notamos que no creo
un segundo volumen, lo que indica que esta apuntando al mismo volumen, lo revisamos mediante un inspect
de cada contenedor.

Volumen contenedor 2:

Volumen contenedor 1:

50 / 123
01-primer-contenedor.md 2024-11-22

Observando que apuntan al mismo sitio. Ademas si modificamos el contenido de la página y


recargamos los sitios sin parar los contenedores notamos lo siguiente

Ambos contenedores cambiaron su nombre SEBAS INC. sin tener que modificar los dos sino el
contenido del wsl del volumen creado inicialmente y al que ambos contenedores apuntan.

Docker Hub
Subir imagenes a docker hub

Siguiendo algunos estandares como el nombre del usuario, a modo de ejemplo bajaremos y subiremos a
docker hub la imagen del Dockerfile

docker build -t jseb99/image:v11 .

Le colocamos el nombre de usuario, en mi caso jseb99, luego iniciaremos sesión desde el CLI

docker login

51 / 123
01-primer-contenedor.md 2024-11-22

Acá nos puede pedir una URL en caso de tenerla o sino directamente intentara acceder a nuestra cuenta de
docker hub donde le ingresamos nuestras credenciales

Luego subiremos la imagen a un registro (en el que este con la sesion iniciada)

docker push jseb99/image:v11

Revisando en docker hub tenemos

52 / 123
01-primer-contenedor.md 2024-11-22

Docker TAG - Etiquetar imagenes

¿Puedo etiquetar una imagen que ya existe sin necesidad de volverla a recrear? DOCKER TAG

docker run --name mi_apache -d -it httpd


docker exec -it mi_apache bash
apt-get update
apt-get install -y wget
docker commit mi_apache nuevo_nombre

Apartir de una imagen y contenedor que he modificado creo un commit, es decir, una imagen nueva. Y si
ahora quiero subirla a mi Dockerhub no me va a dejar.

Debido a que debe estar precedida con el nombre del usuario. Para modificarlo usaremos docker tag

docker tag mi_apache jseb99/mi_apache:latest

53 / 123
01-primer-contenedor.md 2024-11-22

Si comprobamos las imagenes con docker image ls

Notamos que ambas imagenes mi_apache y jseb99/mi_apache apuntan a la misma ID de la imagen, con el
mismo peso. Por lo cual no duplico la imagen sino la etiqueto, pero hace referencia a la misma imagen (su
contenido es el mismo), como un acceso directo. Ahora si realizamos un docker push ya me dejara subirla

Ya aqui se veran los limites de la suscripción que tengamos activa en Docker

Revisamos en docker hub, encontraremos la imagen ya publicada en nuestra cuenta, en este caso no
realizamos un login ya que estabamos iniciados recientemente.

SAVE & LOAD ¿Cómo podriamos pasar una imagen a alguna maquina externa? Mediante el comando SAVE
podemos empaquetar una imagen en un archivo .tar, entonces usaremos el siguiente comando

54 / 123
01-primer-contenedor.md 2024-11-22

docker image save ubuntu -o ubuntu_docker.tar

Donde ubuntu_docker.tar sera el nombre de salida con la extensión .tar, Entonces si revisamos la
ubicación donde nos encontramos...tendremos el archivo empaquetado.

Ademas mediante el siguiente comando podemos ver lo que tiene

tar tvf nombre_archivo

Lo que equivale a las capas de la imagen. Ahora si eliminamos la imagen de docker del computador docker
image rm ubuntu.

Nota: tambien necesita eliminar los contenedores asociados a esa imagen

Ahora si queremos cargar la imagen usaremos lo siguiente:

docker image load -i ubuntu_docker.tar

-i de input

y revisamos las imagenes cargadas docker image ls o en linux docker image ls | grep ubuntu

donde volvemos a tener la imagen con las mismas referencias anteriores (fecha, id, peso)

Docker search ¿Cómo podriamos buscar posibles imagenes que tengamos en Docker Hub? Mediante el
comando docker search, nos permitira buscar una determinada imagen dentro de docker hub

docker search httpd

55 / 123
01-primer-contenedor.md 2024-11-22

La salida es truncada para evitar sobresaturación en la salida por la cantidad de imagenes en docker hub, este
comando muestra:

Nombre de imagen
Descripción
Estrellas
Si es oficial

Siempre ordena por estrellas

Ademas si buscamos con nuestro usuario o alguno en especificamos

Nos muestra las imagenes que subimos con anterioridad, debido a que tambien las nombramos con nuestro
nombre de usuario.

Algunos otros ejemplos:

56 / 123
01-primer-contenedor.md 2024-11-22

docker search httpd --limit=5 #las 5 primeras


docker search redis --filter is-official=true #imagen oficial
docker search httpd --filter stars=30 #con 30 o mas estrellas

Opciones Avanzadas
¿Cómo aprovechar los recursos para trabajar con Docker?

Memoria
Por defecto los contenedores intentan usar la máxima memoria posible, docker establece por defecto un
conjunto de prioridades para impedir que la maquina se venga abajo si tenemos mucho uso de contenedores.

docker run --name apache1 -d -p 9090:80 httpd


docker stats #como iban trabajando los contenedores
docker run --name ubuntu1 -d -it ubuntu
docker stats
docker run --name mysql1 -e MYSQL_ROOT_PASSORD=contra -d mysql
docker stats #comparacion de contenedores

salida comparativa:

Entonces todos esos procesos compiten por el espacio en memoria disponible para los recursos, en
este caso de 3.7 GB, todos los contenedores pueden usar memoria swap.

memoria swap: cuando se reclama en memoria y no hay suficiente, se baja zonas de memoria fria del
contenedor a lo que seria disco, y se recuperan si es necesario.

Uso de memoria

Fuente: constraints de recursos

Para configurar la memoria de un contenedor tenemos -m o --memory=, el valor minimo esta en 6 MB


permitido para un contenedor.
El --memory-swap, la cantidad de memoria que el contenedor se le permite intercambiar con el disco
(Normalmente, suele ser la misma que la memoria). Si se configura solo -m/--memory el contenedor
puede usar tanta memoria swap como la bandera --memory
--memory-swappiness es el porcentaje con el que se realizara el cambio a swap, dependiendo de la
cantidad de paginas usadas por el contenedor y va de 0 a 100.
--memory-reservation memoria limite minima (menor que la memoria) que se activa cuando docker
detecta contención o baja memoria en la maquina host

57 / 123
01-primer-contenedor.md 2024-11-22

--kernel-memory usa la memoria del kernel, no puede ser intercambiada por el disco, por lo que
puede colapsar la maquina. Lo minimo permitido es 6 MB.
--oom-kill-disable si existe un error out of memory el kernel mata los procesos en el contenedor,
solo deshabilitar esta opción cuando tengamos configurado la memoria mediante las opciones -m/--
memory. Si no esta configurado, el host puede correr out of memory y el kernel puede matar los
procesos del sistema para liberar memoria.

Ejemplo:

docker run --name apache1 -d -m 12m httpd

Si revisamos con docker stats

El contenedor ahora solo usa 12 MB máximo no como los 30 MB que usaba en el primer proceso mostrado
anteriormente. Ademas que en ese caso la memoria swap estaria en un valor cercano a 12 MB. Ahora si
usamos el mismo valor:

docker run --name apache1 -d -it -m 256m --memory-swap=256m httpd

Ahora si revisamos el docker stats

Donde en este caso la --memory-swap se desactiva ya que este valor tiene un valor igual a --memory y el
contenedor en esta condición no tendría acceso al intercambio., ademas de ver el limite de memoria que
tiene el contenedor en 256 MB. Ahora si instalamos algo dentro del contenedor

docker exec -it apache1 bash


apt-get update

Vemos los cambios en los recursos...

Instalaremos un simulador de estres

apt-get install stress


stress -m 2 --vm-bytes 100m --vm-keep

58 / 123
01-primer-contenedor.md 2024-11-22

Entonces le pedimos 2 procesos en memoria de 100 MB cada uno y que los guarde. Nos muestra lo siguiente

Vemos que la maquina usa 241 MB y esta en un 94% de uso, pero aun lo puede manejar, ahora le enviaremos
3 procesos mediante stress -m 3 --vm-bytes 100m --vm-keep lo que nos muestra

Un error debido a que la ejecución de esos procesos superan los limites permitidos.

Uso de memory swap

Ahora aplicaremos el ejemplo anterior mediante el uso de la memoria de intercambio.

docker run --name apache1 -d -m 256m --memory-swap 1G httpd


docker exec -it apache1 bash
apt-get update
apt-get install stress
stress -m 2 --vm-bytes 100m --vm-keep

En este punto el funciona normalmente

Ahora si le subimos a 3 recursos mediante

stress -m 3 --vm-bytes 100m --vm-keep

El recurso ya no se para y sigue funcionando, en este caso si usa el total de la memoria, pero internamente
esta usando la memoria swap

Nota: El uso de memoria swap tambien es mas lento ya que usa disco realmente, sin embargo permite
que pares un proceso debido a un pico o algun elevación momentanea de uso de memoria de los
procesos.

CPU
59 / 123
01-primer-contenedor.md 2024-11-22

Por defecto cada contenedor accede a los ciclos de CPU de la maquina ilimitadamente, se pueden poner
limites a este recurso meditante:

--cpus=<value>: Especifica cuantos recursos de la CPU puede usar, si pongo 1.5 este podra usar a lo
maximo 1.5 recursos de la CPU.
--cpu-period=<value>: Especifica el periodo del cronograma CFS de la CPU, el cual es usada con una
cuota --cpu-quota. Por defecto es de 100000 microsegundos.
--cpu-quota=<value>: Cuota de CPU CFS. El número de microsegundos por --cpu-period que el
contenedor es limitado antes de cortarse.
--cpuset-cpus: Limita las CPUs o núcleos que un contenedor usa. Puede anotarse mediante - para un
rango o , para una lista. 0-3 Usara las cpus de la 1 a la 4 y 1,3 usara la segunda y cuarta CPU.
--cpu-shares: bandera que puede incrementarse o disminuirse del defecto (1024, unidad de trabajo)
peso del contenedor. Te da acceso a una mayor o menor proporción de los ciclos de CPU de la
maquina. Esto solo se activa cuando se tienen restricciones en los ciclos de CPU. Entonces cuando se
tengan ciclos de CPU activos los contenedores podran hacer uso de estos.

Docker info nos ofrece tambien el número de CPUs

CPU

Ejemplo: usaremos el comando docker stats para ir revisando los recursos de la maquina, el
comando stress.

docker run -it -d --name apache1 httpd


docker exec -it apache1 bash
apt-get update
apt-get install stress
exit
docker commit apache1 apache-stress:latest
docker rm -f apache1

Ahora con la imagen lista podremos realizar las practicas con las limitaciones

docker run --name apache1 --cpus=0.5 -d -it apache-stress


docker run --name apache2 --cpus=1.5 -d -it apache-stress

El cual apache1 indica que como mucho consumira media CPU y apache2 indica que consumira 1 CPU y
media. Sin embargo, aun el consumo es el mismo, esto es debido a que aun no piden recursos para su
funcionamiento.

¿Que pasa si le pongo mas CPU? docker run --name apache3 --cpus=10 -d -it apache-stress Me
sale el siguiente error

60 / 123
01-primer-contenedor.md 2024-11-22

Nota: solo hace referencia al limite ya que todos los contenedores pueden usar el maximo de CPUs, es
decir, estos valores no se suman sino se refieren a los recursos de CP00U que puede usar.

Ahora si usamos el primer contenedor y lo forzamos para verificar sus limites, en este caso le pediremos 5
procesos de CPU.

docker exec -it apache1 bash


stress -c 5

Si revisamos tenemos lo siguiente

Ahora si le subimos a 10 procesos de cpu tenemos lo siguiente

Seguimos notando el limite de CPU. Mediante otra terminal forzaremos ahora el segundo contenedor

docker exec -it apache2 bash


stress -c 10

Notamos que el segundo contenedor si utiliza 150% de los recursos de la CPU.

Con 3 contenedores solicitando recurso estaria de la siguiente forma, ahora el tercero soporta las 8 cpus que
mi maquina permite.

y le pido un stress de 10 procesos de cpu. Pero este no llega a pedir todo el porcentaje como vemos en la
imagen sino que tiene que nivelarse para manejar los recursos entre todos contenedores que en ese instante
estan solicitando recursos.

CPU shares y CPU sets

61 / 123
01-primer-contenedor.md 2024-11-22

Crearemos el siguiente contenedor

docker run --name apache1 -d -it --cpu-shares=1024 --cpuset-cpus=0 apache-stress


docker run --name apache2 -d -it --cpu-shares=512 --cpuset-cpus=0 apache-stress
docker run --name apache3 -d -it --cpu-shares=512 --cpuset-cpus=0 apache-stress

Donde 1024 se refiere al total de la unidad de la cpu que podra usar, el segundo usara la mitad de potencia al
igual que el tercero y todos usaran la misma CPU. Ahora si le ponemos un stress -c 10 al primer
contenedor tendremos el siguiente uso:

El primer contenedor puede usar todo el porcentaje debido a que no compite por los recursos con los demas
contenedores. Ahora si en el segundo contenedor ponemos un stress -c 10 tenemos:

Ahora ya notamos una repartición de recursos, por ultimo le ponemos un stress -c 10 al tercer contenedor
y tenemos:

Notamos que ahora los tres compiten por los recursos y àpache1 puede llegar a un 50% de los recursos y los
demas se los reparten en un 25% cada uno. O lo mismo que el primero obtiene 1024 y los otros 2 usan 512
cada uno, repartiendose proporcionalmente los recursos de la primer CPU (CPU 0).

Ahora realizaremos un ejemplo cuando compiten con varias CPU:

docker run --name apache1 -d -it --cpu-shares=1024 apache-stress


docker run --name apache2 -d -it --cpu-shares=512 apache-stress
docker run --name apache3 -d -it --cpu-shares=512 apache-stress

Ya no le especificamos las CPUs que podran usar los contenedores. Ahora si repitemos el envío de estres a los
contenedores tenemos lo siguiente, para solo el contenedor 1 tenemos:

Ahora el puede hacer uso de todas las CPUs si estan disponibles, si le enviamos ahora el segundo contenedor
tenemos:
62 / 123
01-primer-contenedor.md 2024-11-22

Ya notamos una repartición proporcional de los recursos uno en 1024 y el otro en 512, en este caso uno sobre
500 y el otro sobre 260. Si le enviamos el tercero, tenemos:

Ahora notamos que el primer contenedor esta usando alrededor de 4 CPUs y los otros 2 se reparten 2 CPUs
cada uno. Manteniendo las proporcionalidades respectivamente.

Drivers
Drivers o mecanismos que usa Docker para guardar los datos, ya sea dentro un disco o de un volumen.
Storage Drivers

Recomendado: todos los datos persistentes guardarlos en volumenes.

La estrategia que utiliza docker para ir lo mas rapido posible es Copy-on-write (CoW) strategy, Todos los
contenedores de inicio usan los datos de las imagenes que tiene docker guardados localmente, estos difieren
es cuando se modifican los contenedores y esta modificación es lo unico que guarda docker en su capa de
escritura, al inicio docker revisa capa por capa por el archivo a actualizar, luego copia el archivo para enviarlo
a la capa superior y cualquier modificación es hecha sobre la copia de tal manera que el contenedor no puede
ver el archivo que existe en las capas inferiores.

En el siguiente enlace puedes seleccionar el driver que necesitas para tu sistema

¿Cómo podemos ver que driver tenemos?

docker info

Y obtenemos:

Políticas de re bote de los contenedores


Normalmente, cuando paramos un contenedor este no vuelve a arrancar, esto se debe a una politica
predefinida internamente, para poder modificar esto podemos usar la bandera de reinicio --restart al
momento de lanzar un contenedor de acuerdo a una serie de valores:

63 / 123
01-primer-contenedor.md 2024-11-22

On-failure (maximos intentos): rebota el contenedor si se ha caído con algún código distinto a cero.
Además podemos indicar cuantas veces reintenta el arranque.
Always: rebota el contenedor siempre cuando se para, salvo que sea una parada normal y deseada. Si
se para de forma normal solo se rebota cuando se rebota el propio demonio de docker, es decir, el
servidor.
Unless-stopped: parecida a always excepto que cuando el contenedor se para manualmente o de
cualquier otra forma no es rebotado incluso después de que arranque el demonio de Docker
No: No se rebota el contenedor de forma automatica bajo ningun concepto.

Ejemplo:

Creamos un archivo de scripting llamado parar.sh y le agregamos lo siguiente:

#/bin/bash
sleep 15
exit 1

Nota: Cualquier salida distinta a 0 es incorrecta.

Añadimos un archivo Dockerfile con la siguiente información:

FROM ubuntu
ADD parar.sh /
RUN chmod +x /parar.sh
CMD /bin/bash /parar.sh

Utilizando una imagen de ubuntu agregara el archivo de scripting al directorio /, añadimos permisos de
ejecución y luego por medio de bash ejecutara parar.sh. Ahora crearemos la imagen a partir del archivo
Dockerfile usando el siguiente comando docker build -t imagen-restart ..

Usando No:

docker run --name t1 -d imagen-restart

Funcionamiento:

Ademas de poder evidenciar que se salio a los 15 segundos.

64 / 123
01-primer-contenedor.md 2024-11-22

Ya no se volvio a encender despues de eso.

Usando Always: Se rebotara siempre y cuando no sea parado manualmente.

docker run --name t1 -d --restart always imagen-restart

Funcionamiento:

Al pararse despues de 15 segundos se vuelve a reiniciar. ¿Cómo podria verificar que rebota continuamente?
Con docker inspect t1 con t1 como el nombre del contenedor:

Donde revisando la propiedad RestartCount nos dice el número de veces que se ha reiniciado. Ahora si lo
paro manualmene con docker stop t1 tendremos:

Este ya no se reinicia pasado los 15 segundos como se tenia preestablecido. Pero si reiniciamos el demonio o
el servidor, que seria nuestro docker desktop en Windows, tendremos:

Es decir que se volvio a reiniciar el contendor automaticamente apenas reiniciamos el daemon o demonio de
docker.

Reinicio manual en windows:

65 / 123
01-primer-contenedor.md 2024-11-22

Usando Unless-stopped:

docker run --name t1 -d --restart unless-stopped imagen-restart

Funcionamiento:

Revisando el número de conteos:

Ahora si ejecuto docker stop t1 y a diferencia de always ejecuto un systemctl restart docker este
seguira parado. En este caso realice un reinicio manual y al revisar con un docker ps -a tendremos:

Por lo cual el contenedor no se reinicia automaticamente.

Usando On-failure:

docker run --name t1 -d --restart on-failure:2 imagen-restart

Y si revisamos el contenedor:

Ya no reinicia y la cantidad de conteos es de 2 según el comando, esto lo revisamos con una inspección al
contenedor:

Donde notamos que despues de 2 conteos no volvio a reiniciarse. Ya no reinicia asi reiniciemos el DAEMON.

66 / 123
01-primer-contenedor.md 2024-11-22

¿Donde se guardan los datos de configuración de los contenedores?

Cuando se crea un contenedor, los datos del contenedor se guardan en ese mismo directorio.

docker run --name apache1 -d httpd

Y si revisamos la ruta \\wsl.localhost\docker-desktop-data\data\docker\containers tendremos una


carpeta con el nombre del id del contenedor creado:

Adentro encontramos lo siguiente:

Fichero de logs: nos muestra lo que va saliendo en el log del apache en este caso
Checkpoints que se van generando
Config.v2.json sería la configuración con la que se ha arrancado el contenedor, normalmente lo que se
ve en docker inspect
Configuración del host, tambien nos lo da el docker inspect
Resolv.conf contiene el DNS para acceder a los recursos
Información de los hosts y mounts.

Loggins: Drivers predefinidos que vienen con docker.

67 / 123
01-primer-contenedor.md 2024-11-22

Si corremos un contenedor de cero y revisamos la información de los logs en el archivo .log podremos ver la
misma información que cuando usamos docker log y el id o nombre del contenedor.

docker run --name apache1 -d httpd


docker logs apache1

Y si reviso lo mismo en el archivo del directorio creado

Y si usamos un inspect sobre el contenedor tambien podriamos ver el tipo de log que estamos utillizando

Nota: al registrar todos los sucesos dentro de un contenedor, es recomendable pensar que hacer con
esa información de los logs para no sobresaturarlos de información y poder revisarlo de manera mas
apropiada.

Cambiar las propiedades de un log driver

De acuerdo a lo anterior dependera del tipo de driver que se tenga, en el caso json-file se podra modificar
el fichero daemon.json que especifica el comportamiento de la configuración general de docker. Lo que
indica que tiene propiedades que indican como se comportan el json-file.

{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

68 / 123
01-primer-contenedor.md 2024-11-22

El daemon.json anterior nos muestra el tipo de log driver, el tamaño maximo y el numero maximo de archivos
para habilitar una rotación automatica del log. Ademas tambien se puede ejecutar lo mismo a traves del
propio contenedor mediante el siguiente comando:

docker run \
--log-driver json-file --log-opt max-size=10m \
alpine echo hello world

Usando las banderas --log-driver y --log-opt.

Configurar Docker - Contextos


Daemon
El proceso background que se esta ejecutando mientras usamos docker se denomina dockerd. A traves, del
cual existen muchos comandos para modificar algunas caracteristicas de docker invocandose a traves del
comando dockerd. Sin embargo, tambien se puede configurar a traves de la edición del archivo
daemon.json. El cual es leido cuando arranca docker.

En Linux el directorio del fichero de configuración se encuentra en /etc/docker y en el caso de los Windows
se encuentra en %programdata%\docker\config\daemon.json, pero revisando lo encontre en
C:\Users\sebas\.docker. El archivo se encuentra con lo siguiente:

{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false
}

Vamos a modificarlo con lo siguiente:

{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"log-driver": "json-file",
"log-opt": {
69 / 123
01-primer-contenedor.md 2024-11-22

"max-size": "5m",
"max-file": "3"
}
}

Reiniciamos docker y ya estaria segun la configuración.

Tener cuidado ya que si modificamos mal el archivo docker desktop no iniciara.

Este archivo tambien lo podemos configurar a traves de Docker Desktop en su configuración:

Como vemos tenemos la configuración que establecimos anteriormente vista desde el docker desktop
ademas de poderlo editar desde esa misma interfaz. Ahora si iniciamos un contenedor debería tener las
configuraciones que establecimos:

docker run --name apache1 -d httpd


docker inspect container_id

Tendremos la configuración establecida en el contenedor:

Crear un segundo docker Es decir tener varios nodos DOCKER dentro de la misma maquina mientras no
impacte en el tema de puertos y demas. Correr multiples demonios

70 / 123
01-primer-contenedor.md 2024-11-22

Las siguientes opciones del daemon deben ser configuradas por cada daemon:

-b, --bridge= Attach containers to a network bridge


--exec-root=/var/run/docker Root of the Docker execdriver
--data-root=/var/lib/docker Root of persisted Docker data
-p, --pidfile=/var/run/docker.pid Path to use for daemon PID file
-H, --host=[] Daemon socket(s) to connect to
--iptables=true Enable addition of iptables rules
--config-file=/etc/docker/daemon.json Daemon configuration file
--tlscacert="~/.docker/ca.pem" Trust certs signed only by this CA
--tlscert="~/.docker/cert.pem" Path to TLS certificate file
--tlskey="~/.docker/key.pem" Path to TLS key file

Este proceso se hace mediante Linux.

¿Puedo usar mi cliente docker para contectarme a un servidor docker que esta en otro sitio?

Desde un solo docker cli puedo conectarme y trabajar con otros nodos docker que esten otro sitio, incluso
con clusters de kubernetes o swarm. En docker existe el comando docker context que nos permite trabajar
con los contextos.

docker context ls #listar contextos

En este caso el que tiene * es el que esta predefinido.

docker context create nombre --docker host=npipe:////./pipe/docker_engine


docker context use nombre
docker context inspect nombre

Docker Compose
Herramienta que nos permite crear escenarios complejos con nuestros con contenedores docker, Nos ayuda a
trabajar multiples contenedores de una manera controlada.

Fichero YAML: Lanzar escenarios de contenedores segun unas instrucciones.

Docker permite el concepto de servicios, microservicios, permitia convertir una aplicación tradicional
(monolitica, db, app-server, gestor de contenidos, web-server) en un entorno de microservicios, donde se

71 / 123
01-primer-contenedor.md 2024-11-22

tienen distintos contenedores que ofrecen las distintas funcionalidades de la aplicación.

Este proceso se puede hacer mediante redes, volumenes, enlaces, etc. pero se vuelve muy complejo de
manejar, por tal motivo se creo docker compose, que orquesta servicios/componentes de
contenedores.

Nota: Docker Desktop incluye docker compose.

YAML
YAML es un lenguaje versátil y legible por humanos para serializar datos, que se utiliza a menudo para escribir
archivos de configuración. Contiene las instrucciones para generar un entorno multi-contenedor que son
llamados servicios.

El nombre del directorio de trabajo es importante, ya que cuando creo un docker-compose el usa el concepto
de proyecto, que hace referencia al entorno de lanzamiento a traves de docker-compose.

En este caso crearemos un directorio pr_nginx en el cual colocaremos un archivo yaml, llamado docker-
compose.yml este debe quedar asi aunque si usamos la opción -f podemos usar un nombre personalizado.

version: "3"
services:
nginx: # nombre personalizable del servicio
image: nginx # imagen hecha o build mediante dockerfile
ports: # puertos
- "80:80"

Y lo lanzamos con el comando

docker-compose up

la bandera -d tambien puede usarse para el modo background

Lo que me retorna los pasos realizados y un log de los sucesos dentro del orquestador:

72 / 123
01-primer-contenedor.md 2024-11-22

Revisando por medio de docker-ps:

Tengo el contenedor activo y escuchando por el puerto 80, lo que revisaremos a continuación:

Como se ve en los pasos se muestra que crea una red, esta tambien la podemos verificar mediante docker
network ls

Vemos que creo la red pr-nginx_default para el contenedor pr-nginx-nginx-1 que podemos revisar con
un inspect sobre la red:

Ademas de poder revisar esto mediante comandos docker-compose de igual forma, en algunos casos es
mejor posicionarse sobre la ubicación del archivo de configuración para que funcione correctamente:

73 / 123
01-primer-contenedor.md 2024-11-22

Ejemplo 2:

Ahora con el proyecto pr_apache crearemos el docker-compose.yml con lo siguiente:

version: "3.9"
services:
apache:
image: httpd
ports:
- "80:80"
python:
image: python

Lo ejecutaremos con docker compose up, ahora si miramos la salida

Notamos que se salio del contenedor de python, esto debido a que no lo iniciamos en modo interactivo, en
cambio apache si logro arrancar debido a que no necesita esta funcionalidad.

En lo anterior notamos este problema donde el contenedor de apache si esta corriendo mientras que el de
python se ha parado. Ademas de tambien crear una red como en el ejemplo anterior.

Parar y eliminar un Compose

Para el ejemplo se crea un nuevo directorio pr_tomcat y se almacena el siguiente docker-compose.yml:

version: "3.9"
services:
svc-apache:
image: httpd
ports:
- "80:80"
svc-tomcat:
image: tomcat
ports:
- 8080:8080
74 / 123
01-primer-contenedor.md 2024-11-22

Me retorna el log y revisando en el navegador en el puerto 80 tenemos:

Con tomcat al no tener una app desplegada me sale lo siguiente:

Revisión de los compose

Mediante otra pestaña ejecutamos docker compose stop y pararemos los compose que se encuentren en
esa ubicación:

A su vez revisamos mediante docker compose ps si existe algun compose activo. Y si ejecuto docker
compose start me vuelven a encender pero no me retorna los logs, lo lanza en modo background (Con ps
podemos verificar este proceso.)

75 / 123
01-primer-contenedor.md 2024-11-22

¿Puedo yo pararlo todo? Con docker compose down no solo para los servicios, sino que borra todo.

Nota: Al ser eliminados ya no se podran revisar con la bandera -a.

¿Qué pasa si invoco el compose en un directorio que no cuenta con el compose?

No funciona, necesito encontrarme en la ubicación del compose (archivo .yml)

Algunos comandos con docker compose

docker compose pause servicio_x #Pausar contenedores


docker compose unpause servicio_x #Despausar contenedores
docker compose rm #Elimina contenedores parados ¿Pregunta?
docker compose up -d servicio_x #Arrancar en modo background
docker compose restart #Reiniciar servicios

Construir imágenes con compose Creamos un archivo Dockerfile con el siguiente contenido:

FROM ubuntu
LABEL Sebastian '[email protected]'
RUN apt-get update
RUN apt-get install -y nginx
ADD web /var/www/html
ENTRYPOINT ["/usr/sbin/nginx/","-g","daemon off;"]
EXPOSE 80

Creamos un archivo docker-compose.yml con el siguiente contenido:

version: "3.9"
services:
web:
76 / 123
01-primer-contenedor.md 2024-11-22

build: .
image: jseb99/web:v1
container_name: web_frontal
ports:
- "80:80"

Construyeme la imagen jseb99/web:v1 a apartir del dockerfile que tengo en el directorio (.)

Una vez configurado lo anterior ya nos venimos a la ubicación del docker-compose.yml y procedemos a
ejecutarlo. Nos muestra lo siguiente:

Donde vemos que primero no encuentra la imagen, y procede a cargar la definición de la construcción del
dockerfile donde ejecuta el dockerfile y finalmente me crea la red con su contenedor. Ahora si verificamos en
el puerto expuesto (80) tenemos lo siguiente:

Tenemos la página predefinida en el directorio web/ el cual se mueve a /var/www/html que es donde usa la
imagen nginx para mostrarse en el puerto (80). Ademas de la verificación mediante el docker compose:

77 / 123
01-primer-contenedor.md 2024-11-22

¿Que pasa si lo paro? Al pararlo el ya no requerira de volver a construir la imagen, como vemos a
continuación:

La imagen ya se ha creado y al momento de bajar el docker compose y volver a crearlo este evitara pasos de
construcción de imagen y buscara primero si ya existe la imagen:

Variables
¿Cómo envío variables de entorno?

Mediante la especificación environment seguido de la definición de cada una de las variables de entorno, en
este caso tenemos el siguiente archivo docker-compose.yml:

services:
database:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=db1
- MYSQL_USER=sebas
- MYSQL_PASSWORD=contra
- MYSQL_ROOT_PASSWORD=contra

Si no especificamos version, docker toma automaticamente la ultima disponible.

En este caso restart me especifica el tipo de rebote en caso de algun error. Ahora ejecutamos el comando
docker compose up

78 / 123
01-primer-contenedor.md 2024-11-22

Nos retorna una serie de logs de los pasos que va ejecutando, ahora mediante otra pestaña podemos
comprobar el funcionamiento del contenedor:

Ademas accedemos al contenedor y verificamos si creo la base de datos:

docker compose exec database bash


mysql -u root -p #ingresamos contra despues del enter
show databases;

79 / 123
01-primer-contenedor.md 2024-11-22

Ahora verificamos el usuario:

mysql -u sebas -p #ingresamos contra despues del enter

80 / 123
01-primer-contenedor.md 2024-11-22

Enviar variables a tráves de un fichero: ENV_FILE Cuando tengo muchas variables y estas cambian de
manera dinamica, quizas es preferible tenerlas en un fichero/archivo separado y luego anexarlas al compose.
Entonces en un nuevo directorio, creamos un archivo de configuración llamado mysql.properties en este
caso el cual contendra lo siguiente:

MYSQL_DATABASE=db1
MYSQL_USER=ironman
MYSQL_PASSWORD=contra
MYSQL_ROOT_PASSWORD=contra

En el archivo compose pondremos lo siguiente:

services:
database:
image: mysql
restart: always
env_file:
- mysql.properties

Luego ejecutamos el comando docker compose up y mediante otra pestaña podemos verificar accediendo
al contenedor y luego al mysql:

81 / 123
01-primer-contenedor.md 2024-11-22

Donde accedimos directame con el usuario creado ironman a la base de datos y mostramos la base de datos
creada, db1 en este caso.

Dependencias: depends_on Ahora buscaremos la integración dentro de un docker compose:

services:
wordpress:
image: wordpress
restart: always
ports:
- 9090:80
environment:
- WORDPRESS_DB_HOST=db-wp
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
db-wp:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass

Acá tenemos dos servicios, el host de la base de datos en wordpress lo enlazamos con el servicio que
queremos usar, en este caso db-wp. Ademas de concidir el usuario, contraseña y nombre de la base de datos
para poder acceder mediante wordpress a la base de datos. Ademas el archivo compose en este caso se llamo
compose.yml que es otro formato disponible.

82 / 123
01-primer-contenedor.md 2024-11-22

Nota: Al arrancar dos servicios y uno dependa del otro primero deberíamos arrancar el servicio base
que de abasto a los demas servicios.

En este caso wordpress depende de db-wp por lo cual primero deberia arrancar db-wp. ¿Cómo podemos
solucionar este problema de manera sencilla con el compose? RTA: usamos la clausula depends_on donde
definimos los servicios de los cuales depende el servicio, de tal manera que quedaría de la siguiente manera el
compose.yml

services:
wordpress:
image: wordpress
restart: always
ports:
- 9090:80
environment:
- WORDPRESS_DB_HOST=db-wp
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
depends_on:
- db-wp
db-wp:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass

Ademas notar que el depends_on es una lista, es decir, le puedo poner multiples servicios que dependan de
otra servicio. Finalmente ejecutamos el compose, en este caso en modo background:

docker compose up -d

Vemos que el servicio db-wp se inicio primero para evitar problemas y seguido de esto se creo el servicio
wordpress. Ahora si quiero ver los logs usariamos docker compose logs nombre_servicio. Ahora si
revisamos el navegador en el puerto (9090) tendremos la página de instalación de wordpress, indicando que
todo funciona correctamente:

83 / 123
01-primer-contenedor.md 2024-11-22

Ademas de que si accedemos al servicio db-wp podemos verificar el usuario y la base de datos creada:

Volúmenes - Gestinados por Docker


Mediante el documento compose.yml usado anteriormente. En el cual agregaremos una clausula nueva
llamada volumes, acá ya se pueden agregar volumenes normales, bind mounts y tmpfs (linux) o npipe
(windows).

services:
wordpress:
image: wordpress
restart: always
ports:
84 / 123
01-primer-contenedor.md 2024-11-22

- 9090:80
environment:
- WORDPRESS_DB_HOST=db-wp
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
depends_on:
- db-wp
volumes:
- /var/www/html
db-wp:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass
volumes:
- /var/lib/mysql

Ejecutamos y revisamos lo siguiente

docker compose up -d #modo background


docker compose ps #revisar servicios
docker volume ls #revisar volumenes
docker inspect contenedor

Volumenes:

Inspeccionando un contenedor:

Donde evidenciamos la dirección y destino generados mediante el compose.yml. Ahora si eliminamos el


compose con docker compose down toca tener cuidad ya que No se eliminan los volumenes creados
mediante el compose

85 / 123
01-primer-contenedor.md 2024-11-22

Named volumes Permiten identificar de una manera mas rápida los volúmenes. Para ello definimos las rutas
mediante variables que serian sus nombres, es decir:

services:
wordpress:
image: wordpress
restart: always
ports:
- 9090:80
environment:
- WORDPRESS_DB_HOST=db-wp
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
depends_on:
- db-wp
volumes:
- wp:/var/www/html
db-wp:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass
volumes:
- db:/var/lib/mysql
volumes:
wp:
db:

De manera general se recomienda usar los volumenes a nivel de todo el archivo (Como se ve al final del
archivo), de esta manera los volumenes estarán disponibles para cualquier servicio generado mediante el
compose.yml. Ahora si los ejecutamos: obtenemos los volumenes creados:

Notamos que estos volumenes han sido creados con el nombre fijado, sin embargo, lo podemos probar con
docker volume ls:

86 / 123
01-primer-contenedor.md 2024-11-22

Ademas revisando en la ruta establecida para los volumenes por docker en el equipo principal (en este caso
usando wsl) tenemos lo siguiente:

la ruta es \\wsl.localhost\docker-desktop-data\data\docker\volumes que son los mismos volumenes


vistos anteriormente.

Bind Mounts (Volúmenes)

Volúmenes que enlazan un directorio del contenedor con uno de la máquina principal (host) y al no contar
tecnicamente como volume se quita de la clausula global. Quedando de la siguiente manera:

services:
wordpress:
image: wordpress
restart: always
ports:
- 9090:80
environment:
- WORDPRESS_DB_HOST=db-wp
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
depends_on:
- db-wp
volumes:
- D:\Cursos\Docker\13-docker-
compose\pr_volumenes\bind_volume\wp:/var/www/html
db-wp:
image: mysql
restart: always
87 / 123
01-primer-contenedor.md 2024-11-22

environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass
volumes:
- db:/var/lib/mysql
volumes:
db:

Como vemos dejamos el volúmen de la base de datos mysql db como volúmen nombrable, por tal motivo
podemos seguirlo dejando en la clausula global de volumes. Ahora lo ejecutamos con un docker compose
up -d, revisando tenemos:

Un solo volúmen, el de la base de datos que se especifico como volúmen nombrado en el archivo, pero si
revisamos la ruta en el host podemos observar lo siguiente:

88 / 123
01-primer-contenedor.md 2024-11-22

Los archivos que se usan para wordpress en docker tambien se tienen de manera local o en el host. Ahora si
revisamos en el contenedor:

docker compose exec -it wordpress bash


ls -lh

Cuidado al borrar ya que los datos enlazados seguiran existiendo sino se borran en la maquina local o
el host donde se hospeden. En este caso tambien borraremos su contenido

Comprobación:

Redes
Para ello usaremos la clausula networks donde definiremos el nombre de las redes ademas de un enlace a su
servicio (en los servicios se pueden asociar multiples redes), quedando de la siguiente manera el archivo
compose.yml:

version: "3.9"
services:
svc-apache:
89 / 123
01-primer-contenedor.md 2024-11-22

image: httpd
networks:
- frontal
ports:
- 80:80
svc-tomcat:
image: tomcat
networks:
- frontal
ports:
- 8080:8080
networks:
frontal:

Entonces ejecutamos con docker compose up -d y obtendremos:

Vemos que crea a red pr_network_frontal predefinida anteriormente. Tambien mediante un docker
network ls podemos verificar su existencia, ahora mediante un inspect verificaremos que contenedores
tiene enlazados:

Como vemos tiene enlazados los contenedores:

pr_network-svc-tomcat-1
pr_network-svc-apache-1

Que son los contenedores creados anteriormente, por lo cual confirma su enlace con la red especificada.
Ademas de poder asociarse a varias redes, en este caso realizaremos una modificación en el archivo
quedando de la siguiente manera:

version: "3.9"
services:

90 / 123
01-primer-contenedor.md 2024-11-22

svc-apache:
image: httpd
networks:
- frontal
ports:
- 80:80
svc-tomcat:
image: tomcat
networks:
- frontal
- middleware
ports:
- 8080:8080
networks:
frontal:
middleware:

Ahora borramos el compose anterior y ejecutamos el nuevo. Creación de las dos redes:

Y si inspeccionamos la red pr_network_middleware:

Verificamos que solo esta enlazada al tomcat, y la red pr_network_frontal:

91 / 123
01-primer-contenedor.md 2024-11-22

Se encuentra enlazada a los dos contenedores, comprobando lo especificado en el archivo de configuración


compose.yml.

Al eliminar el compose tambien se borra la red creada mediante este.

Lo podemos apreciar en la anterior imagen que la red proyecto-docker_wp-network se elimina al momento


de efectuarle un docker compose down, lo que hace que luego al revisar las redes existentes ya no se
encuentre la red proyecto-docker_wp-network. Esto lo menciona el log al momento de efectuar el comando.
Alias en las redes

Usaremos la clausula aliases para definir el alias de las redes, el archivo de configuración sera el siguiente:

services:
wordpress:
image: wordpress
restart: always
ports:
- 9090:80
environment:
- WORDPRESS_DB_HOST=db-host
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
depends_on:
- db-wp
networks:
- wp-network
db-wp:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass

92 / 123
01-primer-contenedor.md 2024-11-22

networks:
wp-network:
aliases:
- db-host
networks:
wp-network:

Donde le asignamos a los dos servicios la misma red, en la segunda añadimos el alias db-host por lo cual
permitira invocar ese servicio mediante db-host o db-wp. Esto tambien lo podemos notar en la variable de
entorno de wordpress enfocada en el host de la base de datos que apunta a db-host en vez de db-wp como
estaba predefinida. Ademas podemos usar los dos como podemos apreciar en la clausula depends-on que
apunta a db-wp en vez de db-host, es decir, podemos usar una u otra.

Para que funcione este enlace deben estar los servicios dentro de la misma red. Ademas nota como
definidos uno con alias y otro sin alias (Uno lleva '-' y otro no)

Una vez ejecutamos ya podremos verificar en el navegador o en la consola:

Consola:

Navegador:

Ahora mediante un docker inspect pr_network_alias-db-wp-1 que fue el contenedor que le pusimos el
alias y revisamos el apartado de redes tenemos:

93 / 123
01-primer-contenedor.md 2024-11-22

Notamos que contiene los nombres:

db-wp
db-host
cadena de numeros
nombre de la red predefinido Que son los predefinidos en el archivo de configuración.

En este caso especial atención a db-host, ya que los otros estan predefinidos por docker.

Trabajo con proyectos


Como se ha notado docker le coloca nombres a los compose de acuerdo al nombre del directorio antes del
nombre del servicio, ademas de aplicar esto a diferentes apartados de docker. Esto lo podemos modificar
para manejar de manera mas general proyectos donde no podamos usar estos nombres. Para poder efectuar
esto usamos la bandera -p que nos permite asignarlo un nombre al proyecto, entonces apoyados del
proyecto anterior y ejecutamos el siguiente comando:

docker compose -p proyectazo up -d

Obteniendo:

94 / 123
01-primer-contenedor.md 2024-11-22

Notamos que ahora los nombres estan asociados a proyectazo y no a pr*network_alias como estaban
anteriormente, ahora si revisamos los servicios con docker compose ps:

Notamos que ahora no encuentra los servicios activos, esto es debido a que _docker busca los servicios con el
nombre del directorio activos y al usar la bandera no los puede encontrar*, para solucionar este inconveniente
usamos el siguiente comando:

docker compose -p proyectazo ps

Al usar esta flag necesitamos especificar siempre esta bandera al momento de usar comandos con
docker compose

En este caso añadimos la bandera y el nombre asociado como nombre del proyecto, en este caso proyectazo
como vimos anteriormente y ahora si obtenemos evidencia de los servicios:

Ahora si nos encontramos en otra ubicación distina al archivo de configuración podemos usar lo siguiente:

docker compose -p proyectazo -f pr_network_alias\compose.yml ps

de tal manera que le indicamos la ruta del archivo del compose y mediante esta bandera -f podemos colocar
el nombre que quiera al fichero yml, solo debemos indicarle cual es el archivo facilitando muchas cosas.

Este proceso tambien lo podemos efectuar si en el archivo de configuración agregamos la clausula name que
nos permite efectuar el cambio de nombre del proyecto mediante el archivo de configuración sin tener que
enviarselo mediante comando, en este caso el archivo de configuración quedaria de la siguiente forma:

## Titulo del proyecto


name: proyecto-docker

services:
wordpress:
image: wordpress

95 / 123
01-primer-contenedor.md 2024-11-22

restart: always
ports:
- 9090:80
environment:
- WORDPRESS_DB_HOST=db-host
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
depends_on:
- db-wp
networks:
- wp-network
db-wp:
image: mysql
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass
networks:
wp-network:
aliases:
- db-host
networks:
wp-network:

En este caso dejamos el nombre proyecto-docker, ahora si ejecutamos el compose con docker compose
up -d obtendremos:

Como podemos ver evidenciamos la creación de los servicios con el nombre proyecto-docker, notamos que
no necesitamos estar en el mismo directorio lo que nos permite enviarle directamente el archivo de
configuración, lo que a su vez nos permite usar otro nombre de archivo .yaml.

96 / 123
01-primer-contenedor.md 2024-11-22

Ademas notamos que no es necesario usar la bandera -f, esta solo es necesaria cuando no nos encontramos
en la ubicación del archivo de configuración, esta sirve para añadir la ruta relativa desde donde nos
encontramos hacia el archivo de configuración para indicarle a docker donde se encuentra el archivo.
Tambien podemos usar la ruta global con la bandera -f para indicarle la ubicación del archivo, como lo
podremos ver a continuación:

docker compose -f D:\Cursos\Docker\13-docker-compose\pr_network_alias\compose.yml


up
-d

Sin embargo por temas de rapidez se recomienda la ruta relativa al ser mas corta y comoda de seguir.

Perfiles
Un perfil es un elemento que nos permite lanzar determinados servicios dependiendo si tengo o no activado
un perfil. Ejemplo: tengo un compose que si lo lanzo en modo desarrollo o modo producción hace una cosa u
otra. Dentro de un archivo de configuración puedo configurar los servicios para que pertenezcan a un perfil y
cuando arranquen esos componentes podre decidir que perfil quiero activar. Para ello usaremos el siguiente
archivo de configuración:

name: proyecto-docker
services:
wordpress:
image: wordpress
container_name: wordpress
restart: always
ports:
- 9090:80

97 / 123
01-primer-contenedor.md 2024-11-22

environment:
- WORDPRESS_DB_HOST=db-host
- WORDPRESS_DB_USER=wp-user
- WORDPRESS_DB_PASSWORD=wp-pass
- WORDPRESS_DB_NAME=wp-db
networks:
- wp-network
profiles:
- desarrollo
- produccion
## BASE DE DATOS DE DESARROLLO
db-wp:
image: mysql
container_name: desarrollo-bd
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass
networks:
wp-network:
aliases:
- db-host
profiles:
- desarrollo
## BASE DE DATOS DE PRODUCCIÓN
db-wp-prod:
image: mysql
container_name: produccion-bd
restart: always
environment:
- MYSQL_DATABASE=wp-db
- MYSQL_USER=wp-user
- MYSQL_PASSWORD=wp-pass
- MYSQL_ROOT_PASSWORD=wp-pass
networks:
wp-network:
aliases:
- db-host
profiles:
- produccion
networks:
wp-network:

Sino se activan los perfiles, ese servicio estara siempre activo para cualquier usuario. Ademas quitamos
la clausula de dependencia para poder probar produccion

Mediante el cual creamos perfiles de acceso a producción y desarrollo, ademas le permitimos acceder a
wordpress mediante los dos perfiles. Ahora para ejecutarlo y activar algun perfil usamos la bandera --
profile como se muestra a continuación:

98 / 123
01-primer-contenedor.md 2024-11-22

docker compose --profile desarrollo up -d

En este caso accederemos mediante el perfil de desarrollo

Ademas podemos notar que cuando lo apagamos necesitamos decir el perfil tambien sino no lo apaga, esto
seria con docker compose --profile desarrollo down.

Ahora probamos con produccion:

Como notamos los servicios que arranca o inicia un proceso son difernetes, en este caso la base de datos
asociada al contenedor una esta definida como produccion-bd y la otra como desarrollo-bd.

Comando COMMAND
Una opción que nos permite sustituir el comando que viene por ejemplo en el dockerfile, como el CMD o el
ENTRYPOINT. Entonces para el Dockerfile usaremos lo siguiente:

FROM ubuntu
LABEL promaster "[email protected]"
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get install -y apache2

ADD apache.sh /usr/local/bin/


RUN chmod +x /usr/local/bin/apache.sh

ADD nginx.sh /usr/local/bin/


RUN chmod +x /usr/local/bin/nginx.sh

CMD ["echo","prueba de comando"]

EXPOSE 80

99 / 123
01-primer-contenedor.md 2024-11-22

Los archivos shell continene lo siguiente:

apache.sh:

#!/bin/bash
echo "Estoy en apache" > /var/www/html/index.html
apachectl -D FOREGROUND

nginx.sh:

#!/bin/bash
echo "Estoy en nginx" > /var/www/html/index.html
nginx -g "daemon off;"

Ahora en el compose.yml tendremos lo siguiente:

version: "3.9"
services:
web-apache:
build: .
image: comando:latest
container_name: apache
ports:
- 8090:80
command: ["/usr/local/bin/apache.sh"]
profiles:
- apache
web-nginx:
build: .
image: comando:latest
container_name: nginx
ports:
- 8080:80
command: ["/usr/local/bin/nginx.sh"]
profiles:
- nginx

Como se ve por medio de un perfil ejecutamos un servicio u otro, en este caso usamos la clausula command
para la ejecución de dos archivos shell que iniciaran apache o nginx segun sea el caso, ademas que ambos
provienen de la misma imagen que instala ngnix y apache, ya depende de que servicio active de esta manera
solo se usara nginx o apache dependiendo de como se envien los parámetros. Usamos valores extras como los
echo para saber que servicio ejecutamos a modo de verificación. Creamos las imagenes, en este caso al ser la
misma imagen no es necesario ejecutar el otro servicio.

docker compose build web-apache

100 / 123
01-primer-contenedor.md 2024-11-22

Ahora con la imagen hecha, ejecutaremos el compose docker compose --profile apache up -d y si
revisamos el puerto 8090 que es en donde tengo el apache:

Obtenemos la página con la información enviada en el archivo shell, ahora si ejecutamos docker compose -
-profile nginx up -d (recordar apagar el anterior) tendremos:

Configurar Memoria y CPU


Usaremos el siguiente compose.yml:

services:
stress1:
image: apache-stress
cpu_count: 1
cpu_shares: 512
mem_limit: 100m
stress2:
image: apache-stress
cpu_count: 1
cpu_shares: 1024
mem_limit: 50m

El cual cuenta con una imagen utilizada anteriormente que esta basada en apache y ademas de eso contiene
stress que nos permitira simular cargas en el sistema. Seguido de eso ejecutamos el compose y revisando
mediante docker stats tenemos lo siguiente:

Indicando los limites que le hemos asignado a los servicios y consecuentemente a sus contenedores. Ahora
mediante el acceso a cada servicio y activando la simulación mediante stress -c 10 en ambos

101 / 123
01-primer-contenedor.md 2024-11-22

contenedores podemos observar como se reparten los servicios para cada contenedor

Debido a que un servicio cuenta con 1024 y otro 512 esto se reparte de la misma forma en el acceso a la CPU.

Configurar Restart
Ahora mediante la imagen realizada imagen-restart creamos el siguiente archivo de configuración:

services:
contenedor1:
image: imagen-restart
restart: unless-stopped
contenedor2:
image: imagen-restart
restart: always
contenedor3:
image: imagen-restart
restart: on-failure:2
contenedor4:
image: imagen-restart
restart: "no"

Y una vez ejecutamos el comando y vemos los contenedores podemos ver algo como lo siguiente:

Aqui notar que el contenedor tratara de funcionar pasados dos reinicios, luego ya no reiniciara. El cuarto al
primer fallo no reinicia. El primero y el segundo siempre trataran de reiniciar, en caso del primero a menos de
que lo pare manualmente seguira reiniciandose. Ahora paramos los contenedores que siguen reiniciandose
manualmente docker compose stop nombre_servicio_activo y seguido de eso reiniciamos docker. En
este caso a vuelto a arrancar el contenedor2 que es el único que estaría vivo despues de hacer un rebote o
reinicio de Docker.

Docker Registry

102 / 123
01-primer-contenedor.md 2024-11-22

Como podemos crear nuestros propios registros/repositorios de imagen, registros privados, evitar el uso de
dockerhub, interactuar y gestionar registros privados. Si queremos trabajar localmente, usaremos el grupo de
imagenes que queremos compartir y evitar el uso de dockerhub, usaremos registros privados para
desplegarlos en el lugar que usemos como medio colaborativo de manera interna (privada)

Crear un registro
Si buscamos en dockerhub registry nos ofrece una imagen que nos da la infraestructura para crear nuestro
repositorio

Exite un standard para los servidores de registro, es que normalmente escuchan por el puerto 5000 y el restar
debe estar en always.

docker run -d -p 5000:5000 --restart always --name registro registry:2

¿Donde guarda las imagenes que se van creando? A su vez me crea un volumen para el almacenamiento:

Subir imagen a un repositorio


En este caso trataremos de subir la imagen apache-stress. En este caso necesitamos etiquetar la imagen
con las configuraciones necesarios.

103 / 123
01-primer-contenedor.md 2024-11-22

docker tag nombre-imagen-a-etiquetar host:puerto/nombre-imagen-final

De acuerdo al formato estandar. host serían una maquina que tenga un DNS y que podamos acceder a
ella de manera remota.

Entonces usaremos de manera practica lo siguiente:

docker tag apache-stress localhost:5000/apache-stress

Ahora si consultamos las imagenes tendremos dos apache-stress:

Una vez que tenemos la etiqueta, hacemos un docker push de la imagen

docker push localhost:5000/apache-stress

Si revisamos un docker inspect registro notamos las ubicaciones de los montajes del volumen.

Si revisamos esa ruta mediante wsl tendremos:

104 / 123
01-primer-contenedor.md 2024-11-22

Donde vemos que al final tenemos un directorio para los repositorios y otro para los blobs
(./images/imagenes). Si revisamos los repositorios tendremos el que subimos apache-stress:

Y en blobs tendremos las imagenes, es decir las capas:

Ahora si queremos acceder mediante el contenedor de registro tendremos que acceder mediante una shell
ya que no podremos mediante bash:

docker exec -it registro sh

Tendremos lo siguiente:

Donde vemos que al final llegando a la ruta que nos indico en el inspect /var/lib/registry terminaremos
llegando a lo revisado en el directorio mediante wsl, donde vemos blobs y repositories.

105 / 123
01-primer-contenedor.md 2024-11-22

Descargar una imagen


Primero borraremos la imagen que teniamos descargada localhost:5000/apache-stress para evitar
redundancias y asi podremos descargar sin problemas del repositorio la misma imagen para comprobar su
funcionamiento.

docker pull localhost:5000/apache-stress

Lo normal es poner tambien la etiqueta con la cual lo subi, por lo cual tambien agregaremos el latest.

Registros inseguros y nombre del servidor

A la imagen que esta en el servidor localhost ¿Podriamos nosotros acceder mediante 127.0.0.1 o a traves
del nombre de la maquina? Si.

106 / 123
01-primer-contenedor.md 2024-11-22

Como vemos podemos acceder tambien mediante la IP, ya que este nombre, ahora que pasa si quiero
acceder a traves del nombre de la maquina:

Notamos que ahora nos retorna un error, debido a que nos da una respuesta HTTP a un cliente HTTPS (Sin
seguridad). En este caso debemos indicarle que confie en ese servidor. Esto podremos hacerlo mediante una
configuración en el archivo de configuración de docker, el archivo DAEMON.json.

{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"experimental": false,
"insecure-registries": ["juanmora:5000","juanmora:5001"]
}

En el cual mediante la propiedad insecure-registries agregamos las maquinas y su puerto que queremos
generar confianza.

Recordar reiniciar docker

Despues del reinicio, procedemos a volver a probar el comando.docker pull juanmora:5000/apache-


stress:latest obteniendo lo siguiente:

"Ya nos permite la descarga mediante nuestra máquina"

Nota: Es importante que los nombres de los registros se configuren con el nombre de una
maquina/servidor que ofrezca esas imagenes, y que de igual donde me encuentre y el usuario pueda
acceder a ese registro en remoto. Ya que localhost sirve para probar pero en un entorno colaborativo
se haria mediante una IP/servidor en remoto.

Cambiar el almacenamiento del registro

107 / 123
01-primer-contenedor.md 2024-11-22

Por defecto el usa /var/lib/docker/volumes pero que pasa si queremos usar otra ubicación. Entonces
supodremos que queremos dejar todo en /registro_docker.

docker run --name registro3 -d -p 5002:5000 --restart always -v


D:\Cursos\Docker\14-docker-registry\registro_docker:/var/lib/registry registry:2

Ahora creamos una etiqueta a apache-stress y lo subimos al repositorio

docker tag apache-stress localhost:5002/apache-stress:latest


docker push localhost:5002/apache-stress:latest

Ahora si revisamos la ruta obtendremos la misma información ya visualizada anteriormente:

Podremos ver el directorio:

apache-stress
sha256

Nota: Esto tambien se puede efectuar con un volumen normal

108 / 123
01-primer-contenedor.md 2024-11-22

Registry API
No existen comandos que nos permitan interactuar de manera sencilla con el repositorio, ya que usualmente
nos toca desplazarnos entre directorios para llegar a la información que necesitamos. Para ello tenemos el
Registry API que nos permite cierta interacción a traves de unos cuantos comandos.

Por ejemplo accedemos a un repositorio y mediante rutas en el navegador vamos buscando los datos.

Ver los repositorios: http://localhost:5000/v2/_catalog lo que me retorna:

Docker Swarm
Nos permite orquestar contenedores, poder configurar y gestionar contenedores para poder tener entornos
de producción complejos y de alto rendimiento. Permite que los contenedores se expandan a traves de
multiples maquinas en forma de cluster. Docker Swarm permite crear un cluster de uno o más nodos Dockers.
Esto permite disponer de "servicios" que se despliegan de forma replicada a lo largo de los nodos del cluster

¿Cómo esta formado un cluster de docker?

Manager: gestores del cluster, podemos tener 1 o varios. Ademas de mantener el estado del cluster.
Worker: nodo encargado del trabajo, donde se despliega el contenedor a partir de una imagen y donde
se iran a ejecutar esos servicios. Normalmente se tienen varios workers

Nodo: máquina fisica o máquina virtual

Servicios en Swarm

Cuando desplegamos una imagen en el Docker Engine se crea un servicio


Un servicio identifica tareas en el contexto de una aplicación, por ejemplo un servidor web, una base de
datos, etc...
Siempre debemos indicar la imagen a usar para crear los contenedores y qué comandos lanzar dentro

Crear un cluster de Swarm


Para la practica se usan maquinas virtuales para simular el cluster

El comando que se usa es docker swarm

109 / 123
01-primer-contenedor.md 2024-11-22

Lo primero sera inicializar un swarm, y para ello necesitamos la IP por la que se quiere trabajar.

docker swarm init --advertise-addr direccion_IP

Que a su vez vemos como podemos añadir mas nodos de tipo worker a travez del join seguido de un token.
Para verificar su funcionamiento usaremos docker info, donde una parte nos mostrará si tenemos el swarm
activo o no, id del cluster, # managers, # nodos:

Otro comando que nos permite ver los nodos que en ese momento forman parte del cluster es docker node
ls.

Añadir un nodo worker al cluster


Vimos que existe un token para poder enlazar con el cluster creado, pero ¿Cómo podemos recuperar el
token? para ello usamos join-token:

docker swarm join-token worker

Y el retorna el token, contra que dirección IP y contra que puerto queremos hacer el enlace.

110 / 123
01-primer-contenedor.md 2024-11-22

Entonces si usamos ya el comando que nos muestra se unirá al cluster.

Docker node solo nos sirve desde el nodo 1 ya que es el cuenta con los metadatos del cluster.

Añadir nodo manager

Para ello usamos:

docker swarm join-token manager

Lo cual nos retorna el comando de ejecución con su token asignado:

Ahora como es un manager si podemos usar los comandos docker node ls, debido a que los manager se
comunican entre si compartiendo los metadatos utilizados.

Aquí podemos evidenciar que existen diferentes estados en el campo manager, entre los nodos manager
activos se dividen en dos:

Leader: nodo gestionador y contiene información del cluster


Reachable: nodos suplentes del lider, es decir, si el lider falla estos entran como lideres.

Cuando usamos docker info, notamos una advertencia al final:

La que nos indica que cuando se tiene una configuración con 2 managers, esto tiene un alto riesgo de perder
el control sobre el cluster, por tal motivo se recomienda una configuración impar de los cluster. Esto docker lo
denomina implementación de tipo raft, recomendando un número impar, para hacer que los nodos se
puedan poner de acuerdo ante el fallo del lider.

Entonces si tengo 3 nodos de tipo manager, soporto la caida de uno de ellos.


Si tengo 5 nodos, soporto la caida de hasta 2 nodos.

Se toleran siempre según N manager: ((n-1)/2)

111 / 123
01-primer-contenedor.md 2024-11-22

Caídas de nodos manager

Para ello tenemos lo siguiente:

Ahora si nos dirigimos al Leader y paramos docker tendremos lo siguiente:

Podemos apreciar que ahora el Leader esta en estado Unreachable y se convirtio en Leader un nodo que
estaba como Reachabler. Sin embargo si se llega a caer otro, si entrare en un conflicto, ya que tendría
problemas para gestionar los nuevos servicios o planificaciones que tuviera que hacer. Si regresamos y
arrancamos el nodo que paramos docker.

El nodo ya no se vuelve lider pero queda disponible para su alcance en caso de que pase otro paron de algun
nodo manager.

Cuando se paran dos nodos manager de tres me generará un error, para solucionarlo se deben volver
a tener los nodos manager recomendados.

Promover o Degradar nodos


¿Puedo yo convertir nodos de tipo manager en worker o viceversa? Si, mediante los comandos promote o
demote

docker node promote id_nodo #tambien sirve el hostname


docker node demote id_nodo

Estos comandos son alias, ya que tenemos con node una serie de funcionalidades como update:

112 / 123
01-primer-contenedor.md 2024-11-22

Vemos la opción --role y vemos que puede ser un worker o un manager

Servicios y tareas
Servicios

Se usan para desplegar aplicaciones


Normalmente un servicio es en realidad un microservicio, es decir, un componente individual que
pertence a una aplicación mas grande
Por ejemplo podemos considerar un servicio a un servidor web, una base de datos o cualquier otro
componente de mi aplicación que este ofreciendo algún recurso al resto
Cuando creó un servicio debo de indicar una serie de caracteristicas asociadas al mismo:
Número de réplicas del servicio que se van a ejecutar
Configuración de CPU y de memoria
Una posible red de tipo "overlay"
El puerto por el que vamos a poder acceder al servicio

Tareas

Cuando se crea un servicio lo que se hace en realidad es lanzar una o varias tareas de tipo réplica en los
nodos.
Las tareas se ejecutan de manera completamente independiente unas de otras

Ejemplo:

Tipos de servicios Replicados y Globales

Replicados: Se distribuyen en los nodos necesarios (ejemplo: debe tener dos replicas)

113 / 123
01-primer-contenedor.md 2024-11-22

Globales: Ejecuta una tarea en cada nodo del cluster

"replicas -> tareas"

Crear un servicio básico y buscar información del servicio

docker service create --name nombre_servicio imagen

accediendo a docker service entramos a la ayuda docker del comando

docker service ls #retorna los servicios


docker service ps nombre_servicio #retorna info servicio

114 / 123
01-primer-contenedor.md 2024-11-22

docker service inspect nombre_servicio #info detallada del servicio


docker service inspect nombre_servicio --pretty #datos mas organizados visualmente

Acceder a un servicio

Creamos un servicio

docker service create --name apache-web --publish published=9000,target=80 httpd

publish es similar al tag -p para enlaces con el contenedor, esn este caso con un servicio. Puerto 9000
en el host conecta con el puerto 80 que escucha el contenedor.

¿Como accedo al recurso?

Podemos entrar con el nombre de la maquina de cualquiera de los ocho nodos va a entrar (porque publish
publica en los ocho nodos con redes overlay). En este caso tenemos 8 maquinas virtuales en un cluster en
amazon:

115 / 123
01-primer-contenedor.md 2024-11-22

Acceso desde maquina remota (usando aws)

Acceso publico para los 8 nodos mediante redes overlay.

Redes Overlay

Cuando revisamos las redes notamos que tenemos dos redes nuevas, docker-gwbridge y ingress que son
creadas automaticamente cuando creamos un docker-swarm.

Estas redes que son overlay estan por encima de otras como las bridge y permite que los contenedores que
funcionan dentro de esas maquinas sean accesibles desde todos los sitios.

Nota: Se necesitan obligatoriamente las dos redes docker-gwbridge e ingress. Sin una no funciona y
toca volver a crear todo.

Cuando usamos un inspect sobre la red ingress nos da (ademas de lo mismo de otras redes):

Contenedor ejecutandose
Peers: endpoints de las maquinas donde se esta interactuando La docker-gwbridge (red que
comunica los daemons entre si de las maquinas del cluster).

Replicas en los Servicios

Las replicas son las posibilidad que yo tengo de clonar un determinado contenedor para que pueda dar
servicio por varios servidores, permitiendo mejorar el rendimiento y tener una mayor seguridad del proyecto
o aplicación.

Primero revisamos los puertos ocupados en los servicios con un service ls:

116 / 123
01-primer-contenedor.md 2024-11-22

Creamos servicio con un número de replicas concretas:

docker service create --name web3 -p 8003:80 --replicas=3 httpd

Ademas si revisamos el servicio ahora nos dara 3 alcances, en 3 nodos:

Verificación (mediante uno de los ocho nodos aleatoriamente permitira su ingreso):

Replicas despues de la creación Mediante el comando scale podremos subir o disminuir esas replicas
dependiendo de nuestras necesidades.

docker service scale nombre_servicio=numero_replicas_requeridas

Obteniendo:

Impedir que un nodo reciba tareas


117 / 123
01-primer-contenedor.md 2024-11-22

Por defecto todos los nodos participan del trabajo de las tareas, incluso si son nodos manager. Mediante el
flag --availability podemos seleccionar entre:

active
pause: deje de recibir peticiones temporalmente
drain: no reciba peticiones

docker node update --availability drain id_nodo

Servicios Globales

A diferencia de las replicas, estos arrancan una tarea por cada uno de los servidores que tengo en el cluster
(Solo en aquellos donde tenga activa la disponibilidad)

docker service create --name web4 -p 8006:80 --mode global httpd

Si añado un nuevo nodo, este se añade automaticamente el web4 al nuevo nodo.

Cómo se comportan las réplicas ante las caidas

Tenemos los siguientes servicios

Vemos las replicas de web1

118 / 123
01-primer-contenedor.md 2024-11-22

Seleccionaremos uno para bajarlo, en este caso el 252.

Ya desde el nodo en cuestión paramos el docker con sudo systemctl stop docker. Notamos que despues
de un momento identifica que el nodo se paro y para la tarea.

Luego si revisamos los nodos tenemos el nodo como down:

Modificar un servicio

Mediante la clausula UPDATE.

Limitar memoria:docker service update --limit-memory 10m web2

Mediante el inspect podemos inspeccionar cambios pasados

Etiquetas un nodo

¿Cómo podemos etiquetar los nodos de nuestro cluster?

119 / 123
01-primer-contenedor.md 2024-11-22

Mediante la bandera label-add podemos actualizar el nombre o la etiqueta del nodo. Ademas que con
label-rm podemos remover un nombre o etiqueta existente.

docker node update --label-add rack=r1 id_nodo


docker node update --label-add rack=r1 id_nodo_2

Ahora si reviso los nodos...

Pero, ¿Donde se ven las etiquetas? para ello podemos usar docker node inspect id_nodo --pretty

Y observamos que dentro de la información del nodo encontramos la etiqueta asignada. Tambien podemos
usar otro comando, pero este nos sirve para filtrar por alguna etiqueta en particular, por ejemplo, buscaremos
los nodos que contengan rack en su etiqueta.

docker node ls -f node.label=rack

Y obtenemos los nodos con esa etiqueta

Restringir nodos en el despliegue de servicios

Vamos a utilizar una etiqueta para indicarle al docker swarm para que solo despliegue las replicas en los
nodos con dicha etiqueta

120 / 123
01-primer-contenedor.md 2024-11-22

docker service create --name web10 --constraint node.labels.rack==r1 nginx

Todos aquellos nodos con la etiqueta rack y su valor sea r1 les pondras el servicio con nginx.

Pero que pasa si le ponemos por defecto las replicas que queremos

docker service create --name web10 --replicas=6 --constraint node.labels.rack==r1


nginx

El intentara lanzar las 6 tareas, pero se restringira en los 2 nodos que en este momento cuentan con la
etiqueta rack

Entonces si revisamos el servicio tendremos lo siguiente:

Vemos que a divido de manera homogenea las replicas entre los 2 nodos. Ahora probaremos con un servicio
de tipo global, ya que estos dejaban una replica en cada uno de los nodos existentes:

docker service create --name web12 --mode global --constraint node.labels.rack!=r1


nginx

En este caso evitamos la etiqueta rack con valor r1 y en todos los demas nodos se lanza el servicio.

121 / 123
01-primer-contenedor.md 2024-11-22

¿Que pasa si ningun nodo tiene esa etiqueta?

El servicio se quedara pendiente... porque no encuentra nodos donde desplegarse:

Si revisamos el servicio, veremos que esta corriendo pero con un mensaje de error de que no encontro un
nodo disponible para desplegarse

¿Como podemos poner de manera opcional esa regla?

Mediante la bandera --placement-pref y con la opción spread igualada a la etiqueta en cuestión.

docker service create --name web16 --placement-pref


'spread=node.labels.desarrollo' --replicas=3 nginx

Las preferencias no pueden ser utilizadas en servicios de tipo global

Entonces ejecutamos y obtenemos lo siguiente:

Quitar un nodo del cluster

Consta de dos pasos:

1. Abandone el cluster

122 / 123
01-primer-contenedor.md 2024-11-22

2. Borrado del nodo del cluster

Para ello primero vamos al nodo en cuestión y usamos lo siguiente:

docker swarm leave

Ahora procedemos a eliminar el nodo del cluster para que no aparezca:

docker node rm id_nodo

123 / 123

También podría gustarte