Artículo Original: https://blogs.msdn.microsoft.com/appconsult/2018/09/20/running-containers-based-on-different-platforms-side-by-side-with-docker-preview/





Cuando hayamos configurado Docker como referido en este post, recordará que en algún momento tuvimos que elegir entre los contenedores de Windows y los de Linux. Los primeros pueden ejecutarse de forma nativa en la máquina host, mientras que los segundos se ejecutan dentro de una máquina virtual Linux basada en Hyper-V.

Sin embargo, no puedes mezclar ambas arquitecturas al mismo tiempo. Si su máquina está configurada para usar contenedores de Linux, no puede implementar y ejecutar un contenedor que ejecute IIS en Windows Server.

Sin embargo, las cosas están a punto de cambiar! Gracias a la última vista previa de Docker para Windows, ahora puede ejecutar contenedores uno al lado del otro, incluso si están basados en plataformas diferentes.

¡Veamos cómo hacerlo!

Cambiando a Docker Edge


Docker Edge es el nombre del canal que entrega versiones preliminares de Docker. Puede pasar del canal estable al canal de borde usando el enlace que encontrará en la sección General de la configuración de Docker para Windows:



Si hace clic en el enlace de otra versión, verá una ventana emergente donde podrá cambiar del canal estable al canal de borde. Notará que la versión de borde se identifica con un logotipo diferente:



Una vez que se complete la instalación, podrás comenzar a jugar con la última versión beta de Docker. Al momento de escribir, su número de versión es 2.0.0.0-beta1, que incluye la versión 18.09.0-ce-beta1 de Docker Engine.




Cambio a contenedores de Windows



 Como primer paso, necesitamos cambiar a los contenedores de Windows. De hecho, podemos ejecutar los contenedores de Linux y Windows lado a lado solo si los contenedores de Windows son la opción predeterminada. Para lograr este objetivo, debe hacer clic derecho en el icono de Docker en la bandeja del sistema y elegir Cambiar a los contenedores de Windows.



Si no tiene la función de Windows llamada Contenedores activada, Docker para Windows la activará. Si ese es el caso, también necesitará reiniciar la máquina al final del proceso.


Si abre ahora un indicador de PowerShell y ejecuta imágenes de la ventana acoplable, notará que la lista está vacía, incluso si ha usado Docker anteriormente. La razón es que, cuando se cambia a un contenedor diferente, se almacenan de una manera diferente y en una ubicación diferente. Los contenedores de Windows, de hecho, se almacenan directamente en el host y no dentro de una máquina virtual. Como tal, no puede compartir las mismas imágenes entre las dos configuraciones.


Ejecutando nuestros primeros contenedores de manera simultánea



Comencemos con algo simple: ejecutar un Linux básico y un contenedor básico de Windows lado a lado. Abra PowerShell y escriba el siguiente comando:

docker run --rm -it microsoft/nanoserver:1803


El comando extraerá la imagen del contenedor para Windows Server, que es la versión basada en la línea de comandos sin ninguna interfaz de usuario. Al usar etiquetas, podemos especificar qué versión de Windows queremos usar. En este caso, estamos retirando el más reciente (actualización de abril de 2018). Como hemos especificado el parámetro -it, nos conectaremos directamente con el terminal al contenedor. Ya que estamos en una máquina con Windows, veremos el símbolo de comando familiar:



Lo que hicimos, en realidad, no es nada especial. Ya que estamos ejecutando Docker utilizando contenedores de Windows, no debería ser una sorpresa que podamos extraer una imagen basada en Windows y ejecutarla como un contenedor. Habríamos podido alcanzar el mismo objetivo con la versión estable de Docker.


Ahora hagamos algo más interesante. Abra otra ventana de PowerShell, para que podamos mantener el contenedor de Windows en funcionamiento. Esta vez, escriba el siguiente comando:

docker run --rm -it ubuntu

Esta vez estamos ejecutando un contenedor de Ubuntu y nos estamos conectando usando un terminal.



