LINUX

Cómo configurar NGINX como proxy inverso usando Docker

Un servidor proxy inverso es un servidor que generalmente se coloca detrás del firewall en una red privada y recupera recursos en nombre de un cliente de uno o más servidores. Un proxy inverso proporciona un nivel adicional de abstracción como terminación SSL, equilibrio de carga, enrutamiento de solicitudes, almacenamiento en caché, compresión, etc. También proporciona control para garantizar un flujo fluido de tráfico entre clientes y servidores. En este tutorial, configuraremos un proxy inverso en NGINX que servirá a dos servidores ascendentes, todos dentro de una ventana acoplable.

La puesta en marcha

Nuestra configuración incluye tres contenedores, dos contenedores para dos servidores ascendentes y un contenedor para un proxy inverso. La solicitud del cliente será interceptada por el proxy y la reenvía al upstream.

proxy inverso nginx dentro de la ventana acoplable

Dentro del contenedor, los puertos y las direcciones IP son privados y no se puede acceder a ellos externamente a menos que estén vinculados al host. Por lo tanto, solo un contenedor puede vincularse al puerto 80 del host de la ventana acoplable. Entonces, ¿cómo puede acceder a varias aplicaciones web que se ejecutan en varios contenedores a través del puerto 80 del host de la ventana acoplable? La respuesta es a través del proxy inverso y usaremos el proxy inverso nginx dentro de un contenedor que vinculará su puerto 80 al puerto 80 del host de la ventana acoplable y reenviará la solicitud a la aplicación web que se ejecuta en varios contenedores.

Configurar servicios web

Dado que configuraremos dos contenedores para dos servicios web, cada uno de ellos tendrá su propio docker-composer.yml, uno para site1 y otro para site2. Recuerde que estos servicios web no se vincularán a ningún puerto externo, la comunicación con el mundo exterior se realizará a través de un proxy inverso. Para este tutorial, estos servicios web devolverán un HTML simple usando nginx, aunque también pueden ser aplicaciones PHP / JSP / Python. También conectaremos estos dos servicios web usando el nombre site1.test y site2.test

Creemos carpetas y archivos para webservice1, es decir, para site1

sitio1
├── docker-compose.yml
└── index.html

root@demohost:~# cd ~
root@demohost:~# mkdir site1
root@demohost:~# cd site1
root@demohost:~/site1# vi docker-compose.yml

version: '2'
services:
app:
image: nginx:1.9
volumes:
- .:/usr/share/nginx/html/
expose:
- "80"

Cree un archivo de índice para el servicio web 1

root@demohost:~/site1# vi index.html

<!DOCTYPE html>
<html>
<head>
<title>Site 1</title>
</head>
<body>
<h1>This is a sample "site1" response</h1>
</body>
</html>

El docker-compose.yml es bastante sencillo. Este servicio web es un servicio de «aplicación» y extraerá la versión 1.9 de nginx. La raíz de site1 desde el host de la ventana acoplable se monta en / usr / share / nginx / html / y se expone el puerto 80. Cree el servicio web 1 con el siguiente comando.

root@demohost:~/site1# docker-compose build

Ahora inicie el contenedor de servicios.

root@demohost:~/site1# docker-compose up -d

Listar el contenedor

root@demohost:~# docker ps -a

De manera similar, cree un segundo contenedor, es decir, el servicio web 2

site2
├── docker-compose.yml
└── index.html

root@demohost:~# cd ~
root@demohost:~# mkdir site2
root@demohost:~# cd site2
root@demohost:~/site2# vi docker-compose.yml

version: '2'
services:
app:
image: nginx:1.9
volumes:
- .:/usr/share/nginx/html/
expose:
- "80"

Cree un archivo de índice para el servicio web 2

root@demohost:~/site2# vi index.html

<!DOCTYPE html>
<html>
<head>
<title>Site 2</title>
</head>
<body>
<h1>This is a sample "site2" response</h1>
</body>
</html>

Construya el servicio web 2 con el siguiente comando.

root@demohost:~/site2# docker-compose build

Ahora inicie el contenedor de servicios.

root@demohost:~/site2# docker-compose up -d

Listar el contenedor

root@demohost:~# docker ps -a

Configurar proxy

