Este artículo es el primero de una serie sobre bases de datos en contenedores. Por ello, empezaremos revisando qué es un contenedor, para luego entrar en nuestro primer caso: SQL Server en contenedores Windows.
¿Qué es un contenedor?
Cuando hablamos de contenedores, hablamos de virtualización a nivel de sistema operativo. Con este método, el núcleo (o kernel) del Sistema Operativo permite que existan varias instancias de espacio de usuario, aisladas unas de otras, de manera que cada instancia parece un servidor real para los procesos que hay en su interior. A estas instancias se les llama contenedores.
La principal diferencia con las máquinas virtuales (VMs) es que éstas ejecutan un Sistema Operativo completo, y requieren de asignación de recursos (CPU, memoria y almacenamiento) de la máquina que hospeda. Por otro lado, los contenedores no requieren un SO dedicado, y por tanto son más ligeros que las VMs.

Los contenedores no son algo nuevo, en UNIX surgió chroot, considerado por muchos un precursor del contenedor, a comienzos de los 80s. Sin embargo, fue al inicio de los 2000, cuando aparecieron varios proyectos importantes de contenedores para Linux y Windows como VServer, Sandboxie o Virtuozzo, por mencionar algunos ejemplos.
Pero no es hasta el 2013 cuando Docker sale a la luz y comienza una revolución en la industria de la ingeniería del software, adoptándose el uso masivo de contenedores.
¿Por qué contenedores?¿Por qué Docker?
Recuerdo hace varios años cómo hacer que tu aplicación funcionase en la máquina de un compañero podía complicarse de forma inimaginable. Si era problema de que faltaba una librería o la versión exacta de cierto componente, driver, etc, por citar solo algunas variables, solía llevar tiempo y dolores de cabeza hacerlo funcionar. En general, esto obligaba a contar con una documentación detallada para la instalación de la aplicación. Y no siempre se contaba con esa documentación o no estaba lo suficientemente actualizada.
Docker permite crear fácilmente aplicaciones y encapsularlas en contenedores. Gracias a que el contenedor tiene todo lo que la aplicación necesita para ejecutar, permite a los desarrolladores compartir fácilmente sus aplicaciones, hace más fácil las fases de pruebas y permite despliegues más sencillos, rápidos y fiables de la aplicaciones.
SQL Server en contenedores Windows
Cuando necesitamos desarrollar y probar scripts de base de datos, normalmente queremos probarlo en un entorno aislado para que lo que hagamos no afecte al resto de personas del equipo y viceversa. Lo ideal es que este entorno aislado sea nuestra máquina, en local. Para trabajar de esta manera necesitamos tener el servidor de SQL Server instalado y configurado. Esto no es sencillo, suele conllevar varios pasos manuales, y no se trata de una tarea de minutos, sino más bien de horas.
Vamos a ver los pasos que podemos seguir para hacerlo con contenedores:
1) Instalar Docker Desktop
Docker nos recomienda usar Docker Desktop para Windows. Sigue estos dos puntos:
- Comprueba que tu equipo cumple con los requisitos mínimos.
- Si lo anterior se cumple, procede con la instalación. Sigue los pasos detallados en esta página del docker hub.
Una vez hayas instalado Docker Desktop, deberíamos verificar que funciona correctamente. Abre una consola de PowerShell como administrador y ejecuta estos comandos:
# Mostramos las versiones del docker client y del docker server
docker version
# Descargamos una imagen pública llamada hello-world del docker hub
docker image pull hello-world
# Probamos que docker funciona correctamente usando la imagen que acabamos de descargar
docker container run hello-world
Comentar que en el anterior comando podríamos haber usado docker run hello-world sin container, pero
2) Crear el contenedor con SQL Server
Descargamos del docker hub la imagen del contenedor que usaremos. Se trata de una imagen pública creada por Microsoft y llamada mssql-server-windows-developer:
docker image pull mssql-server-windows-developer
Este paso de descargar la imagen de la red, no es estrictamente necesario, ya que con «docker container run…» también podemos descargar la imagen si aún no la tenemos. Ahora podemos seguir básicamente dos métodos:
A) Esta es una opción más inmediata, pero los valores de los parámetros están pasados por la línea de comandos, y tendrás que volver a escribir todo la próxima vez que quieras usarlo.
docker container run -e "ACCEPT_EULA=Y" -e "sa_password=XXXXXXXX" -e "MSSQL_AGENT_ENABLED=true" --name bonisql -p 31433:1433 -d microsoft/mssql-server-windows-developer
B) Usando fichero de parámetros, primero vamos a definir una serie de variables que usaremos como parámetros durante la creación del contenedor de MSSQL.
Elegimos una ruta de trabajo y desde nuestra consola, navegamos hasta ella y ahí creamos un archivo con todas las variables. Puedes ver un ejemplo en mi página de github: ps_container_creation_variables_mssql.ps1. Mi consejo es que lo personalices a tu gusto.
A continuación, el siguiente script que vamos a usar, ps_docker_run_mssql.ps1, lo tienes disponible en el mismo repositorio, y una vez lo revises, está listo para ser ejecutado:
cd tu_ruta_de_trabajo
.\ps_container_creation_variables_mssql.ps1
.\ps_docker_run_mssql.ps1
docker container list # equivalente a: docker ps --all
3) Conectarnos a nuestro SQL Server contenerizado
Para esto necesitaremos saber la dirección IP que Docker ha asignado a nuestro contenedor. Podemos ver esto de varias formas, por ejemplo con cualquiera de los siguientes dos comandos:
docker container inspect bonisql
docker container inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' bonisql
El primero te devuelve la configuración completa del contenedor, mientras que el segundo retorna exactamente lo que buscamos.
Sabiendo la IP, el puerto, y las credenciales que usamos durante el docker container run, ya tenemos todo lo necesario para conectar a nuestra instancia de MSSQL, a continuación pongo dos ejemplos:
a) Usando SSMS

