Skip to content

T4. Servidores de aplicaciones

Introducción

En informática, se denomina servidor de aplicaciones a un servidor en una red de computadores que ejecuta ciertas aplicaciones.

Usualmente se trata de un dispositivo de software que proporciona servicios de aplicación a las computadoras cliente. Un servidor de aplicaciones generalmente gestiona la mayor parte (o la totalidad) de las funciones de lógica de negociación y de acceso a los datos de las aplicaciones. Los principales beneficios de la aplicación de la tecnología de servidores de aplicación son la centralización y la disminución de la complejidad en el desarrollo de aplicaciones.

En el modelo de 3 capas (cliente-servidor-datos), el servidor de aplicaciones desarrolla el modelo de negocio y el acceso a los datos.

En la actualidad tenemos 4 stacks tecnológicos dominantes:

  • JakartaEE: en especial el framework Spring, aunque otros como Quarkus también asoman.
  • PHP: en especial el framework Laravel.
  • Javascript: con el servidor Node.js como estándar de la industria, aunque muy pujantes los motores Bun y Deno.
  • Python: con muchos frameworks disponibles de diversa índole, aunque destacan django y flask.

Nos centraremos en las 3 primeras que son las tecnologías desarrolladas ampliamente en los módulos de Desarrollo Web en Entrono de Cliente y Desarrollo Web en Entorno de Servidor.

PROXY INVERSO

JakartaEE

Los componentes más básicos (en los que nos centraremos) son las JSP y los Servlets, ambos distribuidos a menudo empaquetados en archivos .war análogos a los .jar de apicaciones Java SE.

Tecnologías Java de servidor como JSP y JSF realizan funciones similares a las realizadas por PHP, donde se entremezcla código Java y etiquetas HTML. Éstas habitualmente realizaban la implementación de la vista en el modelo-vista-controlador.

Con el auge de Javascript y los frameworks (REACT, ANGULAR, VUE, ...), la capa vista ha sido desplazada casi en la totalidad a dicha tecnología base.

Opciones

Existen muchos muchos servidores de aplicaciones (Tomcat, TomEE, TomEE JAXRS, TomEE+, TomEE PluME, OpenEJB, Blowfish, WildFly, Jetty,...) y cada uno implementa algunas de las tecnologías estandarizadas por JakartaEE.

jakartaEE

Nosotros nos centraremos en Tomcat, aunque TomEE es muy similar al estar construido sobre la base Tomcat.

Paquetes war

Para entender la estructura básica de los archivos .war partiremos del war suministrado y lo desempaqeutaremos jar -xvf paqueteEmpaquetado.war.

Realizaremos modificación del nombre y volveremos a empaquetarlo con jar cf nuevoPaquete.war *.

War de muestra: war

Práctica

Deberemos:

  1. Crear una aplicación echo en JakartaEE.
  2. Levantar un servidor Tomcat que despliegue nuestra aplicación (ver yaml)
  3. Desplegarla en tomcat.tudominio.duckdns.org
  4. [OPCIONAL] Crear una aplicación en Spring utilizando Spring Boot (pendiente final de trimestre) y desplegarla (en jar).

Deberemos subir una copia de los resultados como un zip al directorior jakartaEE.

⚠️ En el README deberás comenzar indicando la URL donde ver el resultado.

🚧 No se corregirá ningún trabajo que no contenga TODOS los archivos necesarios para reproducir el resultado, así como el README.md con la explicación de que pasos deben realizarse para reproducirlo.

ℹ️ El archivo README.md no puede contener archivos fuente o de configuración, sólo extractos parciales si es necesario explicar algo sobre ellos que no pueda realizarse en un comentario dentro del archivo en cuestión. P.e. archivos json.

El yml de tomcat

services:
  tomcat:
    image: tomcat:10
# Para probar vía proxy socks (filtrado en el firewall de AWS)
    ports:
      - 8888:8080
    volumes:
# Dir de despliegue de aplicaciones.
#   Poner aquí los war y los auto-despliega (descomprime)
      - ./aplicaciones:/usr/local/tomcat/webapps

# CORS global (para una app concreta insertar el filtro en el web.xml del direorio META-INF)
      # -/web.xml:/usr/local/tomcat/conf/web.xml

# La red "red_interna" es para conectar con una bbdd sin exponerla a la red "global" del proxy inverso "red_de_proxy"
    networks:
      - red_de_proxy
      - red_interna
    labels:
      caddy: tc.tu_subdominio.duckdns.org
      caddy.reverse_proxy: "{{upstreams 8080}}"

networks:
   red_de_proxy:
      external: 'true'
   red_interna:
      external: 'true'

WebUI

Tomcat dispone de una interfaz de administración gráfica que podemos activar mediante modificación del archivo tomcat-users.xml, habilitándole el rol manager-gui y asignándoselo a un usuario.

Una vez modificada podremos acceder al él en http://localhost:8888/manager/html.

Para su acceso remoto se requerirá modificar los permisos de acceso desde ip pública concreta (expresión regular).

Spring

Si hemos desarrollado una aplicación con Spring, podemos crear una imagen de forma sencilla:

#--- Fase 1 (build):
FROM maven:3.8.3-jdk-11-slim AS build

RUN mkdir /project
COPY . /project
WORKDIR /project

RUN mvn clean package
#--- Fase 2:
FROM adoptopenjdk/openjdk11:jre-11.0.15_10-alpine

RUN mkdir /app
RUN addgroup -g 10001 -S tecogroup
RUN adduser -S teco -u 10001

COPY --from=build /project/target/bmi-1.0.jar /app/bmi.jar
WORKDIR /app

RUN chown -R teco:tecogroup /app

