Empaquetado de aplicaciones principales de ASP.NET con Docker

Las aplicaciones ASP.NET Core son verdaderamente multiplataforma y pueden ejecutarse en nixes y, en consecuencia, en Docker. Veamos cómo pueden empaquetarse para implementarse en Linux y usarse junto con Nginx. Detalles debajo del corte!



Nota: continuamos la serie de publicaciones de versiones completas de artículos de la revista Hacker. Ortografía y puntuación del autor guardado.


Sobre Docker


Casi todos han escuchado sobre la arquitectura de microservicios. El concepto de dividir la aplicación en partes no quiere decir que sea nueva. Pero, lo nuevo es lo viejo olvidado y reciclado.


Si intenta hablar de arquitectura en pocas palabras, la aplicación web se divide en partes unitarias separadas: servicios. Los servicios no interactúan directamente entre sí y no tienen bases de datos comunes. Esto se hace para poder cambiar cada servicio sin consecuencias para los demás. Los servicios se empaquetan en contenedores. Entre los contenedores, Docker gobierna la pelota.


Para describir qué Docker se simplifica muy a menudo, use el término "máquina virtual". Definitivamente hay una similitud, pero es un error decirlo. La forma más fácil de comprender esta diferencia es mirar las siguientes imágenes de la documentación oficial de la ventana acoplable:




Los contenedores utilizan el núcleo del sistema operativo actual y lo dividen entre ellos. Mientras que las máquinas virtuales que usan hipervisor usan recursos de hardware.
Docker Image es un objeto de solo lectura que esencialmente almacena una plantilla para construir un contenedor. Un contenedor es un entorno en el que se ejecuta el código. Las imágenes se almacenan en repositorios. Por ejemplo, el repositorio oficial de Docker Hub le permite almacenar solo una imagen de forma privada. Sin embargo, es gratis, así que incluso por esto debes agradecerles.


INFORMACIÓN


Docker no es el único representante de la contenedorización. Además de eso, hay otras tecnologías. Por ejemplo:


rkt (pronunciado 'cohete') por CoreOS


LXD (pronunciado 'lexdi') por Ubuntu


Contenedores de Windows: nunca lo adivinará nadie.


Ahora que nos hemos familiarizado con la teoría, pasemos a la práctica.


No tiene sentido desmontar la instalación de la ventana acoplable, ya que se puede instalar en muchos sistemas operativos. Solo le indicaré que puede descargarlo para su plataforma desde Docker Store . Si instala Docker en Windows, la virtualización debe estar habilitada en el BIOS y el sistema operativo. Puede leer sobre cómo habilitarlo en 10-ke en el siguiente artículo: Instalación de Hyper-V en Windows10


Crear un proyecto habilitado para Docker


Docker es, por supuesto, un producto Linux, pero si es necesario, puede usarlo cuando desarrolle para Mac o Windows. Al crear un proyecto en Visual Studio, para agregar compatibilidad con Docker, simplemente seleccione la casilla de verificación Habilitar compatibilidad con Docker.


El soporte de Docker se puede agregar a un proyecto existente. Se agrega al proyecto de la misma manera que se agregan varios componentes nuevos. Menú contextual Agregar - Soporte Docker.


Si Docker está instalado y ejecutándose en su máquina, la consola se abrirá automáticamente y se ejecutará el comando


docker pull microsoft/aspnetcore:2.0 

que inicia el proceso de descarga de la imagen. Esta imagen es en realidad un espacio en blanco sobre la base de la cual se creará su imagen. ASP.NET Core 2.1 usa una imagen diferente - microsoft / dotnet: sdk


Los siguientes archivos se crearán automáticamente en el directorio con la solución para usted:
.dockerignore (excluyendo archivos y directorios de la imagen del docker), docker-compose.yml (usando este archivo puede configurar la ejecución de varios servicios), docker-compose.override.yml (configuración auxiliar docker-compose), docker-compose.dcproj ( archivo de proyecto para Visual Studio).


Se creará un archivo Dockerfile en el directorio del proyecto. En realidad, con la ayuda de este archivo creamos nuestra imagen. De manera predeterminada (en caso de que el proyecto se llame DockerServiceDemo), puede verse así:


 FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo/DockerServiceDemo.csproj DockerServiceDemo/ RUN dotnet restore DockerServiceDemo/DockerServiceDemo.csproj COPY . . WORKDIR /src/DockerServiceDemo RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"] 

