Tal y como comentamos en la teoría de Docker tenemos dos formas de crear imágenes y contenedores:
- A través de comandos y la terminal. La desventaja de este método reside en que tendríamos que almacenar los comandos en algún sitio y ejecutarlos cada vez que tengamos que crear las imágenes y los contenedores.
- A través de los ficheros “Dockerfile” y “docker-compose.yml”.
En este apartado nos centraremos en el primer punto y veremos Docker en la práctica con comandos para crear imágenes y ejecutar contenedores.
Antes de pasar a la acción es necesario que instales Docker Desktop y, a continuación, ejecutes el comando docker version ya sea a través de la terminal de Docker Desktop o la terminal del sistema operativo que utilices:

Si la instalación ha sido correcta podrás ver la información referente a la versión de Docker instalada en tu ordenador.
También es necesario que tengas algo de conocimientos en comandos Linux. Como comentamos en la teoría un contenedor puede ser Linux o Windows y, en nuestro caso, trabajaremos con contenedores Linux y, para ello, necesitaremos utilizar algún que otro comando.
Comandos de imágenes en la práctica
Comandos para descargar y listar imágenes
Comenzaremos comprobando que no tenemos ninguna imagen descargada en nuestro ordenador, para ello utilizaremos el comando docker images:

A continuación procederemos a descargar una imagen de Docker Hub, para ello visita la página oficial y elige la imagen que tú quieras, en nuestro caso vamos a utilizar la imagen de SQL Server:
- El primer paso es localizar el nombre de la imagen, podemos verlo en la parte derecha, donde aparece “Docker Pull Command”.

Comando para descargar imagen Docker Hub - El segundo paso consiste en ejecutar el comando en la terminal, deberás esperar unos segundos. Enhorabuena, ya has ejecutado tu primer comando,
docker pull, que permite descargar una imagen.
Como puedes observar se han descargado, en nuestro caso, tres líneas (donde pone “Pull complete”), ¿por qué?, porque son las dependencias o capas de la imagen de SQL Server como veremos más adelante.
Comando docker pull para descargar imagen - Finalmente ejecuta el comando
docker imagesde nuevo para listar todas las imágenes y comprobar que ahora tienes la de SQL Server con un identificador único:
Listar imagenes en Docker
¿Qué son las etiquetas de las imágenes?
Las etiquetas en docker sirven para versionar imágenes, es decir, indicar la versión a descargar. Si te das cuenta antes no hemos indicado ninguna versión, de hecho no sabemos siquiera qué versión se ha descargado.
Pues bien, para indicar la versión al descargar una imagen utilizaremos el comando docker pull imagen:etiqueta y, para conocer la versión, iremos a Docker Hub y allí buscaremos el listado de versiones de la imagen correspondiente.
En nuestro caso vamos a instalar la versión 2019 y 2022 con los comandos docker pull mcr.microsoft.com/mssql/server:2019-latest y docker pull mcr.microsoft.com/mssql/server:2022-latest respectivamente:

Y finalmente verificamos que tenemos las 3 imágenes descargadas en nuestro ordenador:

En este proceso tenemos varios puntos importantes respecto a las etiquetas en Docker:
- La primera vez que descargamos la imagen no indicamos ninguna etiqueta y, como consecuencia, utilizó por defecto la etiqueta “latest” y descargó la última versión. Esto significa que si no indicamos etiquetas utilizará por defecto “latest”.
No es buena práctica descargar imágenes sin indicar la etiqueta, ya sea a través de comandos o a través del fichero Dockerfile.
Supongamos que en vez de descargar SQL Server estamos descargando la versión de .NET para nuestra API… Si no indicas la versión te descargará la última en ese momento y, como consecuencia, es muy posible que tu proyecto no funcione porque la versión que necesita de .NET no es la misma que está descargando. - Cuando hemos descargado la versión 2022-latest ha tardado dos segundos pero… ¿Por qué?, pues porque una de las ventajas de Docker es que reutiliza capas de imágenes ya descargadas.
Por ejemplo, imagina que descargamos la imagen de .NET 6 y 7, éstas muy seguramente compartirán la capa del sistema operativo y algunas de sus dependencias.
Como consecuencia cuando vayamos a descargar .NET 7 no será necesario descargar tantas capas puesto que ya lo ha hecho a la hora de descargar .NET 6. - Si observas te darás cuenta que el id de “latest” es exactamente igual al de “2022-latest”. Esto significa que, a día de hoy, la imagen de “latest” es la misma que la imagen “2022-latest” o, dicho de otro modo, “latest” está apuntando a “2022-latest”.
Por este motivo es peligroso no indicar un tag al descargar una imagen, porque dentro de cinco años “latest” estará apuntando a otra versión (por ejemplo la 2029) y todo ello de manera transparente y sin darnos cuenta. Esto puede dar lugar a que un contenedor que funciona en la actualidad deje de funcionar pasado un tiempo.
¿Cómo eliminar una imagen en Docker?
El comando para eliminar imágenes en Docker es docker image rm imagen[:tag].
Veamos un ejemplo de eliminar imágenes en Docker:
- Primero eliminaremos SQL Server 2019, para ello utilizaremos el comando
docker image rm mcr.microsoft.com/mssql/server:2019-latest.
Como eliminar imagenes en Docker - Después eliminaremos la versión 2022 con el comando
docker image rm mcr.microsoft.com/mssql/server:2022-latest.
Ejemplo eliminar imagen en Docker con comandos
Como puedes ver la eliminación de la versión 2022 no ha tardado nada, ¿Por qué?, por el mismo motivo de antes, la imagen 2022 es una copia de latest y como consecuencia, por debajo, no ha eliminado nada, solo el enlace.
Comandos de contenedores con Docker
¿Cómo crear y eliminar contenedores con comandos?
El comando para crear contenedores es docker create imagen[:etiqueta]. La etiqueta es opcional y, obviamente, debes tener la imagen descargada previamente en tu ordenador.