Ahora que dos servicios web están en funcionamiento dentro del contenedor, procedemos a configurar el proxy inverso dentro de un contenedor. Comenzaremos creando carpetas y archivos para proxy.

apoderado/
├── backend-not-found.html
├── default.conf
├── docker-compose.yml
├── Dockerfile
├── incluye
│ ├── proxy.conf
│ └── ssl.conf
└── ssl
├── site1.crt
├── site1.key
├── site2.crt
└── site2.key

root@demohost:~# mkdir proxy
root@demohost:~# cd proxy/
root@demohost:~/proxy# touch Dockerfile
root@demohost:~/proxy# touch backend-not-found.html
root@demohost:~/proxy# touch default.conf
root@demohost:~/proxy# touch docker-compose.yml
root@demohost:~/proxy# mkdir includes
root@demohost:~/proxy# mkdir ssl
root@demohost:~/proxy# cd ../includes
root@demohost:~/proxy/includes# touch proxy.conf
root@demohost:~/proxy/includes# touch ssl.conf

Edite el Dockerfile con el siguiente contenido

root@demohost:~/proxy# vi Dockerfile

FROM nginx:1.9

#  default conf for proxy service
COPY ./default.conf /etc/nginx/conf.d/default.conf

# NOT FOUND response
COPY ./backend-not-found.html /var/www/html/backend-not-found.html

#  Proxy and SSL configurations
COPY ./includes/ /etc/nginx/includes/

# Proxy SSL certificates
COPY ./ssl/ /etc/ssl/certs/nginx/

Editar backend-not-found.html

root@demohost:~/proxy# vi backend-not-found.html

<html>
<head><title>Proxy Backend Not Found</title></head>
<body >
<h2>Proxy Backend  Not Found</h2>
</body>
</html>

Editar default.conf

root@demohost:~/proxy# vi default.conf

# web service1 config.
server {
listen 80;
listen 443 ssl http2;
server_name site1.test;

# Path for SSL config/key/certificate
ssl_certificate /etc/ssl/certs/nginx/site1.crt;
ssl_certificate_key /etc/ssl/certs/nginx/site1.key;
include /etc/nginx/includes/ssl.conf;

location / {
include /etc/nginx/includes/proxy.conf;
proxy_pass http://site1_app_1;
}

access_log off;
error_log  /var/log/nginx/error.log error;
}

# web service2 config.
server {
listen 80;
listen 443 ssl http2;
server_name site2.test;

# Path for SSL config/key/certificate
ssl_certificate /etc/ssl/certs/nginx/site2.crt;
ssl_certificate_key /etc/ssl/certs/nginx/site2.key;
include /etc/nginx/includes/ssl.conf;

location / {
include /etc/nginx/includes/proxy.conf;
proxy_pass http://site2_app_1;
}

access_log off;
error_log  /var/log/nginx/error.log error;
}

# Default
server {
listen 80 default_server;

server_name _;
root /var/www/html;

charset UTF-8;

error_page 404 /backend-not-found.html;
location = /backend-not-found.html {
allow   all;
}
location / {
return 404;
}

access_log off;
log_not_found off;
error_log  /var/log/nginx/error.log error;
}

En la configuración de nginx, cada uno de los dos servicios web tiene su propio bloque de servidor. Este bloque indica a nginx que pase las solicitudes al contenedor de aplicaciones de servicios web apropiado y son, a saber, site1_app_1 y site2_app_1. Busque este nombre en la salida de docker ps -a en la columna de nombre. La opción proxy_intercept_errors está activada para que nginx devuelva un error del propio contenedor de aplicaciones web en lugar de la respuesta nginx predeterminada. La ruta de configuración / clave / certificados SSL indica a nginx dónde elegir estos archivos.

Editar docker-compose.yml

version: '2'
services:
proxy:
build: ./
networks:
- site1
- site2
ports:
- 80:80
- 443:443

networks:
site1:
external:
name: site1_default
site2:
external:
name: site2_default

El docker-compose.yml anterior creará un servicio proxy y se conectará a dos redes externas, a saber, nuestros dos servicios web. Esto se debe al hecho de que el servicio de proxy debe conectarse a estas redes externas para poder procesar la solicitud que recibe del contenedor de la ventana acoplable de servicios web. El enlace del puerto no 80/443 del servicio de proxy se realiza al puerto 80/443 del host de la ventana acoplable. El nombre de los dos contenedores / servicios web externos es site1_default y site2_default.