La configuración inicial para .NET Core 2.0 no le permitirá compilar la imagen de forma inmediata utilizando el comando de construcción docker. Está configurado para iniciar el archivo docker-compose desde un directorio de un nivel superior. Para que la construcción continúe con éxito, el Dockerfile puede tener un aspecto similar:


 FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo.csproj DockerServiceDemo.csproj RUN dotnet restore DockerServiceDemo.csproj COPY . . WORKDIR /src RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"] 

Todo lo que hice fue eliminar el directorio adicional DockerServiceDemo.


Si usa Visual Studio Code, deberá generar los archivos manualmente. Aunque VS Code tiene una funcionalidad auxiliar en forma de una extensión Docker , agregaré un enlace al manual sobre cómo trabajar con la ventana acoplable desde VS Code - Trabajar con Docker . Sí, el artículo está en inglés, pero con imágenes.


Docker de tres acordes


Para el trabajo diario con la ventana acoplable, solo unos pocos comandos son suficientes para recordar.


El equipo más importante es, por supuesto, construir una imagen. Para hacer esto, debe usar bash / CMD / PowerShell para ir al directorio donde se encuentra el Dockerfile y ejecutar el comando:


 docker build -t your_image_name . 

Aquí, después de la opción -t, se establece el nombre de su imagen. Atención: al final del comando, un espacio después del espacio Este punto significa que se está utilizando el directorio actual. Una imagen se puede etiquetar con una etiqueta (número o nombre). Para hacer esto, coloque dos puntos después del nombre y especifique una etiqueta. Si no se especifica la etiqueta, de forma predeterminada se establecerá con el nombre más reciente. Para enviar una imagen al repositorio, es necesario que el nombre de la imagen incluya el nombre del repositorio. Algo como esto:


 docker build -t docker_account_name/image_name:your_tag . 

Aquí your_docker_account_name es el nombre de su cuenta de Docker Hub.


Si creó la imagen solo con un nombre local que no incluye el repositorio, puede marcar la imagen con un nombre diferente después de la construcción utilizando el siguiente comando:


 docker tag image_name docker_account_name/image_name:your_tag 

Para enviar cambios al concentrador, ahora debe ejecutar el siguiente comando:


 docker push docker_account_name/image_name:your_tag 

Antes de esto, debe iniciar sesión en su cuenta de Docker. En Windows, esto se hace desde la interfaz de usuario de la aplicación, pero en * nix esto se hace mediante el comando:


 docker login 

De hecho, tres equipos no son suficientes. También debe poder verificar el funcionamiento del contenedor. El comando con el que puede iniciar el contenedor se ve así:


 docker run -it -p 5000:80 image_name 

La opción -it creará un pseudo-TTY y su contenedor responderá a las solicitudes. Después de ejecutar el comando, el servicio estará disponible en http: // localhost: 5000 /


-p 5000: 80 asocia el puerto 5000 del contenedor con el puerto 80 del host.


Además, hay tales comandos:


 docker ps –a 

Mostrarle una lista de contenedores. Como se ha agregado el modificador -a, se mostrarán todos los contenedores, no solo los que se están ejecutando actualmente.


 docker rm container_name 

Este comando eliminará el contenedor llamado container_name. rm - abreviatura de eliminar


 docker logs container_name 

Mostrar registros de contenedores


 docker rmi image_name 

Elimina una imagen llamada image_name


Lanzar un contenedor a través de un servidor proxy inverso


El hecho es que las aplicaciones .NET Core utilizan su servidor web Kestrel. Este servidor no se recomienda para producción. Por qué Hay varias explicaciones.
Si hay varias aplicaciones que comparten IP y puerto, Kestrel no podrá distribuir el tráfico. Además, el servidor proxy inverso proporciona una capa adicional de seguridad, simplifica el equilibrio de carga y la configuración de SSL, y también se integra mejor en la infraestructura existente. Para la mayoría de los desarrolladores, la razón más importante para la necesidad de servidores proxy inversos es la seguridad adicional.


Primero, restaure la configuración original de Dockerfile. Después de eso, trataremos con el archivo docker-compose.yml e intentaremos ejecutar nuestro servicio solo. El formato de archivo yml se lee como “yaml” y es una abreviatura de “Yet Another Markup Language” o de “YAML Ain't Markup Language”. Ya sea otro lenguaje de marcado o ningún lenguaje de marcado. De alguna manera, todo no es seguro.


