/ HOWTO

Proyecto VSCode, Nginx y Docker. Con Docker-Compose.


Vamos a explicar cómo poner en funcionamiento una aplicación MVC Core en un contenedor Docker detrás de un proxy inverso (Nginx) corriendo en un segundo contenedor.

Todo esto automatizado mediante “Docker-Compose”.

¿ Por qué tenemos que utilizar un proxy inverso para la aplicación Mvc Core ?

El motivo es que Asp.Net Core proporciona un servidor web llamado Kestrel, aunque es un servidor web muy liviano que responde a cualquier petición http o https y ademas multiplataforma tiene algunos inconvenientes y/o limitaciones:

En cuanto a procesos y servicios: No gestiona ni monitoriza los procesos corriendo. La aplicación Asp Net Core tiene que ser lanzada y debemos asegurarnos que siga funcionando. Esto es algo que Kestrel no puede hacer automáticamente y requiere procesos manuales de control. Si la aplicación produce un error y deja de funcionar, Kestrel no es capaz de volver a levantarse para poder continuar recibiendo peticiones. El servidor Kestrel no es capaz de procesar tareas en paralelo aprovechandi la potencia del servidor.

En cuanto a la seguridad: La autenticación se realiza en servidores Windows. Filtrados por IP. Restricciones de dominios. No permite redirecciones (HTTP Redirect) No tiene consola de administración. No registra en un fichero de logs las peticiones http/https recibidas.

Estructura de nuestra aplicación.

| - app
|- | - Controllers
|- | - Models
|- | - …
|- | - Dockerfile
|- nginx
|- | - Dockerfile
|- | - nginx.conf
|- docker-compose.yml

Como comentamos antes, prepararemos dos contenedores independientes conectando el servidor WEB a la Aplicación:

Servidor App: que contendrá la aplicación Asp.Net Core. Servidor Nginx: contendrá un Nginx funcionando en modo proxy inverso con una configuración acorde.

Nos apoyamos en docker-compose para configurar y arrancar los contenedores.

Contenedor Nginx

Para crear el contenedor de Nginx debemos crear un fichero de configuración NGINX.CONF que realizará de pasarela entre el cliente y la aplicación:

Nginx.conf

worker_processes 4;

events { worker_connections 1024; }

http {
	sendfile on;
	upstream app_servers {
		server app:5000;
	}

	server {
		listen 80;
		location / {
			proxy_pass http://app_servers;
			proxy_redirect off;
			proxy_set_header Host $host;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header X-Forwarded-Host $server_name;
		}
	}
}

Utilizamos el nombre “APP” como referencia de la IP del servidor WEB. (server app:5000) Podríamos utilizar como configuración el texto “127.0.0.1:5000” y el servidor web prestará servicio únicamente a nuestro equipo real. (el que corre los dos contenedores)

En la configuración del fichero docker-compose.yml, se indicó que debe utilizar este fichero de configuración de ngingx

Ahora creamos el fichero Dockerfile en el directorio nginx que se utilizará para configurar el servicio.

FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf

Contenedor App

En la carpeta de la aplicación Asp.Net Core creamos el otro fichero Dockerfile.

FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app
COPY bin/Debug/netcoreapp2.0/publish .
ENV ASPNETCORE_URLS http://+:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "./app.dll"]

Importante: se tiene que verificar que la aplicación esta compilada con el SDK 2.1 de Asp.Net Core. En caso contrario el contenedor no arrancará y no funcionará la página web.

Ahora prepararemos el fichero Docker-compose.

A través del archivo docker-compose.yml se configuran los contenedores, puertos, ficheros de configuración y conexiones entre el contenedor PROXY y el contenedor APP (links).

version: '2'

services:
	app:
		build:
			context: ./app
			dockerfile: Dockerfile
		expose:
			- "5000"
	proxy:
		build:
			context: ./nginx
			dockerfile: Dockerfile
		ports:
			- "8080:80"
		links:
			- app

En el fichero de docker-compose se definen dos servicios: app y proxy.

Para cada uno de ellos, la sección build le indica a docker-compose como construir los contenedores utilizando las imágenes individuales, en el momento de construir la aplicación. Las secciones “expose” y “ports” controlan la forma en que los servicios interactúan con el host y con la red interna. El servicio proxy está vinculado al servicio de la aplicación. Esto significa que cuando se abre el proxy se inicia la aplicación.

