1. Contexto de la práctica
En esta práctica vas a desplegar una pequeña infraestructura de servicios utilizando Docker y Docker Compose.
El objetivo no es únicamente “levantar contenedores”, sino comprender cómo se organizan los servicios, cómo se comunican entre ellos, cómo se almacenan los datos, cómo se exponen puertos al exterior y cómo se puede documentar y justificar una instalación técnica.
Durante la práctica se trabajará con servicios similares a los vistos en clase:
- Servidor web HTTP con Apache.
- PHP para ejecutar código dinámico.
- MySQL como base de datos relacional.
- phpMyAdmin como herramienta de administración.
- MongoDB como base de datos NoSQL.
- Mongo Express como herramienta de administración.
- Redes Docker.
- Volúmenes persistentes.
- Variables de entorno.
- Logs y comprobaciones.
- Copias de seguridad básicas.
- Documentación técnica.
2. Objetivos de aprendizaje
Al finalizar la práctica, el alumno deberá ser capaz de:
- Explicar qué es Docker y para qué se utiliza.
- Diferenciar una imagen de un contenedor.
- Crear y ejecutar contenedores mediante Docker Compose.
- Configurar servicios conectados entre sí.
- Usar volúmenes para conservar datos.
- Usar redes Docker para aislar servicios.
- Configurar variables de entorno.
- Desplegar una aplicación PHP conectada a MySQL.
- Desplegar y consultar una base de datos MongoDB.
- Comprobar el estado de los servicios mediante comandos.
- Interpretar errores básicos mediante logs.
- Documentar una instalación técnica de forma clara.
- Defender oralmente el trabajo realizado.
3. Resultado final esperado
Al terminar la práctica deberás tener funcionando una infraestructura compuesta por varios contenedores:
| Servicio | Tecnología | Puerto externo sugerido | Función |
|---|---|---|---|
| Web PHP | Apache + PHP | 8080 | Servidor web principal |
| MySQL | MySQL | 3306 o solo interno | Base de datos relacional |
| phpMyAdmin | phpMyAdmin | 8081 | Administración de MySQL |
| MongoDB | MongoDB | 27017 o solo interno | Base de datos NoSQL |
| Mongo Express | Mongo Express | 8082 | Administración de MongoDB |
La aplicación PHP deberá mostrar una página web con información del alumno, conexión a MySQL y, opcionalmente, conexión a MongoDB.
4. Normas de entrega
El alumno deberá entregar:
- Carpeta completa del proyecto.
- Archivo
docker-compose.yml. - Archivo
.env. - Código fuente de la aplicación PHP.
- Capturas de pantalla.
- Documento de memoria en PDF.
- Comandos utilizados.
- Respuestas a las preguntas de reflexión.
- Evidencias de funcionamiento.
- Breve vídeo opcional de demostración.
5. Estructura recomendada del proyecto
Crea una carpeta llamada:
practica-docker-servicios-nombre-apellido
Dentro de ella deberás crear una estructura similar a esta:
practica-docker-servicios-nombre-apellido/
│
├── docker-compose.yml
├── .env
├── README.md
│
├── web/
│ ├── index.php
│ ├── conexion_mysql.php
│ ├── insertar.php
│ ├── listar.php
│ └── estilos.css
│
├── mysql/
│ └── init.sql
│
├── evidencias/
│ ├── captura_01_docker_version.png
│ ├── captura_02_contenedores.png
│ ├── captura_03_web_funcionando.png
│ ├── captura_04_phpmyadmin.png
│ ├── captura_05_mongo_express.png
│ └── captura_06_logs.png
│
└── memoria/
└── memoria_practica.pdf
6. Fase 1 — Preparación del entorno
6.1. Comprobar Docker
Ejecuta los siguientes comandos:
docker --version
docker compose version
Guarda una captura de pantalla donde se vea el resultado.
Preguntas de reflexión
Responde en tu memoria:
- ¿Qué diferencia hay entre Docker y Docker Compose?
- ¿Qué problema soluciona Docker cuando trabajamos con varios servicios?
- ¿Por qué puede ser útil usar Docker en un entorno educativo o de laboratorio?
6.2. Comprobar que Docker funciona
Ejecuta:
docker run hello-world
Evidencia obligatoria
Incluye una captura del resultado del comando.
Pregunta de reflexión
Explica con tus palabras qué ha ocurrido al ejecutar hello-world.
Debes mencionar:
- Si Docker ha usado una imagen local o la ha descargado.
- Qué es un contenedor.
- Por qué el contenedor termina después de ejecutarse.
7. Fase 2 — Primer diseño de la infraestructura
Antes de escribir el docker-compose.yml, realiza un pequeño esquema de la infraestructura.
Puedes hacerlo con una tabla, un dibujo o un diagrama sencillo.
Debe aparecer:
- Servicio web.
- Base de datos MySQL.
- phpMyAdmin.
- MongoDB.
- Mongo Express.
- Red interna.
- Volúmenes de datos.
- Puertos publicados.
Ejemplo conceptual:
Navegador
|
| puerto 8080
v
Apache + PHP
|
| red interna docker
v
MySQL
Navegador
|
| puerto 8081
v
phpMyAdmin
|
v
MySQL
Navegador
|
| puerto 8082
v
Mongo Express
|
v
MongoDB
Preguntas de reflexión
- ¿Por qué no es recomendable instalar todos los servicios dentro del mismo contenedor?
- ¿Qué ventajas tiene separar web, base de datos y herramientas de administración?
- ¿Qué servicios deberían ser accesibles desde el navegador y cuáles deberían quedar solo dentro de la red Docker?
8. Fase 3 — Creación del archivo .env
Crea un archivo llamado .env en la raíz del proyecto.
Ejemplo:
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=asir_db
MYSQL_USER=asir_user
MYSQL_PASSWORD=asir_password
MONGO_INITDB_ROOT_USERNAME=admin
MONGO_INITDB_ROOT_PASSWORD=adminpassword
MONGO_DATABASE=asir_mongo
WEB_PORT=8080
PHPMYADMIN_PORT=8081
MONGO_EXPRESS_PORT=8082
Importante
El archivo .env permite separar configuración del archivo docker-compose.yml.
No deberías escribir directamente las contraseñas dentro del docker-compose.yml si puedes evitarlo.
Preguntas de reflexión
- ¿Qué ventaja tiene usar variables de entorno?
- ¿Por qué puede ser peligroso subir un archivo
.envreal a un repositorio público? - ¿Qué información sensible contiene este archivo?
9. Fase 4 — Creación del docker-compose.yml
Crea el archivo docker-compose.yml.
Propuesta base:
services:
web:
image: php:8.2-apache
container_name: asir_web_php
ports:
- "${WEB_PORT}:80"
volumes:
- ./web:/var/www/html
depends_on:
- mysql
- mongodb
networks:
- red_asir
mysql:
image: mysql:8.0
container_name: asir_mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
- ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- red_asir
phpmyadmin:
image: phpmyadmin:latest
container_name: asir_phpmyadmin
restart: always
ports:
- "${PHPMYADMIN_PORT}:80"
environment:
PMA_HOST: mysql
PMA_PORT: 3306
depends_on:
- mysql
networks:
- red_asir
mongodb:
image: mongo:latest
container_name: asir_mongodb
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
volumes:
- mongo_data:/data/db
networks:
- red_asir
mongo-express:
image: mongo-express:latest
container_name: asir_mongo_express
restart: always
ports:
- "${MONGO_EXPRESS_PORT}:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_INITDB_ROOT_USERNAME}
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
ME_CONFIG_MONGODB_SERVER: mongodb
depends_on:
- mongodb
networks:
- red_asir
volumes:
mysql_data:
mongo_data:
networks:
red_asir:
driver: bridge
9.1. Preguntas sobre el docker-compose.yml
Responde en tu memoria:
- ¿Qué significa
services? - ¿Qué diferencia hay entre
imageycontainer_name? - ¿Para qué sirve
ports? - ¿Qué diferencia hay entre el puerto de la izquierda y el de la derecha en esta línea?
- "8080:80"
- ¿Para qué sirve
volumes? - ¿Qué pasaría con los datos de MySQL si no usásemos un volumen?
- ¿Qué hace
depends_on? - ¿Qué limitación tiene
depends_on? - ¿Para qué sirve definir una red propia llamada
red_asir? - ¿Por qué el servicio web puede conectarse a MySQL usando el nombre
mysql?
10. Fase 5 — Script inicial de MySQL
Dentro de la carpeta mysql, crea el archivo init.sql.
Ejemplo:
CREATE TABLE IF NOT EXISTS alumnos (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
apellido VARCHAR(100) NOT NULL,
curso VARCHAR(100) NOT NULL,
fecha_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO alumnos (nombre, apellido, curso)
VALUES
('Alumno', 'Ejemplo', 'ASIR'),
('Docker', 'Compose', 'Servicios');
Este script se ejecutará automáticamente cuando se cree por primera vez el contenedor de MySQL y el volumen esté vacío.
Preguntas de reflexión
- ¿Cuándo se ejecutan los scripts dentro de
/docker-entrypoint-initdb.d/? - Si modificas
init.sqldespués de haber creado el volumen, ¿se vuelve a ejecutar automáticamente? - ¿Qué tendrías que hacer para reiniciar completamente la base de datos desde cero?
- ¿Por qué hay que tener cuidado al borrar volúmenes?
11. Fase 6 — Crear la aplicación PHP
Dentro de la carpeta web, crea el archivo index.php.
Ejemplo:
<?php
$host = 'mysql';
$db = 'asir_db';
$user = 'asir_user';
$password = 'asir_password';
$conexionCorrecta = false;
$error = '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $password);
$conexionCorrecta = true;
} catch (PDOException $e) {
$error = $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Práctica Docker ASIR</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<main>
<h1>Práctica Docker, Docker Compose y Servicios</h1>
<section>
<h2>Datos del alumno</h2>
<p><strong>Nombre:</strong> Escribe aquí tu nombre</p>
<p><strong>Curso:</strong> ASIR</p>
<p><strong>Módulo:</strong> Implantación de aplicaciones / Servicios / Sistemas</p>
</section>
<section>
<h2>Estado de la conexión con MySQL</h2>
<?php if ($conexionCorrecta): ?>
<p class="ok">Conexión correcta con MySQL.</p>
<?php else: ?>
<p class="error">Error de conexión con MySQL.</p>
<pre><?php echo $error; ?></pre>
<?php endif; ?>
</section>
<section>
<h2>Listado de alumnos desde MySQL</h2>
<?php
if ($conexionCorrecta) {
$consulta = $pdo->query("SELECT * FROM alumnos");
echo "<table>";
echo "<tr><th>ID</th><th>Nombre</th><th>Apellido</th><th>Curso</th><th>Fecha</th></tr>";
while ($fila = $consulta->fetch(PDO::FETCH_ASSOC)) {
echo "<tr>";
echo "<td>" . htmlspecialchars($fila['id']) . "</td>";
echo "<td>" . htmlspecialchars($fila['nombre']) . "</td>";
echo "<td>" . htmlspecialchars($fila['apellido']) . "</td>";
echo "<td>" . htmlspecialchars($fila['curso']) . "</td>";
echo "<td>" . htmlspecialchars($fila['fecha_registro']) . "</td>";
echo "</tr>";
}
echo "</table>";
}
?>
</section>
</main>
</body>
</html>
11.1. Archivo CSS
Crea estilos.css:
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
main {
width: 80%;
margin: 40px auto;
background-color: white;
padding: 30px;
border-radius: 10px;
}
h1 {
color: #333;
}
section {
margin-bottom: 30px;
}
.ok {
color: green;
font-weight: bold;
}
.error {
color: red;
font-weight: bold;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #333;
color: white;
}
td, th {
padding: 10px;
border: 1px solid #ccc;
}
11.2. Problema intencionado
Con la imagen oficial php:8.2-apache, puede ocurrir que PHP no tenga activada la extensión necesaria para conectarse a MySQL mediante PDO.
Si al abrir la web aparece un error relacionado con MySQL o PDO, deberás investigarlo y solucionarlo.
Pista
Puede ser necesario crear un Dockerfile personalizado para el servicio web.
12. Fase 7 — Crear una imagen personalizada para PHP
Crea un archivo llamado Dockerfile dentro de la carpeta web.
FROM php:8.2-apache
RUN docker-php-ext-install pdo pdo_mysql mysqli
Ahora modifica el servicio web en el docker-compose.yml.
Antes:
web:
image: php:8.2-apache
Después:
web:
build: ./web
El servicio completo quedaría así:
web:
build: ./web
container_name: asir_web_php
ports:
- "${WEB_PORT}:80"
volumes:
- ./web:/var/www/html
depends_on:
- mysql
- mongodb
networks:
- red_asir
Preguntas de reflexión
- ¿Qué diferencia hay entre usar
imagey usarbuild? - ¿Para qué sirve un
Dockerfile? - ¿Qué hace esta línea?
RUN docker-php-ext-install pdo pdo_mysql mysqli
- ¿Por qué necesitamos instalar extensiones de PHP?
- ¿Qué ventaja tiene crear nuestra propia imagen personalizada?
13. Fase 8 — Levantar los servicios
Ejecuta:
docker compose up -d
Después comprueba:
docker compose ps
También puedes usar:
docker ps
Evidencias obligatorias
Incluye capturas donde se vea:
- Los contenedores levantados.
- Los puertos publicados.
- El estado de cada contenedor.
Preguntas de reflexión
- ¿Qué significa ejecutar Docker Compose con
-d? - ¿Qué diferencia hay entre
docker compose psydocker ps? - ¿Qué significa que un contenedor esté en estado
Up? - ¿Qué harías si un contenedor aparece como
Restarting?
14. Fase 9 — Comprobación de servicios en navegador
Accede desde el navegador a:
http://localhost:8080
Deberías ver la aplicación PHP.
Accede también a:
http://localhost:8081
Deberías ver phpMyAdmin.
Accede a:
http://localhost:8082
Deberías ver Mongo Express.
Evidencias obligatorias
Incluye capturas de:
- Página PHP funcionando.
- phpMyAdmin mostrando la base de datos.
- Tabla
alumnosdentro de MySQL. - Mongo Express funcionando.
Preguntas de reflexión
- ¿Por qué accedemos a la web usando
localhost:8080y nolocalhost:80? - ¿Qué usuario y contraseña has utilizado para entrar en phpMyAdmin?
- ¿Qué base de datos aparece creada automáticamente?
- ¿Qué diferencia hay entre administrar MySQL desde línea de comandos y hacerlo desde phpMyAdmin?
- ¿Qué riesgos tendría dejar phpMyAdmin abierto en un servidor real?
15. Fase 10 — Insertar datos desde PHP
Crea un archivo llamado insertar.php.
<?php
$host = 'mysql';
$db = 'asir_db';
$user = 'asir_user';
$password = 'asir_password';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $password);
$sql = "INSERT INTO alumnos (nombre, apellido, curso) VALUES (:nombre, :apellido, :curso)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':nombre' => 'NombreAlumno',
':apellido' => 'ApellidoAlumno',
':curso' => 'ASIR'
]);
echo "Registro insertado correctamente.";
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
Accede a:
http://localhost:8080/insertar.php
Después vuelve a:
http://localhost:8080/index.php
Comprueba que el nuevo registro aparece en la tabla.
Trabajo obligatorio
Modifica el archivo para que inserte tus propios datos.
Preguntas de reflexión
- ¿Qué ocurre cada vez que recargas
insertar.php? - ¿Por qué puede ser peligroso insertar datos directamente sin un formulario controlado?
- ¿Qué diferencia hay entre una consulta normal y una consulta preparada?
- ¿Qué problema de seguridad ayudan a evitar las consultas preparadas?
16. Fase 11 — Crear formulario de inserción
Crea una página formulario.php.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Formulario de alumnos</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<main>
<h1>Insertar alumno</h1>
<form action="guardar.php" method="post">
<label>Nombre:</label><br>
<input type="text" name="nombre" required><br><br>
<label>Apellido:</label><br>
<input type="text" name="apellido" required><br><br>
<label>Curso:</label><br>
<input type="text" name="curso" required><br><br>
<button type="submit">Guardar</button>
</form>
</main>
</body>
</html>
Crea ahora guardar.php.
<?php
$host = 'mysql';
$db = 'asir_db';
$user = 'asir_user';
$password = 'asir_password';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nombre = $_POST['nombre'] ?? '';
$apellido = $_POST['apellido'] ?? '';
$curso = $_POST['curso'] ?? '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $password);
$sql = "INSERT INTO alumnos (nombre, apellido, curso) VALUES (:nombre, :apellido, :curso)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':nombre' => $nombre,
':apellido' => $apellido,
':curso' => $curso
]);
echo "Alumno guardado correctamente.";
echo "<br><a href='index.php'>Volver al listado</a>";
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
}
Evidencia obligatoria
Incluye captura del formulario y del registro insertado.
Preguntas de reflexión
- ¿Qué método HTTP utiliza el formulario?
- ¿Qué diferencia hay entre
GETyPOST? - ¿Dónde llegan los datos enviados por el formulario?
- ¿Qué validaciones mínimas debería tener este formulario?
- ¿Por qué no deberíamos confiar nunca únicamente en la validación del navegador?
17. Fase 12 — Trabajo con MongoDB
MongoDB es una base de datos NoSQL. En lugar de trabajar con tablas y filas, trabaja con bases de datos, colecciones y documentos.
Accede a Mongo Express:
http://localhost:8082
Crea una base de datos llamada:
asir_mongo
Crea una colección llamada:
inventario
Inserta al menos tres documentos similares a estos:
{
"nombre": "Servidor web",
"tipo": "contenedor",
"servicio": "Apache PHP",
"puerto": 8080
}
{
"nombre": "Base de datos relacional",
"tipo": "contenedor",
"servicio": "MySQL",
"puerto": 3306
}
{
"nombre": "Base de datos NoSQL",
"tipo": "contenedor",
"servicio": "MongoDB",
"puerto": 27017
}
Evidencias obligatorias
Incluye capturas de:
- Base de datos creada.
- Colección creada.
- Documentos insertados.
Preguntas de reflexión
- ¿Qué diferencia hay entre una tabla de MySQL y una colección de MongoDB?
- ¿Qué diferencia hay entre una fila y un documento?
- ¿Qué ventajas puede tener MongoDB frente a MySQL?
- ¿Qué ventajas puede tener MySQL frente a MongoDB?
- ¿En qué tipo de proyecto usarías cada uno?
18. Fase 13 — Comandos de administración
Ejecuta y documenta los siguientes comandos.
Ver contenedores activos
docker ps
Ver todos los contenedores
docker ps -a
Ver imágenes
docker images
Ver redes
docker network ls
Ver volúmenes
docker volume ls
Ver logs del servicio web
docker compose logs web
Ver logs de MySQL
docker compose logs mysql
Entrar dentro del contenedor web
docker exec -it asir_web_php bash
Dentro del contenedor, ejecuta:
ls -la /var/www/html
Después sal:
exit
Evidencias obligatorias
Incluye capturas de al menos cinco comandos anteriores.
Preguntas de reflexión
- ¿Para qué sirve
docker ps? - ¿Qué información muestra
docker images? - ¿Qué diferencia hay entre una imagen y un contenedor?
- ¿Qué utilidad tienen los logs?
- ¿Para qué puede servir entrar dentro de un contenedor?
- ¿Por qué no deberíamos modificar manualmente demasiadas cosas dentro de un contenedor en producción?
19. Fase 14 — Persistencia de datos
Vas a comprobar que los datos sobreviven aunque los contenedores se detengan.
Ejecuta:
docker compose down
Después vuelve a levantar:
docker compose up -d
Comprueba:
- Que la web sigue funcionando.
- Que los datos de MySQL siguen estando.
- Que los documentos de MongoDB siguen estando.
Ahora ejecuta:
docker compose down -v
Vuelve a levantar:
docker compose up -d
Comprueba qué ha ocurrido con los datos.
Mucho cuidado
El parámetro -v elimina los volúmenes asociados al proyecto.
Preguntas de reflexión
- ¿Qué diferencia hay entre
docker compose downydocker compose down -v? - ¿Por qué se han conservado los datos en el primer caso?
- ¿Por qué se han perdido los datos en el segundo caso?
- ¿Qué papel tienen los volúmenes en Docker?
- ¿Por qué la persistencia es fundamental en bases de datos?
20. Fase 15 — Copia de seguridad de MySQL
Realiza una copia de seguridad de la base de datos MySQL.
Ejemplo:
docker exec asir_mysql mysqldump -u root -p asir_db > backup_asir_db.sql
El sistema pedirá la contraseña de root.
Después comprueba que se ha creado el archivo:
ls -lh backup_asir_db.sql
Evidencia obligatoria
Incluye captura del archivo generado.
Preguntas de reflexión
- ¿Qué es
mysqldump? - ¿Por qué es importante hacer copias de seguridad?
- ¿Dónde se ha guardado el archivo SQL?
- ¿Qué información contiene ese archivo?
- ¿Sería suficiente esta copia de seguridad en un entorno real? Justifica la respuesta.
21. Fase 16 — Restauración básica de MySQL
Para comprobar la restauración, puedes crear una nueva base de datos desde phpMyAdmin o desde consola e importar el archivo.
Ejemplo orientativo:
docker exec -i asir_mysql mysql -u root -p asir_db < backup_asir_db.sql
Preguntas de reflexión
- ¿Qué diferencia hay entre exportar e importar una base de datos?
- ¿Qué riesgos existen al restaurar una copia sobre una base de datos que ya contiene información?
- ¿Qué medidas tomarías antes de restaurar una copia en producción?
22. Fase 17 — Comprobación de red entre contenedores
Entra en el contenedor web:
docker exec -it asir_web_php bash
Instala herramientas de red si fuera necesario:
apt update
apt install -y iputils-ping
Prueba conectividad:
ping mysql
ping mongodb
Sal del contenedor:
exit
Preguntas de reflexión
- ¿Por qué podemos hacer
ping mysqly no necesitamos conocer la IP del contenedor? - ¿Qué papel hace el DNS interno de Docker?
- ¿Cambiaría la IP del contenedor si lo eliminamos y lo volvemos a crear?
- ¿Por qué es mejor usar nombres de servicio que direcciones IP?
23. Fase 18 — Seguridad básica
Analiza la configuración actual.
Responde:
- ¿Qué puertos están expuestos al equipo anfitrión?
- ¿Qué servicios son accesibles desde el navegador?
- ¿Tiene sentido exponer MySQL directamente al exterior?
- ¿Tiene sentido exponer MongoDB directamente al exterior?
- ¿Qué contraseñas son débiles?
- ¿Qué cambiarías si esto fuese un servidor real?
- ¿Qué riesgos tiene usar
rootpara administrar la base de datos? - ¿Qué riesgos tiene dejar herramientas como phpMyAdmin o Mongo Express accesibles públicamente?
Mejora obligatoria
Modifica el docker-compose.yml para que MySQL y MongoDB no publiquen puertos hacia el exterior.
Es decir, evita configuraciones como:
ports:
- "3306:3306"
o:
ports:
- "27017:27017"
Los servicios internos deben comunicarse por la red Docker, no necesariamente por puertos públicos.
Pregunta de reflexión
Si MySQL no tiene puerto publicado, ¿por qué phpMyAdmin puede seguir conectando con él?
24. Fase 19 — Personalización del proyecto
El alumno deberá personalizar la práctica.
Como mínimo deberá:
- Cambiar el nombre de los contenedores.
- Cambiar el nombre de la base de datos.
- Cambiar usuario y contraseña.
- Personalizar la página principal.
- Insertar datos propios.
- Añadir una nueva tabla en MySQL.
- Añadir una nueva colección en MongoDB.
- Documentar los cambios realizados.
Nueva tabla obligatoria
Crea una tabla llamada servicios.
Ejemplo:
CREATE TABLE IF NOT EXISTS servicios (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
imagen VARCHAR(100) NOT NULL,
puerto VARCHAR(20),
descripcion TEXT
);
Inserta al menos cinco servicios:
- Apache/PHP.
- MySQL.
- phpMyAdmin.
- MongoDB.
- Mongo Express.
Preguntas de reflexión
- ¿Qué campos has elegido para la tabla
servicios? - ¿Por qué has elegido esos campos?
- ¿Qué relación existe entre esta tabla y la infraestructura desplegada?
- ¿Cómo podrías mostrar esta tabla desde PHP?
25. Fase 20 — Ampliación opcional
Para subir nota, puedes realizar una o varias de estas mejoras.
Opción A — Añadir Nginx como proxy inverso
Añade un contenedor Nginx que actúe como punto de entrada.
Debe permitir acceder a la aplicación PHP desde otro puerto o ruta.
Opción B — Añadir Adminer
Añade Adminer como alternativa a phpMyAdmin.
Opción C — Añadir Redis
Añade Redis como servicio adicional y explica para qué se utiliza.
Opción D — Crear una aplicación PHP más completa
La aplicación deberá permitir:
- Insertar alumnos.
- Listar alumnos.
- Borrar alumnos.
- Editar alumnos.















![[Reto] - Dockerizar app JEE (Tomcat 10 + MySQL) 4b9bc3d2-8253-4715-adf6-d5b1eed959d3](https://laaventuradeaprender.com/wp-content/uploads/2026/02/4b9bc3d2-8253-4715-adf6-d5b1eed959d3-150x150.png)