Como configurar nginx para servidores tomcat independientes?

Buenas!, veréis, me ha tocado la tarea de modernizar una aplicación de la edad de piedra y me estoy topando con algunos baches. Me explico:

Tengo una serie de servidores de aplicaciones tomcat con la siguiente estructura:

(partner1 - tomcatServer1)
http://myServerIp1:8080/ ->tomcat index
http://myServerIp1:8080/webapp1 ->webapp1(main)
http://myServerIp1:8080/webapp2 ->webapp2
(partner2 - tomcatServer2)
http://myServerIp2:8080/ ->tomcat index
http://myServerIp2:8080/webapp1 ->webapp1(main)
http://myServerIp2:8080/webapp2 ->webapp2
http://myServerIp2:8080/webapp3 ->webapp3


Como podéis ver, se aprecian una serie de inconveninetes:
    Lo primero el uso de una ip publica por cada servidor. esto es un problema si quiero un certificado firmado por una CA ademas de los problemas de seguridad que implican dejar visible la IP. No obstante este es un problema para mas adelante ya que la cosa no empieza aquí.
    El segundo es que cada servidor Tomcat contiene varias aplicaciones y hay que servir todas como se pueda. No parece que sea un problema serio, en realidad no deberia serlo

Hablamos de servidores físicos independientes con aplicaciones tomcat, asi que lo que se ha propuesto es usar un servidor aparte usando nginx como proxy inverso que redireccione usando subdominios a cada servidor especifico. Así debería quedar la estructura final:

(partner1 - tomcatServer1)
http://partner1.myDomain.com/ ->webapp1(main)
http://partner1.myDomain.com/webapp2 ->webapp2
(partner2 - tomcatServer2)
http://partner2.myDomain.com/ ->webapp1(main)
http://partner2.myDomain.com/webapp2 ->webapp2
http://partner2.myDomain.com/webapp3 ->webapp3


Una vez conseguido esto, tocaría pelearse con el tema del uso de ssl, pero repito, es algo que dejaremos para otro día ya que ni siquiera he conseguido ser capaz de hacer un proxy_pass de forma correcta a una aplicación tomcat... ni al propio index de tomcat!! [tomaaa] (me carga solo el texto, parece que no es capaz de cargar la hoja de estilos) aparte de otros problemas que he visto que trataremos mas adelante.
Básicamente la idea es sustituir las ips por un dominio que diferencie la aplicación principal de cada cliente con el subdominio como se ejemplifica mas arriba.
Por supuesto, mi objetivo principal no era ver el index de tomcat, sin embargo he llegado a ese punto porque cuando intentaba cargar la aplicación me encontraba con una bellísima página en blanco, sin errores aparentes... la nada. Pensé que era culpa de haber sido un poco bruto y haber hecho directamente un proxy_pass directo a la aplicación, así que lo cambié directamente a tomcat y ahí me di cuenta de lo comentado.

Buscando por internet, una de las sugerencias ya respondidas era hacer que tomcat hiciera un vps por aplicación, lo cual es interesante ya que me permitiria definir directamente varios location estando seguro de que funcionaria cada aplicación... Pero probando solo con la aplicación principal el resultado volvía a ser la pantalla blanca de la incertidumbre. [+risas]

Necesito vuestra ayuda y, a pesar de todo lo explicado arriba, no quiero una solución al conjunto entero del problema, por lo menos iré pasito a pasito. Lo primero hacer que cargue bien tomcat, y para ello he dejado tomcat tal como estaba, sin virtual hosts, y ademas he tumbado las aplicaciones para evitar distracciones

Es decir, por ahora voy a intentar primero que funcione la siguiente estructura:
(partner1 - tomcatServer1)
http://myServerIp1:8080/ ->tomcat index
http://partner1.myDomain.com/ ->tomcat index

Y este sería mi archivo correspondiente de nginx

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    index index.html index.htm index.nginx-debian.html;
    server_name partner1.myDomain.com;

    location / {
        proxy_pass   http://myServerIp1:8080/;
        proxy_set_header Host               $host;
        proxy_set_header X-Real-IP          $remote_addr;
        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
        try_files $uri $uri/ =404;
    }
}


Si alguno usa debian 10, verá que es una pequeña modificación del fichero que viene por defecto.
Docker. Todo lo puedes solucionar con un par de dockers.

Si no lo conoces, te diré que son (incorrectamente hablando) «mini máquinas virtuales».

Cada tomcat estaría en un docker distinto (hay dockers específicos de tomcat, se montan en segundos).

Después tendrías dos dockers más: nginx-proxy y letsencrypt companion.

Básicamente, el único docker que tendría un puerto abierto sería el de nginx y él sería el encargado de redirigir el tráfico a cada docker de tomcat según la URL en la que se acceda.

Además, si usas el letsencrypt te generará él solo el certificado SSL usando letsencrypt companion.


