Herramientas de usuario

Herramientas del sitio


informatica:sistemas_operativos:cursos:docker_a_fondo_introduccion_kubernetes:contenedores_windows

Contenedores Windows

Notas del curso Docker a fondo e Introducción a Kubernetes: aplicaciones basadas en contenedores

Este módulo aplica solamente a Docker Desktop en Windows. En este módulo vamos a estudiar:

  • De qué manera ejecuta Docker Desktop en Windows contenedores Linux
  • Cómo ejecutar contenedores nativos Windows en Docker Desktop.

A modo de ejemplo desplegaremos una aplicación ASP.NET tradicional que accede a un SQL Server. Tanto la aplicación ASP.NET como el SQL Server serán contenedores Windows.

No se introducirá ningún concepto nuevo de Docker en este módulo, pero es importante conocerlo si queremos desplegar aplicaciones Windows usando contenedores. Si no te interesa ejecutar contenedores nativos de Windows puedes obviar este módulo, que en cualquier caso no contará para el progreso del curso.

Historia de los contenedores Windows

El mundo de los contenedores debe sus raíces al sistema operativo Linux. La tecnología de contenedores está basada en elementos que provee el kernel de Linux (concretamente los namespaces y los cgroups). Hablando estrictamente desde el punto de vista técnico, no es necesario Docker ni ninguna otra herramienta adicional para usar contenedores en Linux. Aunque, por supuesto, si no usas ninguna herramienta, el soporte que tienes es de muy bajo nivel y nada sencillo de usar. De hecho, este fue el gran mérito de Docker: abstraernos de todo eso para permitirnos crear y usar contenedores fácilmente.

Sirva eso para indicar que los contenedores requieren soporte del sistema operativo y, en Windows, dicho soporte no estuvo disponible hasta “Windows 10 Anniversary Edition” en verano de 2016.

Versiones de Windows y nomenclatura

Desde que salió Windows 10, este sistema ha recibido numerosas actualizaciones menores, que no revisten mayor importancia para el tema que nos ocupa, y algunas actualizaciones mayores que cambian aspectos fundamentales del sistema: añaden funcionalidades y modifican el software que viene preinstalado. A pesar de que nos referimos con el nombre genérico de “Windows 10”, realmente tenemos varias versiones importantes del sistema operativo. Y para complicar la cosa más, cada versión tiene tres nombres (el nombre comercial, el número de versión y el interno) y según qué documentación consultes se usa uno u otro.

En lo que respecta a los contenedores en Windows, estas son las versiones del sistema operativo que nos interesan:

  • Aniversary Edition (1607 - Redstone 1). Esa fue la primera versión que soportó contenedores Windows. El nombre comercial fue “Anniversary Edition”, el número de versión es 1607 y el nombre interno “Restone 1 (RS1)”. Esta versión salió en agosto de 2016.
  • Creators Update (1703 - Redstone 2). Esta versión salió en abril de 2017.
  • Fall Creators Update (1709 - Redstone 3). Se lanzó en octubre de 2017.
  • April 2018 Update (1803 - Redstone 4). Esta versión salió en abril de 2018.
  • October 2018 Update (1809 - Redstone 5). Esta versión salió en octubre del 2018.

Así, por ejemplo, si lees que Windows 1803 soporta X característica, es lo mismo que decir que “Windows 10 - April 2018 Update” la soporta o que dicha característica se soporta en Redstone 4 (o RS4).

A partir de aquí, por si tanto lío de nombres no bastase, se cambió la nomenclatura y se empezó a adoptar una nomenclatura de “año-Hx” con dos dígitos para el año y donde x vale 1 o 2 en función de si la actualización es la primera o la segunda del año. Eso fue porque Microsoft empezó a sacar versiones con una cadencia de dos por año. Así, a modo de ejemplo tenemos:

  • 19H1 (mayo 2019)
  • 19H2 (noviembre 2019)
  • 20H1 (mayo 2020)
  • 20H2 (octubre 2020)
  • … (¿Vas pillando la idea no?)

A medida que han ido apareciendo las sucesivas revisiones de Windows 10 y posteriormente de Windows 11, se han ido agregando características a los contenedores y se ha ido mejorando su experiencia.

Tipos de contenedores en Windows

Windows soporta dos tipos principales de contenedores: contenedores de Windows Server y contenedores Hyper-V. Contenedores Windows Server

Son el “equivalente Windows” a los contenedores Linux. En este caso, todos los contenedores que se ejecutan en el host comparten el mismo kernel del sistema operativo. Cuando usamos este tipo de contenedores debemos tener presente que cada versión mayor de Windows (RS1, RS2, …) es como si fuese un sistema operativo distinto que puede ejecutar contenedores tan solo de la misma versión.