CMD ["java", "-jar", "bmi.jar"]

Suponiendo que bmi.jar es nuestra aplicación Spring.

Fuente: Eric Cabrel

Laravel

  1. Ventajas de Sails
  2. Uso de Sails
  3. Paso a paso
  4. Crear un Dockerfile

ℹ️ Para los que como a mí no nos guste tener que instalar nada, podemos hacer uso de los siguientes alias:

  • PHP: alias php='docker run -u $(id -u):$(id -g) --rm -it -v $(pwd):/usr/src/myapp -w /usr/src/myapp php:8.2-cli php'
  • COMPOSER: alias composer='docker run -u $(id -u):$(id -g) --rm -it -v $(pwd):/app composer composer'

Tener en cuenta que podemos sustituir la versión de php según las siguientes características:

  • php-cli: para herramientas de cli
  • php-apache: lista para usar. Basada en Debian por lo que de gran tamaño.
  • php-fpm: para servir de intérprete a servidores (como ningx).
  • php-alpine: para versión compacta basada en Alpine.

También podemos levantar una app Laravel via la imágen de Bitnami.

🛈 Recuerda que si en el inicio falla la base de datos tendremos que “migrarla” con el comando php artisan migrate. Podemos hacerlo dentro del contenedor con docker exec -it {{CONTENEDOR}} php artisan migrate

Práctica

Crea la aplicación “hola mundo” de Laravel y sube los resultados al directorio laravel del zip y hazlo público en laravel.tudominio.duckdns.org

⚠️ En el README deberás comenzar indicando la URL donde ver el resultado.

🚧 No se corregirá ningún trabajo que no contenga TODOS los archivos necesarios para reproducir el resultado, así como el README.md con la explicación de que pasos deben realizarse para reproducirlo.

ℹ️ El archivo README.md no puede contener archivos fuente o de configuración, sólo extractos parciales si es necesario explicar algo sobre ellos que no pueda realizarse en un comentario dentro del archivo en cuestión. P.e. archivos json.

Referencias Laravel

Node.js

  1. Crear server.js
  2. Crear proyecto npm init
  3. Instalar dependencias npm install express. Si nuestra aplicación no se llamara server.js deberemos añadir el script start "start": "node app.js" al package.json (donde app.js es el nombre de la aplicación desarrollada).
  4. Crear Dockerfile. Con CMD nos permite sobreescribir el arranque y utilizar el contenedor para correr el script de test si lo hubiera.
  5. Crear compose.yml

ℹ️ Para un proyecto ocasional no merece la pena instalar node. Podemos correrlo en un contenedor con: alias node='docker run --rm -it -u $(id -u):$(id -g) -v $(pwd):/app -w /app node:latest'

Pudiendo correr luego node npm init y node npm install express para generar package.json e instalar el módulo express.

server.js:

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Dockerfile

# Utiliza una imagen base de Node.js mínima
FROM node:alpine

# Establece el directorio de trabajo en el directorio raíz de la imagen
WORKDIR /usr/src/app

# Copia el package.json y el archivo lock (si lo tienes)
COPY package*.json ./

# Instala las dependencias
RUN npm install

# Copia el resto de los archivos de la aplicación
COPY . .

# Exponer el puerto 3000
EXPOSE 3000

# Comando para iniciar la aplicación
CMD [ "npm", "start" ]
#ENTRYPOINT [ "node", "server.js" ]

Práctica

Reproduce lo anterior y muestra el resultado. Súbela a al directorio nodejs del zip y hazlo público en node.tudominio.duckdns.org

⚠️ En el README deberás comenzar indicando la URL donde ver el resultado.

🚧 No se corregirá ningún trabajo que no contenga TODOS los archivos necesarios para reproducir el resultado, así como el README.md con la explicación de que pasos deben realizarse para reproducirlo.

ℹ️ El archivo README.md no puede contener archivos fuente o de configuración, sólo extractos parciales si es necesario explicar algo sobre ellos que no pueda realizarse en un comentario dentro del archivo en cuestión. P.e. archivos json.

Flask

  1. Crear app.py.
  2. Crear Dockerfile.
  3. Crear compose.yml.

Tras cada cambio de la aplicación app.py deberemos reconstruir con docker compose build o más flexible montando un volumen.

app.py

# Importar librería
from flask import Flask

# Crear instancia a clase Flask
app = Flask(__name__)

# Asociar ruta a función
@app.route("/")
def hello_world():
 return "<p>Hello, World!</p>"

Dockerfile

# Establecemos la imagen base de Python
FROM python:bookworm

# Definimos el directorio de trabajo
WORKDIR /app

# Copiamos los archivos de requerimientos y la aplicación
COPY requirements.txt ./
COPY app.py ./

# Instalamos las dependencias
RUN pip install -r requirements.txt

# Exponemos el puerto 5000 para comunicarnos con la aplicación
EXPOSE 5000

# Definimos el comando de arranque
CMD ["python", "-m", "flask", "run"]

Donde requirements.txt contiene flask únicamente.

Práctica

Reproduce lo anterior y muestra el resultado. Súbela al directorio flask y ubícalo en la url flask.tudominio.duckdns.org

⚠️ En el README deberás comenzar indicando la URL donde ver el resultado.

🚧 No se corregirá ningún trabajo que no contenga TODOS los archivos necesarios para reproducir el resultado, así como el README.md con la explicación de que pasos deben realizarse para reproducirlo.

ℹ️ El archivo README.md no puede contener archivos fuente o de configuración, sólo extractos parciales si es necesario explicar algo sobre ellos que no pueda realizarse en un comentario dentro del archivo en cuestión. P.e. archivos json.