Hay una herramienta de ayuda para gestionar dockers llamada docker-compose, lo que te permite definir todo simplemente creando un fichero txt con la configuración:

version: "3"
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    restart: always
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - '/var/run/docker.sock:/tmp/docker.sock:ro'
      - /etc/nginx/vhost.d
      - /usr/share/nginx/html
      - /etc/nginx/dhparam
      - /etc/nginx/certs
      - ./my-custom-config.conf:/etc/nginx/conf.d/my-custom-config.conf:ro

  letsencrypt:
    restart: always
    container_name: letsencrypt
    image: jrcs/letsencrypt-nginx-proxy-companion:v1.13
    volumes_from:
      - nginx-proxy
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'

  server1:
    container_name: server1
    image: tomcat:8-jdk8
    volumes:
      - webs/web1.war:/usr/local/tomcat/webapps/ROOT.war
    environment:
      VIRTUAL_HOST:  server1.example.com
      LETSENCRYPT_HOST:  server1.example.com


  server2:
    container_name: server1
    image: tomcat:8-jdk8
    volumes:
      - webs/web2.war:/usr/local/tomcat/webapps/ROOT.war
    environment:
      VIRTUAL_HOST:  server2.example.com
      LETSENCRYPT_HOST:  server2.example.com

  server3:
    container_name: server1
    image: tomcat:8-jdk8
    volumes:
      - webs/web3.war:/usr/local/tomcat/webapps/ROOT.war
    environment:
      VIRTUAL_HOST:  server3.example.com
      LETSENCRYPT_HOST:  server3.example.com



Más o menos sería así. Una vez tengas docker y docker-compose instalado, creas el fichero docker-compose.yml con ese contenido, ejecutas docker-compose up -d y compose descargará y creará los distintos dockers.


Evidentemente tendrás que aprender algo de docker si no sabes para modificar los ajustes que necesites (definir los volúmenes para no perder datos!!) y todo eso, pero si te vas a poner a mantener muchos servidores, sobretodo si son todos «parecidos» (como es tener varios tomcats) es mucho más sencillo.

Y si después tienes que migrar a otro hosting o lo que sea, con llevarte el docker-compose.yml y las carpetas de los volúmenes lo tienes andando en el nuevo hosting en 10 minutos.
Conozco Docker y para eso tendría que implementar una capa mas de nginx en cada servidor. estoy hablando de un servidor de nginx para servir como a 20 servidores tomcat(por poner un numero), y hablo de servidores, no de maquinas virtuales ni contenedores, no se trata de una aplicación pequeña. Al final el servidor de nginx que interesa seguiría teniendo los mismos problemas. Ademas habria que hacer cambios importantes en la aplicación. Como he dicho hay muchos problemas anexionados a ella. Accesos a bases de datos, envíos de mensajes por tcp a pelo... En mi opinión es mas sencillo usar bien nginx que adaptar "el monstruo" a docker.

EDIT: Me estoy leyendo mas detenidamente tu comentario. asi que si me he equivocado en algo que haya dicho no dudes en decirmelo. Aun asi veo que tu propuesta sigue basandose en una misma maquina con nginx y tomcat cuando nginx en ningun momento esta acompañada de tomcat, estan todas en servidores distintos.

EDIT2: Ok, no pierdo nada por probar, parece sencillo y debo reconocer que no soy muy dado al uso de docker, quizá sea un buen punto de partida. No obstante me gustaría solucionar igualmente el problema con nginx y el proxy_pass mal hecho que tengo.

Gracias! mañana me pongo a ello y en cuanto vea el resultado comento un poco por encima
Mira esta pagina, quizas te ayude a generar un archivo de configuracion para tu caso

https://www.digitalocean.com/community/tools/nginx
1985a escribió:Mira esta pagina, quizas te ayude a generar un archivo de configuracion para tu caso

https://www.digitalocean.com/community/tools/nginx

Pues la verdad es que es un paso. ha empezado a funcionar casi como deberia. ya me he metido en la aplicacion, dirige bien hacia ella pero me tengo que estudiar los archivos de configuracion que crea porque estoy teniendo problemas a la hora de enviar una cookie concreta

Edit, estoy mirando el comportamiento de la aplicación, comparando la servida desde la ip de forma directa y la servida con nginx, y me he dado cuenta de lo siguiente:

Aplicación servida por tomcat a través de la ip de forma directa:
    nada mas entrar crea un JSESSIONID(o similar)
    Si recargas la página, ese JSESSIONID se mantiene
    Te logueas y lo pasa como cookie al navegador

Aplicación servida por nginxt a través del dominio:
    nada mas entrar crea un JSESSIONID(o similar)
    Si recargas la página, ese JSESSIONID se renueva
    Te logueas y NO lo pasa como cookie al navegador, así que no te valida el primer modulo que carga y peta.
    en ocasiones, ese JSESSIONID, se renueva incluso pasando el login, por lo que sigues estando sin validar.