Merece la pena mencionar, que también podría valer usar la IP de nuestra máquina, host u hospedador, pero cambiando al puerto que se usó durante docker container run. Ejemplo: Para -p 31433:1433 podríamos usar cualquier de las siguientes opciones:
- IP_del_contenedor,1433
- IP_del_contenedor (ya que 1433 es por defecto)
- IP_del_host,31433
b) Usando powershell
$ConnectionString = "Data source=172.19.240.100;Initial Catalog= master;User Id=sa;Password=XXXXX"
Invoke-Sqlcmd -ConnectionString $ConnectionString -Query "SELECT 1"
Y una vez conectados a nuetro SQL contenerizado, podemos crear una base de datos, crear tablas, ejecutar consultas, etc. Cómo en cualquier otro servidor!
4) Configuración avanzada
Todo lo anterior nos cubre lo básico para empezar a usar contenedores de MSSQL en Windows. Pero ¿Y si queremos más? Y si queremos…
- Asignar una IP fija a nuestro contenedor.
- Configurar el servicio SQL Agent y otros.
- Restaurar una copia de seguridad de una base de datos.
- Hacer que los cambios en la base de datos persistan.
Vayamos por partes entonces…
Asignar una IP fija a nuestro contenedor
Cuando se instala Docker Desktop y se inicia el servicio de docker en Windows, se debe crear de forma automática una red de tipo NAT en docker. Se puede identificar con el siguiente comando:
docker network ls
Para consultar la subred asociada a nat podemos usar:
docker network inspect --format='{{range .IPAM.Config}}{{.Subnet}}{{end}}' nat
Si por alguna razón la red virtual no existiese, la podemos crear:
docker network create --driver=nat dockerlab_network --subnet=172.19.240.0/20 --gateway=172.19.240.1
Sabiendo el rango IP de la subred, podemos asignar una dirección y usarla en nuestro script para ejecutar el contenedor:
$env:mssql_container_network = 'nat'
$env:mssql_container_ip = '172.19.240.100'
.\ps_container_creation_variables_mssql.ps1
docker run -e "ACCEPT_EULA=$env:mssql_container_eula" `
-e "SA_PASSWORD=$env:mssql_container_sa_passw" `
-e "MSSQL_AGENT_ENABLED=$env:mssql_container_agent" `
--name $env:mssql_container_name `
--network $env:mssql_container_network `
--ip $env:mssql_container_ip `
-p $env:mssql_container_port `
-d $env:mssql_container_image
Configurar el servicio SQL Agent y otros
Por defecto cuando creamos nuestro contenedor, el SQL Agent viene deshabilitado. Por lo tanto necesitamos añadir la opción -e «MSSQL_AGENT_ENABLED=true», como hicimos en el paso 2) anteriormente. Pero aún así, hace falta iniciar el servicio. Lo podemos hacer de la siguiente forma:
# Comprueba los servicios iniciales
docker container exec $env:mssql_container_name powershell.exe /C "Get-Service *SQL* | Select-Object status, name, displayName, StartType"
# Para y deshabilita los que no interesen
docker container exec $env:mssql_container_name powershell.exe /C "Stop-Service SQLTELEMETRY"
docker container exec $env:mssql_container_name powershell.exe /C "Set-Service SQLTELEMETRY -StartupType Disabled"
# Habilita e inicia el SQLServerAgent
docker container exec $env:mssql_container_name powershell.exe /C "Set-Service sqlserveragent -StartupType Automatic"
docker container exec $env:mssql_container_name powershell.exe /C "Start-Service sqlserveragent"
# Resumen de los servicios después de la configuración
docker container exec $env:mssql_container_name powershell.exe /C "Get-Service *SQL* | Select-Object status, name, displayName, StartType"
Restaurar una copia de seguridad de una base de datos
Para ello vamos a ver como se organiza la estructura de directorios dentro de nuestro contenedor:
docker container exec bonisql powershell.exe /C "ls"
docker container exec bonisql powershell.exe /C "ls 'C:\Program Files\Microsoft SQL Server\'"
docker container exec bonisql powershell.exe /C "ls 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\'"
docker container exec bonisql powershell.exe /C "ls 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup'"
Para copiar un archivo .bak a la ruta ..Backup del contenedor, usaremos el comando docker cp:
docker cp D:\SQLServer\Backups\WideWorldImporters-Full.bak bonisql:'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup'
Pero si nuestro contenedor está levantado, veremos el siguiente error:
docker : Error response from daemon: filesystem operations against a running Hyper-V container are not supported
Por lo tanto, sólo podemos copiar un fichero al contenedor si está detenido, hacemos lo siguiente:
docker container stop bonisql
docker cp D:\SQLServer\Backups\WideWorldImporters-Full.bak bonisql:'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup'
docker container start bonisql
Luego en nuestro cliente de SQL, conectados a la instancia del contenedor, podemos restaurar la base de datos como haríamos normalmente, por GUI o usando T-SQL:
USE [master];
-- Obtener nombres lógicos de los archivos dentro de la copia de seguridad (backup)
RESTORE FILELISTONLY
FROM DISK='C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\WideWorldImporters-Full.bak';
-- Escribir el comando, cambiando las rutas para la nueva ubicación
RESTORE DATABASE WideWorldImporters
FROM DISK='C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\WideWorldImporters-Full.bak'
WITH MOVE 'WWI_Primary' TO 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data\WideWorldImporters.mdf'
, MOVE 'WWI_UserData' TO 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data\WideWorldImporters_UserData.ndf'
, MOVE 'WWI_Log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data\WideWorldImporters.ldf'
, MOVE 'WWI_InMemory_Data_1' TO 'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Data\WideWorldImporters_InMemory_Data_1'
, STATS = 10;
Fácil, ¿verdad? 🙂 Análogamente, también podemos crear backups dentro del contenedor y copiarlos a nuestra máquina host.
Hacer que la base de datos persista
Hasta ahora todo lo que hemos hecho en el contenedor es efímero. Los contenedores inicialmente son stateless, útil para aplicaciones pero no para bases de datos. Surge así la necesidad de persistencia. La forma recomendada por Docker para conseguirla, es mediante el uso de volúmenes.
.\ps_container_creation_variables_mssql_advanced.ps1
docker run -e "ACCEPT_EULA=$env:mssql_container_eula" `
-e "SA_PASSWORD=$env:mssql_container_sa_passw" `
-e "MSSQL_AGENT_ENABLED=$env:mssql_container_agent" `
--volume=D:/SQLServer/Data/:C:/temp/ `
-e attach_dbs="[{'dbName':'persistida','dbFiles':['C:\\temp\\persistida_data.mdf','C:\\temp\\persistida_log.ldf']}]" `
--name $env:mssql_container_name `
--network $env:mssql_container_network `
--ip $env:mssql_container_ip `
-p $env:mssql_container_port `
-d $env:mssql_container_image
En este último bloque de código, vemos el uso los dos parámetros que necesitamos:
–volume : Aquí indicamos una ruta del host junto con una ruta dentro del contenedor (guest). Fíjate en el uso de «/». Se establece un mapeo de rutas.
-e attach_dbs ; En este parámetro indicamos la ruta mapeada del contenedor. Observa que aquí se usa doble barra «\\».
Comentar que antes de ejecutar el bloque anterior, debes colocar los ficheros de la base de datos (.mdf y .ldf) en la ruta del host que vamos a mapear.

La prueba final de creación del contenedor con todos los parámetros comentados (básicos, red, volumenes, etc) ha tardado sólo 59 segundos…
Ahora si quieres probarlo tú mismo, aquí tienes un enlace a todos los ejemplos usados y que completan esta guía.
Notas finales
Los contenedores de SQL Server en Windows son una excelente herramienta para desarrollar y/o probar nuestros cambios de base de datos. Cómo hemos visto, una vez que nuestro entorno está configurado, levantar un contenedor con un servicio de MSSQL es realmente rápido.
Esto es sólo la punta del iceberg, hay mucho más por aprender y probar, te invito a echar un vistazo a la documentación de Docker .
Espero haber sido de ayuda. ¡Hasta el próximo artículo!