Puesta en marcha. Ahora nos queda poner a funcionar todo.

# docker-compose up

El resultado debe ser algo similar a esto.

# docker-compose up
Creating network "example_default" with the default driver
Building app
Step 1/6 : FROM microsoft/dotnet:2.1-aspnetcore-runtime
2.1-aspnetcore-runtime: Pulling from microsoft/dotnet
be8881be8156: Already exists
f854db899319: Pull complete
4591fd524b8e: Pull complete
65f224da8749: Pull complete
Digest: sha256:a43b729b84f918615d4cdce92a8bf59e3e4fb2773b8491a7cf4a0d728886eeba
Status: Downloaded newer image for microsoft/dotnet:2.1-aspnetcore-runtime
 ---> fcc3887985bb
Step 2/6 : WORKDIR /app
 ---> Running in 5988fd2bf58d
Removing intermediate container 5988fd2bf58d
 ---> 9b6d60216582
Step 3/6 : COPY bin/Debug/netcoreapp2.1/publish .
 ---> 456409271d22
Step 4/6 : ENV ASPNETCORE_URLS http://+:5000
 ---> Running in 691a51d9faf0
Removing intermediate container 691a51d9faf0
 ---> 0f5734bc4871
Step 5/6 : EXPOSE 5000
 ---> Running in 3ad31643f928
Removing intermediate container 3ad31643f928
 ---> 72cfe8553d9a
Step 6/6 : ENTRYPOINT ["dotnet", "app.dll"]
 ---> Running in b083ac9cd683
Removing intermediate container b083ac9cd683
 ---> cf17701523d3
Successfully built cf17701523d3
Successfully tagged example_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Building proxy
Step 1/2 : FROM nginx
 ---> c82521676580
Step 2/2 : COPY nginx.conf /etc/nginx/nginx.conf
 ---> 67e4b106c1df
Successfully built 67e4b106c1df
Successfully tagged example_proxy:latest
WARNING: Image for service proxy was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating example_app_1
Creating example_proxy_1
Attaching to example_app_1, example_proxy_1
app_1    | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
app_1    |       No XML encryptor configured. Key {d01c550b-de04-41aa-ba22-76e5673dfab6} may be persisted to storage in unencrypted form.
app_1    | Hosting environment: Production
app_1    | Content root path: /app
app_1    | Now listening on: http://[::]:5000
app_1    | Application started. Press Ctrl+C to shut down.

En este caso la aplicación Asp.Net Core es una webapi. Cuando pedimos al navegador la ruta localhost:8080 (que es el puerto que se ha mapeado en el contenedor nginx) indicando la ruta del método (api/values) se puede verificar que la webapi responde un objeto json.

https://app:8080/api/values

JSON
0: "value1" 
1: "value2"

O en texto

["value1","value2"]

En la ventana donde tengamos funcionando el “docker-compose” veremos la salida de cada petición http resuelta.

En este caso nuestro contenedor Recibe peticiones del IP 172.18.0.1.

proxy_1  | 172.18.0.1 - - [14/Aug/2018:21:10:24 +0000] "GET / HTTP/1.1" 404 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
proxy_1  | 172.18.0.1 - - [14/Aug/2018:21:10:25 +0000] "GET /favicon.ico HTTP/1.1" 404 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
proxy_1  | 172.18.0.1 - - [14/Aug/2018:21:10:25 +0000] "GET /favicon.ico HTTP/1.1" 404 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
proxy_1  | 172.18.0.1 - - [14/Aug/2018:21:10:29 +0000] "GET /api HTTP/1.1" 404 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
proxy_1  | 172.18.0.1 - - [14/Aug/2018:21:10:51 +0000] "GET /api/values HTTP/1.1" 200 30 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"

¿ Porqué ese IP ?

Por que el servidor Nginx recibe peticiones del IP 172.18.0.1 que es el IP asignado a la placa de red de nuestro equipo real.

ifconfig
br-cd7311f5f302: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
        RX packets 15  bytes 1411 (1.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 87  bytes 12246 (11.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Subscríbete y recibirás los últimos artículos semanalmente en tu email.