Es decir, es como si nginx cambiara la sesion en cada petición o no fuera capaz de enviarme la cookie
No se como tienes la configuración ahora que te funciona pero igual te falta el

    proxy_set_header                Cookie $http_cookie;


en la config del nginx.
miguej escribió:No se como tienes la configuración ahora que te funciona pero igual te falta el

    proxy_set_header                Cookie $http_cookie;


en la config del nginx.


Pues no termino de conseguirlo. tengo lo siguiente, y en gran medida es por la web de digital ocean que nos pasaron un poco mas arriba:

---------------
-----nginx.conf
---------------
user                 www-data;
pid                  /run/nginx.pid;
worker_processes     auto;
worker_rlimit_nofile 65535;

events {
    multi_accept       on;
    worker_connections 65535;
}

http {
    charset              utf-8;
    sendfile             on;
    tcp_nopush           on;
    tcp_nodelay          on;
    server_tokens        off;
    log_not_found        off;
    types_hash_max_size  2048;
    client_max_body_size 16M;

    # MIME
    include              mime.types;
    default_type         application/octet-stream;

    # Logging
    access_log           /var/log/nginx/access.log;
    error_log            /var/log/nginx/error.log warn;

    # Load configs
    include              /etc/nginx/conf.d/*.conf;
    include              /etc/nginx/sites-enabled/*;
}

---------------
-----General
---------------


# favicon.ico
location = /favicon.ico {
    log_not_found off;
    access_log    off;
}

# robots.txt
location = /robots.txt {
    log_not_found off;
    access_log    off;
}

# gzip
gzip            on;
gzip_vary       on;
gzip_proxied    any;
gzip_comp_level 6;
gzip_types      text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;

---------------
-----Proxy
---------------


proxy_http_version                 1.1;
proxy_cache_bypass                 $http_upgrade;

# Proxy headers
proxy_set_header Upgrade           $http_upgrade;
proxy_set_header Connection        "upgrade";
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_set_header X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Port  $server_port;

# Proxy timeouts
proxy_connect_timeout              60s;
proxy_send_timeout                 60s;
proxy_read_timeout                 60s;


---------------
-----webapp
---------------


server {
    listen      80;
    listen      [::]:80;
    server_name sub.dominio.etc;

    # logging
    access_log  /var/log/nginx/sub.dominio.etc.access.log;
    error_log   /var/log/nginx/sub.dominio.etc.error.log warn;

    # reverse proxy
    location / {
        proxy_pass http://xx.xx.xx.xx:yyyy/webapp/;
        include    nginxconfig.io/proxy.conf;
    }

    # additional config
    include nginxconfig.io/general.conf;
}


He probado añadiendo tu directiva pero no ha variado. Mirando la cookie del JSESSIONID en las dos paginas(IP y Dominio) resulta que parece que aun así si que las guarda de la misma forma. pero persiste el problema de renovarse en cada petición, es como si no mantuviera la sesión.
PD: en las siguientes imagenes me he tomado la molestia de tapar las direcciones reales pero represento un ejemplo real
https://drive.google.com/file/d/1a_d14g ... sp=sharing
https://drive.google.com/file/d/1DdLUi2 ... sp=sharing
Prueba añadiendo

proxy_cookie_path ~*^/.* /;


dentro del Location de /
por si acaso
@alohl669 ¿cómo lo llevas? parece un caso interesante
@NaOnaK Rendido desde el punto de vista configuración manual, en su día aprecié mucho el recurso de @1985a (realmente es muy útil) pero acabé recurriendo al uso de docker tal como sugirió @elchicosinhada. Hasta el día de hoy es la mejor solución, o al menos la que te presenta una solución mas despreocupada y potente a la larga.

No he vuelto a postear porque aun me encuentro "dockerizando" el 100% de la aplicación, pero debo decir que de entrada me ha hecho olvidarme por completo de los escalones con los que me estaba topando(gracias @elchicosinhada). Por ahora estoy usando una configuración mixta, ya tenia el servidor de nginx así que no iba a desperdiciarlo y me sigue interesando mucho tenerlo físicamente separado.

El tema de docker... para que negarlo, era el siguiente paso lógico si de verdad pretendía "modernizar la infraestructura" como argumenté en su momento peeero, lo que me temo(y en realidad espero con ansias) es que esto no terminará con nginx y docker, lo he estado moviendo un poco y puede que nos planteemos implementar los contenedores con kubernetes.
Si estás usando un nginx «normal» supongo que lo habrás configurado para que redirija a cada docker según la URL y a un puerto específico, no?

El nginx-proxy que te comentaba la ventaja que tiene es que lo gestiona el solo entero y no tienes que poner «abrir puertos» en el los dockers de los tomcats.

Si alguna vez te da la vena, puedes echarle un ojo aquí: https://hub.docker.com/r/jwilder/nginx-proxy
10 respuestas