Dado que el kernel es el mismo para todos los contenedores y es el kernel del SO subyacente, todos los contenedores deben ser para la misma versión del kernel del host. Eso significa que, si el host es, por ejemplo, un Windows 1709, todos los contenedores deben estar creados a partir de imágenes base de Windows 1709.

Este tipo de contenedores solo está disponible en Windows Server y no en versiones de usuario de Windows.

Contenedores Hyper-V

La otra opción que permite Windows es ejecutar un contenedor en una “mini máquina virtual” altamente optimizada usando Hyper-V. Este modo es el único soportado por Windows 10 o posterior. El uso de esta “mini-MV” es completamente transparente para nosotros (es decir, no vamos a verla desde el administrador de Hyper-V). Las ventajas de los contenedores Hyper-V frente a los contenedores Windows Server es que ofrecen un mayor aislamiento. Por contra, tienen una pequeña pérdida de rendimiento.

En este caso el kernel no es compartido, lo que implica que cada contenedor puede tener una versión propia de este, por lo que puedes usar versiones distintas sin problema.

Cuando uses Docker Desktop en Windows 10 o posterior y habilites los contenedores Windows, este tipo de contenedores serán los que crearás y usarás.

Matriz de compatibilidad de Windows y contenedores

Recapitulemos: tenemos dos “sabores” principales de Windows. Por un lado, Windows 10 o posterior, que admite solo contenedores Hyper-V y, por otro lado, Windows Server que admite contenedores Hyper-V y contenedores Windows Server.

Los contenedores Windows Server deben ser de la misma versión de kernel que el host sobre el que se ejecuten, mientras que los contenedores Hyper-V pueden ser de versiones distintas. A pesar de eso, no suele ser posible ejecutar un contenedor de una versión posterior de Windows, incluso usando un contenedor Hyper-V (p. ej. no puedes usar un contenedor basado en una imagen base de Windows 1803 en un Windows 1709).

Observa cómo se cumplen las dos reglas anteriores:

  • Windows 10+ y Windows Server solo admiten Hyper-V para contenedores con versiones anteriores o iguales a la del host.
  • Windows Server solo admite contenedores Windows Server para contenedores con la misma versión del host.

Imágenes base de Windows

Cuando crees contenedores Windows usarás (directa o indirectamente) alguna de las imágenes base de Windows (provistas por Microsoft). La imagen que uses definirá la versión del contenedor (1703, 1709, …) y, por lo tanto, en qué versiones de Windows se podrá ejecutar este contenedor.

Pero, al margen de la versión, las imágenes base de Windows se dividen en dos grandes grupos: las basadas en Server Core y las basadas en Nano Server.

Windows Server Core

Una de las opciones de instalación que Windows Server ofrece (desde la versión 2008 R2) es la llamada “Windows Server Core”. Se trata de una instalación “minimalista” del SO en la cual componentes no necesarios no se instalan. Así, un Windows Server Core, no tiene interfaz gráfica. Binarios, como el explorador de archivos, no se instalan tampoco, y eso redunda en un menor tamaño del SO.

Windows Server Core está pensado para ser instalado en servidores desasistidos, en los que una interfaz gráfica no es necesaria, ya que se administran (remota o localmente) con herramientas de línea de comandos, tales como Powershell o SSH. Con la aparición de los contenedores Windows, Microsoft ofrece una imagen base de Windows Server Core llamada mcr.microsoft.com/windows/servercore/

Desde hace un tiempo Microsoft ha empezado a migrar sus imágenes de Docker Hub a su propio registro (abierto). Dicho registro es mcr.microsoft.com. Aún se encuentran imágenes antiguas en Docker Hub, pero todas las nuevas imágenes están ya únicamente en dicho registro y es el que deberías utilizar.

A pesar de ser una instalación minimalista del sistema, la verdad es que las imágenes de Windows Server Core ocupan mucho espacio. Aunque con cada iteración MS ha ido reduciéndolo, el tamaño de las imágenes sigue siendo bastante grande:

Como se puede observar en la imagen anterior, el tamaño de distintas imágenes de Windows Server Core se ha ido reduciendo en cada versión. A pesar de eso, en la versión 1809 todavía supera los 4GB y la versión 20H1 es un poco mayor superando los 5GB. En fin, son tamaños “grandes” para lo que se suele usar en contenedores.

Windows Nano Server