Como podéis observar lo único que nos devuelve la creación del contenedor es un id que, como te imaginarás, es el identificador del contenedor creado, apúntalo porque lo necesitaremos más adelante.
Para eliminar un contenedor es exactamente igual pero en vez de utilizar docker create utilizaremos docker rm idContenedor.
Ejecutar, parar y listar contenedores
Una vez creado podemos ejecutar el contenedor a través del comando docker start id y, posteriormente, procedemos a listar los contenedores que tenemos con el comando docker ps:

La información del listado de contenedores que podemos observar en la anterior imagen es:
- Id del contenedor, aunque mucho más corto que el que nos devolvió
docker create. - Nombre de la imagen con la que se ha creado el contenedor.
- Comando que usará el contenedor para iniciarse.
- Fecha de creación.
- Estado.
- Puertos del contenedor, en este caso solo tiene puertos privados y, por lo tanto, solo será accesible desde la red interna o desde otros contenedores, pero no desde fuera.
- Nombre del contenedor, como no le hemos dado ninguno Docker le ha asignado uno aleatorio.
A continuación vamos a parar el contenedor con el comando docker stop id.
Si ahora ejecutas docker ps para acceder al listado de contenedores verás que no hay nada, esto es porque el comando docker ps por defecto solo muestra los contenedores iniciados, si quieres ver todos tendrás que utilizar el comando docker ps -a:

¿Cómo nombrar un contenedor en Docker con comandos?
Durante los apartados anteriores hemos interactuado con los contenedores a través de su id y esto es un poco tedioso. Pues bien, podemos utilizar todos los comandos anteriores con el nombre del contenedor, lo único que debes hacer es asignar un nombre personalizado al contenedor cuando lo crees, esto se consigue añadiendo --name NombrePersonalizado al principio del comando create:

Después podrás utilizar el nombre en vez del id en los comandos que hemos visto como, por ejemplo, docker rm SQLServer.
Modo interactivo y logs en Docker
Si te has dado cuenta, cuando iniciamos el contenedor de SQL Server, éste se detiene a los cinco segundos. Docker dispone de la opción -i ó --interactive para ver los logs cuando creamos e iniciamos contenedores, nos será de gran ayuda para detectar el problema.
También podemos utilizar el comando docker logs SQLServer para ver los logs después de que éstos se hayan generado, es decir, después de que el contenedor se haya iniciado.