Mi archivo docker-compose predeterminado se ve así:


 version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile 

El archivo docker-compose.override.yml agrega varias configuraciones a la configuración:
versión: '3.4'


 services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80" 

Podemos construir la solución creada usando docker-compose build, llamando al comando docker-compose up, lanzaremos nuestro contenedor. ¿Funciona todo? Luego ve al siguiente paso. Cree el archivo nginx.info. La configuración será aproximadamente la siguiente:


 worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:80; } server { listen 80; location / { proxy_pass http://app_servers; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

Aquí indicamos que nginx escuchará en el puerto 80 (listen 80;). Y las solicitudes recibidas serán redirigidas al puerto 80 del host en el contenedor dockerservicedemo. Además, le decimos a nginx qué encabezados transmitir.


Podemos usar http en nginx y acceder al sitio web a través de https. Cuando una solicitud https pasa por un proxy http, no se pasa mucha información de https a http. Además, cuando se usa un proxy, se pierde la dirección IP externa. Para que esta información se transmita en los encabezados, debe cambiar el código de nuestro proyecto ASP.NET y agregar el siguiente código al comienzo del método Configure del archivo Startup.cs:


  app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); 

La mayoría de los servidores proxy usan los encabezados X-Fordered-For y X-Fordered-Proto. Son estos encabezados los que se especifican ahora en la configuración de nginx.


Ahora incluya la imagen nginx y el archivo nginx.conf en la configuración doker-compose. La precaución en los espacios YAML es importante:


 version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile ports: - 5000:80 proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf ports: - 80:80 

Aquí agregamos proxies a nuestra configuración como una imagen nginx. Adjuntamos a esta imagen un archivo de configuración externo. Lo montamos en el sistema de archivos del contenedor utilizando un mecanismo llamado volumen. Si agrega al final: ro, entonces el objeto se montará de solo lectura.


El proxy escucha el puerto 80 externo de la máquina en la que se está ejecutando el contenedor y envía una solicitud al puerto 80 interno del contenedor.


Al ejecutar el comando doker-compose up, completaremos, es decir, extraeremos la imagen nginx del repositorio e iniciaremos nuestro contenedor junto con el contenedor proxy. Ahora en http: // localhost: 80 / será accesible a través de nginx. En el puerto 5000, la aplicación "gira" también bajo Kestrel.


Podemos verificar que la solicitud a la aplicación web pase por el proxy inverso. Abra las herramientas de desarrollador en el navegador Chrome y vaya a la pestaña Red. Haga clic en localhost aquí y seleccione la pestaña Encabezados.



Lanzamos el contenedor a través de proxies y HTTPS


ASP.NET Core 2.1 trajo consigo mejoras en el soporte HTTPS.
Digamos que el siguiente middleware le permite redirigir desde una conexión no segura a una segura:


 app.UseHttpsRedirection(); 

Y el siguiente le permite utilizar el Protocolo de seguridad de transporte estricto HTTP: HSTS.


 app.UseHsts(); 

HSTS es una característica del protocolo HTTP / 2, cuya especificación se lanzó en 2015. Esta funcionalidad es compatible con los navegadores modernos e informa que el sitio web usa solo https. Por lo tanto, se produce protección contra un ataque de degradación durante el cual el atacante puede aprovechar la situación mediante el uso de la transición a un protocolo http inseguro. Por ejemplo, degradar TLS o incluso reemplazar un certificado.


Por lo general, este tipo de ataque se usa junto con ataques de hombre en el medio. Debe saber y recordar que HSTS no lo salva de una situación en la que un usuario visita el sitio utilizando el protocolo http y luego lo redirige a https. Existe la llamada lista de precarga de Chrome , que contiene enlaces a sitios que admiten https. Otros navegadores (Firefox, Opera, Safari, Edge) también admiten listas de sitios https creados en base a la lista de Chrome. Pero todas estas listas están lejos de todos los sitios.


La primera vez que ejecute una aplicación Core en Windows, recibirá un mensaje que indica que se ha creado e instalado un certificado de desarrollador. Al hacer clic en el botón e instalar el certificado, lo hará confiable. Desde la línea de comandos en macOS, puede agregar confianza al certificado con el comando:
dotnet dev-certs https –trust


Si la utilidad dev-certs no está instalada, puede instalarla con el comando:


 dotnet tool install --global dotnet-dev-certs 

Cómo agregar un certificado de confianza en Linux depende de la distribución.
Para fines de prueba, utilizamos el certificado del desarrollador. Las acciones con un certificado firmado por CA son similares. Si lo desea, puede usar certificados LetsEncrypt gratuitos


Puede exportar el certificado de desarrollador a un archivo con el comando


 dotnet dev-certs https -ep ___.pfx 

El archivo debe copiarse en el directorio% APPDATA% / ASP.NET / Https / en Windows o en /root/.aspnet/https/ en macOS / Linux.


Para que el contenedor tome la ruta al certificado y su contraseña, cree secretos de usuario con los siguientes contenidos:


 { "Kestrel":{ "Certificates":{ "Default":{ "Path": "/root/.aspnet/https/__.pfx", "Password": "___" } } } } 

Este archivo almacena datos sin cifrar y, por lo tanto, solo se usa durante el desarrollo. Se crea un archivo en Visual Studio llamando al menú contextual en el icono del proyecto o utilizando la utilidad de secretos de usuario en Linux.


En Windows, el archivo se guardará en el directorio% APPDATA% \ Microsoft \ UserSecrets \ <user_secrets_id> \ secrets.json, y en macOS y Linux se guardará en ~ / .microsoft / usersecrets / <user_secrets_id> /secrets.json


Para guardar la configuración para producción, algunas distribuciones de Linux pueden usar systemd. La configuración se guarda bajo el atributo Servicio. Por ejemplo, así:


 [Service] Environment="Kestrel _ Certificates _ Default _Path=/root/.aspnet/https/__.pfx" Environment="Kestrel _ Certificates _ Default _Password=___" 

A continuación, daré y analizaré de inmediato la versión de trabajo de la configuración del acoplador para el proxy y el contenedor a través de https.


Archivo Docker-compose:


 version: '3.4' services: dockerservicedemo21: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile  override: version: '3.4' services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:44392;http://+:80 - ASPNETCORE_HTTPS_PORT=44392 ports: - "59404:80" - "44392:44392" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf - ./DockerServiceDemo/cert.crt:/etc/nginx/cert.crt - ./DockerServiceDemo/cert.rsa:/etc/nginx/cert.rsa ports: - "5001:44392" 

Ahora describiré momentos incomprensibles. ASPNETCORE_URLS nos permite no indicar en el código de la aplicación usando app.UseUrl el puerto en el que la aplicación está escuchando.


ASPNETCORE_HTTPS_PORT realiza una redirección similar a la que haría el siguiente código:
services.AddHttpsRedirection (opciones => opciones.HttpsPort = 44392)


Es decir, el tráfico de las solicitudes http se redirigirá a un puerto específico de solicitudes https.
Al usar los puertos, se indica que la solicitud del puerto externo 59404 se redirigirá al contenedor 80 y del puerto externo 44392 al 44392. Teóricamente, dado que hemos configurado un servidor proxy inverso, podemos eliminar puertos con estos redireccionamientos.
Usando volúmenes, un directorio con un certificado pfx y una aplicación UserSecrets se montan con una contraseña y un enlace al certificado.


La sección de proxy indica que las solicitudes del puerto externo 5001 se redirigirán al puerto nginx 44392. Además, se monta un archivo de configuración nginx, así como un certificado y una clave de certificado.


Para que puedan crear un solo certificado pfx (que ya tenemos) para crear archivos crt y rsa, puede usar OpenSSL. Primero necesitas extraer el certificado:


 openssl pkcs12 -in ./_.pfx -clcerts -nokeys -out domain.crt 

Y luego la clave privada:


 openssl pkcs12 -in ./_.pfx -nocerts -nodes -out domain.rsa 

La configuración de nginx es la siguiente:


 worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:44392; } server { listen 44392 ssl; ssl_certificate /etc/nginx/cert.crt; ssl_certificate_key /etc/nginx/cert.rsa; location / { proxy_pass https://app_servers; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } 

El servidor proxy está escuchando en el puerto 44392. Este puerto recibe solicitudes del puerto de host 5001. A continuación, el proxy redirige las solicitudes al puerto 44392 del contenedor dockerdemoservice.


Una vez que haya entendido estos ejemplos, obtendrá buenos antecedentes para trabajar con Docker, microservicios y nginx.


Le recordamos que esta es la versión completa de un artículo de la revista Hacker . Su autor es Alexey Sommer .

Source: https://habr.com/ru/post/es435914/


All Articles