Microsoft le dio otra vuelta de tuerca al concepto de Windows Server Core y el resultado es Windows Nano Server. Al igual que Server Core, se trata de un SO sin interfaz gráfica, pero Nano Server ocupa mucho menos espacio a costa de eliminar más características todavía. Por ejemplo, Nano Server no soporta inicios de sesión locales, y es ideal para escenarios de un solo proceso, es decir, precisamente para contenedores. Usar imágenes base de Nano Server debe ser tu primera opción en contenedores Windows. Solo debes utilizar imágenes basadas en Server Core si tu contenedor requiere utilizar algún componente no disponible en Nano Server.

La imagen base de Nano Server se llama mcr.microsoft.com/windows/nanoserver

Observa como los tamaños de Nano Server son mucho menores: las últimas versiones ocupan menos de 300MB. Comparados con los 4-5GB de la misma versión de Server Core, se puede ver que la reducción es muy importante.

Imágenes base de tecnologías tradicionales de MS

Generalmente no usarás directamente las imágenes base de Windows, sino que usarás alguna imagen que ya contiene alguna tecnología preinstalada. Por ejemplo, si tienes un servidor desarrollado con Windows Communication Foundation usarás la imagen mcr.microsoft.com/dotnet/framework/wcf. Si eres desarrollador en tecnologías Microsoft sabrás que WCF es una API para desarrollar servicios web que forma parte de la plataforma .NET. En función de la versión de .NET que quieras usar y de la versión del SO base, deberás usar un tag u otro de esta imagen. Así, si quieres usar .NET 4.7.2 y el SO es Server Core 1803, entonces el tag es 4.7.2-windowsservercore-1803. Es un poco complejo porque no todas las versiones de .NET están disponibles de serie en todos los SO (por ejemplo, .NET 4.6.2 está solo disponible en Server Core ltsc2016).

Otra imagen tradicional es la de ASP.NET que se llama mcr.microsoft.com/dotnet/framework/aspnet. Al igual que la de WCF hay varios tags para combinar versiones de .NET Framework con versiones del SO base.

Las imágenes que dependen de la plataforma .NET (como WCF o ASP.NET) no se soportan en Nano Server, así que solo existen para Server Core.

Por supuesto, .NET Core es otra tecnología importante en el mundo Microsoft. Su imagen es microsoft/dotnet. Al igual que el resto tiene varios tags para indicar tanto la versión de .NET Core como la del SO base, que en este caso sí que es Nano Server.

A modo de comparación la siguiente imagen muestra los tamaños de la imagen microsoft/dotnet en Windows Nano Server 1809 y en Linux Alpine:

Puedes ver como el tamaño de la imagen que contiene el SDK es casi el mismo, tanto en Alpine como en Nano Server. Por su parte, la imagen que contiene el runtime sigue siendo sustancialmente menor en Linux Alpine.

¡Imágenes de Linux y Windows juntas! ¿Qué clase de brujería es esa? Es una brujería ancestral, ya perdida. Esta imagen está tomada con una versión antigua de Docker Desktop que soportaba LCOW, que permitía ejecutar a la vez contenedores Windows y Linux. Con el soporte de WSL2, LCOW ha sido declarado obsoleto y ya no se soporta. Así que, en las versiones actuales de Docker Desktop no puedes ver a la vez las imágenes de ambos sistemas desde un terminal Windows: debes usar un terminal WSL2 para ver las imágenes Linux y uno Windows para manejar las imágenes Windows.

Habilitar contenedores Windows

Para poder habilitar contenedores Windows es necesario que dispongas de, al menos, Windows Server 2016 o Windows 10 Anniversary Edition, aunque es recomendable que uses la versión más actualizada posible.

Habilitar contenedores en Windows 10 o posterior

Vamos a ver cómo habilitarlos en Windows 10+. El primer paso es ir a la opción “Programs” del panel de control y allí seleccionar “Turn Windows features on or off” (Activar o desactivar características de Windows):

Una vez te aparezcan las opciones debes marcar la opción de “Containers”:

Ahora ya tienes activo el soporte para contenedores Windows. Solo falta configurar Docker for Windows para que los use.

Configurar Docker

Basta con abrir el menú contextual de Docker (haciendo clic con el botón derecho en el icono de la ballena en la zona de notificaciones) y seleccionar la opción “Switch to Windows containers”.

Docker se reiniciará. Una vez reiniciado ya podrás empezar a usar contenedores Windows.

Cuando tengas los contenedores Windows habilitados, no verás ninguno de tus contenedores ni imágenes Linux que tengas.