Genere certificados y claves para ambos servicios web dentro de la carpeta ssl.

Para Site1

root@demohost:~/proxy# cd ssl
root@demohost:~/proxy/ssl# sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout site1.key -out site1.crt
Generating a 2048 bit RSA private key
..........................+++
..............+++
writing new private key to 'site1.key'
-----

Para Site2

root@demohost:~/proxy/ssl# sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout site2.key -out site2.crt
Generating a 2048 bit RSA private key
....................+++
..........................................+++
writing new private key to 'site2.key'
-----

Edite proxy.conf dentro del directorio de inclusión.

root@demohost:~/proxy/includes# vi proxy.conf

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-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_intercept_errors on;

Edite la configuración de SSL dentro de la carpeta de inclusión

root@demohost:~/proxy/includes# vi ssl.conf

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-
ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-
SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-
GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-
AES128-SHAECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-
SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:
DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-
DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:
AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-
CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;

Para la resolución de nombres para dos servicios web, agregue las siguientes dos líneas en / etc / hosts

root@demohost:~/proxy# vi /etc/hosts

172.31.30.78 site1.test
172.31.30.78 site2.test

La dirección IP anterior es la IP privada de docker-host. Recuerde, la solicitud del cliente llegará al puerto 80 de dockerhost, que se asignará al puerto 80 del contenedor nginx.

Construye el contenedor de proxy

root@demohost:~/proxy# docker-compose build
Building proxy
Step 1 : FROM nginx:1.9
---> c8c29d842c09
Step 2 : COPY ./default.conf /etc/nginx/conf.d/default.conf
---> Using cache
---> 4c459326c3a2
Step 3 : COPY ./backend-not-found.html /var/www/html/backend-not-found.html
---> Using cache
---> e3d817f5fb8e
Step 4 : COPY ./includes/ /etc/nginx/includes/
---> Using cache
---> 0c5ca9eb16d8
Step 5 : COPY ./ssl/ /etc/ssl/certs/nginx/
---> Using cache
---> 92007e83d405
Successfully built 92007e83d405

Ejecute el contenedor de proxy

root@demohost:~/proxy# docker-compose up -d
Building proxy
Step 1 : FROM nginx:1.9
---> c8c29d842c09
Step 2 : COPY ./default.conf /etc/nginx/conf.d/default.conf
---> 4c459326c3a2
Removing intermediate container 86c1ea72022e
Step 3 : COPY ./backend-not-found.html /var/www/html/backend-not-found.html
---> e3d817f5fb8e
Removing intermediate container 51b12caded59
Step 4 : COPY ./includes/ /etc/nginx/includes/
---> 0c5ca9eb16d8
Removing intermediate container 66f2c8dd0d56
Step 5 : COPY ./ssl/ /etc/ssl/certs/nginx/
---> 92007e83d405
Removing intermediate container 29bca9e3ba0a
Successfully built 92007e83d405
Creating proxy_proxy_1

Ahora enumere todos los contenedores en ejecución.

root@demohost:~/# docker ps -a

El comando anterior enumerará los tres contenedores.

Mostrar contenedores en ejecución

Para verificar eso, hemos configurado el proxy inverso correctamente, use curl para obtener una respuesta de dos servicios web del host de la ventana acoplable.

root@demohost:~/proxy# curl site1.test
<!DOCTYPE html>
<html>
<head>
<title>Site1</title>
</head>
<body>
<h1>This is a sample "Site1" response</h1>
</body>
</html>
root@demohost:~/proxy# curl site2.test
<!DOCTYPE html>
<html>
<head>
<title>Site2</title>
</head>
<body>
<h1>This is a sample "Site2" response</h1>
</body>
</html>

Conclusión

Dado que tenemos un proxy inverso en contenedores, puede agregar más servicios web cuando lo necesite. Pero este método debe iniciar y detener el contenedor cada vez que agrega servicios. Esto se puede automatizar utilizando las API de Docker y alguna plantilla básica. Esto conduce a implementaciones indoloras y mejora la disponibilidad.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba
Cerrar