Como puedes observar nos está diciendo que nos faltan variables de configuración para que pueda ejecutarse correctamente, en este caso concreto es la variable que indica si aceptamos o no los términos y condiciones de SQL Server.
Variables de configuración al crear contenedor
Como hemos visto antes, a nuestro contenedor le faltaba, como mínimo, una variable de configuración. Pues bien, para establecer variables de configuración en contenedores son necesarios dos pasos:
- Lee la documentación de la imagen que deseas instanciar en un contenedor para conocer todos sus parámetros. Por ejemplo, para el caso que nos ocupa, si leemos la documentación de la imagen de SQL Server veremos que nos hacen falta los parámetros «ACCEPT_EULA=Y», «MSSQL_SA_PASSWORD=<your_strong_password>» y «MSSQL_PID=’Developer’».
- Ejecuta el comando de crear contenedor con la opción
-e parametro=’valor’tantas veces como parámetros quieras añadir.
Docker create con variables de configuracion
Como puedes comprobar en la imagen ahora el contenedor no se detiene automáticamente porque ya no da error al ejecutarlo.
Una pregunta muy interesante para ver si has comprendido la teoría es… ¿Por qué los parámetros de configuración se pasan al comando create y no al comando start?. Pues porque podemos instanciar la misma imagen en distintos contenedores para, por ejemplo, tener distintos entornos.
Con las variables de configuración podemos crear entornos distintos con distintas configuraciones como, por ejemplo, distintos usuarios y contraseñas. Esto se consigue creando varios contenedores que tengan distintas configuraciones.
Comunicación con contenedores, puertos internos y externos
Una vez hemos conseguido configurar e iniciar nuestro contenedor te estarás preguntando… ¿Cómo me conecto al contenedor Docker desde localhost?. Aunque este esté levantado, tal y como lo tenemos configurado ahora, no podemos porque no hemos mapeado ningún puerto y, dado que los contenedores por defecto no son accesibles desde fuera, no tenemos acceso.
Lo único que podemos ver desde docker ps es que tenemos un contenedor corriendo con el puerto 1433, pero ese puerto es interno, solo se utiliza para la comunicación entre contenedores, no para permitir la comunicación externa.

Todo ello se soluciona a través del mapeo de puertos donde le indicaremos a Docker que el puerto privado XX del contenedor se corresponde al puerto YY del ordenador anfitrión.
Para ello utilizaremos el comando docker create -p XX:YY donde el primer número corresponde al puerto del ordenador anfitrión y el segundo al puerto interno del contenedor. Por comodidad utilizaremos el 1433 en ambos casos ya que es el puerto por el que se inicia SQL Server:

Una vez hecho esto ya podéis conectaros desde SQL Server Management Studio (SSMS) o el programa de vuestra elección. En el caso de que fuera un contenedor con una aplicación web ya podríais acceder desde el navegador a través de la ruta correspondiente como, por ejemplo, “http://localhost:8080/index”.
Error conexión SQL Server y Docker desde localhost
Os seré sincero, pese a introducir correctamente los parámetros anteriores he tenido muchos problemas a la hora de conectarme a la base de datos SQL Server en un contenedor desde SSMS. Os comento algunos pasos y soluciones para conectar a un contenedor Docker desde localhost:
- Activa la opción “Add the *.docker.internal names to the host’s /etc/hosts file (Requires password)” en la configuración de Docker dentro del apartado general.

Permitir acceso a contenedor en localhost - Reinicia el ordenador para que los cambios en el archivo host se apliquen.
- Accede al archivo hosts de Windows (C:\Windows\System32\drivers\etc) y busca la línea “# Added by Docker Desktop”, apunta la ip.
- Prueba con distintas ip, a mi personalmente me han funcionado “localhost”, “host.docker.internal” y la ip que copiamos en el punto anterior.
De manera adicional, la soluciones a los errores para conectarnos desde localhost a SQL Server dentro de un contenedor son:
- Conectate a través de SQL Server Management Studio (SSMS) y no a través de Visual Studio, no se por qué pero no conseguí conectarme a través del IDE.
- Verifica que el usuario sea “sa” (usuario por defecto de SQL Server) y, como password, la que hayas establecido en el comando create.
- Verifica que estás utilizando la autenticación de SQL Server y que tienes marcada la opción de confiar en el certificado.

No obstante, si seguís teniendo problemas, en Internet existe mucha documentación acerca de cómo configurar Docker y Windows para acceder desde localhost a un contenedor. Estos errores no se deben a que hayamos introducido mal los comandos, se deben a la configuración de Docker Desktop y Windows.
Finalmente, como podéis comprobar, tenemos acceso al contenedor SQL Server a través de distintas ip y nombres:

¿Cómo crear volúmenes y almacenamiento persistente?
Si recuerdas, en la teoría comentamos que los datos de un contenedor no se borran cuando éste se reinicia, pero sí cuando éste es recreado, veamos un ejemplo de comandos de volúmenes en Docker:
- Lo primero que haremos será iniciar el contenedor de nuestra base de datos SQL Server.
- A continuación crearemos una base de datos de prueba.
- Paramos el contenedor con el comando
docker stop SQLServer. - Volvemos a iniciarlo con el comando
docker start SQLServer.
Como puedes observar la base de datos sí persiste lo que significa que el almacenamiento en los contenedores es persistente entre reinicios.

Ahora vamos a seguir los mismos pasos pero, en vez de reiniciarlo, vamos a eliminarlo y volver a crearlo:

En este caso la base de datos no existe, ello significa que el almacenamiento no es persistente entre distintos contenedores y, como hemos creado un contenedor nuevo, la base de datos ya no está disponible.
Los volúmenes sirven para este caso donde recreamos un contenedor o para el caso donde tenemos varios contenedores y queremos compartir la información entre ellos.
Los comandos para crear un volúmen y asignarlo a un contenedor son:
- Crear volumen,
docker volume create MyVolume, crea el volumen asignándole un nombre. - Asignar o mapear volumen,
docker create -v MyVolume:/home… etc, asigna el volumen “MyVolume” a la carpeta “home” del contenedor a la hora de crearlo. Todo lo que almacenemos en dicha carpeta estará disponible para otros contenedores e incluso en nuestro sistema anfitrión.
Como imaginarás el primer paso para utilizar un volumen en nuestra base de datos SQL Server es conocer dónde almacena ésta la información dentro de un sistema Linux, en nuestro caso se almacena en “/var/opt/mssql/data/”.
Después simplemente crearemos el volumen y añadiremos a nuestro comando create el argumento -v:

Una vez hecho esto el almacenamiento pasa a ser persistente y nuestra base de datos no desaparecerá aunque eliminemos y creemos de nuevo el contenedor.
Error de acceso denegado a volumen con SQL Server en Docker
Tras mucho investigar llegue a la raíz del problema y es que las imágenes de SQL Server pasaron de utilizar el usuario root a no utilizarlo.
En resumen nosotros utilizaremos el parámetro --user=root en la creación del contenedor pero, en ambientes de producción o más serios, te recomiendo que sigas la segunda solución donde se recomienda que analices qué usuario utiliza SQL Server internamente y que le des permiso a la carpeta del volumen a dicho usuario.
Una vez solventado el error, introduciremos los siguientes comandos:

Y vamos a seguir los siguientes pasos para verificar que ahora, con volúmenes, el almacenamiento es persistente en todos los casos:
- Creamos una base de datos.
- Investigamos dónde almacena Docker Desktop los volúmenes para nuestro sistema operativo concreto, en nuestro caso es en “\\wsl$\docker-desktop\mnt\docker-desktop-disk\data\docker\volumes”.
- Verificamos que en la carpeta anterior se han creado los archivos de la base de datos.
- Reiniciamos el contenedor y verificamos que la base de datos existe.
- Eliminamos el contenedor, volvemos a crearlo e iniciarlo y verificamos, al igual que antes, que la base de datos no ha desaparecido.
¿Para qué sirve el comando docker run?
Ahora pasaremos a utilizar uno de los comandos más útiles y populares, el comando docker run que nos permite ejecutar tres pasos en uno y ahorrar tiempo:
docker pull imagen:etiqueta, primero examina si existe la imagen y, si es así, la descarga.docker create imagen:etiqueta, después crea un contenedor con los parámetros que le pasemos. Los parámetros del comando run son exactamente iguales a los utilizados en el comando create.docker start nombreContenedor, finalmente intenta iniciar el contenedor de la misma manera que lo hace el comando start.
Para probar este comando vamos a ejecutar lo siguiente:
- Creamos contenedor con create de la misma manera que antes.
- Eliminamos contenedor con
docker rm SQLServer. - Copiamos el comando de create y reemplazamos la palabra “create” por “run”.
Como puedes comprobar la sintaxis y el resultado es exactamente el mismo que create con la única diferencia de que este comando inicia automáticamente el contenedor y, si la imagen no existiese previamente, la descargaría:

¿Cómo acceder a la terminal de un contenedor de Docker?
En un apartado anterior vimos cómo conectarnos al contenedor desde un programa como SSMS o el navegador a lo que tenga el contenedor ejecutándose como, por ejemplo, una base de datos o una aplicación .NET. Sin embargo también es posible acceder al servidor y la terminal que hay dentro del contenedor para introducir comandos, realizar pruebas o depurar.
Pues bien, simplemente tendremos que introducir la opción -it dentro del comando run y, al final, indicar la terminal a utilizar, en este caso sh:

Otra alternativa es utilizar el comando exec después de iniciar el contenedor con las mismas opciones, es decir, -it y sh al final:

Esto nos permitirá ver qué está ocurriendo dentro del contenedor, depurar errores, revisar logs… etc.
Finalmente solo cabe comentar que podríamos ver muchísimos más comandos como, por ejemplo, cómo crear redes en Docker a través de comandos pero no lo veo útil ya que en Docker no se trabaja con comandos sino con los archivos de configuración Dockerfile y docker-compose.yml.
No obstante y, tal y como has podido ver, es interesante practicar un poco con la terminal y conocer qué hay por debajo ya que esto refuerza la teoría que vimos en el capítulo anterior y nos proporciona una base de conocimiento mucho más sólida para después pasar a trabajar con Dockerfile y docker-compose.yml.