Si en lugar de la opción “Switch to Windows containers” aparece “Switch to Linux containers” es que ya estás usando contenedores Windows.

Usar contenedores Linux y Windows de forma simultánea

No es posible usar contenedores Linux y Windows de forma simultánea. En versiones anteriores de Docker Desktop existía el modo LCOW que permitía eso, pero dicho modo se discontinuó y ya no funciona.

Si usas Docker Desktop integrado con WSL2 (la configuración recomendada) puedes seguir gestionando tus contenedores Linux usando el terminal de WSL2, mientras usas el terminal de Windows para gestionar los contenedores Windows.

Redes en contenedores Windows

En el módulo de redes vimos los tipos principales de redes en Docker, pero estábamos muy centrados en Docker para Linux. Todo lo visto en el módulo de redes es válido para Docker Desktop for Windows cuando está ejecutando contenedores Linux, pero cuando cambiamos al modo de contenedores Windows, aparecen otros tipos de redes. Esas redes existen solo en Windows.

Es posible que la salida del comando sea distinta en tu caso. En concreto, las redes de tipo ics puede que sean distintas, ya que se corresponden a adaptadores virtuales de Hyper-V. Seguramente tendrás la de Default Switch y, si tienes WSL2 instalado, la de WSL. El resto van a depender de tu configuración de Hyper-V.

Las redes de tipo ics son redes vinculadas a los adaptadores de Hyper-V que tengas: un contenedor ejecutándose en esas redes será accesible desde otras máquinas que estén conectadas a dicho adaptador. Red de tipo nat

Los contenedores que se ejecuten en una red de tipo nat se conectarán a un conmutador (switch) interno de Hyper-V. La red llamada nat es la red “por defecto” de los contenedores Windows (de manera análoga a la red llamada bridge en contenedores Linux), aunque podemos crear redes nat adicionales.

Cualquier red de tipo nat (incluso la llamada nat) soporta DNS, por lo que los contenedores se pueden llamar por su nombre (recuerda que en la red bridge, por defecto, eso no ocurría):

Al igual que las redes bridge, en una red de tipo nat los puertos de los contenedores deben exponerse para ser accesibles.

En tu máquina Windows verás un adaptador virtual Hyper-V que da soporte a cada red de nat:

La imagen anterior es interesante porque nos muestra como después de crear una red de tipo nat nos aparece el adaptador de Hyper-V asociado (la última entrada que muestra ipconfig). Luego podemos ver que al ejecutar un contenedor y vincularlo a esa nueva red (mediante --network), el contenedor obtiene una IP interna vinculada al rango de IPs de este adaptador.

Una vez borramos la red (docker network rm <nombre-red>) el adaptador de Hyper-V desaparece también.

Si conozco la IP del contenedor puedo acceder directamente a él:

La red de tipo transparent

Esa red, como su nombre indica, es “transparente” en el sentido de que un contenedor que se ejecuta en ella está conectado directamente a la red del host a través de una adaptador Hyper-V externo.

Eso significa que el contenedor obtiene una IP en la misma red del host, ya sea de forma estática o bien a través de DHCP (usando el mismo servidor DHCP que haya usado el host).

En la imagen anterior se puede ver como el contenedor container-trans termina teniendo una IP en el mismo rango (192.168.1.xx) que el host, y se puede ver que comparten el gateway de acceso a Internet (que se trata, en este caso, de mi router).

Resumen

Al igual que en el caso de Linux, hay más tipos de redes en Windows, pero son para escenarios avanzados que quedan fuera del alcance de este curso. Para más información te sugiero que leas la documentación oficial al respecto. El objetivo de esa lección era, simplemente, que conocieras los tipos más habituales que te vas a encontrar en tu día a día.

Crear un contenedor Windows con .Net 5

Vamos a ver un ejemplo de cómo crear un contenedor Windows, usando una tecnología más moderna. En este caso seguiremos con .NET en sus versiones recientes. Vamos a ver cómo, en este caso, no hay apenas diferencias a como lo haríamos en Linux, y lo veremos también en la práctica.

Lo primero es que te descargues el fichero hellonet-code.zip y lo descomprimas en una carpeta vacía. Te aparecerá un directorio con una sencilla aplicación de ejemplo de ASP.NET.

El proyecto contiene un Dockerfile multistage (como los que vimos en el módulo sobre cómo compilar con Docker), por lo que no necesitas nada para generar la imagen, salvo Docker en modo contenedores Windows.

Construyendo la imagen

Para construir la imagen basta con lanzar un docker build, lo cual te debería construir la imagen. Ahora bien, te puedes encontrar con el siguiente problema:

Si te ocurre eso es debido a la configuración de tus adaptadores de red.

Lo que sucede es que debemos indicarle a Docker qué adaptador de red usar al crear el contenedor que ejecuta el Dockerfile, ya que elige el primero que esté disponible y si ese no tiene salida a Internet, entonces obtendrás ese error.

Observa que ese error se debe a que durante la compilación se intenta acceder a Internet para descargarse los paquetes. En este caso se trata de un error de Nuget (el gestor de paquetes de .NET), pero lo mismo te ocurriría si usaras cualquier gestor de paquetes (npm, maven, go init, pip, etc….).

Para arreglar ese problema lo primero que necesitas es abrir una consola de PowerShell y, luego, ver en qué orden tienes tus adaptadores de red usando el comando:

Get-NetIPInterface -AddressFamily IPv4 | Sort-Object -Property InterfaceMetric

La siguiente captura muestra la salida de este comando en mi máquina. En la tuya puede ser distinta, claro, ya que dependerá de tus adaptadores de red:

Por defecto, Docker intenta conectarse a la primera interfaz de red (la que tenga el valor de InterfaceMetric más bajo). En mi caso eso corresponde a Ethernet 3 que parece que está desconectada.

Para arreglar esa situación voy a forzar que Ethernet 2 sea la interfaz de red con menor InterfaceMetric usando el comando Set-NetIPInterface. Este comando requiere derechos administrativos:

Set-NetIPInterface -InterfaceAlias 'Ethernet 3' -InterfaceMetric 5
Set-NetIPInterface -InterfaceAlias 'Ethernet' -InterfaceMetric 4

El primer comando es para que la interfaz Ethernet 3 (que, honestamente, no sé por qué no tiene valor en InterfazMetric) tenga el valor de 5, y el segundo es para que Ethernet (que como puedes ver está habilitada) tenga el valor de 4 y sea la primera:

Después de cambiar esa configuración, el comando docker build debería funcionar sin problemas.

Ejecutando el contenedor en distintas redes

Red NAT

Empezaremos por la red NAT por defecto. Eso implica tan solo ejecutar un docker run, enlazando los puertos sin ninguna otra configuración. Dado que en mi caso he llamado hellonet5:win a la imagen lanzo el comando:

docker run -p 9500:80 testwin

Una vez el contenedor está en marcha, puedo acceder a localhost:9500 y veré la salida del contenedor:

A modo de detalle, observa como desde WSL2 no puedo acceder al contenedor. Eso es debido a la manera en que funciona la conectividad entre WSL2 y Windows. Es de esperar que en futuras versiones se arregle el problema.

Si la petición la realizas usando la IP interna del contenedor, también llegas a él. Observa como en este caso no debes usar el puerto 9500 ya que el contenedor escucha por su puerto 80:

Red ics

Vamos a usar ahora una red ics, en concreto la de “WSL. Para ello lanzo el comando:

docker run --network "WSL" testwin

Ahora debemos averiguar cuál es la IP del contenedor. Para ello puedes usar el comando docker inspect y una vez la tengas, puedes ver que tienes conectividad con el contenedor usando esa IP y el puerto del contenedor (80). A diferencia del caso anterior, tienes conectividad tanto desde Windows como desde WSL:

Otra red que es interesante es la del “Default Switch”. Para ello usa el comando:

docker run --network "Default Switch" hellonet5:win

Si ahora usas docker inspect para ver la IP del contenedor, no vas a recibirla. Parece que Docker no obtiene la IP de los contenedores asignados a esa red. Pero no pasa nada: puedes ejecutar el comando ipconfig en el contenedor para verla:

En mi caso, la IP del contenedor es la 172.21.208.131. Accediendo desde un terminal de Windows a esa IP puedo acceder al contenedor, pero no puedo desde WSL2:

Pero… ¡espera que hay más! La red a la que está conectado el contenedor es la red de “Default Switch” y eso significa que el contenedor debería ser accesible a otras máquinas en esa red, ¿verdad? En mi caso tengo una MV en Hyper-V con un Ubuntu y dicha MV usa el “Default Switch”, y como puedes observar puedo acceder al contenedor:

En la captura ha sido marcada la IP de la MV, para que veas que se encuentra dentro del mismo rango (el rango asignado por el Default Switch) y como hay conectividad con el contenedor.

Recursos

informatica/sistemas_operativos/cursos/docker_a_fondo_introduccion_kubernetes/contenedores_windows.txt · Última modificación: por tempwin