Como puede ver, ahora estamos conectados al contenedor de Ubuntu y podemos ejecutar comandos estándar de UNIX. ¿Qué sucede si enumeramos todos los contenedores en ejecución ahora utilizando una tercera ventana de PowerShell?

PS C:\Users\mpagani> docker ps
CONTAINER ID        IMAGE                  COMMAND                    CREATED             STATUS              PORTS               NAMES
344687a13103        microsoft/nanoserver   "c:\\windows\\system32…"   22 seconds ago      Up 9 seconds                            naughty_perlman
0ffb5ec1d7de        ubuntu                 "/bin/bash"                2 minutes ago       Up 2 minutes                            youthful_jepsen

Ambos contenedores se ejecutan al mismo tiempo, a pesar de que el primero se basa en Windows y el segundo en Linux.


Probando otro escenario con un servidor web.



Microsoft ofrece una imagen para IIS para Docker llamada microsoft / iis, que puede usar como servidor web para alojar sus aplicaciones web. Vamos a intentarlo ejecutando el siguiente comando:

docker run --rm -p 8080:80 -d microsoft/iis

La imagen se extraerá de Docker Hub, luego Docker girará un nuevo contenedor para ella. Al usar el parámetro -p, estamos exponiendo el puerto 80 interno al puerto 8080 del host. Si abrimos con el navegador la URL http: // localhost: 8080 veremos la página de IIS predeterminada:



Ahora repitamos el experimento que hicimos en el primer post de la serie. Ejecutemos una nueva instancia de nginx, un popular servidor web basado en Linux. Puedes usar el siguiente comando:

docker run --rm -p 8181:80 -d nginx

El comando es el mismo que el anterior. Acabamos de cambiar la imagen a usar (nginx) y el puerto donde exponer el servidor web a 8181, ya que IIS ya usa 8080. Ahora dirija su navegador a http: // localhost: 8181 para ver la página predeterminada de NGINX.



Y si ejecuta de nuevo ahora docker ps, puede ver que los dos contenedores se ejecutan uno al lado del otro:

PS C:\Users\mpagani> docker ps
CONTAINER ID        IMAGE               COMMAND                   CREATED             STATUS              PORTS                  NAMES
657d728b3f53        nginx               "nginx -g 'daemon of…"    2 minutes ago       Up 2 minutes        0.0.0.0:8181->80/tcp   recursing_pare
0f72c8b4438d        microsoft/iis       "C:\\ServiceMonitor.e…"   6 minutes ago       Up 4 minutes        0.0.0.0:8080->80/tcp   elated_bardeen

¿Cómo podemos estar seguros de que los dos contenedores se ejecutan en dos plataformas diferentes? Podemos obtener ayuda de Visual Studio Code para verificar esto. Si recuerdas cuando hablamos de la integración de Docker en Visual Studio Code, tenemos una opción para conectarnos al shell de un contenedor en ejecución. Abra Visual Studio Code y vaya a la pestaña Docker. Debajo de la sección Contenedores, debería ver la misma salida del comando anterior docker ps:



Ahora haga clic derecho en el contenedor de microsoft / iis y elija Adjuntar Shell. Bajo el capó, Visual Studio Code ejecutará el siguiente comando para usted:

docker exec -it 0f72c8b4438da48cf5af9335aa86c3d719961cdf9c60a4abf33e1629e9f16085 powershell

docker exec se utiliza para ejecutar un comando en un contenedor específico. En este ejemplo específico:
  • Estamos usando el parámetro -it para que podamos crear una conexión interactiva
  • La larga serie de letras y números es el identificador completo de nuestro contenedor IIS
  • Visual Studio Code ha detectado automáticamente que hemos configurado Docker para ejecutar contenedores de Windows, por lo que invoca el comando powershell

Como se esperaba, ahora tenemos un terminal de PowerShell abierto en la instancia de Windows que ejecuta IIS:



Ahora tratemos de hacer lo mismo con el contenedor NGINX. Haga clic derecho sobre él y elija Adjuntar Shell:



Bueno, esto no se ve bien. La razón es que Visual Studio Code ha intentado ejecutar el mismo comando exacto que hemos visto con el contenedor IIS, lo que significa que hemos intentado abrir un terminal PowerShell en una máquina que ejecuta Linux. Ahora está bastante claro por qué no funcionó
Podemos solucionar esto repitiendo manualmente el comando (solo presione la flecha hacia arriba en su indicador de PowerShell para volver a mostrarlo) y cambie el PowerShell con / bin / sh.

docker exec -it 0f72c8b4438da48cf5af9335aa86c3d719961cdf9c60a4abf33e1629e9f16085 /bin/sh



Esta vez estamos usando el shell de UNIX y, como tal, podemos conectarnos correctamente a la máquina de Linux que ejecuta nginx. ¡Esta es una clara demostración de que los dos contenedores se basan en plataformas diferentes!


Construyendo una imagen para una plataforma específica.



Retomemos el trabajo que hemos realizado en las publicaciones anteriores. Si recuerdas, habíamos construido una solución hecha por tres aplicaciones:

  • Una aplicación web basada en ASP.NET MVC Core 2.1
  • Una API web basada en .NET Core 2.1
  • Un redis caché

Las dos primeras aplicaciones se crearon utilizando una imagen personalizada, construida sobre la oficial proporcionada por Microsoft, que contiene el .NET Core SDK y el tiempo de ejecución. Cuando extrae una imagen de un repositorio, de forma predeterminada, Docker obtendrá la más adecuada para su configuración de Docker.
Podemos ver esto reconstruyendo las dos imágenes personalizadas. Vamos a utilizar el mismo archivo Docker exacto que hemos visto en las otras publicaciones, sin cambiar nada. Solo ejecutaremos el comando de compilación estándar desde la raíz del proyecto. Primero muévase a la raíz del proyecto de la aplicación web y ejecute el siguiente comando:

docker build -t qmatteoq/testwebapp .

Luego muévase a la raíz del proyecto de API web y ejecute el siguiente comando:

docker build -t qmatteoq/testwebapi .

Ahora aprovechemos el archivo Docker Compose que hemos incorporado en la publicación anterior para iniciar toda la solución. Abra un símbolo del sistema en la misma carpeta donde tiene el archivo docker-compose.yml y ejecute:

docker-compose up

Debería obtener, al final, el mismo resultado del post anterior. Al presionar http: // localhost: 8080, debería ver el sitio web de prueba, que está recuperando la lista de artículos de la API web. Si actualiza la página, esta vez la lista se recuperará del caché de Redis. Si ejecuta docker ps, verá los tres contenedores corriendo juntos:

PS C:\Users\mpagani> docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                  NAMES
5999bb0ed94c        qmatteoq/testwebapi   "dotnet TestWebApi.d…"   34 seconds ago      Up 26 seconds       80/tcp                 newsfeed
15e273ff1be7        qmatteoq/testwebapp   "dotnet TestWebApp.d…"   34 seconds ago      Up 25 seconds       0.0.0.0:8080->80/tcp   webapp
890a8839a1c3        redis                 "docker-entrypoint.s…"   34 seconds ago      Up 26 seconds       6379/tcp               rediscache
Sin embargo, hay una diferencia en comparación con la última publicación, incluso si no se nota fuera de la caja. Abra de nuevo la sección Docker en Visual Studio Code y muévase a la sección Contenedores:



Haga clic derecho en la aplicación web (qmatteoq / testwebapp) y elija Adjuntar Shell:



¿Puedes ver la diferencia? Nos hemos conectado al contenedor que ejecuta nuestra aplicación .NET Core mediante PowerShell. Esto sucede porque Docker admite el concepto de imágenes multiplataforma. Esto significa que un desarrollador puede publicar varias versiones de la misma imagen para diferentes plataformas. Microsoft hace esto para las imágenes de .NET Core, ya que .NET Core es multiplataforma.

