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.

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:
- Crear una aplicación echo en JakartaEE.
- Levantar un servidor Tomcat que despliegue nuestra aplicación (ver yaml)
- Desplegarla en
tomcat.tudominio.duckdns.org - [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.mdcon la explicación de que pasos deben realizarse para reproducirlo.ℹ️ El archivo
README.mdno 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¶
ℹ️ 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 condocker 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.mdcon la explicación de que pasos deben realizarse para reproducirlo.ℹ️ El archivo
README.mdno 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¶
- Crear
server.js - Crear proyecto
npm init - Instalar dependencias
npm install express. Si nuestra aplicación no se llamaraserver.jsdeberemos añadir el script start"start": "node app.js"alpackage.json(dondeapp.jses el nombre de la aplicación desarrollada). - Crear
Dockerfile. ConCMDnos permite sobreescribir el arranque y utilizar el contenedor para correr el script de test si lo hubiera. - 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 initynode npm install expresspara generarpackage.jsone 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.mdcon la explicación de que pasos deben realizarse para reproducirlo.ℹ️ El archivo
README.mdno 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¶
- Crear
app.py. - Crear
Dockerfile. - 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.mdcon la explicación de que pasos deben realizarse para reproducirlo.ℹ️ El archivo
README.mdno 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.