Desde que ejecutamos Docker con contenedores de Windows, cuando construimos la imagen para nuestra aplicación web, Docker retiró la versión de Windows del .NET Core SDK y los tiempos de ejecución. Sin embargo, esto es completamente transparente para nosotros. No cambiamos nada en el Dockerfile, seguimos usando las mismas declaraciones FROM que estábamos usando en las publicaciones anteriores:

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
 
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src


¿Hay alguna forma de forzar el uso de una plataforma específica, en caso de que nuestra máquina pueda ejecutar contenedores multiplataforma? La respuesta es sí, gracias a una nueva bandera llamada --plataforma que se ha agregado a la última versión beta del motor Docker. De hecho, la encontrarás en la documentación marcada como experimental. Después de la bandera, debe especificar la plataforma que desea utilizar, que puede ser Linux o Windows.

Puedes especificar la plataforma de tres maneras:


  • Añadiendo el parámetro --platform al comando de docker build de la ventana acoplable. De esta manera, cada instrucción FROM incluida en el Dockerfile extraerá una imagen basada en la plataforma especificada, si existe.
  • Agregando el parámetro --platform al comando de docker pull de la ventana acoplable, en caso de que quiera tomar una sola imagen para la plataforma especificada. Cuando haga esto, puede seguir usando el comando de docker build  de la ventana acoplable sin ningún indicador adicional, ya que Docker usará automáticamente la imagen que ya está en el caché en lugar de descargar una nueva.
  • Añadiendo el parámetro --platform al comando de docker run de la ventana acoplable. De esta manera, Docker obtendrá la imagen correcta para la plataforma seleccionada antes de ejecutar un contenedor basado en ella.

Si desea intentarlo, elimine las imágenes personalizadas testwebapp y testwebapi que ha creado anteriormente, más las imágenes dotnet estándar que el Dockerfile ha extraído automáticamente.

Para eliminar una imagen, primero debe capturar el identificador con el comando docker images, luego puede llamar a docker rmi seguido del identificador. Alternativamente, puede usar la pestaña Docker en Visual Studio Code, haga clic con el botón derecho en una de las imágenes en la sección Imágenes y elija Remove image.


Ahora vamos a reconstruir nuestras dos imágenes personalizadas a partir del Dockerfile. Esta vez, sin embargo, ejecutaremos los siguientes comandos:

docker build -t qmatteoq/testwebapp --platform linux .
 
docker build -t qmatteoq/testwebapi --platform linux .

Gracias al --platform switch, cuando Docker analizará el comando FROM, extraerá las imágenes basadas en Linux para .NET Core.

Podemos verificar esto ejecutando nuevamente el comando docker-compose up después de haber reconstruido la imagen para la aplicación web y las API web. Si hacemos clic derecho en los contenedores en ejecución en Visual Studio Code y elegimos nuevamente la opción Adjuntar Shell, veremos el mismo error que antes:



Ahora nuestra aplicación web se ejecuta en Linux y, como tal, no podemos usar PowerShell. Necesitamos ejecutar el comando / bin / sh:

docker exec -it 0f72c8b4438da48cf5af9335aa86c3d719961cdf9c60a4abf33e1629e9f16085 /bin/sh

Esta vez podemos conectarnos correctamente al terminal y ver las listas de archivos dentro del contenedor usando comandos UNIX:




Conluyendo


En esta publicación, hemos visto cómo la próxima versión de Docker para Windows brindará soporte para ejecutar contenedores multiplataforma en la misma máquina Windows. En el post hemos visto múltiples ejemplos en acción:
  • Un contenedor de Ubuntu que se ejecuta lado a lado con un contenedor de Windows Server
  • Un IIS en el contenedor de Windows ejecutándose en paralelo con un NGINX en el contenedor de Linux
  • Una solución .NET Core que se ejecuta con un contenedor de Linux y Windows, ya que es una tecnología multiplataforma

Puede jugar con esta función hoy gracias al canal Docker Edge, que ofrece versiones preliminares de la plataforma antes de lanzarlas en el canal estable.