Categoría: Sistemas

  • MongoDB — El Ministerio de las Promesas Imposibles

    MongoDB — El Ministerio de las Promesas Imposibles

    1. Presentación de la práctica

    Bienvenidos al Ministerio de las Promesas Imposibles, una institución ficticia donde los políticos registran sus promesas electorales y el presupuesto necesario para cumplirlas.

    El problema es que el Ministerio ha perdido el control de sus datos. Hay promesas que se repiten, presupuestos disparatados, proyectos que nadie recuerda haber aprobado y políticos que llevan años prometiendo lo mismo.

    El Gobierno ha decidido contratar a un grupo de administradores de sistemas para modernizar la base de datos.

    Vuestra misión será construir y gestionar esa base de datos utilizando MongoDB.

    A lo largo de la práctica aprenderéis a crear documentos, consultar información, modificar promesas incumplidas y eliminar registros obsoletos.

    Todo ello mediante la terminal y sin necesidad de desarrollar una aplicación web.

    Objetivos de aprendizaje

    Al finalizar, el alumno deberá ser capaz de:

    • Comprender qué es MongoDB y en qué se diferencia de MySQL.
    • Crear y administrar una base de datos NoSQL.
    • Comprender las colecciones y los documentos JSON/BSON.
    • Insertar uno o varios documentos.
    • Realizar consultas con filtros y operadores.
    • Modificar documentos existentes.
    • Eliminar registros.
    • Realizar consultas sencillas de ordenación y estadísticas.
    • Comprender el significado de CRUD.

    2. ¿Qué es MongoDB?

    MongoDB es una base de datos NoSQL orientada a documentos.

    En una base de datos tradicional como MySQL almacenamos información en tablas formadas por filas y columnas. En MongoDB trabajamos con colecciones de documentos que tienen una estructura similar a JSON.

    MySQLTablas y filas

    MongoDBColecciones y documentos

    MySQLMongoDB
    Base de datosBase de datos
    TablaColección
    FilaDocumento
    ColumnaCampo
    Clave primaria_id
    SQLAPI de consultas MongoDB

    Por ejemplo, una promesa política podría almacenarse así:

    
    {
      "politico": "Pepe Promesas",
      "partido": "Partido del Unicornio",
      "promesa": "WiFi gratis hasta en los ascensores",
      "presupuesto": 250000,
      "cumplida": false,
      "etiquetas": ["tecnologia", "internet"]
    }
    

    Observad que tenemos cadenas de texto, números, valores booleanos y arrays dentro de un mismo documento.

    MongoDB almacena los documentos internamente en BSON, una representación binaria que admite tipos adicionales a los de JSON.

    3. Preparación del laboratorio

    Utilizaremos Docker para no tener que instalar directamente el servidor MongoDB en cada máquina virtual.

    Los comandos funcionarán en Linux y también en Windows desde PowerShell, siempre que Docker esté instalado y en ejecución.

    Paso 1. Comprobar Docker

    Abrimos la terminal y ejecutamos:

    
    docker --version
    docker ps
    

    Si Docker está funcionando, podremos consultar la versión y los contenedores activos.

    Paso 2. Descargar MongoDB

    
    docker pull mongo:8.0
    

    Usaremos la imagen oficial de MongoDB 8.0, evitando que una actualización automática de latest cambie la versión durante la práctica.

    Paso 3. Crear nuestro contenedor

    
    docker run -d \
      --name ministerio-mongo \
      -v ministerio_datos:/data/db \
      mongo:8.0
    

    En este comando:

    • -d ejecuta el contenedor en segundo plano.
    • --name identifica nuestro contenedor.
    • -v crea un volumen persistente para almacenar la base de datos.
    • mongo:8.0 es la imagen que estamos utilizando.

    Para esta práctica accederemos a MongoDB directamente desde dentro del contenedor. No necesitamos publicar el puerto 27017 ni dejar la base de datos accesible desde la red.

    Importante: el servidor se utiliza sin autenticación únicamente dentro de este laboratorio aislado. No es una configuración adecuada para producción.

    Paso 4. Comprobar el funcionamiento

    
    docker ps
    

    Deberíamos observar un contenedor llamado ministerio-mongo en estado Up.

    Paso 5. Entrar en MongoDB

    
    docker exec -it ministerio-mongo mongosh
    

    Se abrirá la consola interactiva de MongoDB, llamada mongosh.

    A partir de ahora, los comandos que escribamos dentro de ella son instrucciones de MongoDB, no comandos Bash.

    Podemos comprobar la conexión:

    
    db.runCommand({ ping: 1 })
    

    El resultado debería incluir:

    
    { "ok": 1 }
    

    Referencia: Documentación de la imagen oficial de MongoDB. Docker Hub+1

    4. Crear nuestra primera base de datos

    El Ministerio necesita una base de datos donde registrar sus promesas.

    Escribimos dentro de mongosh:

    
    use ministerio
    

    Podemos comprobar qué base de datos estamos utilizando:

    
    db.getName()
    

    Resultado:

    ministerio

    Ahora creamos una colección llamada promesas.

    
    db.createCollection("promesas")
    

    Comprobamos las colecciones:

    
    show collections
    

    Y para visualizar las bases de datos que realmente tienen datos almacenados:

    
    show dbs
    

    Concepto importante: en MongoDB, seleccionar una base de datos con use no significa que ya esté creada físicamente. Se materializa cuando almacenamos información, como al crear nuestra primera colección.

    5. CREATE — Insertar documentos

    Primera operación CRUD: CREATE

    El Ministro de Transformación Digital, Pepe Promesas, ha anunciado su primera medida electoral:

    *«Todos los ciudadanos tendrán WiFi gratuito, incluso en los ascensores y en las cuevas». *

    Necesitamos registrar esta promesa en nuestra base de datos.

    Paso 1. Insertar un documento

    
    db.promesas.insertOne({
      politico: "Pepe Promesas",
      partido: "Partido del Unicornio",
      promesa: "WiFi gratis hasta en los ascensores",
      categoria: "Tecnologia",
      presupuesto: 250000,
      votos: 1200,
      cumplida: false
    })
    

    El resultado mostrará una confirmación similar a esta:

    {
      acknowledged: true,
      insertedId: ObjectId('...')
    }

    El campo insertedId contiene el identificador único asignado al documento.

    MongoDB genera automáticamente _id cuando no proporcionamos uno.

    Paso 2. Insertar varios documentos

    El Ministerio acaba de celebrar una reunión extraordinaria. Como era de esperar, han aparecido numerosas promesas nuevas.

    Vamos a introducirlas todas de golpe.

    
    db.promesas.insertMany([
      {
        politico: "Lola Subsidios",
        partido: "Union del Bocadillo",
        promesa: "Bocadillos gratis todos los viernes",
        categoria: "Alimentacion",
        presupuesto: 800000,
        votos: 4500,
        cumplida: false
      },
      {
        politico: "Paco Impuestos",
        partido: "Partido del Unicornio",
        promesa: "Eliminar todos los impuestos los lunes",
        categoria: "Economia",
        presupuesto: 5000000,
        votos: 9000,
        cumplida: false
      },
      {
        politico: "Rosa Aeropuertos",
        partido: "Frente de las Rotondas",
        promesa: "Construir un aeropuerto en cada pueblo",
        categoria: "Infraestructuras",
        presupuesto: 15000000,
        votos: 3200,
        cumplida: false
      },
      {
        politico: "Manolo Digital",
        partido: "Union del Bocadillo",
        promesa: "Sustituir funcionarios por robots",
        categoria: "Tecnologia",
        presupuesto: 3000000,
        votos: 11000,
        cumplida: true
      },
      {
        politico: "Carmen Futuro",
        partido: "Frente de las Rotondas",
        promesa: "Construir un carril bici hasta la Luna",
        categoria: "Infraestructuras",
        presupuesto: 50000000,
        votos: 1500,
        cumplida: false
      },
      {
        politico: "Toni Fiestas",
        partido: "Partido del Unicornio",
        promesa: "Declarar los lunes festivos",
        categoria: "Trabajo",
        presupuesto: 2000000,
        votos: 18000,
        cumplida: true
      },
      {
        politico: "Susana Pantallas",
        partido: "Union del Bocadillo",
        promesa: "Una pantalla gigante en cada rotonda",
        categoria: "Tecnologia",
        presupuesto: 650000,
        votos: 2700,
        cumplida: false
      }
    ])
    

    Ya tenemos ocho promesas electorales registradas, contando la primera.

    Paso 3. Contar los documentos

    
    db.promesas.countDocuments({})
    

    Resultado esperado:

    8

    Comprueba que lo has entendido

    ¿Qué diferencia existe entre insertOne() e insertMany()? ¿Qué ocurre si ejecutamos dos veces la misma inserción sin especificar un identificador?

    La segunda inserción generaría nuevos documentos con identificadores diferentes, creando registros duplicados en cuanto a contenido.

    6. READ — Consultar documentos

    Segunda operación CRUD: READ

    El Ministerio necesita consultar sus promesas. Los políticos empiezan a preguntar qué habían prometido durante la campaña.

    Paso 1. Mostrar todas las promesas

    
    db.promesas.find()
    

    El método find() devuelve los documentos de una colección.

    También podemos utilizar:

    
    db.promesas.find().toArray()
    

    Esto permite ver los resultados materializados como un array en la consola, algo cómodo para conjuntos pequeños.

    Paso 2. Buscar un político concreto

    Queremos saber qué prometió Paco Impuestos:

    
    db.promesas.find({
      politico: "Paco Impuestos"
    })
    

    El resultado será el documento correspondiente a Paco.

    Paso 3. Buscar promesas incumplidas

    
    db.promesas.find({
      cumplida: false
    })
    

    Deberían aparecer seis promesas.

    Ahora buscamos las cumplidas:

    
    db.promesas.find({
      cumplida: true
    })
    

    Deberían aparecer dos.

    Paso 4. Consultar por presupuestos

    El Tribunal de Cuentas ha solicitado todas las promesas cuyo presupuesto supera un millón de euros.

    Utilizaremos el operador $gt, que significa greater than (mayor que).

    
    db.promesas.find({
      presupuesto: { $gt: 1000000 }
    })
    

    Otros operadores importantes:

    OperadorSignificado
    $gtMayor que
    $gteMayor o igual que
    $ltMenor que
    $lteMenor o igual que
    $eqIgual que
    $neDistinto de
    $inIncluido en una lista

    Paso 5. Combinar condiciones

    Queremos conocer las promesas de tecnología que siguen incumplidas.

    
    db.promesas.find({
      categoria: "Tecnologia",
      cumplida: false
    })
    

    Cuando incluimos varias condiciones en un mismo filtro, MongoDB aplica un AND implícito.

    En este caso aparecerán las promesas de Pepe Promesas y Susana Pantallas.

    Paso 6. Utilizar OR

    El Ministerio quiere localizar las promesas que pertenezcan a las categorías de Economía o Trabajo.

    
    db.promesas.find({
      $or: [
        { categoria: "Economia" },
        { categoria: "Trabajo" }
      ]
    })
    

    Paso 7. Mostrar solamente algunos campos

    No siempre necesitamos visualizar todos los campos de cada documento.

    Queremos mostrar solamente el nombre del político, su promesa y el presupuesto.

    
    db.promesas.find(
      {},
      {
        _id: 0,
        politico: 1,
        promesa: 1,
        presupuesto: 1
      }
    )
    

    El 1 indica que queremos incluir un campo. El 0 indica que deseamos excluirlo.

    En este ejemplo también ocultamos _id, que se muestra por defecto.

    Paso 8. Ordenar los resultados

    Queremos descubrir cuáles son las promesas más caras.

    
    db.promesas.find(
      {},
      { _id: 0, politico: 1, presupuesto: 1 }
    ).sort({ presupuesto: -1 })
    

    En la ordenación:

    • 1 significa ascendente.
    • -1 significa descendente.

    Paso 9. Limitar resultados

    El Ministerio solamente quiere las tres promesas más caras.

    
    db.promesas.find(
      {},
      { _id: 0, politico: 1, presupuesto: 1 }
    ).sort({ presupuesto: -1 }).limit(3)
    

    El resultado esperado es:

    PolíticoPresupuesto
    Carmen Futuro50.000.000 €
    Rosa Aeropuertos15.000.000 €
    Paco Impuestos5.000.000 €

    7. UPDATE — Modificar documentos

    Tercera operación CRUD: UPDATE

    Han pasado varios meses y el Ministerio necesita actualizar la situación de sus promesas.

    Paso 1. Modificar un documento

    Pepe Promesas asegura que el WiFi ya funciona en todos los ascensores.

    Aunque nadie lo ha podido comprobar, el Ministerio decide marcar la promesa como cumplida.

    
    db.promesas.updateOne(
      { politico: "Pepe Promesas" },
      { $set: { cumplida: true } }
    )
    

    El operador $set permite cambiar uno o varios campos.

    La respuesta habitual incluye:

    matchedCount: 1
    modifiedCount: 1

    matchedCount indica cuántos documentos coincidieron con el filtro, y modifiedCount cuántos fueron modificados.

    Para comprobarlo:

    
    db.promesas.findOne({
      politico: "Pepe Promesas"
    })
    

    Paso 2. Modificar varios campos

    El Gobierno descubre que el carril bici lunar tendrá un coste mayor del previsto.

    
    db.promesas.updateOne(
      { politico: "Carmen Futuro" },
      {
        $set: {
          presupuesto: 75000000,
          cumplida: false
        }
      }
    )
    

    Paso 3. Incrementar un valor numérico

    Durante una manifestación política, Paco Impuestos ha ganado 500 votos adicionales.

    En lugar de consultar los votos actuales y escribir el nuevo total, utilizamos $inc.

    
    db.promesas.updateOne(
      { politico: "Paco Impuestos" },
      { $inc: { votos: 500 } }
    )
    

    Si tenía 9.000 votos, ahora tendrá 9.500.

    También podemos restar:

    
    db.promesas.updateOne(
      { politico: "Paco Impuestos" },
      { $inc: { votos: -200 } }
    )
    

    Ahora tendrá 9.300.

    Paso 4. Actualizar varios documentos

    El Ministerio decide incrementar en un 10 % el presupuesto de todas las promesas tecnológicas.

    
    db.promesas.updateMany(
      { categoria: "Tecnologia" },
      { $mul: { presupuesto: 1.10 } }
    )
    

    updateMany() modifica todos los documentos que cumplan la condición. $mul multiplica el valor numérico por el factor indicado.

    Paso 5. Incorporar un campo nuevo

    El Ministerio ha creado un departamento de excusas.

    Ahora cada promesa necesita un campo llamado excusa.

    
    db.promesas.updateOne(
      { politico: "Rosa Aeropuertos" },
      {
        $set: {
          excusa: "No encontramos suficientes pilotos"
        }
      }
    )
    

    Este campo no existía anteriormente en el documento. MongoDB permite añadirlo sin alterar los demás documentos de la colección.

    Este ejemplo ilustra una de las características de MongoDB: su modelo flexible de documentos.

    8. DELETE — Eliminar documentos

    Cuarta operación CRUD: DELETE

    Tras una auditoría interna, el Ministerio decide eliminar algunas promesas de su registro.

    Paso 1. Eliminar una promesa

    Se ha decidido cancelar el proyecto de las pantallas en las rotondas.

    
    db.promesas.deleteOne({
      politico: "Susana Pantallas"
    })
    

    El resultado esperado incluye:

    deletedCount: 1

    Comprobamos que ha desaparecido:

    
    db.promesas.find({
      politico: "Susana Pantallas"
    })
    

    No debería devolver ningún documento.

    Paso 2. Eliminar varios documentos

    El Ministerio ha decidido eliminar todas las promesas con presupuestos inferiores a 300.000 €.

    Primero consultaremos los documentos afectados:

    
    db.promesas.find({
      presupuesto: { $lt: 300000 }
    })
    

    En este punto de nuestra práctica, la única promesa que cumple esa condición es la de Pepe Promesas, cuyo presupuesto se incrementó hasta 275.000 €.

    Ahora sí, ejecutamos:

    
    db.promesas.deleteMany({
      presupuesto: { $lt: 300000 }
    })
    

    Advertencia: una operación deleteMany({}) sin filtros eliminaría todos los documentos de la colección. Debemos comprobar siempre la condición antes de ejecutarla.

    Paso 3. Comprobar el resultado

    
    db.promesas.countDocuments({})
    

    Si hemos seguido todos los pasos en orden, tendremos seis documentos.

    9. Resumen visual de CRUD

    10. Consultas adicionales — El escándalo de los presupuestos

    Hasta ahora hemos aprendido las cuatro operaciones fundamentales. Pero MongoDB también permite realizar operaciones de análisis.

    El Ministerio quiere conocer algunos datos estadísticos sobre sus promesas.

    ¿Cuánto dinero ha prometido el Gobierno?

    Utilizaremos una operación de agregación:

    
    db.promesas.aggregate([
      {
        $group: {
          _id: null,
          presupuestoTotal: { $sum: "$presupuesto" }
        }
      }
    ])
    

    $group agrupa documentos y $sum suma los valores del campo indicado.

    ¿Cuántas promesas tiene cada partido?

    
    db.promesas.aggregate([
      {
        $group: {
          _id: "$partido",
          totalPromesas: { $sum: 1 }
        }
      },
      {
        $sort: { totalPromesas: -1 }
      }
    ])
    

    Esto es una introducción a las aggregation pipelines. No necesitamos profundizar en ellas todavía, pero sí comprender que MongoDB puede hacer algo más que buscar documentos.

    11. Reto individual — La campaña electoral del alumno

    Misión: Operación Elecciones 404

    Actividad autónoma · 30–40 minutos · Sin soluciones

    El Ministerio ha decidido convocar nuevas elecciones. Cada alumno deberá crear su propio partido político ficticio y registrar sus promesas absurdas utilizando MongoDB.

    Primera parte: creación del partido

    El alumno debe crear una nueva colección llamada elecciones.

    En ella registrará al menos cinco promesas inventadas por él. Cada documento deberá contener:

    • Nombre del político.
    • Partido político.
    • Promesa electoral.
    • Categoría.
    • Presupuesto.
    • Número de votos.
    • Estado de cumplimiento.
    • Un campo adicional de su elección.

    Los nombres y promesas deben ser ficticios y utilizar el humor o la sátira.

    Segunda parte: operaciones obligatorias

    Lista de comprobación

    0 de 13Crear la colección elecciones.Insertar cinco promesas con insertMany().Mostrar todas las promesas.Buscar las promesas de un partido concreto.Encontrar las promesas cuyo presupuesto supere 1.000.000 €.Mostrar solamente los campos político, promesa y presupuesto.Ordenar las promesas de mayor a menor presupuesto.Mostrar únicamente las dos promesas más votadas.Modificar una promesa para marcarla como cumplida.Incrementar los votos de un político en 1.000.Añadir un campo llamado excusa a un documento.Eliminar una promesa utilizando deleteOne().Mostrar cuántos documentos quedan en la colección.

    Reiniciar comprobación

    Tercera parte: reto adicional

    Los alumnos que terminen antes podrán intentar tres operaciones más sin recibir los comandos directamente:

    1. Calcular el presupuesto total de todas las promesas.
    2. Averiguar qué partido tiene más promesas registradas.
    3. Mostrar únicamente las promesas incumplidas y ordenarlas por número de votos.

    12. Entrega del trabajo

    El alumno deberá preparar un archivo README.md que incluya una breve explicación de la práctica, los comandos utilizados y las capturas de las operaciones principales.

    No es necesario entregar una aplicación web. El objetivo es demostrar que sabe trabajar directamente con MongoDB desde la consola.

    Criterios de evaluación

    ApartadoPuntos
    Creación de BD y colecciones1
    Inserción de documentos2
    Consultas y filtros3
    Actualización de documentos2
    Eliminación de documentos1
    Documentación y explicaciones1
    Total10

    13. Cierre y limpieza del laboratorio

    Para salir de la consola de MongoDB:

    
    exit
    

    Ya en la terminal Linux o PowerShell, podemos detener el contenedor:

    
    docker stop ministerio-mongo
    

    Para volver a utilizarlo otro día:

    
    docker start ministerio-mongo
    docker exec -it ministerio-mongo mongosh
    

    Gracias al volumen ministerio_datos, nuestra información seguirá almacenada aunque detengamos el contenedor.

    Si queremos eliminar el contenedor al terminar:

    
    docker rm -f ministerio-mongo
    

    El volumen seguirá existiendo. Para eliminar también todos los datos, de forma irreversible:

    
    docker volume rm ministerio_datos
    

    14. Preguntas finales para reflexionar

    Antes de entregar, cada alumno deberá contestar brevemente:

    1. ¿Qué diferencia existe entre una tabla de MySQL y una colección de MongoDB?
    2. ¿Para qué sirve el campo _id?
    3. ¿Qué diferencia hay entre find() y findOne()?
    4. ¿Cuándo utilizarías updateOne() en lugar de updateMany()?
    5. ¿Qué ocurriría si ejecutases deleteMany({})?
    6. ¿Por qué puede resultar útil que dos documentos de una colección tengan campos diferentes?
    7. ¿Para qué nos sirve utilizar un volumen Docker en esta práctica?
  • Operación MONCLOA.GIT — El Ministerio de las Promesas Imposibles

    Operación MONCLOA.GIT — El Ministerio de las Promesas Imposibles

    Introducción: bienvenidos al ministerio

    El Gobierno de la República de Gitlandia acaba de crear el Ministerio de las Promesas Imposibles, una institución encargada de desarrollar un programa electoral que cambiará constantemente.

    El problema es que cada vez que un político cambia de opinión, los funcionarios sobrescriben los documentos anteriores. Nadie sabe qué prometieron ayer, quién modificó una propuesta o cuándo desapareció una medida electoral.

    El ministro de Tecnología ha decidido contratar a un administrador de sistemas para solucionar el problema: tú.

    Tu misión será poner en funcionamiento un sistema de control de versiones que permita conservar todas las promesas electorales, identificar sus modificaciones, recuperar propuestas anteriores y publicar el programa en Internet.

    A lo largo de la práctica se producirán varios incidentes: promesas incumplidas, presupuestos disparatados, borrados accidentales y hasta un intento de manipulación del programa electoral.

    Toda la actividad es ficticia. La sátira se dirige a situaciones políticas generales, sin necesidad de utilizar partidos o políticos reales.

    Objetivos de aprendizaje

    Al terminar, cada alumno deberá ser capaz de:

    1. Instalar y configurar Git desde la terminal.
    2. Crear e inicializar un repositorio local.
    3. Comprender el directorio de trabajo, el área de preparación (staging) y los commits.
    4. Añadir, modificar, eliminar y recuperar archivos.
    5. Consultar el historial y comparar versiones.
    6. Utilizar .gitignore para excluir determinados archivos.
    7. Crear y utilizar ramas individualmente.
    8. Trabajar con un repositorio remoto en GitHub.
    9. Subir, descargar y sincronizar cambios.
    10. Clonar un repositorio y continuar trabajando sobre él.

    En esta práctica cada estudiante trabaja individualmente. Las colaboraciones, los conflictos entre usuarios y las pull requests quedarán para la segunda práctica.

    Antes de comenzar: comprender Git

    Git es un sistema de control de versiones distribuido. Permite registrar el estado de los archivos de un proyecto en diferentes momentos y recuperar su historial.

    Git no es lo mismo que GitHub: Git funciona localmente y no necesita Internet. GitHub es un servicio que puede alojar repositorios remotos y facilitar el intercambio de proyectos.

    Esquema conceptual simplificado: primero se modifican archivos, después se seleccionan los cambios y finalmente se registran mediante un commit.

    Fase 1. Preparación del entorno

    Paso 1. Comprobar si Git está instalado

    Abre una terminal de tu máquina virtual Ubuntu y ejecuta:

    git --version

    Si está instalado, aparecerá algo parecido a:

    git version 2.x.x

    Si aparece command not found, instala Git:

    sudo apt update
    sudo apt install git -y

    Después, verifica nuevamente su versión.

    Pregunta para el alumno: ¿Por qué crees que Git puede funcionar sin conexión a Internet?

    Paso 2. Configurar nuestra identidad

    Cada vez que realicemos un commit, Git registrará la identidad del autor.

    Configura tu nombre y correo electrónico:

    git config --global user.name "Tu Nombre"
    git config --global user.email "tu-correo@example.com"

    Utiliza un correo propio o el correo privado que GitHub te proporcione para los commits.

    Consulta tu configuración:

    git config --global --list

    Y configura la rama inicial predeterminada:

    git config --global init.defaultBranch main

    Importante: esta configuración es global para tu usuario de Linux. No es necesario repetirla para cada proyecto.

    Paso 3. Crear nuestro espacio de trabajo

    Crearemos una carpeta donde almacenar nuestros proyectos.

    mkdir -p ~/proyectos-git
    cd ~/proyectos-git
    
    mkdir ministerio-promesas
    cd ministerio-promesas

    Comprueba dónde estás:

    pwd

    Visualiza el contenido:

    ls -la

    El directorio debería estar vacío.

    Paso 4. Inicializar un repositorio

    Ejecuta:

    git init -b main

    Acabamos de convertir una carpeta normal en un repositorio Git.

    Compruébalo:

    ls -la

    Verás una carpeta oculta llamada .git.

    Esta carpeta contiene la información necesaria para que Git pueda gestionar el repositorio: objetos, referencias, configuración e historial.

    No es necesario modificar manualmente su contenido.

    Ahora ejecuta:

    git status

    Debería aparecer un mensaje parecido a:

    On branch main
    
    No commits yet
    
    nothing to commit

    Primer reto: ¿Qué sucedería si eliminásemos la carpeta .git? ¿Desaparecerían los archivos de nuestro proyecto? ¿Y el historial?

    Fase 2. Primer programa electoral

    Paso 5. Crear el documento oficial

    El presidente de Gitlandia ha anunciado el primer programa electoral:

    «Prometemos que nadie volverá a quedarse sin batería en el móvil, que todos los lunes serán festivos y que los exámenes se resolverán por votación popular».

    Vamos a escribir estas propuestas en un archivo Markdown.

    Ejecuta:

    nano programa.md

    Introduce el siguiente contenido:

    # Programa Electoral de Gitlandia
    
    ## Elecciones Generales
    
    Presidente: Don Commit Permanente
    
    ## Promesas oficiales
    
    1. WiFi gratis en todo el país.
    2. Los lunes serán festivos.
    3. Cada ciudadano tendrá un robot asistente.
    4. Los exámenes serán voluntarios.
    5. Las impresoras funcionarán siempre.
    
    ## Lema
    
    "Prometemos hasta que se llene el disco duro".

    Guarda con Ctrl + O, pulsa Intro y cierra con Ctrl + X.

    Comprueba el contenido:

    cat programa.md

    Paso 6. Consultar el estado del repositorio

    Ejecuta:

    git status

    Aparecerá el archivo programa.md como archivo no seguido (untracked).

    Esto significa que Git todavía no está controlando sus versiones.

    Ahora ejecuta:

    git add programa.md
    git status

    El archivo aparecerá preparado para incluirse en un commit.

    Acabas de añadirlo al área de preparación, también llamada staging area.

    Paso 7. Registrar el primer commit

    Un commit es un registro de una versión del proyecto, acompañado de información sobre su autor, fecha y mensaje.

    Ejecuta:

    git commit -m "Primer programa electoral de Gitlandia"

    Consulta el historial:

    git log

    Verás una entrada con un identificador, autor, fecha y mensaje.

    También puedes obtener una versión resumida:

    git log --oneline

    Comprobación: el profesor debe poder identificar el primer commit y comprobar que el repositorio está limpio mediante git status.

    Fase 3. Primer escándalo: las promesas cambian

    Paso 8. Modificar una promesa

    El ministro de Economía acaba de descubrir que los lunes festivos son caros.

    El Gobierno ha decidido modificar la segunda promesa:

    Antes: Los lunes serán festivos.

    Después: Los lunes serán festivos únicamente si no hay trabajo pendiente.

    Edita el archivo:

    nano programa.md

    Modifica esa línea, guarda el archivo y ejecuta:

    git diff

    Git mostrará los cambios respecto de la versión preparada o registrada anteriormente.

    Normalmente, las líneas eliminadas aparecen con - y las nuevas con +.

    Ejemplo de comparación

    – 2. Los lunes serán festivos.

    + 2. Los lunes serán festivos únicamente si no hay trabajo pendiente.

    Ahora registra la modificación:

    git add programa.md
    
    git commit -m "Economía limita los lunes festivos"

    Comprueba:

    git log --oneline

    Ya deberías tener dos commits.

    Paso 9. Segunda modificación: llega la oposición

    La oposición acusa al Gobierno de hacer promesas imposibles. El presidente responde anunciando nuevas medidas.

    Añade al final de programa.md:

    ## Nuevas medidas
    
    6. Un unicornio eléctrico para cada familia.
    7. Internet gratuito incluso dentro de los túneles.
    8. Los atascos serán declarados ilegales.
    9. Se eliminarán los errores 404 por decreto.

    Antes de confirmar, ejecuta:

    git status
    git diff

    Añade el archivo al staging:

    git add programa.md

    Ahora observa qué se va a registrar:

    git diff --staged

    Finalmente:

    git commit -m "Ampliación del programa con medidas revolucionarias"

    Reto: interpreta lo que estás viendo

    Responde en tu documentación: ¿qué diferencia existe entre git diff y git diff --staged? ¿Por qué puede interesarnos seleccionar los cambios antes de confirmarlos?

    Fase 4. El presupuesto secreto

    Paso 10. Crear varios documentos

    El Ministerio comienza a tener departamentos.

    Crea la estructura:

    mkdir -p economia comunicacion secretos

    Crea un archivo de presupuesto:

    cat > economia/presupuesto.txt <<'EOF'
    PRESUPUESTO GENERAL DE GITLANDIA
    
    WiFi nacional: 500 millones
    Unicornios eléctricos: 800 millones
    Robot por ciudadano: 2000 millones
    Publicidad institucional: 9000 millones
    
    Total: pendiente de calcular
    EOF

    Ahora un comunicado oficial:

    cat > comunicacion/nota.txt <<'EOF'
    COMUNICADO DEL GOBIERNO
    
    El presupuesto está perfectamente controlado.
    Las calculadoras de la oposición están manipuladas.
    EOF

    Y un documento que no debería publicarse:

    echo "Clave ficticia de demostración: VOTO123" > secretos/claves.txt

    Paso 11. Evitar archivos innecesarios con .gitignore

    Los archivos excluidos por .gitignore no se añadirán automáticamente al control de versiones cuando todavía no están siendo seguidos.

    Crea:

    nano .gitignore

    Introduce:

    # Documentos internos ficticios
    secretos/
    
    # Archivos temporales
    *.tmp
    
    # Copias de seguridad
    *.bak
    
    # Configuraciones locales
    .env

    Comprueba:

    git status

    El archivo dentro de secretos/ no aparecerá como pendiente de seguimiento.

    Puedes verificar la regla:

    git check-ignore -v secretos/claves.txt

    Ahora registra los documentos públicos y las reglas de exclusión:

    git add .gitignore economia/ comunicacion/
    
    git status
    
    git commit -m "Creación de departamentos y presupuesto oficial"

    Advertencia: .gitignore no convierte una contraseña en segura. No debe utilizarse Git para publicar secretos. Si alguna credencial real se ha incluido en un commit, ignorarla posteriormente no elimina el secreto del historial.

    Fase 5. Crisis institucional: recuperar los errores

    Esta fase es especialmente importante: aprenderemos a solucionar problemas sin destruir nuestro trabajo.

    Paso 12. Recuperar un archivo modificado

    Un funcionario decide cambiar el presupuesto utilizando un procedimiento bastante cuestionable:

    echo "TODO EL PRESUPUESTO PARA HELADOS" > economia/presupuesto.txt

    Comprueba el resultado:

    cat economia/presupuesto.txt
    git diff

    El ministro entra en pánico.

    Necesitamos recuperar la última versión confirmada del archivo.

    Ejecuta:

    git restore economia/presupuesto.txt

    Vuelve a comprobar:

    cat economia/presupuesto.txt

    El presupuesto original debería haber regresado.

    git restore ha descartado la modificación no registrada. Si hubiésemos querido conservarla, primero tendríamos que haberla guardado en otro lugar.

    Paso 13. Recuperar un archivo eliminado

    Un nuevo ministro decide que la mejor manera de evitar las críticas es eliminar los comunicados.

    Ejecuta:

    rm comunicacion/nota.txt

    Comprueba:

    git status

    El archivo aparecerá como eliminado.

    Recupéralo:

    git restore comunicacion/nota.txt

    Comprueba que existe nuevamente:

    ls comunicacion/
    cat comunicacion/nota.txt

    Paso 14. Quitar un cambio del staging

    En esta ocasión, el funcionario ha preparado accidentalmente un cambio.

    echo "Impuesto por respirar aire premium" >> economia/presupuesto.txt
    
    git add economia/presupuesto.txt
    git status

    El archivo ya está en staging.

    Para retirarlo sin perder la modificación:

    git restore --staged economia/presupuesto.txt

    Comprueba:

    git status
    cat economia/presupuesto.txt

    La modificación continúa en el archivo, pero ya no está preparada para un commit.

    Finalmente, descarta el experimento:

    git restore economia/presupuesto.txt

    Diferencia esencial

    ComandoResultado
    git restore archivoDescarta los cambios locales no preparados en ese archivo
    git restore --staged archivoLo retira del staging, conservando el contenido
    git add archivoPrepara cambios para el próximo commit
    git commitRegistra un nuevo punto del historial

    Fase 6. La hemeroteca: Git nunca olvida

    Paso 15. Investigar el historial de promesas

    Un periodista solicita el programa electoral original para comprobar qué decía el Gobierno antes de modificar sus promesas.

    Ejecuta:

    git log --oneline

    El resultado será parecido a este (los identificadores serán diferentes):

    e3a45b1 Creación de departamentos y presupuesto oficial
    b729c81 Ampliación del programa con medidas revolucionarias
    a16d729 Economía limita los lunes festivos
    c871a93 Primer programa electoral de Gitlandia

    Cada línea representa un commit.

    Consulta los detalles del último:

    git show HEAD

    HEAD indica el commit actual de nuestra rama.

    Consulta la versión anterior:

    git show HEAD~1

    También podemos investigar los cambios de un archivo:

    git log --oneline -- programa.md

    Y comprobar cómo era en el primer commit. En nuestro caso, hemos realizado cuatro commits, por lo que:

    git show HEAD~3:programa.md

    Atención: HEAD~3 significa retroceder tres commits desde el actual, no necesariamente acceder siempre al primero. En esta práctica funciona si se ha seguido exactamente la secuencia.

    Paso 16. Comparar dos momentos de la historia

    Consulta el historial para localizar los identificadores de dos commits.

    git log --oneline

    Después ejecuta, sustituyendo los identificadores:

    git diff ID_ANTIGUO ID_NUEVO -- programa.md

    Por ejemplo, si los identificadores fueran c871a93 y b729c81:

    git diff c871a93 b729c81 -- programa.md

    Con este procedimiento puedes demostrar que el Gobierno cambió sus promesas.

    Investigación periodística

    Elabora un pequeño informe respondiendo: ¿cuál fue la promesa modificada? ¿En qué commit ocurrió? ¿Qué mensaje acompañaba al cambio? ¿Cómo puedes recuperar el contenido de la versión original sin modificar el proyecto actual?

    Fase 7. El Gobierno comete un error histórico

    Ahora vamos a ver otra de las funciones más útiles de Git: revertir un commit completo.

    Paso 17. Registrar una medida equivocada

    El presidente firma accidentalmente un decreto que establece:

    «Todos los ciudadanos deberán pagar una cuota de 100 euros por utilizar el teclado de su ordenador».

    Añade esta línea al programa electoral:

    echo "10. Impuesto de 100 euros por utilizar el teclado." >> programa.md

    Registra el cambio:

    git add programa.md
    git commit -m "Aprobado nuevo impuesto sobre los teclados"

    La medida provoca una crisis nacional. El Gobierno quiere anularla, pero conservar la evidencia histórica de que fue aprobada.

    Ejecuta:

    git revert --no-edit HEAD

    Observa el historial:

    git log --oneline

    Deberían aparecer tanto el commit que introdujo el impuesto como otro posterior que lo revierte.

    ¿Por qué es importante? Porque Git no ha borrado el commit anterior. Ha registrado una nueva operación que anula su efecto.

    Comprueba que la medida ya no aparece:

    cat programa.md

    Así queda la historia del impuesto

    No confundas git revert con git restore. El primero crea un commit que deshace otro commit; el segundo permite recuperar archivos o quitar cambios del staging sin registrar necesariamente un nuevo commit. git Documentation+1

    Fase 8. Crear una rama: el programa electoral alternativo

    Aunque todavía no vamos a trabajar en equipo, es importante comprender las ramas. Un desarrollador individual puede utilizarlas para experimentar sin modificar su versión principal.

    Paso 18. Crear el programa de la oposición

    El partido de la oposición, llamado Partido del Código Libre (PCL), propone sustituir algunas medidas por otras todavía más absurdas.

    Desde main, crea una rama:

    git switch -c oposicion

    Comprueba:

    git branch

    Debería aparecer:

      main
    * oposicion

    El asterisco identifica la rama actual.

    Ahora crea un archivo nuevo:

    cat > oposicion.md <<'EOF'
    # Programa de la oposición
    
    Partido del Código Libre
    
    1. Prohibir las reuniones que pudieron ser un correo.
    2. Obligar a que todos los políticos usen Linux.
    3. Convertir el Congreso en un servidor de Minecraft.
    4. Que los ministros aprueben un examen de informática.
    5. Sustituir los discursos por mensajes de 140 caracteres.
    EOF

    Registra el documento:

    git add oposicion.md
    
    git commit -m "Programa alternativo de la oposición"

    Comprueba:

    ls
    git log --oneline

    Paso 19. Volver al Gobierno

    Regresa a la rama principal:

    git switch main

    Lista los archivos:

    ls

    Observa que oposicion.md ya no aparece.

    Esto ocurre porque el archivo se creó y se registró en otra rama.

    Vuelve a la oposición:

    git switch oposicion

    El archivo vuelve a aparecer.

    Paso 20. Fusionar la oposición con el Gobierno

    El presidente anuncia un pacto histórico: incorporará las propuestas de la oposición al repositorio oficial.

    Regresa a la rama principal:

    git switch main

    Fusiona la rama de la oposición:

    git merge oposicion

    Comprueba:

    ls
    git log --oneline --graph --all --decorate

    El archivo oposicion.md debe aparecer en la rama principal.

    En esta situación lo normal es que Git pueda realizar una fusión de tipo fast-forward, porque main no tenía nuevos commits desde que se creó la rama.

    No siempre aparece un commit específico de fusión. Es importante que los alumnos entiendan este comportamiento.

    Puedes eliminar la rama individual una vez integrada:

    git branch -d oposicion

    El contenido fusionado no desaparece.

    Fase 9. Llega la revolución: publicar en GitHub

    Hasta este momento hemos trabajado completamente en local. El repositorio existe únicamente dentro de nuestra máquina virtual.

    Ahora vamos a publicarlo en un servicio remoto.

    Paso 21. Crear una cuenta en GitHub

    Entra en GitHub y crea una cuenta si no tienes una.

    Después, crea un nuevo repositorio desde el botón de creación de repositorios.

    Configúralo con estos datos:

    CampoValor
    Repository nameministerio-promesas
    DescriptionPrograma electoral ficticio de Gitlandia
    VisibilidadPúblico, o privado si así lo indica el profesor
    Add READMEDesactivado
    Add .gitignoreDesactivado
    Choose a licenseNinguna por ahora

    Es importante no inicializar el repositorio remoto con archivos, porque ya tenemos un proyecto local con su propio historial. GitHub Docs

    Paso 22. Vincular Git local con GitHub

    Copia la dirección HTTPS del repositorio que acabas de crear.

    Será similar a:

    https://github.com/TU_USUARIO/ministerio-promesas.git

    Vuelve a la terminal, dentro de ministerio-promesas.

    Ejecuta:

    git remote add origin https://github.com/TU_USUARIO/ministerio-promesas.git

    Sustituye TU_USUARIO por tu nombre de usuario de GitHub.

    Consulta los remotos:

    git remote -v

    Verás una dirección de envío (push) y otra de descarga (fetch).

    origin es simplemente el nombre que hemos asignado al repositorio remoto. No es una palabra reservada que tenga que utilizarse obligatoriamente.

    Paso 23. Publicar el primer repositorio

    Ejecuta:

    git push -u origin main

    La opción -u establece la relación de seguimiento entre nuestra rama local main y la rama remota correspondiente.

    A partir de ese momento, los siguientes envíos podrán hacerse simplemente mediante:

    git push

    Importante: autenticación

    GitHub no admite utilizar la contraseña normal de tu cuenta como contraseña de Git para operaciones HTTPS. Tendrás que usar un método de autenticación compatible, como Git Credential Manager, GitHub CLI o un token de acceso personal con los permisos adecuados.

    Una opción especialmente cómoda para un entorno de prácticas es utilizar GitHub CLI:

    gh auth login

    Este comando requiere tener gh instalado. En el asistente selecciona GitHub.com, HTTPS y sigue las instrucciones de autenticación.

    Nunca escribas tokens ni contraseñas reales dentro de los archivos de la práctica.

    Paso 24. Comprobar la publicación

    Vuelve al navegador, abre tu repositorio de GitHub y actualiza la página.

    Deberían aparecer programa.md, oposicion.md, los directorios públicos, .gitignore y el historial.

    Comprueba especialmente que secretos/claves.txt no se ha publicado.

    Paso 25. Crear un README para los visitantes

    Un repositorio debería explicar qué contiene y cómo utilizarlo.

    Crea:

    nano README.md

    Añade:

    # Ministerio de las Promesas Imposibles
    
    Bienvenido al repositorio oficial del Gobierno
    de Gitlandia.
    
    ## Objetivo
    
    Aprender Git mediante un programa electoral
    ficticio que cambia constantemente.
    
    ## Archivos
    
    - programa.md: Programa electoral oficial.
    - oposicion.md: Propuestas de la oposición.
    - economia/: Presupuestos.
    - comunicacion/: Comunicados oficiales.
    
    ## Tecnología
    
    - Linux
    - Git
    - GitHub
    - Markdown
    
    ## Advertencia
    
    Todos los personajes y propuestas son ficticios.
    
    Proyecto educativo individual.

    Registra y publica los cambios:

    git add README.md
    
    git commit -m "Documentación oficial del repositorio"
    
    git push

    Actualiza la página de GitHub.

    El contenido del README debería mostrarse automáticamente en la página del proyecto.

    Fase 10. Sincronización y clonación

    Paso 26. Simular una modificación desde otro ordenador

    Queremos comprobar qué sucede cuando el repositorio remoto tiene cambios que aún no existen en nuestra máquina virtual.

    Abre el archivo README.md directamente en GitHub y utiliza su opción de edición.

    Añade al final:

    ## Comunicado de última hora
    
    El ministro anuncia que se estudiará
    la posibilidad de cumplir alguna promesa.

    Guarda la modificación mediante un commit desde GitHub.

    Acabamos de crear una versión nueva que existe en GitHub, pero todavía no en nuestra máquina virtual.

    Paso 27. Descargar los cambios

    Vuelve a la terminal de Ubuntu.

    Ejecuta:

    git fetch origin

    Este comando descarga información y objetos del repositorio remoto, pero no integra automáticamente los cambios en tu rama local.

    Comprueba las diferencias:

    git log --oneline HEAD..origin/main

    Después ejecuta:

    git pull --ff-only

    En este caso, como no hemos realizado nuevos commits locales después de la publicación, Git debería poder actualizar mediante fast-forward.

    Comprueba el README:

    cat README.md

    La modificación introducida desde GitHub debería estar disponible localmente.

    Pregunta: ¿Qué diferencia existe entre git fetch, git pull y git push?

    Paso 28. Clonar el proyecto en otro directorio

    Vamos a simular que hemos adquirido un nuevo ordenador o que necesitamos restaurar el proyecto después de perder la máquina virtual.

    Sal del directorio de trabajo:

    cd ~/proyectos-git

    Crea un directorio nuevo:

    mkdir ordenador-nuevo
    cd ordenador-nuevo

    Clona tu repositorio:

    git clone https://github.com/TU_USUARIO/ministerio-promesas.git

    Entra en él:

    cd ministerio-promesas

    Comprueba:

    git status
    git remote -v
    git log --oneline
    ls -la

    El repositorio clonado tendrá los archivos publicados y su historial Git.

    No contendrá los archivos locales ignorados que no se subieron a GitHub.

    Última misión

    Desde este nuevo clon, añade a README.md una sección titulada «El Ministerio sobrevive al cambio de ordenador».

    Después:

    git add README.md
    
    git commit -m "El Ministerio sobrevive al cambio de ordenador"
    
    git push

    Regresa al repositorio original:

    cd ~/proyectos-git/ministerio-promesas

    Y actualízalo:

    git pull --ff-only

    Comprueba que el cambio realizado en el nuevo clon aparece también en el original.

    Hemos trabajado sobre el mismo proyecto desde dos directorios diferentes, sin necesitar un trabajo colaborativo entre personas.

    Fase 11. El examen del ministro

    En esta última fase, los alumnos realizarán pequeñas operaciones sin copiar exactamente la secuencia anterior. Deben identificar los comandos necesarios y justificar su elección.

    Misiones finales

    Marca las completadas

    0 de 7 misiones

    0%Crear un archivo ministros.md con tres ministros ficticios y registrar un commit.Añadir una cuarta promesa al programa electoral, identificar el cambio con git diff y registrarlo.Modificar accidentalmente economia/presupuesto.txt y recuperar la versión confirmada.Crear un archivo temporal discurso.tmp y comprobar que Git lo ignora.Crear una rama llamada reforma, añadir una propuesta, registrarla y fusionarla con main.Publicar todos los cambios en GitHub y comprobar que el repositorio local está sincronizado.Mostrar el historial completo con un gráfico de los commits.

    Comandos que deben dominar

    ComandoFunción
    git init -b mainInicializar repositorio
    git statusConsultar estado
    git add archivoPreparar cambios
    git commit -m "mensaje"Registrar versión
    git log --onelineConsultar historial
    git diffComparar cambios
    git show HEADInspeccionar último commit
    git restore archivoRecuperar contenido
    git restore --staged archivoRetirar del staging
    git revert HEADRevertir un commit
    git switch -c ramaCrear y cambiar de rama
    git merge ramaFusionar ramas
    git remote -vConsultar remotos
    git pushEnviar commits
    git fetchDescargar información remota
    git pullDescargar e integrar cambios
    git clone URLClonar repositorio

    Adaptación para Windows

    En Windows pueden instalar Git for Windows y utilizar Git Bash, que permite ejecutar prácticamente todos los comandos Linux de esta práctica.

    Para quienes utilicen PowerShell, las principales diferencias están en los comandos del sistema, no en Git.

    OperaciónBash (Linux y Git Bash)PowerShell
    Directorio actualpwdpwd
    Listar archivosls -laGet-ChildItem -Force
    Crear carpetamkdir proyectomkdir proyecto
    Mostrar archivocat archivo.txtGet-Content archivo.txt
    Eliminar archivorm archivo.txtRemove-Item archivo.txt
    Editar archivonano archivo.txtnotepad archivo.txt
    Consultar Gitgit statusgit status

    Para mantener la práctica sencilla, recomiendo que todos trabajen con la terminal Linux de su máquina virtual, usando Windows solamente como demostración adicional.

    Entrega de la práctica

    Cada alumno deberá entregar un archivo README.md o informe.md con las respuestas a las preguntas, las explicaciones de los incidentes y las capturas necesarias.

    Además, deberá facilitar el enlace de su repositorio en GitHub y demostrar que contiene el historial de commits.

  • Práctica guiada: Docker Desktop, Portainer, Apache, PHP y MySQL

    Práctica guiada: Docker Desktop, Portainer, Apache, PHP y MySQL

    1. Presentación de la práctica

    En esta actividad aprenderemos a utilizar Docker para poner en funcionamiento una pequeña aplicación web formada por dos contenedores independientes.

    El primero tendrá un servidor Apache con PHP. El segundo alojará una base de datos MySQL con información sobre videojuegos de PC.

    No tendremos que programar la aplicación: el profesor facilitará todos los archivos necesarios. Nuestro trabajo será instalar, configurar, conectar, comprobar y publicar los contenedores.

    Arquitectura que vamos a construir

    2. Material facilitado por el profesor

    He preparado un ZIP con todos los archivos necesarios para los alumnos.

    Los alumnos deberán descomprimir el ZIP en una carpeta de Windows llamada practica_docker_juegos.

    Parte 1. Instalación de Docker Desktop

    Paso 1. Descargar Docker Desktop

    1. Accede a Docker Desktop.
    2. Descarga la versión para Windows.
    3. Ejecuta el instalador.
    4. Mantén seleccionada la opción de utilizar WSL 2, si aparece.
    5. Finaliza la instalación y reinicia Windows cuando sea necesario.

    Paso 2. Comprobar el funcionamiento

    Abre Docker Desktop y espera a que el motor de Docker esté funcionando.

    Abre PowerShell y ejecuta:

    docker --version

    Después:

    docker info

    Si este último comando muestra información del servidor, Docker está funcionando.

    Si indica que WSL no está instalado, abre PowerShell como administrador y ejecuta:

    wsl --install

    Reinicia el ordenador cuando termine la instalación y vuelve a abrir Docker Desktop.

    Parte 2. Crear nuestra cuenta de Docker Hub

    Docker Hub es un repositorio en Internet que permite descargar imágenes creadas por otras personas y publicar las nuestras.

    1. Accede a Docker Hub.
    2. Selecciona Sign up.
    3. Registra tu cuenta y verifica el correo electrónico.
    4. Accede a Docker Desktop.
    5. Pulsa Sign in e inicia sesión con la cuenta recién creada.

    Guarda tu nombre de usuario de Docker Hub. Lo necesitaremos al finalizar la práctica.

    Parte 3. Instalar Portainer

    Portainer es una herramienta de administración visual de Docker. Nos permitirá controlar contenedores, imágenes, redes y otros recursos.

    Portainer CE : gérer Docker et Podman visuellement

    Paso 1. Instalar la extensión

    1. Abre Docker Desktop.
    2. Entra en Settings → Extensions y comprueba que estén habilitadas.
    3. Abre Extensions Marketplace.
    4. Busca Portainer.
    5. Selecciona la extensión Portainer CE publicada por Portainer.io.
    6. Pulsa Install o Open in Docker Desktop, según la interfaz.

    También puedes localizarla en Portainer CE para Docker Desktop.

    Paso 2. Acceder a Portainer

    Abre Portainer desde el apartado Extensions. Si solicita crear un usuario administrador, completa el asistente y selecciona el entorno local de Docker.

    Comprueba que puedes acceder al apartado Containers.

    Esta extensión se encuentra disponible oficialmente para Docker Desktop.

    Paso 2. Configuración inicial

    Si Portainer solicita crear una cuenta administradora local:

    1. Introduce un nombre de usuario.
    2. Elige una contraseña.
    3. Completa el asistente.

    Selecciona el entorno Docker local disponible. Según la versión, puede aparecer como local o con un nombre similar.

    Paso 3. Conocer Portainer

    Localiza los apartados principales.

    SecciónUtilidad
    ContainersCrear, iniciar, detener y administrar contenedores
    ImagesDescargar, etiquetar y publicar imágenes
    NetworksCrear redes para comunicar contenedores
    VolumesAdministrar almacenamiento persistente
    EventsConsultar acontecimientos del entorno
    RegistriesConfigurar repositorios como Docker Hub

    Durante esta práctica trabajaremos principalmente en Containers, Images y Networks.

    Comprobación 3: puedes abrir Portainer y acceder al entorno Docker local.

    PARTE 4. NUESTRO PRIMER CONTENEDOR: HELLO WORLD

    Antes de crear nuestra aplicación web, vamos a comprobar cómo funciona Docker utilizando una imagen muy sencilla.

    La imagen hello-world muestra un mensaje y termina su ejecución.

    Paso 1. Descargar la imagen

    Desde Portainer:

    1. Selecciona Images.
    2. Busca el apartado para descargar imágenes (Pull image).
    3. Selecciona Docker Hub como registro.
    4. Introduce:
    hello-world:latest

    5. Pulsa Pull the image.

    Espera a que termine.

    La imagen debería aparecer en el listado de imágenes disponibles.

    Paso 2. Crear el contenedor

    1. Accede a Containers.
    2. Pulsa Add container.
    3. Introduce los siguientes valores:
    CampoValor
    Namemi-hola-mundo
    Imagehello-world:latest
    NetworkDefault
    Restart policyNo
    1. No es necesario publicar ningún puerto.
    2. Pulsa Deploy the container.

    Paso 3. Comprobar el resultado

    Abre la lista de contenedores.

    Localiza mi-hola-mundo.

    Es probable que aparezca con el estado Exited o detenido.

    Esto no significa que haya fallado. El programa ha terminado su ejecución.

    Selecciona el contenedor y entra en Logs.

    Busca el mensaje:

    Hello from Docker!

    Paso 4. Eliminar el contenedor

    Vuelve al listado de contenedores.

    1. Selecciona mi-hola-mundo.
    2. Pulsa Remove.
    3. Confirma la eliminación.

    Después abre Images y comprueba que la imagen hello-world sigue disponible.

    Pregunta de reflexión

    ¿Por qué hemos podido eliminar el contenedor sin eliminar también la imagen?

    PARTE 5. CREAR NUESTRA PRIMERA RED DOCKER

    Vamos a crear una red privada para que Apache y MySQL puedan comunicarse.

    La llamaremos:

    juegos-net

    Paso 1. Crear la red desde Portainer

    1. Entra en Networks.
    2. Pulsa Add network.
    3. Introduce:
    CampoValor
    Namejuegos-net
    Driverbridge
    InternalDesactivado
    Enable IPv6Desactivado
    1. Deja el resto de opciones por defecto.
    2. Pulsa Create the network.

    Paso 2. Verificarla

    Comprueba que juegos-net aparece en el listado.

    Más adelante conectaremos ambos contenedores a esta red.

    PARTE 6. CREAR EL CONTENEDOR APACHE

    Vamos a crear nuestro primer servidor web.

    Utilizaremos una imagen oficial de Apache HTTP Server llamada httpd.

    Paso 1. Descargar Apache

    Abre Images y descarga:

    httpd:2.4-trixie

    Esta variante está basada en Debian, por lo que podremos utilizar apt para instalar programas.

    Paso 2. Crear el contenedor

    Accede a Containers → Add container.

    Introduce los siguientes datos:

    CampoValor
    Nameweb-juegos
    Imagehttpd:2.4-trixie
    Restart policyUnless stopped

    En el apartado de publicación manual de puertos, añade:

    HostContainer
    808080

    En Advanced container settings → Network, selecciona:

    juegos-net

    En Advanced container settings → Env, añade esta variable:

    NameValue
    DB_HOSTbd-juegos

    Esta variable indicará al servidor web cómo localizar la base de datos cuando la creemos.

    Pulsa Deploy the container.

    Paso 3. Verificar el estado

    En el listado de contenedores debería aparecer:

    web-juegos     running

    Paso 4. Abrir nuestra primera página web

    En Windows, abre el navegador:http://localhost:8080

    Debería aparecer el mensaje:

    It works!

    Esto significa que Apache está funcionando.

    ¿Qué hemos conseguido?

    Hemos creado un servidor web Linux dentro de Docker, sin instalar Apache directamente en Windows.

    El puerto 8080 pertenece a nuestro equipo, mientras que Apache escucha en el puerto 80 dentro del contenedor.

    PARTE 7. INSTALAR PHP Y MYSQLI EN APACHE

    Para incorporar PHP al contenedor httpd y conectarlo con tu contenedor MySQL nstalaremos PHP-FPM dentro del mismo contenedor y configuraremos Apache para que le envíe los archivos .php. Apache admite esta integración mediante mod_proxy_fcgi. Apache HTTP Server Version 2.4

    Instalar solamente php y php-mysql no basta: puedes tener funcionando php -v y que Apache todavía no interprete PHP.

    Los siguientes pasos están preparados para un contenedor httpd basado en Debian, con apt.

    1. Abre una consola dentro de tu contenedor Apache

    En Portainer: Containers → tu contenedor Apache → Console → /bin/bash, como usuario root.

    También puedes entrar desde la terminal de tu ordenador, sustituyendo web-juegos por el nombre real:

    docker exec -it web-juegos bash

    2. Instala PHP-FPM y los conectores MySQL

    Dentro del contenedor:

    apt update
    apt install -y php-fpm php-cli php-mysql

    Comprueba la instalación:

    php -v
    php -m | grep -E 'mysqli|pdo_mysql'

    Deberían aparecer mysqli y pdo_mysql.

    3. Configura PHP-FPM

    Vamos a hacer que PHP-FPM escuche en el puerto 9000, únicamente dentro del propio contenedor.

    Primero, guarda la versión instalada en una variable:

    PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')

    Comprueba su valor:

    echo "$PHP_VERSION"

    Cambia la configuración del proceso PHP-FPM:

    sed -i 's|^[[:space:]]*listen[[:space:]]*=.*|listen = 127.0.0.1:9000|' \
    "/etc/php/$PHP_VERSION/fpm/pool.d/www.conf"

    Inícialo:

    service "php${PHP_VERSION}-fpm" start

    No necesitas publicar el puerto 9000 en Docker, porque Apache y PHP-FPM están dentro del mismo contenedor.

    4. Habilita la integración en Apache

    La configuración del Apache de la imagen httpd está normalmente en:

    /usr/local/apache2/conf/httpd.conf

    Haz una copia antes de modificarla:

    cp -n /usr/local/apache2/conf/httpd.conf \
    /usr/local/apache2/conf/httpd.conf.antes-php

    Activa los dos módulos necesarios:

    sed -i \
    's|^#LoadModule proxy_module modules/mod_proxy.so|LoadModule proxy_module modules/mod_proxy.so|' \
    /usr/local/apache2/conf/httpd.conf
    sed -i \
    's|^#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so|LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so|' \
    /usr/local/apache2/conf/httpd.conf

    Crea un archivo específico para la configuración de PHP:

    cat > /usr/local/apache2/conf/extra/php-fpm.conf <<'EOF'
    DirectoryIndex index.php index.html
    
    <FilesMatch "\.php$">
        SetHandler "proxy:fcgi://127.0.0.1:9000"
    </FilesMatch>
    EOF

    Inclúyelo en la configuración principal, sin duplicarlo si ya existe:

    grep -qxF 'Include conf/extra/php-fpm.conf' /usr/local/apache2/conf/httpd.conf \
    || echo 'Include conf/extra/php-fpm.conf' >> /usr/local/apache2/conf/httpd.conf

    Comprueba la configuración:

    httpd -t

    Si aparece Syntax OK, aplica los cambios:

    httpd -k graceful

    5. Comprueba que Apache interpreta PHP

    Crea una página de prueba:

    cat > /usr/local/apache2/htdocs/prueba.php <<'EOF'
    <?php
    echo "<h1>PHP funciona dentro de mi contenedor httpd</h1>";
    echo "<p>Versión: " . PHP_VERSION . "</p>";
    echo "<p>MySQLi: " . (extension_loaded('mysqli') ? 'disponible' : 'no disponible') . "</p>";
    EOF

    Abre en tu navegador, usando el puerto que tengas publicado:

    http://localhost:puerto/prueba.php

    PARTE 9. CREAR EL CONTENEDOR MYSQL

    Ahora necesitaremos una base de datos donde almacenar nuestros videojuegos.

    Utilizaremos MySQL 8.4.

    Paso 1. Descargar MySQL

    En Portainer:

    1. Abre Images.
    2. Selecciona Docker Hub.
    3. Descarga:
    mysql:8.4

    4. Espera a que aparezca en el listado de imágenes.

    Paso 2. Crear el contenedor

    Accede a Containers → Add container.

    Introduce:

    CampoValor
    Namebd-juegos
    Imagemysql:8.4
    Restart policyUnless stopped

    En la publicación manual de puertos:

    HostContainer
    33073306

    En Advanced container settings → Network, selecciona:

    juegos-net

    Paso 3. Configurar las variables de MySQL

    Busca Advanced container settings → Env.

    Añade una por una las siguientes variables:

    NameValue
    MYSQL_ROOT_PASSWORDRootClase2026
    MYSQL_DATABASEjuegos
    MYSQL_USERalumno
    MYSQL_PASSWORDClaveJuegos2026

    Estas variables permiten inicializar MySQL automáticamente.

    Sus funciones son:

    • MYSQL_ROOT_PASSWORD: contraseña del administrador.
    • MYSQL_DATABASE: base de datos que queremos crear.
    • MYSQL_USER: usuario que utilizará la aplicación PHP.
    • MYSQL_PASSWORD: contraseña de ese usuario.

    Estas credenciales son únicamente de laboratorio. No deberán reutilizarse en servicios reales.

    Paso 4. Crear MySQL

    Pulsa Deploy the container.

    Regresa a Containers.

    Comprueba que aparece bd-juegos.

    Paso 5. Comprobar que MySQL está funcionando

    Selecciona bd-juegos y pulsa Logs.

    Espera a que aparezca un mensaje similar a:

    ready for connections

    Durante el primer arranque MySQL necesita inicializar sus archivos y crear la base de datos.

    Paso 6. Acceder a MySQL

    Dentro de Portainer:

    1. Selecciona bd-juegos.
    2. Accede a Console.
    3. Selecciona /bin/sh.
    4. Pulsa Connect.

    Escribe:

    mysql -u root -p

    Cuando solicite la contraseña, introduce:

    RootClase2026

    La contraseña no se mostrará mientras escribes.

    Una vez dentro, ejecuta:

    SHOW DATABASES;

    Debería aparecer la base de datos juegos.

    Para salir del cliente MySQL:

    exit;

    Comprobación: tenemos un servidor MySQL dentro de Docker, conectado a la misma red que Apache.

    PARTE 10. CREAR LA BASE DE DATOS DE VIDEOJUEGOS

    El profesor proporciona el siguiente código SQL.

    Este código realizará tres operaciones:

    1. Seleccionar la base de datos juegos.
    2. Crear una tabla llamada videojuegos.
    3. Insertar cinco videojuegos de ejemplo.

    Paso 1. Crear el archivo SQL dentro de MySQL

    Desde la consola Linux del contenedor bd-juegos, copia y pega todo este bloque:

    cat > /docker-entrypoint-initdb.d/01-juegos.sql <<'EOF'
    USE juegos;
    
    CREATE TABLE IF NOT EXISTS videojuegos (
        id INT AUTO_INCREMENT PRIMARY KEY,
        titulo VARCHAR(100),
        genero VARCHAR(50),
        plataforma VARCHAR(50)
    );
    
    INSERT INTO videojuegos
    (titulo, genero, plataforma)
    VALUES
    ('Minecraft', 'Sandbox', 'PC'),
    ('Age of Empires II', 'Estrategia', 'PC'),
    ('Portal 2', 'Puzles', 'PC'),
    ('Stardew Valley', 'Simulacion', 'PC'),
    ('Hollow Knight', 'Metroidvania', 'PC');
    EOF

    No es necesario escribir el código línea por línea.

    Selecciona el bloque completo, cópialo y pégalo en la consola.

    Hemos guardado el SQL en /docker-entrypoint-initdb.d/ para que también pueda utilizarse al crear nuevos contenedores a partir de nuestra futura imagen MySQL.

    Paso 2. Ejecutar el SQL

    Ejecuta:

    mysql -u root -p juegos < /docker-entrypoint-initdb.d/01-juegos.sql

    Introduce la contraseña del administrador:

    RootClase2026

    Si no aparece ningún error, el script habrá terminado.

    No vuelvas a ejecutar el script sobre esta misma base de datos, ya que volvería a insertar los registros.

    Paso 3. Verificar la tabla

    Accede nuevamente al cliente MySQL:

    mysql -u root -p

    Introduce la contraseña.

    Ahora ejecuta:

    USE juegos;

    Y después:

    SHOW TABLES;

    Debe aparecer:

    videojuegos

    Paso 4. Comprobar los datos

    SELECT * FROM videojuegos;

    El resultado esperado es:

    idtitulogeneroplataforma
    1MinecraftSandboxPC
    2Age of Empires IIEstrategiaPC
    3Portal 2PuzlesPC
    4Stardew ValleySimulacionPC
    5Hollow KnightMetroidvaniaPC

    Ya tenemos nuestra primera base de datos funcionando dentro de Docker.

    Sal del cliente MySQL escribiendo:

    exit;

    PARTE 11. INSTALAR NUESTRA APLICACIÓN PHP

    Nuestra aplicación será muy sencilla.

    Mostrará el título, el género y la plataforma de los videojuegos almacenados en MySQL.

    No tendremos que desarrollar el código; solamente introducirlo dentro del contenedor Apache.

    Paso 1. Volver al contenedor Apache

    En Portainer:

    1. Entra en Containers.
    2. Selecciona web-juegos.
    3. Pulsa Console.
    4. Abre /bin/sh.

    Paso 2. Crear el archivo PHP

    Copia todo este bloque de código y pégalo directamente en la consola de Portainer:

    cat > /usr/local/apache2/htdocs/index.php <<'EOF'
    <?php
    $conexion = new mysqli(
        getenv("DB_HOST") ?: "bd-juegos",
        "alumno",
        "ClaveJuegos2026",
        "juegos"
    );
    
    if ($conexion->connect_error) {
        die("Error de conexion con MySQL");
    }
    
    $resultado = $conexion->query("SELECT * FROM videojuegos");
    ?>
    
    <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <title>Juegos de PC</title>
    </head>
    <body>
    
    <h1>Catalogo de videojuegos</h1>
    
    <?php
    while ($juego = $resultado->fetch_assoc()) {
        echo "<p>";
        echo htmlspecialchars($juego["titulo"]);
        echo " - ";
        echo htmlspecialchars($juego["genero"]);
        echo " - ";
        echo htmlspecialchars($juego["plataforma"]);
        echo "</p>";
    }
    ?>
    
    </body>
    </html>
    EOF

    El código es sencillo, pero contiene todo lo necesario:

    • Conexión con MySQL.
    • Consulta de los videojuegos.
    • Lectura de resultados.
    • Generación de una página HTML.

    El servidor MySQL se busca utilizando la variable DB_HOST, que configuramos desde Portainer.

    Paso 3. Eliminar la página predeterminada

    El servidor Apache incluye una página llamada index.html.

    Vamos a eliminarla para que se muestre directamente nuestro index.php.

    Desde la consola:

    rm /usr/local/apache2/htdocs/index.html

    Paso 4. Abrir la aplicación

    Accede desde Windows a:http://localhost:8080

    Debería mostrarse una página similar a la siguiente.

    La página real tendrá un diseño HTML mucho más sencillo, pero debe mostrar los mismos cinco videojuegos.

    Paso 5. Comprobar Portainer

    Vuelve a Portainer y verifica que los dos contenedores están activos:

    ContenedorEstadoPuerto Windows
    web-juegosRunning8080
    bd-juegosRunning3307

    Ambos deben pertenecer a juegos-net.

    ¿Cómo funciona la aplicación?

    Cuando escribimos localhost:8080 en el navegador:

    1. Docker dirige la petición al contenedor Apache.
    2. Apache identifica el archivo index.php.
    3. PHP ejecuta el programa.
    4. PHP solicita los videojuegos a MySQL.
    5. MySQL devuelve los registros.
    6. PHP genera la página HTML que vemos en el navegador.

    Hemos conseguido ejecutar una aplicación PHP conectada a una base de datos MySQL utilizando dos contenedores independientes.

    PARTE 12. CREAR NUESTRAS PROPIAS IMÁGENES

    Ahora aprenderemos una de las funciones más interesantes de Docker: crear nuestras propias imágenes a partir de los contenedores que hemos configurado.

    Esto nos permitirá reutilizar las modificaciones realizadas.

    Paso 1. Crear la imagen de Apache + PHP

    En Portainer:

    1. Accede a Containers.
    2. Selecciona web-juegos.
    3. Busca la opción Create image dentro de los detalles del contenedor.
    4. Introduce un nombre para la imagen:
    mi-web-juegos

    Y la etiqueta:

    v1

    Si Portainer muestra un único campo para el nombre y la etiqueta, introduce:

    mi-web-juegos:v1

    5. Confirma la creación de la imagen.

    Es posible que tarde un poco, ya que hemos instalado nuevos paquetes dentro del contenedor.

    Paso 2. Verificar la imagen

    Entra en Images.

    Debería aparecer:

    mi-web-juegos:v1

    Esta imagen contendrá Apache, PHP, el conector MySQL, nuestra configuración CGI y la página index.php.

    La configuración de arranque que hemos utilizado permite que Apache siga iniciándose con su comando predeterminado.

    Paso 3. Crear la imagen de MySQL

    Ahora repetiremos el procedimiento.

    1. Accede a Containers.
    2. Selecciona bd-juegos.
    3. Pulsa Create image.
    4. Introduce:
    mi-bd-juegos:v1

    5. Confirma la operación.

    Paso 4. Verificar las dos imágenes

    Abre Images.

    Deberías encontrar:

    mi-web-juegos:v1
    mi-bd-juegos:v1

    Importante sobre MySQL: las imágenes creadas desde contenedores no incorporan los datos guardados en volúmenes. Lo que hemos incluido en nuestra imagen MySQL es el archivo 01-juegos.sql, que permitirá reconstruir la tabla y sus datos al iniciar una base de datos nueva.

    PARTE 13. PUBLICAR NUESTRAS IMÁGENES EN DOCKER HUB

    Ahora compartiremos nuestras imágenes en Docker Hub.

    Para ello utilizaremos Portainer.

    Paso 1. Configurar el acceso a Docker Hub

    En Portainer, entra en el apartado Registries.

    Si Docker Hub aparece configurado, comprueba que permite utilizar tus credenciales. En caso contrario:

    1. Pulsa Add registry.
    2. Selecciona Docker Hub.
    3. Introduce tus datos de acceso. Si solicita un token de acceso, puedes crearlo desde la configuración de seguridad de tu cuenta de Docker Hub.
    4. Guarda la configuración.

    No publiques las contraseñas utilizadas para acceder a tu cuenta.

    Paso 2. Etiquetar nuestra primera imagen

    Abre Images y localiza:

    mi-web-juegos:v1

    Selecciona la opción Tag.

    Supongamos que tu usuario de Docker Hub es alumnoasir.

    Asigna la etiqueta:

    alumnoasir/mi-web-juegos:v1

    Sustituye alumnoasir por tu nombre real de usuario de Docker Hub.

    Paso 3. Publicar la imagen web

    En el listado de imágenes localiza la nueva etiqueta y selecciona Push, normalmente identificada por una flecha hacia arriba.

    Selecciona Docker Hub si Portainer solicita elegir un registro.

    Espera a que termine la subida.

    Paso 4. Publicar la imagen MySQL

    Repite el mismo procedimiento con:

    mi-bd-juegos:v1

    Asignándole:

    alumnoasir/mi-bd-juegos:v1

    Pulsa Push.

    La documentación de Portainer describe el proceso de etiquetado y publicación de imágenes desde su interfaz.

    Paso 5. Verificar Docker Hub

    Entra en:Docker Hub

    Dentro de tus repositorios deberías encontrar:

    RepositorioEtiqueta
    mi-web-juegosv1
    mi-bd-juegosv1

    Para esta práctica, crea los repositorios como privados si tu cuenta lo permite.

    Recuerda que la imagen web contiene credenciales de ejemplo y que Docker Hub no debe utilizarse para publicar secretos reales.

    Comprobación: hemos creado y publicado dos imágenes propias sin utilizar comandos Docker.

    PARTE 14. CREAR UNA SEGUNDA INSTALACIÓN CON OTROS PUERTOS

    En esta parte final vamos a demostrar que nuestras imágenes pueden utilizarse para crear otros contenedores.

    No eliminaremos los originales.

    Crearemos una segunda instalación de la aplicación.

    ElementoInstalación originalSegunda instalación
    Contenedor Apacheweb-juegosweb-juegos-copia
    Puerto web Windows80808081
    Contenedor MySQLbd-juegosbd-juegos-copia
    Puerto MySQL Windows33073308
    Red Dockerjuegos-netjuegos-copia-net

    Paso 1. Crear una segunda red

    Desde Portainer:

    1. Accede a Networks.
    2. Pulsa Add network.
    3. Nombre:
    juegos-copia-net
    1. Driver: bridge.
    2. Pulsa Create the network.

    Paso 2. Descargar nuestra imagen MySQL

    Accede a Images.

    Selecciona Docker Hub y busca:

    alumnoasir/mi-bd-juegos:v1

    Sustituye el nombre de usuario.

    Pulsa Pull the image.

    Aunque la imagen ya exista localmente, este paso permite comprobar que puede recuperarse desde Docker Hub.

    Paso 3. Crear el nuevo MySQL

    Accede a Containers → Add container.

    Introduce:

    CampoValor
    Namebd-juegos-copia
    Imagealumnoasir/mi-bd-juegos:v1
    Host port3308
    Container port3306
    Networkjuegos-copia-net

    En Advanced container settings → Env, configura:

    NameValue
    MYSQL_ROOT_PASSWORDRootClase2026
    MYSQL_DATABASEjuegos
    MYSQL_USERalumno
    MYSQL_PASSWORDClaveJuegos2026

    Deja los volúmenes con su configuración predeterminada y no reutilices el volumen de datos de la primera base de datos.

    Pulsa Deploy the container.

    Paso 4. Verificar la segunda base de datos

    Abre los registros de bd-juegos-copia.

    Espera a que MySQL termine de inicializarse.

    El archivo SQL que habíamos incluido en la imagen deberá ejecutarse durante la inicialización de esta base de datos nueva.

    Para comprobarlo, abre Console dentro de bd-juegos-copia y entra en MySQL:

    mysql -u root -p

    Introduce la contraseña:

    RootClase2026

    Ejecuta:

    USE juegos;
    SELECT * FROM videojuegos;

    Deben aparecer los cinco videojuegos.

    Sal de MySQL:

    exit;

    Paso 5. Descargar nuestra imagen Apache + PHP

    En Portainer, abre Images.

    Busca:

    alumnoasir/mi-web-juegos:v1

    Pulsa Pull the image.

    Paso 6. Crear el segundo contenedor Apache

    Entra en Containers → Add container.

    Introduce:

    CampoValor
    Nameweb-juegos-copia
    Imagealumnoasir/mi-web-juegos:v1
    Host port8081
    Container port80
    Networkjuegos-copia-net

    En Advanced container settings → Env añade:

    NameValue
    DB_HOSTbd-juegos-copia

    Esta variable es importante porque la segunda página web debe conectarse al segundo contenedor MySQL, no al primero.

    Pulsa Deploy the container.

    Paso 7. Comprobar las dos páginas

    Ahora abre las siguientes direcciones:

    Primera instalación. http://localhost:8080

    Apache y MySQL originales

    Segunda instalación. http://localhost:8081

    Contenedores creados a partir de nuestras imágenes

    Ambas páginas deberán mostrar los mismos cinco videojuegos.

    Hemos conseguido crear dos instalaciones independientes a partir de nuestras imágenes.

    Paso 8. Comprobación final desde Portainer

    Accede a Containers.

    Deberías tener cuatro contenedores activos:

    ContenedorEstado esperado
    web-juegosRunning
    bd-juegosRunning
    web-juegos-copiaRunning
    bd-juegos-copiaRunning

    PARTE 15. PEQUEÑAS COMPROBACIONES PRÁCTICAS

    Estas actividades permiten comprobar que hemos entendido el funcionamiento de los contenedores.

    Ejercicio 1. Detener Apache

    Desde Portainer, detén el contenedor web-juegos.

    Intenta abrir:

    http://localhost:8080

    ¿Qué sucede?

    Ahora vuelve a iniciar el contenedor desde Portainer y comprueba que la página funciona nuevamente.

    Ejercicio 2. Detener MySQL

    Detén bd-juegos.

    Vuelve a abrir la primera página web.

    ¿Qué sucede cuando PHP intenta consultar la base de datos?

    Inicia nuevamente MySQL y comprueba que la web recupera su funcionamiento.

    Ejercicio 3. Comprobar la independencia de las instalaciones

    Detén web-juegos y bd-juegos, pero deja funcionando los dos contenedores de la segunda instalación.

    Comprueba si sigue funcionando:

    http://localhost:8081

    Explica por qué ocurre.

    Vuelve a iniciar los contenedores originales cuando termines.

    Preguntas de comprensión

    Responde brevemente a las siguientes cuestiones:

    • ¿Por qué no debemos considerar una imagen Docker como una copia de seguridad de la base de datos?
    • ¿Qué es una imagen Docker?
    • ¿Qué es un contenedor?
    • ¿Por qué Hello World aparece detenido después de ejecutarse?
    • ¿Qué utilidad tiene Portainer?
    • ¿Para qué sirve una red Docker?
    • ¿Por qué hemos creado dos contenedores diferentes?
    • ¿Qué significa publicar el puerto 8080:80?
    • ¿Para qué utilizamos las variables de entorno de MySQL?
    • ¿Qué función tiene el conector MySQLi de PHP?
    • ¿Qué diferencia existe entre detener y eliminar un contenedor?
    • ¿Para qué sirve crear una imagen de un contenedor que hemos configurado?
    • ¿Qué ventajas tiene subir nuestra imagen a Docker Hub?
    • ¿Por qué podemos ejecutar dos servidores Apache utilizando los puertos 8080 y 8081?

    PARTE 17. CONCLUSIONES

    Con esta práctica hemos aprendido a utilizar Docker de manera visual a través de Portainer.

    Hemos instalado y configurado dos servicios independientes, Apache con PHP y MySQL, y hemos conseguido que se comuniquen mediante una red privada.

    También hemos creado nuestras propias imágenes, las hemos publicado en Docker Hub y las hemos reutilizado para desplegar una segunda instalación sin repetir manualmente la instalación de los programas.

    Lo más importante es comprender que una imagen permite crear contenedores, mientras que un contenedor es una instancia en ejecución de esa imagen.

    En una próxima práctica aprenderemos a realizar estas mismas operaciones desde la terminal y, posteriormente, a utilizar Docker Compose para automatizar el despliegue de varios servicios.

  • Proyecto Loot Figures — Parte 1: implantación en XAMPP y Ubuntu Server

    Proyecto Loot Figures — Parte 1: implantación en XAMPP y Ubuntu Server

    1. Presentación del proyecto

    En esta actividad vamos a implantar Loot Figures, una tienda web de figuras de plástico inspiradas en personajes de videojuegos.

    La aplicación ya está desarrollada. Nuestro trabajo consistirá en examinar sus archivos, preparar los entornos necesarios, configurar la base de datos y conseguir que funcione en dos instalaciones diferentes:

    1. En nuestro ordenador, utilizando XAMPP.
    2. En la máquina virtual Ubuntu Server que hemos preparado en clase.

    Además de conseguir que la tienda se abra en el navegador, tendremos que comprobar sus funciones y documentar el procedimiento.

    Implantar una aplicación no consiste únicamente en copiar archivos. También debemos identificar sus requisitos, configurar los servicios, preparar la base de datos, ajustar permisos y comprobar que todo funciona correctamente.


    2. Objetivos de aprendizaje

    Al terminar esta primera parte, deberás ser capaz de:

    • Identificar los archivos y las carpetas de una aplicación PHP.
    • Abrir un proyecto completo en VS Code o PhpStorm.
    • Reconocer la función de los modelos, los DAO y los archivos que reciben datos.
    • Diferenciar el código PHP del código que ejecuta el navegador.
    • Preparar un entorno con Apache, PHP y MySQL o MariaDB.
    • Crear una base de datos e importar su estructura.
    • Configurar la conexión entre PHP y la base de datos.
    • Ejecutar un script de carga de datos.
    • Configurar Apache para servir la carpeta pública del proyecto.
    • Implantar la misma aplicación en Windows y Ubuntu Server.
    • Comprobar su funcionamiento y resolver incidencias.
    • Elaborar documentación técnica que permita repetir la instalación.

    3. Material necesario

    Para realizar la actividad necesitarás:

    • El archivo del proyecto: loot-figures-php-clase.zip.
    • Un ordenador con XAMPP.
    • VS Code o PhpStorm.
    • Un navegador web.
    • La máquina virtual Ubuntu Server utilizada en clase.
    • Un usuario de Ubuntu con permisos para ejecutar sudo.
    • Conexión entre tu ordenador y la máquina virtual.

    La aplicación utiliza PHP 8.x, una base de datos MySQL o MariaDB y las extensiones PHP necesarias para trabajar con mysqli, sesiones y archivos.

    En esta primera parte no utilizaremos Docker. Vamos a preparar los servicios directamente para comprender qué necesita la aplicación.

    4. Resultado que debes conseguir

    Al finalizar, tendrás dos instalaciones independientes:

    EntornoAplicaciónBase de datosAcceso
    Ordenador con XAMPPCopia local del proyectoBase de datos de XAMPPhttp://localhost:8080
    Máquina virtual Ubuntu ServerCopia del proyecto en el servidorBase de datos de Ubuntuhttp://IP_DEL_SERVIDOR:8080

    Cada instalación tendrá su propia base de datos.

    Los cambios realizados en una instalación no aparecerán automáticamente en la otra.

    Utilizaremos el puerto 8080 para la tienda. De esta forma, podemos conservar las páginas o servicios que ya estén utilizando el puerto 80.


    Bloque A. Examinar el proyecto antes de instalarlo

    5. Descargar y descomprimir los archivos

    Descarga el ZIP proporcionado por el profesor y descomprímelo.

    La carpeta principal del proyecto se llama:

    loot-figures-clase

    Comprueba que dentro aparecen, entre otras, estas carpetas:

    app
    config
    database
    docs
    public
    scripts

    No comiences modificando archivos. Primero vamos a identificar qué contiene el proyecto.

    6. Abrir el proyecto en VS Code o PhpStorm

    Opción A: VS Code

    1. Abre VS Code.
    2. Selecciona Archivo → Abrir carpeta.
    3. Elige la carpeta principal loot-figures-clase.
    4. Comprueba que aparecen todas las carpetas del proyecto en el explorador lateral.
    5. Abre el archivo README.md.

    Opción B: PhpStorm

    1. Abre PhpStorm.
    2. Selecciona Open / Abrir.
    3. Elige la carpeta principal loot-figures-clase.
    4. Espera a que termine de analizar el proyecto.
    5. Abre el archivo README.md.

    Debes abrir la carpeta completa, no únicamente un archivo PHP ni solo la carpeta public.

    El editor nos permite consultar y modificar el código. Para ejecutar la aplicación necesitaremos un servidor PHP.

    7. Reconocer las carpetas del proyecto

    Examina la estructura y completa una tabla como esta en tu documentación:

    Carpeta o archivo¿Para qué sirve?
    app/modelosClases que representan clientes, productos, pedidos y mensajes
    app/daoClases que realizan las consultas a la base de datos
    app/Conexion.phpCódigo que abre la conexión con MySQL mediante mysqli
    configConfiguración de la aplicación
    database/schema.sqlInstrucciones SQL para crear las tablas
    publicArchivos que debe servir Apache al navegador
    public/apiArchivos PHP que reciben peticiones y devuelven datos
    public/partialsPartes comunes: cabecera, navegación y pie
    public/assetsJavaScript, CSS, logo e imágenes
    public/uploadsImágenes subidas desde la administración
    scripts/seed.phpScript que introduce los datos de ejemplo
    docsDocumentación del proyecto

    Preguntas que debes responder

    1. ¿Qué diferencia hay entre app/modelos y app/dao?
    2. ¿Qué archivo contiene la configuración de la base de datos?
    3. ¿Qué carpeta debe publicar Apache?
    4. ¿Por qué la configuración y los scripts de instalación no deben quedar dentro del directorio público?
    5. ¿Qué diferencia hay entre un archivo PHP y un archivo JavaScript?
    6. ¿Qué función tienen header.php, nav.php y footer.php?

    8. Seguir una operación sencilla

    Abre estos archivos:

    public/api/contacto.php
    app/modelos/Mensaje.php
    app/dao/MensajeDAO.php

    Localiza el recorrido de los datos:

    1. Se reciben los datos del formulario.
    2. Se validan.
    3. Se crea un objeto Mensaje.
    4. Se crea el DAO.
    5. El DAO inserta el mensaje en la base de datos.
    6. PHP devuelve una respuesta JSON.

    Identifica estas instrucciones:

    $mensaje = new Mensaje(0, $nombre, $email, $texto);
    
    $dao = new MensajeDAO($conexion);
    
    $dao->insertar($mensaje);

    No es necesario comprender todavía todo el proyecto. En esta fase debes poder explicar qué responsabilidad tiene cada archivo.

    Evidencia: incluye una captura del proyecto abierto en el editor y una explicación breve de este recorrido.


    Bloque B. Implantación local con XAMPP

    9. Preparar XAMPP

    Si todavía no tienes XAMPP, descárgalo desde su web oficial:

    Descargar XAMPP

    Las instrucciones siguientes utilizan una instalación de Windows situada en:

    C:\xampp

    Si tu instalación está en otra ubicación, adapta las rutas.

    Abre el panel de control de XAMPP e inicia:

    • Apache.
    • MySQL.

    Aunque el panel muestre el nombre MySQL, XAMPP puede incluir MariaDB como motor de base de datos.

    Comprueba estas direcciones:

    http://localhost
    http://localhost/phpmyadmin

    phpMyAdmin nos permitirá gestionar la base de datos de esta instalación. En la configuración básica de XAMPP se accede a él desde el propio equipo. apachefriends.org

    Evidencia: captura del panel con Apache y MySQL iniciados.

    10. Colocar el proyecto

    Crea esta carpeta:

    C:\proyectos

    Coloca dentro el proyecto descomprimido:

    C:\proyectos\loot-figures-clase

    Comprueba que existe:

    C:\proyectos\loot-figures-clase\public\index.php

    La carpeta del proyecto estará fuera de htdocs. Configuraremos Apache para que publique únicamente su carpeta public.

    Después, abre esta ubicación definitiva en VS Code o PhpStorm.

    11. Configurar Apache para la tienda

    11.1. Revisar la carga de VirtualHosts

    Abre este archivo:

    C:\xampp\apache\conf\httpd.conf

    Busca la línea:

    Include conf/extra/httpd-vhosts.conf

    Si aparece comentada con #, elimina ese carácter.

    Guarda el archivo.

    11.2. Añadir la configuración

    Abre:

    C:\xampp\apache\conf\extra\httpd-vhosts.conf

    Añade al final:

    Listen 8080
    
    <VirtualHost *:8080>
        ServerName localhost
    
        DocumentRoot "C:/proyectos/loot-figures-clase/public"
    
        <Directory "C:/proyectos/loot-figures-clase/public">
            Options -Indexes
            AllowOverride All
            Require local
        </Directory>
    </VirtualHost>

    Importante: si ya existe una instrucción Listen 8080 en la configuración de Apache, no la añadas otra vez.

    En la configuración de Apache hemos escrito las rutas con /.

    ¿Qué significa esta configuración?

    InstrucciónFunción
    Listen 8080Apache escucha también en el puerto 8080
    VirtualHost *:8080Define el sitio que responderá en ese puerto
    DocumentRootIndica la carpeta pública de la aplicación
    AllowOverride AllPermite aplicar las instrucciones de .htaccess
    Require localPermite el acceso desde el propio ordenador

    Detén y vuelve a iniciar Apache desde XAMPP.

    Si no arranca, revisa los mensajes y registros del servicio antes de continuar.

    12. Crear la base de datos

    Abre:

    http://localhost/phpmyadmin

    Selecciona la pestaña SQL y ejecuta:

    CREATE DATABASE loot_figures
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;
    
    CREATE USER 'loot_app'@'localhost'
    IDENTIFIED BY 'CambiaEstaClaveBD!';
    
    GRANT SELECT, INSERT, UPDATE, DELETE
    ON loot_figures.*
    TO 'loot_app'@'localhost';

    Hemos creado:

    • La base de datos loot_figures.
    • El usuario loot_app.
    • Los permisos necesarios para las operaciones de la tienda.

    La aplicación utilizará este usuario, no la cuenta root.

    Si la base o el usuario ya existen por un intento anterior, revisa lo que has creado antes de repetir las instrucciones. No elimines bases de datos para resolver un error sin comprobar su contenido.

    13. Importar las tablas

    En phpMyAdmin:

    1. Selecciona la base de datos loot_figures.
    2. Abre la pestaña Importar.
    3. Selecciona el archivo:
    C:\proyectos\loot-figures-clase\database\schema.sql
    1. Ejecuta la importación.
    2. Comprueba que aparecen estas tablas:
    users
    products
    orders
    order_items
    messages
    login_attempts

    La importación debe realizarse con la cuenta de administración de la base de datos, porque crea las tablas.

    No vuelvas a importar el esquema sobre una instalación que ya tiene sus tablas.

    Evidencia: captura de la base de datos con las tablas creadas.

    14. Crear el archivo de configuración

    Dentro de config, copia:

    config.example.php

    y llama a la copia:

    config.php

    Puedes hacerlo desde el editor o el explorador de archivos.

    Edita config.php y utiliza estos valores:

    <?php
    
    return [
        'db' => [
            'host' => 'localhost',
            'port' => 3306,
            'name' => 'loot_figures',
            'user' => 'loot_app',
            'password' => 'CambiaEstaClaveBD!'
        ],
    
        'secure_cookie' => false,
        'debug' => false
    ];

    Si tu servicio de base de datos utiliza otro puerto, debes cambiar el valor port.

    En esta práctica trabajamos con HTTP local, por eso secure_cookie permanece en false.

    No copies config.docker.php: esa configuración corresponde a otro entorno.

    15. Comprobar PHP y cargar los datos de ejemplo

    Abre una terminal de PowerShell.

    Comprueba el PHP de XAMPP:

    & "C:\xampp\php\php.exe" -v

    Después, sitúate en el proyecto:

    Set-Location "C:\proyectos\loot-figures-clase"

    Ejecuta:

    & "C:\xampp\php\php.exe" scripts/seed.php

    El script creará los usuarios y productos de demostración.

    Si aparece un error, comprueba:

    • Que MySQL está iniciado.
    • Que existe config/config.php.
    • Que la contraseña y el puerto son correctos.
    • Que has importado las tablas.

    El archivo seed.php se ejecuta desde la terminal. No tienes que abrirlo mediante una URL.

    16. Abrir la tienda en localhost

    Abre:

    http://localhost:8080

    La tienda debería mostrar la home, el logo, las imágenes y los productos.

    No abras index.php haciendo doble clic. Tampoco utilices Live Server para ejecutar esta aplicación PHP. Debes acceder mediante Apache.

    Cuentas de demostración

    RolCorreoContraseña
    Administradoradmin@loot.localAdminDemo!2026
    Clientecliente@loot.localClienteDemo!2026

    Estas cuentas utilizan direcciones ficticias. No necesitas disponer de esos correos.

    17. Comprobar el funcionamiento local

    Realiza estas pruebas:

    Zona pública

    1. Abre la home.
    2. Accede al catálogo.
    3. Busca una figura.
    4. Filtra por rareza.
    5. Cambia el orden de los productos.
    6. Abre el detalle de una figura.

    Registro y cuenta

    1. Crea una cuenta con datos ficticios.
    2. Cierra sesión.
    3. Vuelve a entrar.
    4. Accede a tu perfil.
    5. Cambia el teléfono y comprueba que se guarda.

    Carrito y compra

    1. Entra como cliente.
    2. Añade una figura al carrito.
    3. Cambia la cantidad.
    4. Añade otra figura.
    5. Elimina una línea.
    6. Introduce una dirección ficticia.
    7. Confirma el pedido.
    8. Comprueba que aparece en el historial.

    La compra es una simulación y no realiza ningún cobro.

    Administración

    1. Entra como administrador.
    2. Accede al listado de productos.
    3. Modifica el nombre, precio o stock de una figura.
    4. Comprueba el resultado en el catálogo.
    5. Crea un usuario de prueba.
    6. Modifica sus datos.
    7. Cambia su contraseña.
    8. Comprueba que puede iniciar sesión con la nueva contraseña.
    9. Sube una fotografía a un producto.

    Contacto

    1. Envía un mensaje desde la página de contacto.
    2. Entra como administrador.
    3. Comprueba que el mensaje aparece en el panel.

    El mensaje se guarda en la base de datos. No se envía un correo electrónico.

    18. Observar una petición JSON

    Abre las herramientas de desarrollo del navegador y selecciona la pestaña Red / Network.

    Recarga el catálogo y localiza la petición a:

    api/listar_productos.php

    Examina su respuesta.

    Responde:

    1. ¿Qué información contiene el JSON?
    2. ¿Qué campos identifican el nombre y el precio del producto?
    3. ¿El navegador recibe el código fuente PHP?
    4. ¿Qué archivo JavaScript utiliza esos datos para construir las tarjetas?

    Después, visita:

    http://localhost:8080/ejemplo_clase.php

    Este formulario permite observar un envío clásico por POST.

    Utiliza otra cuenta ficticia para probarlo. Después del envío verás la respuesta JSON directamente en el navegador.

    Explica la diferencia entre este formulario y el registro habitual de la tienda.


    Bloque C. Implantación en Ubuntu Server

    19. Preparar la máquina virtual

    Utiliza la máquina virtual creada en clase.

    Antes de comenzar:

    1. Comprueba que puedes iniciar sesión.
    2. Identifica tu usuario.
    3. Comprueba que puedes ejecutar sudo.
    4. Anota la versión de Ubuntu.
    5. Comprueba la dirección IP.
    6. Realiza una instantánea si el programa de virtualización lo permite.

    Ejecuta:

    whoami
    cat /etc/os-release
    ip -br a

    También puedes consultar:

    hostname -I

    En los siguientes ejemplos utilizaremos:

    192.168.1.50

    Es una IP de ejemplo. Debes sustituirla por la dirección real de tu servidor.

    20. Comprobar la comunicación con la máquina virtual

    Desde tu ordenador, intenta acceder por SSH si ya lo configuraste en clase:

    ssh alumno@192.168.1.50

    Sustituye alumno por tu usuario real.

    Si la máquina utiliza NAT

    Puede ser necesario configurar una redirección de puertos en el programa de virtualización.

    Por ejemplo:

    Puerto del ordenadorPuerto de UbuntuUso
    222222SSH
    80818080Tienda

    Con esa configuración, SSH se utilizaría así:

    ssh -p 2222 alumno@127.0.0.1

    Y la tienda de Ubuntu se abriría desde el ordenador mediante:

    http://localhost:8081

    Si utilizas una red en la que el ordenador puede acceder directamente a la IP de Ubuntu, no necesitas esas redirecciones.

    Documenta el modo de red que estás utilizando.

    21. Instalar los servicios necesarios

    En Ubuntu Server ejecuta:

    sudo apt update

    Después:

    sudo apt install apache2 mysql-server php libapache2-mod-php php-mysql php-cli unzip

    Apache atenderá las peticiones web; PHP ejecutará el código del servidor; MySQL almacenará los datos. La documentación de Ubuntu recoge la instalación de Apache y de PHP con su módulo para Apache. Ubuntu Server documentation

    Si algunos paquetes ya estaban instalados, revisa el resultado del comando: no es necesario reinstalarlos manualmente.

    Activa los servicios:

    sudo systemctl enable --now apache2
    sudo systemctl enable --now mysql

    Comprueba su estado:

    systemctl status apache2 --no-pager
    systemctl status mysql --no-pager

    Ambos deben aparecer activos.

    Evidencia: captura de los servicios funcionando.

    22. Comprobar la versión y las extensiones de PHP

    Ejecuta:

    php -v

    Después:

    php -m

    Comprueba que aparecen:

    mysqli
    mysqlnd
    fileinfo
    session

    Anota la versión de PHP de Ubuntu y compárala con la de XAMPP.

    23. Transferir el proyecto al servidor

    Para esta instalación, utiliza el ZIP original entregado por el profesor. Crearemos una base de datos nueva en Ubuntu.

    Puedes transferir el archivo mediante SFTP o con scp.

    Ejemplo, ejecutado desde tu ordenador y desde la carpeta donde está el ZIP:

    scp loot-figures-php-clase.zip alumno@192.168.1.50:~/

    Si utilizas la redirección SSH del ejemplo anterior:

    scp -P 2222 loot-figures-php-clase.zip alumno@127.0.0.1:~/

    Observa que scp utiliza -P mayúscula para indicar el puerto.

    En Ubuntu comprueba que ha llegado:

    ls -lh ~/loot-figures-php-clase.zip

    Descomprime:

    cd ~
    unzip loot-figures-php-clase.zip

    Comprueba:

    ls ~/loot-figures-clase

    Mueve el proyecto:

    sudo mv ~/loot-figures-clase /var/www/

    La ubicación final será:

    /var/www/loot-figures-clase

    Si ya existe una carpeta con ese nombre por un intento anterior, revisa su contenido antes de repetir el movimiento.

    24. Configurar Apache en Ubuntu

    24.1. Añadir el puerto

    Abre:

    sudo nano /etc/apache2/ports.conf

    Añade:

    Listen 8080

    Conserva las instrucciones existentes y no dupliques Listen 8080 si ya aparece.

    24.2. Crear el sitio

    Ejecuta:

    sudo nano /etc/apache2/sites-available/loot-figures.conf

    Introduce:

    <VirtualHost *:8080>
        DocumentRoot /var/www/loot-figures-clase/public
    
        <Directory /var/www/loot-figures-clase/public>
            Options -Indexes
            AllowOverride All
            Require all granted
        </Directory>
    
        ErrorLog ${APACHE_LOG_DIR}/loot-figures-error.log
        CustomLog ${APACHE_LOG_DIR}/loot-figures-access.log combined
    </VirtualHost>

    Guarda el archivo.

    Activa el sitio:

    sudo a2ensite loot-figures.conf

    Comprueba la sintaxis:

    sudo apache2ctl configtest

    Si aparece:

    Syntax OK

    reinicia Apache:

    sudo systemctl restart apache2

    No desactives los sitios anteriores de clase: esta tienda utiliza otro puerto.

    25. Crear la base de datos en Ubuntu

    Accede a MySQL:

    sudo mysql

    La instalación de MySQL mediante los paquetes de Ubuntu permite administrar el servicio y crear cuentas para las aplicaciones. Ubuntu Server documentation

    Ejecuta:

    CREATE DATABASE loot_figures
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;
    
    CREATE USER 'loot_app'@'localhost'
    IDENTIFIED BY 'CambiaEstaClaveBD!';
    
    GRANT SELECT, INSERT, UPDATE, DELETE
    ON loot_figures.*
    TO 'loot_app'@'localhost';

    Sal:

    EXIT;

    Importa las tablas:

    sudo mysql loot_figures < /var/www/loot-figures-clase/database/schema.sql

    Comprueba el resultado:

    sudo mysql -e "SHOW TABLES FROM loot_figures;"

    Debes encontrar las mismas tablas que en XAMPP.

    26. Configurar la conexión de la aplicación

    Copia la plantilla:

    sudo cp /var/www/loot-figures-clase/config/config.example.php /var/www/loot-figures-clase/config/config.php

    Edita:

    sudo nano /var/www/loot-figures-clase/config/config.php

    Utiliza:

    <?php
    
    return [
        'db' => [
            'host' => 'localhost',
            'port' => 3306,
            'name' => 'loot_figures',
            'user' => 'loot_app',
            'password' => 'CambiaEstaClaveBD!'
        ],
    
        'secure_cookie' => false,
        'debug' => false
    ];

    La base de datos está dentro de la propia máquina Ubuntu, por eso la aplicación utiliza localhost como servidor de base de datos.

    No debes poner la IP del ordenador Windows en esta configuración.

    27. Preparar propietarios y permisos

    Apache necesita leer los archivos del proyecto. Además, debe poder escribir en la carpeta donde se guardan las imágenes subidas.

    Configura el propietario y el grupo:

    sudo chown -R root:www-data /var/www/loot-figures-clase

    Configura los directorios:

    sudo find /var/www/loot-figures-clase -type d -exec chmod 750 {} \;

    Configura los archivos:

    sudo find /var/www/loot-figures-clase -type f -exec chmod 640 {} \;

    Permite que Apache gestione las imágenes subidas:

    sudo chown -R www-data:www-data /var/www/loot-figures-clase/public/uploads

    Comprueba:

    ls -ld /var/www/loot-figures-clase/public/uploads
    ls -l /var/www/loot-figures-clase/config/config.php

    Preguntas

    1. ¿Qué representa el usuario www-data?
    2. ¿Por qué Apache necesita permiso de escritura en uploads?
    3. ¿Por qué no necesita escribir en todos los archivos PHP?
    4. ¿Qué diferencia hay entre los permisos de un archivo y los de un directorio?

    No utilices chmod 777 para resolver problemas de permisos. Debes identificar qué usuario necesita acceso y qué operación debe realizar.

    28. Cargar los datos de ejemplo

    Ejecuta el script utilizando el usuario que puede leer los archivos del proyecto:

    sudo -u www-data php /var/www/loot-figures-clase/scripts/seed.php

    Comprueba los productos:

    sudo mysql -e "SELECT id, name, stock FROM loot_figures.products;"

    Comprueba los usuarios sin mostrar los hashes de contraseña:

    sudo mysql -e "SELECT id, name, email, role FROM loot_figures.users;"

    Evidencia: captura de los datos creados.

    29. Comprobar el acceso web

    Desde tu ordenador abre:

    http://192.168.1.50:8080

    Sustituye la IP por la del servidor.

    Si utilizas la redirección NAT propuesta, abre:

    http://localhost:8081

    Si no puedes acceder

    Comprueba el estado de Apache:

    systemctl status apache2 --no-pager

    Comprueba los puertos:

    sudo ss -lntp

    Revisa el cortafuegos:

    sudo ufw status

    Si UFW está activo, permite el puerto de la tienda:

    sudo ufw allow 8080/tcp

    No es necesario activar ni desactivar el cortafuegos para realizar esta comprobación.

    Consulta el registro de errores del sitio:

    sudo tail -n 50 /var/log/apache2/loot-figures-error.log

    Importante: escribir localhost en el navegador de tu ordenador apunta a tu ordenador. No apunta a Ubuntu, salvo que hayas configurado una redirección de puertos.

    30. Repetir las pruebas funcionales

    Realiza en Ubuntu las mismas pruebas que hiciste en XAMPP:

    • Navegación pública.
    • Catálogo y filtros.
    • Registro.
    • Login y cierre de sesión.
    • Edición del perfil.
    • Carrito y pedido.
    • Historial.
    • Administración de productos.
    • Administración de clientes.
    • Cambio de contraseña.
    • Subida de una imagen.
    • Envío y consulta de un mensaje.

    Comprueba especialmente la subida de imágenes: permite verificar que los permisos de uploads están preparados correctamente.

    31. Comprobar qué está publicando Apache

    Intenta abrir:

    http://IP_DEL_SERVIDOR:8080/config/config.php
    http://IP_DEL_SERVIDOR:8080/database/schema.sql
    http://IP_DEL_SERVIDOR:8080/scripts/seed.php

    Estas direcciones no deben permitir acceder a esos archivos, porque están fuera de la carpeta pública.

    En cambio, debe funcionar:

    http://IP_DEL_SERVIDOR:8080/api/listar_productos.php

    Esta dirección devolverá los productos en JSON.

    Explica por qué unas rutas son accesibles y otras no.


    Bloque D. Comparación, documentación y entrega

    32. Comparar las dos instalaciones

    Completa esta tabla con los datos reales de tu trabajo:

    AspectoXAMPPUbuntu Server
    Sistema operativo
    Versión de PHP
    Motor y versión de base de datos
    Ubicación del proyecto
    Carpeta pública
    Archivo de configuración del sitio Apache
    Dirección de acceso
    Puerto web
    Usuario de la base de datos
    Gestión de permisos de uploads
    Ubicación de los registros de errores

    Responde también:

    1. ¿Qué pasos son iguales en los dos entornos?
    2. ¿Qué pasos cambian?
    3. ¿Qué tareas facilita XAMPP?
    4. ¿Qué servicios has tenido que preparar en Ubuntu?
    5. ¿Por qué el editor no sustituye al servidor web?
    6. ¿Dónde se ejecuta PHP?
    7. ¿Dónde se ejecuta JavaScript?
    8. ¿Por qué necesitas copiar archivos y preparar también la base de datos?

    33. Registrar las pruebas

    Incluye una tabla de comprobación para cada instalación:

    PruebaResultado esperadoResultado obtenidoEvidencia
    Abrir la homeSe muestran diseño e imágenes
    Consultar catálogoSe muestran los productos
    Registrar clienteSe crea la cuenta
    Iniciar sesiónSe accede a la cuenta
    Crear pedidoSe guarda y aparece en el historial
    Editar productoEl cambio aparece en el catálogo
    Cambiar contraseñaFunciona la nueva contraseña
    Subir imagenSe guarda y se muestra
    Enviar contactoEl administrador puede leerlo

    No marques una prueba como correcta sin realizarla.

    34. Documentar incidencias

    Si encuentras un problema, registra:

    • Qué estabas intentando hacer.
    • El mensaje de error.
    • Qué comprobaste.
    • La causa que identificaste.
    • La solución aplicada.
    • Cómo verificaste que funcionaba.

    Si no encuentras incidencias, indica las comprobaciones preventivas que has realizado: configuración, servicios, puertos, permisos y conexión a la base de datos.

    35. Documentación que debes entregar

    Prepara un README.md con estas secciones:

    1. Título, autor y objetivo.
    2. Descripción de la aplicación.
    3. Requisitos.
    4. Estructura del proyecto.
    5. Instalación en XAMPP.
    6. Instalación en Ubuntu Server.
    7. Configuración de Apache y base de datos.
    8. Pruebas funcionales.
    9. Incidencias y soluciones.
    10. Comparación de los entornos.
    11. Conclusiones personales.

    Guarda las capturas en una carpeta:

    capturas

    Puedes insertarlas así:

    ![Panel de XAMPP con los servicios iniciados](capturas/xampp-servicios.png)

    Los comandos deben aparecer en bloques de código:

    ```bash
    sudo systemctl status apache2 --no-pager
    ```

    La documentación debe explicar tu instalación real, con tus rutas, tu IP, tus versiones y tus resultados.

    No entregues únicamente una copia de este enunciado.

    36. Entrega final

    Entrega un ZIP con:

    • README.md.
    • La carpeta capturas.
    • Los fragmentos de configuración necesarios para comprender tu instalación, con las contraseñas sustituidas por un marcador.

    Por ejemplo:

    'password' => 'CONTRASEÑA_OCULTA'

    No es necesario incluir los discos de la máquina virtual ni una copia completa de XAMPP.

    Conserva las dos instalaciones funcionando para poder demostrarlas en clase.

    37. Comprobación final antes de entregar

    • La aplicación funciona en XAMPP.
    • La aplicación funciona en Ubuntu Server.
    • Has comprobado las funciones de cliente y administrador.
    • La subida de imágenes funciona en ambos entornos.
    • Apache sirve únicamente la carpeta public.
    • Has identificado los modelos, DAO y archivos de operaciones.
    • Has observado una respuesta JSON.
    • La documentación contiene pasos, comandos y evidencias.
    • Las capturas se visualizan correctamente en el README.
    • Puedes explicar qué has configurado y por qué.

    La actividad se considerará completada cuando puedas mostrar las dos instalaciones y explicar el recorrido de una petición desde el navegador hasta la base de datos.

  • Proyecto: Operación HAWKINS

    Proyecto: Operación HAWKINS

    Diseño de una infraestructura empresarial con VLSM y servicios de red

    1. Introducción

    Audio presentación

    El Centro de Investigación Avanzada de Hawkins es una organización ficticia inspirada en la serie Stranger Things. Tras varios incidentes en sus instalaciones, la dirección ha decidido sustituir su red improvisada por una infraestructura profesional, segmentada, documentada y preparada para crecer.

    El centro está formado por tres ubicaciones:

    • Sede Central de Hawkins: oficinas, laboratorios, seguridad, servidores y zona de visitantes.
    • Estación de Campo Starcourt: pequeño centro remoto de observación y comunicaciones.
    • Búnker NINA: instalación aislada en la que se realizan pruebas especialmente sensibles.

    Vuestro equipo ha sido contratado como consultora de sistemas y redes. Tendréis que analizar las necesidades de la organización, diseñar el direccionamiento mediante VLSM, construir una maqueta funcional y desplegar varios servicios de red.

    No se valorará únicamente que los equipos puedan comunicarse. La empresa exige una solución lógica, segura, ampliable, comprobada y correctamente documentada.


    2. Modalidad de trabajo

    • Trabajo individual o en parejas, según indique el profesor.
    • Herramienta principal recomendada: Cisco Packet Tracer.
    • Red IPv4 asignada a todo el proyecto: 172.20.0.0/16.
    • Todos los cálculos de subnetting deberán realizarse manualmente y explicarse además de mostrar las tablas completas.
    • En Packet Tracer no es necesario colocar físicamente todos los dispositivos indicados. Se representará cada red con un número reducido de equipos, pero el direccionamiento deberá admitir la cantidad real solicitada.
    • No es necesario realizar la configuración en los routers y switch, aunque si debe estar bien explicado en la documentación.

    3. Objetivos de aprendizaje

    Al terminar el proyecto, el alumnado deberá ser capaz de:

    1. Analizar las necesidades de una infraestructura con varias sedes.
    2. Crear subredes de distinto tamaño mediante VLSM.
    3. Calcular direcciones de red, broadcast, rangos utilizables y máscaras.
    4. Organizar departamentos mediante redes o VLAN independientes.
    5. Configurar direccionamiento estático y dinámico.
    6. Permitir la comunicación entre redes mediante routing.
    7. Aplicar reglas básicas de segmentación y control de acceso.
    8. Verificar el funcionamiento mediante pruebas reproducibles.
    9. Elaborar documentación técnica en Markdown.

    4. Historia del proyecto

    La infraestructura antigua utilizaba una única red para empleados, cámaras, servidores y visitantes. Esto ha provocado direcciones duplicadas, dificultad para localizar averías y acceso de usuarios no autorizados a recursos internos.

    La nueva red recibirá el nombre HAWKNET. Cada departamento deberá disponer de su propia subred. Las tres sedes estarán conectadas mediante enlaces WAN y compartirán determinados servicios alojados en la sede central.

    La dirección establece cuatro prioridades:

    • separar el tráfico de los departamentos;
    • garantizar que los nombres internos se resuelvan mediante DNS;
    • proporcionar configuración automática a los puestos de usuario;
    • proteger los laboratorios y la red de gestión frente a visitantes y dispositivos IoT.

    5. Topología general requerida

    La maqueta deberá contener, como mínimo:

    • un router o dispositivo de capa 3 en cada sede;
    • switches suficientes para representar la segmentación solicitada;
    • un switch principal en la sede central;
    • enlaces troncales cuando se utilicen VLAN;
    • un enlace entre la sede central y Starcourt;
    • un enlace entre la sede central y el búnker NINA;
    • servidores centralizados en Hawkins;
    • al menos un equipo cliente representativo en cada subred;
    • una nube o router ISP que represente Internet;
    • puntos de acceso inalámbricos para empleados y visitantes.

    El diseño exacto queda a criterio del equipo, pero todas las decisiones deberán justificarse.


    6. Necesidades de direccionamiento

    Se utilizará la red 172.20.0.0/16. A partir de ella debéis crear todas las subredes necesarias mediante VLSM.

    Las cantidades representan la capacidad que debe soportar cada red, no el número de dispositivos que hay que insertar en Packet Tracer.

    6.1. Sede Central de Hawkins

    Red o departamentoDispositivos actualesCrecimiento mínimo previsto
    Sensores, puertas y dispositivos IoT85020 %
    Wi-Fi de visitantes42020 %
    Personal administrativo19020 %
    Laboratorio de Energía11020 %
    Videovigilancia y seguridad física7520 %
    Investigadores6020 %
    Centro de Operaciones4220 %
    Servidores internos2620 %
    Telefonía IP2420 %
    Administración de red1220 %

    6.2. Estación de Campo Starcourt

    Red o departamentoDispositivos actualesCrecimiento mínimo previsto
    Personal de operaciones4820 %
    Sensores y cámaras3020 %
    Invitados1820 %
    Administración de red620 %

    6.3. Búnker NINA

    Red o departamentoDispositivos actualesCrecimiento mínimo previsto
    Laboratorio restringido2820 %
    Seguridad1420 %
    Servidores locales620 %
    Administración de red520 %

    6.4. Enlaces entre routers

    Debéis reservar una subred independiente para cada uno de estos enlaces:

    • Hawkins ↔ Starcourt.
    • Hawkins ↔ NINA.
    • Hawkins ↔ ISP.

    Cada enlace solo necesita las direcciones imprescindibles para sus dos extremos. Deberéis decidir si usar /30 o /31 y comprobar si la opción elegida es compatible con el simulador y los dispositivos utilizados.


    7. Normas para realizar el VLSM

    1. Calculad la capacidad necesaria sumando el crecimiento del 20 % y redondeando siempre hacia arriba.
    2. Recordad incluir las direcciones que vayan a consumir puertas de enlace, servidores u otros dispositivos de infraestructura.
    3. Ordenad las redes desde la que necesita más direcciones hasta la que necesita menos.
    4. Asignad a cada red el bloque más pequeño que cubra su necesidad.
    5. No pueden existir subredes solapadas.
    6. No debe desperdiciarse un bloque excesivamente grande sin una justificación técnica.
    7. La primera dirección utilizable de cada LAN se asignará, salvo justificación, a su puerta de enlace.
    8. Los servidores, routers, switches gestionables e impresoras usarán direcciones estáticas.
    9. Los clientes de usuario recibirán su configuración mediante DHCP.

    Para cada subred debéis mostrar el razonamiento utilizado. No será suficiente entregar una tabla generada automáticamente.


    8. Tabla obligatoria de direccionamiento

    La documentación incluirá una tabla como la siguiente, completada para todas las redes:

    SedeRed/VLANNecesidad con crecimientoPrefijoMáscara decimalDirección de redPrimer hostÚltimo hostBroadcastGateway
    HawkinsIoT

    También se añadirá una segunda tabla para los dispositivos configurados:

    DispositivoInterfazDirección IPMáscara/prefijoGatewayDNSAsignación
    SRV-DNS-01Fa0Estática

    9. VLAN y segmentación

    En la sede central cada departamento deberá estar separado mediante una VLAN o interfaz física diferente. Si se utilizan VLAN, debéis:

    • asignar un identificador y un nombre descriptivo a cada VLAN;
    • configurar los puertos de acceso;
    • utilizar una VLAN de gestión distinta de la VLAN nativa;
    • documentar qué puertos pertenecen a cada VLAN.

    Los identificadores de VLAN son libres, pero deben seguir un criterio coherente. Por ejemplo, se pueden agrupar por sede o por tipo de servicio.


    10. Routing entre sedes

    Todos los routers deberán conocer las redes remotas. El equipo elegirá una de estas opciones:

    El fallo de una ruta no podrá ocultarse añadiendo rutas innecesarias o utilizando una única red sin segmentación.


    11. Servicios obligatorios

    La red de servidores y las redes de administración no utilizarán DHCP para sus dispositivos principales.

    11.2. DNS

    El dominio interno será:

    hawkins.lab

    El DNS deberá contener, al menos, estos registros:

    11.3. Servidor web e intranet

    El servidor web mostrará una página sencilla con:

    • nombre y logotipo ficticio de la organización;
    • estado de las tres sedes;
    • mensaje de bienvenida;
    • nombre de los integrantes del equipo;
    • fecha de la última comprobación.

    La web pública de pruebas podrá ser accesible desde todas las redes. La intranet solo deberá ser accesible desde redes internas autorizadas.


    12. Requisitos básicos de seguridad

    La segmentación debe traducirse en reglas de acceso. Se configurarán ACL u otro mecanismo equivalente para cumplir, como mínimo, estas políticas:

    1. La red de visitantes solo puede acceder al DNS necesario, al portal web y a Internet.
    2. Los visitantes no pueden iniciar conexiones hacia las redes internas.
    3. La red IoT no puede acceder a Administración de red ni a los equipos de investigadores.
    4. Solo Administración de red puede gestionar routers y switches mediante SSH.
    5. Telnet deberá permanecer deshabilitado.
    6. El laboratorio NINA no será accesible desde redes de invitados.
    7. Seguridad podrá consultar las cámaras y sensores de las tres sedes.
    8. Los servidores DNS, DHCP y correo deberán ser accesibles únicamente desde las redes que necesiten cada servicio.

    Las reglas se colocarán lo más cerca posible del origen o destino y se documentará el motivo de cada una.


    13. Acceso a Internet y NAT

    La sede central se conectará a un router ISP. Debéis configurar:

    • una ruta por defecto hacia el ISP;
    • NAT/PAT para que las redes privadas autorizadas puedan salir a Internet;
    • un servidor externo de prueba, por ejemplo www.vecna-news.net, situado en una red que no pertenezca a 172.20.0.0/16;
    • bloqueo de salida para las redes que, según vuestra política, no deban acceder a Internet.

    No es necesario publicar servidores internos mediante NAT estático, aunque puede realizarse como ampliación.


    14. Nombres y configuración de dispositivos

    Los nombres deberán permitir identificar su ubicación y función. Ejemplos:

    RTR-HAW-01
    RTR-STAR-01
    RTR-NINA-01
    SW-HAW-CORE-01
    SW-HAW-LAB-01
    SRV-HAW-DNS-01
    PC-STAR-OPS-01

    15. Fases de realización

    Fase 1. Análisis

    1. Leed el caso completo.
    2. Identificad las sedes, departamentos, servidores y enlaces.
    3. Dibujad un primer esquema lógico.
    4. Decidid qué servicios serán centrales y cuáles serán locales.
    5. Anotad las restricciones de seguridad.

    Fase 2. Cálculo VLSM

    1. Calculad el número de hosts con crecimiento.
    2. Ordenad las redes de mayor a menor.
    3. Determinad el prefijo mínimo de cada una.
    4. Asignad los bloques dentro de 172.20.0.0/16.
    5. Calculad red, primer host, último host y broadcast.
    6. Comprobad que no existen solapamientos.
    7. Reservad espacio para futuras ampliaciones.

    Fase 3. Construcción de la topología

    1. Colocad routers, switches, servidores y clientes.
    2. Cablead correctamente los dispositivos.
    3. Asignad nombres a todos los equipos.
    4. Cread VLAN y configurad puertos.
    5. Configurad puertas de enlace.

    Fase 4. Direccionamiento y routing

    1. Configurad interfaces y subinterfaces.
    2. Asignad direcciones estáticas a infraestructura.
    3. Configurad rutas estáticas u OSPF.

    Fase 5. Pruebas y documentación

    1. Ejecutad el plan de pruebas.
    2. Corregid los fallos encontrados.
    3. Capturad evidencias relevantes.
    4. Completad el README.md.
    5. Revisad que otra persona pueda comprender y reproducir el proyecto.

    No se considerará demostrada una política si únicamente se prueba el caso permitido. Cuando corresponda, deberán probarse tanto el acceso correcto como su bloqueo.


    17. Entregables

    La entrega contendrá una carpeta comprimida con esta estructura orientativa:

    operacion-hawkins/
    ├── README.md
    ├── packet-tracer/
    │   └── hawknet.pkt
    ├── configuraciones/
    │   ├── RTR-HAW-01.txt
    │   ├── RTR-STAR-01.txt
    │   └── RTR-NINA-01.txt
    ├── diagramas/
    │   ├── topologia-logica.png
    │   └── topologia-fisica.png
    

    El archivo principal se llamará exactamente README.md y utilizará Markdown. Más adelante, cuando se haya trabajado Git, el mismo proyecto deberá poder incorporarse a un repositorio sin reorganizar toda la documentación.


    18. Contenido obligatorio del README.md

    1. Portada: título, integrantes, curso y fecha.
    2. Resumen ejecutivo del proyecto.
    3. Descripción de las tres sedes.
    4. Requisitos detectados.
    5. Diagrama lógico de red.
    6. Explicación paso a paso del cálculo VLSM.
    7. Tabla completa de direccionamiento.
    8. Tabla de VLAN.
    9. Inventario y función de los dispositivos.
    10. Explicación del routing seleccionado.
    11. Reglas de seguridad y ACL aplicadas.
    12. Configuración de NAT/PAT.
    13. Plan de pruebas y resultados.
    14. Problemas encontrados y soluciones.
    15. Conclusiones y posibles mejoras.
    16. Fuentes consultadas.

    Las capturas deben estar recortadas, ser legibles y llevar un pequeño texto explicativo. No se admitirán decenas de capturas sin contexto ni configuraciones pegadas sin explicación.


    19. Condiciones de aceptación

    El proyecto se considerará funcional cuando:

    • todas las subredes estén calculadas correctamente;
    • no haya direcciones duplicadas ni bloques solapados;
    • la comunicación entre sedes funcione;
    • los nombres internos se resuelvan mediante DNS;
    • los servicios obligatorios sean accesibles desde las redes autorizadas;
    • los accesos prohibidos sean realmente bloqueados;
    • el acceso exterior utilice NAT/PAT;
    • las pruebas estén documentadas;
    • el archivo de Packet Tracer se abra sin errores;
    • el README permita entender el trabajo sin una explicación oral adicional.

    Un proyecto en el que “todo puede comunicarse con todo” no cumplirá los requisitos de segmentación y seguridad.


    Las ampliaciones solo puntuarán si la parte obligatoria funciona y están correctamente explicadas.


    20. Penalizaciones importantes

    • Subredes solapadas o uso incorrecto de red/broadcast.
    • Archivo .pkt que no abre o que no coincide con la documentación.
    • Capturas de pantalla ajenas o configuraciones copiadas que el equipo no pueda explicar.
    • Uso de una sola red para evitar el trabajo de VLSM.

    21. Defensa del proyecto

    El profesor podrá seleccionar al azar cualquier dispositivo o prueba. El equipo tendrá que:

    Todos los integrantes deben conocer el proyecto completo. La división de tareas no exime de comprender el trabajo de la otra persona.


    23. Pregunta final de reflexión

    Incluid al final del README una respuesta razonada a estas cuestiones:

    • ¿Qué ventajas ofrece VLSM frente a utilizar la misma máscara en todas las redes?
    • ¿Qué ocurriría si empleados, servidores, cámaras y visitantes compartieran la misma red?
    • ¿Qué servicio causaría un impacto mayor si dejara de funcionar: DHCP, DNS o routing? Justificad la respuesta según vuestra infraestructura.
    • ¿Qué parte del diseño cambiaríais si la empresa duplicara su tamaño?

    Misión

    La red de Hawkins no debe limitarse a “tener pings”. Debe ser una infraestructura organizada, útil y defendible. Vuestro objetivo es demostrar que podéis convertir las necesidades de una organización en un diseño IP correcto, desplegar los servicios básicos y verificar que cada usuario accede únicamente a los recursos que necesita.

  • Proyecto: despliegue, ampliación y documentación de un servidor web con Ubuntu

    Proyecto: despliegue, ampliación y documentación de un servidor web con Ubuntu

    1. Presentación de la actividad

    Audio de introducción

    En esta actividad vas a desplegar y configurar un servidor web con Ubuntu siguiendo tres recursos proporcionados por el profesor. El objetivo no es limitarse a copiar comandos y realizar capturas de pantalla. Deberás comprender lo que haces, comprobar que cada elemento funciona y crear una documentación técnica que permita a otra persona repetir el proceso.

    El trabajo se desarrollará en tres partes consecutivas:

    1. Montaje del servidor web con Ubuntu.
    2. Ampliación UFW
    3. Configuración de Apache mediante archivos .htaccess.

    Cada parte amplía la anterior. Al terminar tendrás un único servidor funcional y un único documento técnico que describa todo el proyecto desde la preparación del entorno hasta las pruebas finales.

    2. Recursos obligatorios

    Debes seguir los tres recursos en el orden indicado. Puedes consultar otras fuentes para resolver problemas o ampliar las explicaciones, pero deberás citarlas en el apartado de referencias.

    3. Objetivos de aprendizaje

    Al finalizar la actividad deberás ser capaz de:

    • Preparar un sistema Ubuntu para funcionar como servidor.
    • Instalar, configurar y comprobar los servicios solicitados.
    • Identificar la función de los principales archivos, directorios, módulos, usuarios, permisos y puertos utilizados.
    • Aplicar configuraciones de Apache y comprobar sus efectos.
    • Interpretar los resultados de comandos de diagnóstico.
    • Detectar errores frecuentes y documentar su solución.
    • Valorar los riesgos de seguridad de una configuración.
    • Redactar documentación técnica clara y reproducible utilizando Markdown.
    • Organizar evidencias sin convertir el documento en una sucesión de capturas.
    • Preparar el proyecto para publicarlo posteriormente en un repositorio Git.

    4. Resultado que debes conseguir

    Debes disponer de un servidor Ubuntu desde el que se puedan demostrar las configuraciones solicitadas en las tres partes. Además, entregarás una documentación técnica completa en un archivo llamado exactamente:

    README.md

    La extensión correcta es .md, no .me. El archivo se escribirá con sintaxis Markdown.

    La documentación debe ser una ampliación razonada de los materiales. Esto significa que no debes copiar literalmente las explicaciones del profesor. Tienes que describir el proceso con tus propias palabras, explicar qué hace cada elemento importante y aportar pruebas de funcionamiento.

    5. Modalidad de realización

    • La actividad será individual, salvo que el profesor indique expresamente otra modalidad.
    • Cada estudiante deberá utilizar su propia máquina virtual o servidor asignado.
    • No se compartirán archivos README.md, capturas ni textos entre compañeros.
    • Sí se permite ayudar a localizar un error, siempre que cada estudiante comprenda y documente su propia solución.

    6. Preparación antes de comenzar

    Antes de modificar el servidor, anota en tu documentación:

    • Sistema de virtualización o equipo empleado.
    • Versión de Ubuntu.
    • Nombre de host.
    • Dirección IP del servidor.
    • Tipo de red de la máquina virtual: NAT, puente, red interna u otra.
    • Recursos asignados: CPU, memoria RAM y disco.
    • Dirección IP o nombre del equipo cliente desde el que realizarás las pruebas.

    Incluye una tabla similar a esta, completada con tus datos reales:

    ElementoConfiguración utilizada
    Sistema operativoUbuntu Server …
    Nombre del servidor…
    Dirección IP…
    CPU y RAM…
    Disco…
    Tipo de red…
    Equipo cliente…

    Recomendación previa

    Si trabajas con una máquina virtual, crea una instantánea antes de comenzar cada parte. Así podrás volver al estado anterior si una configuración deja el servidor inaccesible. Anota en el README.md cuándo has creado esas instantáneas.

    Protección de datos

    No incluyas en la entrega:

    • Contraseñas reales.
    • Claves privadas.
    • Tokens o credenciales.
    • Cookies o sesiones del Campus Virtual.
    • Direcciones públicas o datos personales que no sean necesarios.

    Oculta estos datos en las capturas. Utiliza valores de ejemplo cuando expliques credenciales.

    7. Parte 1: montaje del servidor web con Ubuntu

    Sigue el recurso de la parte 1 y documenta el proceso. Tu explicación deberá incluir, como mínimo, los siguientes bloques cuando aparezcan en el procedimiento realizado:

    7.1 Preparación del sistema

    Documenta la actualización del sistema y cualquier paquete previo necesario.

    Para cada comando importante:

    1. Escríbelo en un bloque de código.
    2. Explica qué hace.
    3. Describe las opciones o parámetros utilizados.
    4. Indica el resultado esperado.
    5. Añade una comprobación objetiva.

    Ejemplo del nivel de explicación esperado:

    sudo systemctl status apache2

    Este comando consulta el estado del servicio Apache. En la salida se debe comprobar que aparece como activo y en ejecución. No es suficiente con escribir «funciona»: hay que indicar qué dato de la salida permite afirmarlo.

    7.2 Instalación y prueba de Apache

    Debes explicar:

    • Qué es Apache y qué función cumple en el proyecto.
    • Qué paquete se instala.
    • Cómo se inicia, detiene, reinicia y consulta el servicio.
    • Qué directorio contiene inicialmente el sitio web.
    • Qué puerto utiliza HTTP.
    • Cómo accedes desde el equipo cliente.
    • Cómo compruebas el servicio desde terminal y desde navegador.

    Incluye una página web propia de prueba. No entregues únicamente la página predeterminada de Apache. La página debe mostrar, al menos, tu nombre, el título del proyecto y una indicación de que el servidor está operativo.

    7.3 Acceso remoto y transferencia de archivos

    Documenta los métodos de acceso o transferencia indicados en el recurso. Explica:

    • Qué servicio o programa interviene.
    • Qué puerto utiliza.
    • Qué usuario empleas y a qué directorio puede acceder.
    • Qué permisos has configurado.
    • Cómo has transferido o editado un archivo del sitio.
    • Cómo has verificado que el cambio aparece en el navegador.

    Si se utiliza FTP, añade una breve valoración de seguridad: diferencia entre FTP sin cifrar y alternativas como SFTP. No publiques credenciales reales.

    7.4 PHP

    Si el procedimiento incluye PHP, documenta:

    • Paquetes instalados y función de cada uno.
    • Versión instalada.
    • Integración con Apache.
    • Creación y ejecución de una página PHP de prueba.
    • Diferencia entre ejecutar PHP desde el servidor web y desde la terminal.

    Si utilizas una página con phpinfo(), elimínala o restríngele el acceso al finalizar, ya que puede mostrar información sensible del servidor. Explica esta decisión en el documento.

    7.5 Acceso mediante SSH o Visual Studio Code

    Si realizas este apartado, incluye:

    • Herramientas empleadas.
    • Configuración de la conexión sin mostrar contraseñas o claves privadas.
    • Directorio remoto abierto.
    • Prueba de edición de un archivo.
    • Evidencia del resultado servido por Apache.

    7.6 Comprobación de la parte 1

    Antes de continuar, verifica al menos:

    • El servidor responde a través de la red.
    • Apache está activo.
    • La página propia se carga desde el cliente.
    • Los archivos se encuentran en el directorio correcto.
    • PHP funciona, si forma parte del procedimiento.
    • El acceso remoto o la transferencia funciona, si se ha configurado.

    Incluye una tabla de pruebas:

    PruebaComando o acciónResultado esperadoResultado obtenidoEstado
    Estado de Apachesystemctl status apache2Servicio activo…Superada/No superada
    Acceso webAbrir http://IP_SERVIDORCarga la web propia……
    Prueba de PHPAbrir la URL de pruebaPHP genera la respuesta……

    8. Parte 2: ampliación del Campus Virtual

    Accede al recurso de la parte 2 con tu cuenta del Campus Virtual y realiza todos los pasos en el orden indicado.

    Como este bloque amplía el servidor de la parte 1, comienza explicando:

    • Qué nueva necesidad resuelve.
    • Qué servicio, componente o configuración añade.
    • Qué requisitos de la parte 1 necesita.
    • Qué cambios producirá en el servidor.

    Documentación obligatoria de cada paso

    Para cada acción relevante de la guía del campus incluye:

    1. Objetivo del paso. Qué se pretende conseguir.
    2. Comando o configuración. Escrito con el lenguaje correcto en un bloque de código.
    3. Explicación. Qué hace y por qué es necesario.
    4. Archivo afectado. Ruta completa y fragmento modificado, si corresponde.
    5. Comprobación. Comando, petición, acceso desde navegador o prueba equivalente.
    6. Resultado. Qué ocurrió realmente en tu servidor.
    7. Incidencia. Error encontrado y solución aplicada, si la hubo.

    No copies el texto completo del Campus Virtual. Reescribe el procedimiento con tus propias palabras y conserva únicamente los comandos, rutas y fragmentos de configuración necesarios.

    Comprobación de la parte 2

    Crea una tabla con todas las pruebas solicitadas por la guía. Debe quedar claro qué evidencia demuestra que la ampliación funciona y cómo podría repetirla otra persona.

    9. Parte 3: configuración de .htaccess en Apache

    Realiza la práctica de la parte 3 sobre el servidor ya configurado. Antes de empezar, crea una copia de seguridad de los archivos que vayas a modificar y documenta cómo podrías restaurarlos.

    9.1 Activación de .htaccess

    Explica:

    • Qué es un archivo .htaccess.
    • En qué directorios puede actuar.
    • Qué función cumple la directiva AllowOverride.
    • Qué diferencia existe entre AllowOverride None y AllowOverride All.
    • Qué archivo de Apache has modificado.
    • Cómo has validado la configuración antes de reiniciar o recargar el servicio.
    • Cómo has comprobado que Apache continúa funcionando.

    9.2 Autenticación básica

    Configura una zona restringida y documenta:

    • Directorio protegido.
    • Creación del archivo de usuarios.
    • Directivas de autenticación utilizadas.
    • Prueba sin autenticar, con credenciales incorrectas y con credenciales correctas.
    • Código de estado HTTP o comportamiento observado en cada caso.

    No muestres la contraseña. Investiga y explica por qué la autenticación básica debería utilizarse junto con HTTPS y por qué es preferible guardar .htpasswd fuera del directorio público cuando sea posible.

    9.3 Páginas de error personalizadas

    Crea páginas propias para los errores solicitados, por ejemplo 403 y 404. Las páginas deberán tener un diseño mínimo coherente con tu sitio.

    Comprueba cada error provocándolo de forma controlada e indica:

    • URL o acción de prueba.
    • Código esperado.
    • Código recibido.
    • Página mostrada.

    9.4 Cabeceras de seguridad

    Aplica las cabeceras propuestas en la práctica y crea una tabla como esta:

    CabeceraValor aplicadoRiesgo que ayuda a reducirMétodo de comprobación
    X-Frame-Options………
    X-Content-Type-Options………
    Referrer-Policy………
    Permissions-Policy………

    Comprueba las cabeceras con las herramientas de desarrollo del navegador o con una petición de terminal, no solo mediante una captura del archivo de configuración.

    Si el recurso incluye una cabecera considerada antigua o desaconsejada por navegadores actuales, indícalo en tu análisis y explica qué mecanismo moderno se utiliza en su lugar. Esta observación deberá estar respaldada por una fuente técnica.

    9.5 Redirecciones

    Configura y prueba las redirecciones solicitadas. Explica la diferencia entre una redirección permanente y una temporal, incluyendo:

    • Código de estado.
    • Efecto para el navegador.
    • Posible efecto en caché.
    • Uso habitual.
    • Implicaciones generales para buscadores.

    Incluye una prueba donde se puedan observar la respuesta y el destino final.

    9.6 Reescritura de URLs

    Si la guía utiliza mod_rewrite, explica:

    • Qué problema resuelve.
    • Diferencia entre redirección y reescritura interna.
    • Módulo necesario.
    • Significado de la regla aplicada.
    • URL escrita por el usuario y recurso procesado realmente.

    Debes demostrar el funcionamiento con al menos un ejemplo propio diferente del ejemplo literal de la guía.

    9.7 Resto de configuraciones del recurso

    Completa también las configuraciones adicionales incluidas en la guía, como control de indexación, protección u ocultación de recursos, caché o compresión, cuando aparezcan en el procedimiento. Para cada una explica el objetivo, la configuración aplicada, la prueba realizada y su resultado.

    9.8 Comprobación de la parte 3

    Prepara una tabla final de pruebas que incluya, como mínimo:

    • Acceso a la zona restringida.
    • Error 403.
    • Error 404.
    • Cabeceras de seguridad.
    • Redirección temporal.
    • Redirección permanente.
    • Reescritura de URL.
    • Resto de funcionalidades realizadas.

    10. Ampliaciones técnicas obligatorias

    Tu trabajo no debe ser una reproducción literal de los enlaces. Añade las siguientes ampliaciones:

    Ampliación A: mapa del sistema

    Incluye un diagrama Mermaid que muestre, al menos, el equipo cliente, el servidor Ubuntu, Apache y los componentes principales instalados. Ejemplo de sintaxis:

    flowchart LR
        A[Cliente] -->|HTTP| B[Apache en Ubuntu]
        B --> C[Sitio web]
        B --> D[PHP]

    Adapta el diagrama a tu instalación real. No copies el ejemplo sin modificarlo.

    Ampliación B: relación de puertos y servicios

    Incluye una tabla con los servicios utilizados, sus puertos, protocolo de transporte, finalidad y estado final. Si un puerto no debe quedar accesible, explícalo.

    Ampliación C: seguridad

    Identifica al menos cinco riesgos o decisiones de seguridad observados durante las tres partes. Para cada uno indica:

    • Riesgo.
    • Configuración relacionada.
    • Consecuencia posible.
    • Medida de mejora.
    • Prioridad: baja, media o alta.

    Ampliación D: diagnóstico

    Incluye una pequeña guía de resolución de problemas con un mínimo de cinco incidencias. Pueden ser errores que hayas encontrado o errores razonables que hayas reproducido de forma segura.

    SíntomaPosible causaComprobaciónSolución
    Apache no arrancaError de sintaxis……
    La web no responde desde el cliente………

    Ampliación E: comandos de comprobación

    Crea una sección de consulta rápida con los comandos que permitan comprobar el estado del servidor. No basta con enumerarlos: añade una frase que explique qué verifica cada uno.

    11. Estructura obligatoria del README.md

    El documento deberá contener, como mínimo, esta estructura:

    # Despliegue y ampliación de un servidor web con Ubuntu
    
    ## 1. Autor y datos de la práctica
    ## 2. Resumen del proyecto
    ## 3. Objetivos
    ## 4. Entorno y requisitos
    ## 5. Esquema de la infraestructura
    ## 6. Parte 1: montaje del servidor web
    ## 7. Parte 2: ampliación del Campus Virtual
    ## 8. Parte 3: configuración de .htaccess
    ## 9. Pruebas finales
    ## 10. Problemas encontrados y soluciones
    ## 11. Análisis de seguridad
    ## 12. Conclusiones
    ## 13. Referencias

    Puedes añadir subsecciones, pero no eliminar las anteriores.

    12. Requisitos de formato Markdown

    En el README.md deberás utilizar correctamente:

    • Títulos y subtítulos con #, ## y ###.
    • Párrafos breves y explicativos.
    • Listas numeradas y no numeradas.
    • Tablas.
    • Enlaces con texto descriptivo.
    • Imágenes con texto alternativo.
    • Bloques de código indicando el lenguaje: bash, apache, html, php, etc.
    • Código corto entre comillas invertidas.
    • Avisos o notas mediante citas >.
    • Al menos un diagrama Mermaid.
    • Índice con enlaces internos si el documento es largo.

    No utilices capturas para mostrar texto que puede copiarse, como comandos o configuraciones. Escríbelo en bloques de código y reserva las imágenes para demostrar resultados visuales relevantes.

    13. Capturas y evidencias

    Guarda las imágenes en una carpeta llamada img y utiliza nombres descriptivos:

    img/01-apache-activo.png
    img/02-web-desde-cliente.png
    img/03-zona-restringida.png
    img/04-error-404.png

    Insértalas con rutas relativas:

    ![Apache activo en el servidor](img/01-apache-activo.png)

    Cada evidencia debe cumplir estas condiciones:

    • Tener relación directa con una prueba.
    • Ser legible.
    • Mostrar solo la información necesaria.
    • Tener una explicación antes o después de la imagen.
    • Ocultar contraseñas, tokens y otros datos sensibles.
    • No repetir varias capturas que demuestren exactamente lo mismo.

    14. Organización de la entrega

    La carpeta deberá tener una estructura similar a esta:

    apellido-nombre-servidor-web/
    ├── README.md
    ├── img/
    │   ├── 01-apache-activo.png
    │   ├── 02-web-desde-cliente.png
    │   └── ...
    ├── web/
    │   ├── index.html
    │   ├── 403.html
    │   ├── 404.html
    │   └── ...
    └── config/
        ├── htaccess.txt
        └── ...

    Los archivos de configuración se entregarán como copia para su revisión. Si un archivo debe llamarse .htaccess en el servidor, puedes conservar ese nombre dentro de la entrega o añadir una copia legible como htaccess.txt. Nunca incluyas archivos de contraseñas reales como .htpasswd.

    15. Entrega actual mediante ZIP

    Como todavía no hemos trabajado Git, en esta primera entrega comprime la carpeta completa en un archivo ZIP con este nombre:

    apellido-nombre-servidor-web.zip

    Antes de subirlo, descomprímelo en una carpeta temporal y comprueba que:

    • Existe README.md en la raíz.
    • El documento se abre correctamente.
    • Las imágenes se muestran usando rutas relativas.
    • Se incluyen los archivos web y las configuraciones necesarias.
    • No hay contraseñas ni datos sensibles.
    • No se incluyen discos virtuales, imágenes ISO, copias completas de la máquina virtual ni carpetas innecesarias.

    Sube el ZIP al espacio de entrega indicado por el profesor.

    16. Publicación posterior en Git

    La entrega mediante ZIP es provisional. Cuando estudiemos Git y GitHub, deberás publicar esta misma actividad en un repositorio.

    Por este motivo, organiza desde ahora el trabajo como si ya fuera un repositorio:

    • Un único README.md principal en la raíz.
    • Carpetas con nombres simples, sin espacios ni tildes.
    • Rutas relativas para imágenes y enlaces internos.
    • Archivos ordenados y con nombres descriptivos.
    • Ninguna contraseña o credencial guardada.
    • Un archivo .gitignore cuando aprendamos Git, si el proyecto lo necesita.

    Más adelante se valorará la evolución del documento mediante commits. No será válido crear el repositorio al final y subir todo sin mostrar el proceso de mejora cuando se explique esa parte de la asignatura.

    17. Defensa y comprobación por parte del profesor

    El profesor podrá solicitar que el estudiante:

    • Explique cualquier comando incluido.
    • Localice un archivo de configuración.
    • Reinicie o recargue un servicio.
    • Demuestre una de las pruebas.
    • Modifique una regla sencilla.
    • Interprete un error.
    • Justifique una decisión de seguridad.

    Una configuración que funciona pero que el estudiante no puede explicar se considerará incompleta.

    18. Criterios de evaluación

    CriterioPeso
    Parte 1: instalación, configuración y pruebas20 %
    Parte 2: realización, explicación y pruebas15 %
    Parte 3: .htaccess, seguridad y pruebas25 %
    Ampliaciones técnicas y capacidad de análisis15 %
    Calidad del README.md y uso de Markdown15 %
    Organización, evidencias, referencias y entrega10 %
    Total100 %

    Para obtener una valoración alta

    El documento debe permitir reproducir el proyecto, explicar el propósito de las configuraciones, incluir pruebas objetivas, analizar riesgos y mostrar capacidad para resolver problemas.

    Penalizaciones importantes

    Podrán reducir la calificación:

    • Copiar y pegar los recursos sin elaborar explicaciones propias.
    • Incluir comandos que no se han ejecutado o resultados inventados.
    • Presentar capturas sin explicación.
    • No demostrar el funcionamiento de una parte.
    • Utilizar rutas absolutas locales para las imágenes del README.md.
    • Entregar enlaces o imágenes rotos.
    • Exponer contraseñas o credenciales.
    • No respetar la estructura de la entrega.

    La ausencia de una de las tres partes impedirá considerar el proyecto completo.

    19. Lista de comprobación final del estudiante

    Antes de entregar, confirma cada punto:

    • He completado las tres partes.
    • Mi archivo principal se llama README.md.
    • He escrito las explicaciones con mis propias palabras.
    • Puedo explicar todos los comandos que aparecen.
    • He indicado mi entorno y configuración de red.
    • He incluido un diagrama Mermaid adaptado a mi instalación.
    • He realizado y documentado pruebas objetivas.
    • He añadido las ampliaciones técnicas obligatorias.
    • He documentado al menos cinco problemas o casos de diagnóstico.
    • He analizado al menos cinco aspectos de seguridad.
    • Las imágenes tienen nombres descriptivos y rutas relativas.
    • No he incluido contraseñas, claves ni datos sensibles.
    • Las referencias utilizadas aparecen citadas.
    • He comprobado el ZIP después de descomprimirlo.
    • La estructura está preparada para convertirla posteriormente en un repositorio Git.

    20. Idea clave de la actividad

    El resultado no es solo un servidor que funciona. El verdadero producto de este proyecto es una documentación técnica que demuestre qué has construido, por qué lo has configurado así, cómo sabes que funciona y cómo podría mantenerlo o reproducirlo otra pe

  • Crear un entorno DEV tipo XAMPP/MAMP con Docker

    Crear un entorno DEV tipo XAMPP/MAMP con Docker

    Objetivo del proyecto

    En esta práctica vamos a crear un entorno de desarrollo web usando Docker. El objetivo es tener un sistema parecido a XAMPP o MAMP, pero más profesional, portable y fácil de reconstruir.

    El entorno permitirá trabajar con:

    • Apache
    • PHP
    • Extensiones comunes de PHP
    • MySQL
    • phpMyAdmin
    • Mailpit para probar correos
    • Redis como servicio adicional opcional
    • Una carpeta local donde guardaremos todos nuestros proyectos web

    Todos los contenedores tendrán el prefijo:

    DEV_

    De esta forma podremos identificarlos fácilmente dentro de Docker.


    1. ¿Qué vamos a construir?

    El sistema tendrá esta estructura:

    Carpeta local del alumno
    │
    ▼
    +-----------------------------+
    | Carpeta DEVLOCAL |
    | |
    | proyecto1/ |
    | proyecto2/ |
    | pruebas/ |
    | _docker/ |
    +-------------+---------------+
    │
    ▼
    +-----------------------------+
    | Docker Compose |
    +-------------+---------------+
    │
    ▼
    +-----------------------------+
    | DEV_apache_php |
    | DEV_mysql |
    | DEV_phpmyadmin |
    | DEV_mailpit |
    | DEV_redis |
    +-----------------------------+

    El alumno podrá crear una carpeta con un proyecto PHP y verlo desde el navegador.

    Por ejemplo:

    http://localhost:8080/pruebas/

    2. Requisitos previos

    Cada alumno necesitará:

    • Un ordenador con Windows, Ubuntu o macOS.
    • Docker instalado.
    • Un editor de código, por ejemplo VS Code.
    • Terminal o consola.
    • Navegador web.

    Docker Compose se usará para definir y levantar varios contenedores a la vez. Docker lo describe como una herramienta para definir y ejecutar aplicaciones multicontenedor.


    3. Instalación de Docker en Windows

    En Windows se recomienda usar Docker Desktop.

    Docker Desktop para Windows incluye Docker Engine, Docker CLI y Docker Compose. La documentación oficial indica que Docker Desktop puede usar WSL 2 como motor de ejecución en Windows.

    Pasos recomendados

    1. Instalar o activar WSL 2.
    2. Instalar Docker Desktop para Windows.
    3. Reiniciar el equipo si lo solicita.
    4. Abrir Docker Desktop.
    5. Comprobar que Docker está funcionando.

    Desde PowerShell o Terminal de Windows:

    docker --version
    docker compose version

    Si ambos comandos responden con una versión, Docker está funcionando correctamente.

    Carpeta recomendada en Windows

    Para evitar problemas con permisos o sincronización, se recomienda crear la carpeta directamente en C:\.

    mkdir C:\DEVLOCAL

    No es recomendable usar carpetas dentro de OneDrive para este tipo de práctica.


    4. Instalación de Docker en Ubuntu

    En Ubuntu se puede instalar Docker Engine directamente desde los repositorios oficiales de Docker. La documentación oficial recomienda configurar primero el repositorio apt de Docker y después instalar Docker Engine y sus plugins.

    Actualizar paquetes

    sudo apt update
    sudo apt upgrade -y

    Instalar dependencias

    sudo apt install -y ca-certificates curl gnupg

    Crear carpeta para claves

    sudo install -m 0755 -d /etc/apt/keyrings

    Descargar clave oficial de Docker

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
    sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

    Dar permisos a la clave

    sudo chmod a+r /etc/apt/keyrings/docker.gpg

    Añadir repositorio oficial

    echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
    https://download.docker.com/linux/ubuntu \
    $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

    Instalar Docker

    sudo apt update
    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

    Comprobar instalación

    docker --version
    docker compose version

    Permitir usar Docker sin sudo

    sudo usermod -aG docker $USER

    Después de este comando, el alumno debe cerrar sesión y volver a entrar.

    Carpeta recomendada en Ubuntu

    mkdir -p ~/DEVLOCAL

    5. Instalación de Docker en macOS

    En macOS se recomienda instalar Docker Desktop para Mac. La documentación oficial diferencia entre Mac con chip Intel y Mac con Apple Silicon, por lo que hay que descargar la versión adecuada.

    Pasos recomendados

    1. Descargar Docker Desktop para Mac.
    2. Elegir la versión correcta:
      • Mac Intel.
      • Mac Apple Silicon.
    3. Instalar la aplicación.
    4. Abrir Docker Desktop.
    5. Esperar a que Docker arranque completamente.
    6. Comprobar desde Terminal:
    docker --version
    docker compose version

    Carpeta recomendada en macOS

    En el caso de este ejemplo usaremos:

    /Users/antoniooteroveiga/DEVLOCAL

    Para otro usuario sería algo similar a:

    /Users/nombre_usuario/DEVLOCAL

    6. Crear la estructura del proyecto

    La estructura será la misma en Windows, Ubuntu y macOS.

    Windows

    Desde PowerShell:

    mkdir C:\DEVLOCAL
    mkdir C:\DEVLOCAL\_docker
    mkdir C:\DEVLOCAL\_docker\php
    mkdir C:\DEVLOCAL\_docker\apache
    mkdir C:\DEVLOCAL\pruebas

    Ubuntu

    Desde Terminal:

    mkdir -p ~/DEVLOCAL/_docker/php
    mkdir -p ~/DEVLOCAL/_docker/apache
    mkdir -p ~/DEVLOCAL/pruebas

    macOS

    Desde Terminal:

    mkdir -p /Users/antoniooteroveiga/DEVLOCAL/_docker/php
    mkdir -p /Users/antoniooteroveiga/DEVLOCAL/_docker/apache
    mkdir -p /Users/antoniooteroveiga/DEVLOCAL/pruebas

    La estructura final será:

    DEVLOCAL/
    ├── _docker/
    │ ├── docker-compose.yml
    │ ├── inicio.sh
    │ ├── inicio.bat
    │ ├── php/
    │ │ ├── Dockerfile
    │ │ ├── php.ini
    │ │ └── msmtprc
    │ └── apache/
    │ └── 000-default.conf
    └── pruebas/
    └── index.php

    7. Crear el archivo docker-compose.yml

    Este archivo define todos los servicios del entorno.

    Debe estar dentro de:

    DEVLOCAL/_docker/docker-compose.yml

    Contenido:

    services:

    apache_php:
    build:
    context: .
    dockerfile: php/Dockerfile
    container_name: DEV_apache_php
    restart: unless-stopped
    ports:
    - "8080:80"
    volumes:
    - ../:/var/www/html
    - ./apache/000-default.conf:/etc/apache2/sites-available/000-default.conf
    - ./php/php.ini:/usr/local/etc/php/conf.d/dev-custom.ini
    - ./php/msmtprc:/etc/msmtprc
    depends_on:
    - mysql
    - mailpit
    networks:
    - DEV_network

    mysql:
    image: mysql:8.4
    container_name: DEV_mysql
    restart: unless-stopped
    ports:
    - "3307:3306"
    environment:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_DATABASE: devdb
    MYSQL_USER: devuser
    MYSQL_PASSWORD: devpass
    volumes:
    - DEV_mysql_data:/var/lib/mysql
    networks:
    - DEV_network

    phpmyadmin:
    image: phpmyadmin:latest
    container_name: DEV_phpmyadmin
    restart: unless-stopped
    ports:
    - "8081:80"
    environment:
    PMA_HOST: mysql
    PMA_PORT: 3306
    PMA_USER: root
    PMA_PASSWORD: root
    UPLOAD_LIMIT: 256M
    depends_on:
    - mysql
    networks:
    - DEV_network

    mailpit:
    image: axllent/mailpit:latest
    container_name: DEV_mailpit
    restart: unless-stopped
    ports:
    - "8025:8025"
    - "1025:1025"
    networks:
    - DEV_network

    redis:
    image: redis:latest
    container_name: DEV_redis
    restart: unless-stopped
    ports:
    - "6379:6379"
    networks:
    - DEV_network

    volumes:
    DEV_mysql_data:

    networks:
    DEV_network:
    name: DEV_network

    8. Crear el archivo Dockerfile

    Este archivo construirá nuestro contenedor de Apache con PHP.

    Debe estar en:

    DEVLOCAL/_docker/php/Dockerfile

    Contenido:

    FROM php:8.3-apache

    RUN apt-get update && apt-get install -y \
    git \
    unzip \
    zip \
    curl \
    msmtp \
    msmtp-mta \
    libzip-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    libicu-dev \
    libonig-dev \
    libxml2-dev \
    libssl-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install \
    mysqli \
    pdo \
    pdo_mysql \
    zip \
    gd \
    intl \
    mbstring \
    bcmath \
    soap \
    opcache \
    && a2enmod rewrite headers expires \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

    COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

    WORKDIR /var/www/html

    Con esto tendremos disponibles muchas extensiones habituales de PHP:

    • mysqli
    • pdo_mysql
    • gd
    • zip
    • intl
    • mbstring
    • bcmath
    • soap
    • opcache

    También tendremos instalado Composer, muy útil para proyectos PHP modernos.


    9. Crear el archivo php.ini

    Debe estar en:

    DEVLOCAL/_docker/php/php.ini

    Contenido:

    display_errors = On
    display_startup_errors = On
    error_reporting = E_ALL

    upload_max_filesize = 256M
    post_max_size = 256M
    memory_limit = 512M
    max_execution_time = 300
    max_input_vars = 5000

    date.timezone = Europe/Madrid

    opcache.enable = 1
    opcache.enable_cli = 1

    sendmail_path = "/usr/bin/msmtp -t"

    Este archivo ajusta PHP para un entorno de desarrollo.

    No es una configuración pensada para producción. En producción no deberíamos mostrar errores directamente al usuario.


    10. Crear el archivo msmtprc

    Este archivo permitirá enviar correos desde PHP hacia Mailpit.

    Debe estar en:

    DEVLOCAL/_docker/php/msmtprc

    Contenido:

    defaults
    auth off
    tls off

    account mailpit
    host mailpit
    port 1025
    from devlocal@example.test

    account default : mailpit

    Mailpit capturará los correos y podremos verlos desde el navegador.


    11. Crear la configuración de Apache

    Debe estar en:

    DEVLOCAL/_docker/apache/000-default.conf

    Contenido:

    <VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html

    <Directory /var/www/html>
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/dev_error.log
    CustomLog ${APACHE_LOG_DIR}/dev_access.log combined
    </VirtualHost>

    La directiva importante es:

    AllowOverride All

    Esto permite usar archivos .htaccess, necesarios en muchos proyectos PHP, como WordPress, Laravel o aplicaciones con URLs amigables.


    12. Crear una página de prueba

    Creamos el archivo:

    DEVLOCAL/pruebas/index.php

    Contenido:

    <?php

    echo "<h1>Entorno DEV funcionando</h1>";
    echo "<p>Apache y PHP están funcionando correctamente.</p>";

    echo "<h2>Información de PHP</h2>";
    phpinfo();

    13. Levantar el entorno manualmente

    Windows

    Desde PowerShell:

    cd C:\DEVLOCAL\_docker
    docker compose up -d --build

    Ubuntu

    Desde Terminal:

    cd ~/DEVLOCAL/_docker
    docker compose up -d --build

    macOS

    Desde Terminal:

    cd /Users/antoniooteroveiga/DEVLOCAL/_docker
    docker compose up -d --build

    14. Comprobar los contenedores

    Ejecutamos:

    docker ps

    Deberíamos ver contenedores parecidos a estos:

    DEV_apache_php
    DEV_mysql
    DEV_phpmyadmin
    DEV_mailpit
    DEV_redis

    15. Acceder al entorno desde el navegador

    Apache + PHP

    http://localhost:8080

    Proyecto de pruebas

    http://localhost:8080/pruebas/

    phpMyAdmin

    http://localhost:8081

    Datos de acceso:

    Servidor: mysql
    Usuario: root
    Contraseña: root

    También podemos usar:

    Servidor: mysql
    Usuario: devuser
    Contraseña: devpass

    Mailpit

    http://localhost:8025

    MySQL desde el equipo anfitrión

    Para conectarse desde DBeaver, DataGrip, VS Code o MySQL Workbench:

    Host: localhost
    Puerto: 3307
    Usuario: devuser
    Contraseña: devpass
    Base de datos: devdb

    MySQL desde PHP

    Desde código PHP, el host no será localhost, sino el nombre del servicio Docker:

    Host: mysql
    Puerto: 3306
    Usuario: devuser
    Contraseña: devpass
    Base de datos: devdb

    16. Probar conexión con MySQL desde PHP

    Creamos el archivo:

    DEVLOCAL/pruebas/mysql.php

    Contenido:

    <?php

    $host = "mysql";
    $db = "devdb";
    $user = "devuser";
    $pass = "devpass";

    $conexion = new mysqli($host, $user, $pass, $db);

    if ($conexion->connect_error) {
    die("Error de conexión: " . $conexion->connect_error);
    }

    echo "<h1>Conexión correcta con MySQL</h1>";
    echo "<p>La base de datos devdb está disponible.</p>";

    $conexion->close();

    Abrimos en el navegador:

    http://localhost:8080/pruebas/mysql.php

    Si todo está correcto, veremos un mensaje confirmando la conexión.


    17. Probar envío de correo con Mailpit

    Creamos el archivo:

    DEVLOCAL/pruebas/mail.php

    Contenido:

    <?php

    $destino = "alumno@example.test";
    $asunto = "Prueba desde PHP";
    $mensaje = "Este correo ha sido enviado desde PHP y capturado por Mailpit.";
    $cabeceras = "From: devlocal@example.test";

    if (mail($destino, $asunto, $mensaje, $cabeceras)) {
    echo "<h1>Correo enviado correctamente</h1>";
    echo "<p>Abre Mailpit para verlo.</p>";
    } else {
    echo "<h1>Error al enviar el correo</h1>";
    }

    Abrimos:

    http://localhost:8080/pruebas/mail.php

    Después entramos en Mailpit:

    http://localhost:8025

    Ahí debería aparecer el correo capturado.


    18. Crear script de inicio para Ubuntu y macOS

    Creamos el archivo:

    DEVLOCAL/_docker/inicio.sh

    Contenido:

    #!/bin/bash

    clear

    echo "=============================================="
    echo " ENTORNO DEVLOCAL - DOCKER PHP MYSQL"
    echo "=============================================="
    echo ""

    DEV_PATH="$(cd "$(dirname "$0")" && pwd)"

    cd "$DEV_PATH" || exit 1

    echo "Levantando contenedores DEV..."
    echo ""

    docker compose up -d

    echo ""
    echo "Esperando a que los servicios arranquen..."
    sleep 3

    echo ""
    echo "=============================================="
    echo " CONTENEDORES ACTIVOS"
    echo "=============================================="
    echo ""

    docker ps --filter "name=DEV_"

    echo ""
    echo "=============================================="
    echo " INFORMACION DE CONEXION"
    echo "=============================================="
    echo ""

    echo "Servidor Apache + PHP:"
    echo " URL principal: http://localhost:8080"
    echo " Carpeta web: Carpeta DEVLOCAL del alumno"
    echo ""

    echo "Proyecto de prueba:"
    echo " http://localhost:8080/pruebas/"
    echo ""

    echo "phpMyAdmin:"
    echo " URL: http://localhost:8081"
    echo " Servidor: mysql"
    echo " Usuario root: root"
    echo " Password root: root"
    echo ""

    echo "MySQL desde PHP:"
    echo " Host: mysql"
    echo " Puerto: 3306"
    echo " Base de datos: devdb"
    echo " Usuario: devuser"
    echo " Password: devpass"
    echo ""

    echo "MySQL desde el equipo:"
    echo " Host: localhost"
    echo " Puerto: 3307"
    echo " Base de datos: devdb"
    echo " Usuario: devuser"
    echo " Password: devpass"
    echo ""

    echo "Mailpit:"
    echo " Panel web: http://localhost:8025"
    echo " SMTP interno: mailpit:1025"
    echo ""

    echo "Redis:"
    echo " Host desde Docker: redis"
    echo " Host desde equipo: localhost"
    echo " Puerto: 6379"
    echo ""

    echo "=============================================="
    echo " COMANDOS UTILES"
    echo "=============================================="
    echo ""

    echo "Parar entorno:"
    echo " docker compose down"
    echo ""

    echo "Ver logs:"
    echo " docker compose logs -f"
    echo ""

    echo "Entrar al contenedor PHP:"
    echo " docker exec -it DEV_apache_php bash"
    echo ""

    echo "Entrar a MySQL:"
    echo " docker exec -it DEV_mysql mysql -u root -p"
    echo ""

    echo "=============================================="
    echo " ENTORNO DEV LISTO"
    echo "=============================================="
    echo ""

    Damos permisos de ejecución:

    chmod +x inicio.sh

    Lo ejecutamos:

    ./inicio.sh

    19. Crear script de inicio para Windows

    Creamos el archivo:

    DEVLOCAL/_docker/inicio.bat

    Contenido:

    @echo off
    cls

    echo ==============================================
    echo ENTORNO DEVLOCAL - DOCKER PHP MYSQL
    echo ==============================================
    echo.

    cd /d "%~dp0"

    echo Levantando contenedores DEV...
    echo.

    docker compose up -d

    echo.
    echo Esperando a que los servicios arranquen...
    timeout /t 3 > nul

    echo.
    echo ==============================================
    echo CONTENEDORES ACTIVOS
    echo ==============================================
    echo.

    docker ps --filter "name=DEV_"

    echo.
    echo ==============================================
    echo INFORMACION DE CONEXION
    echo ==============================================
    echo.

    echo Servidor Apache + PHP:
    echo URL principal: http://localhost:8080
    echo Carpeta web: Carpeta DEVLOCAL del alumno
    echo.

    echo Proyecto de prueba:
    echo http://localhost:8080/pruebas/
    echo.

    echo phpMyAdmin:
    echo URL: http://localhost:8081
    echo Servidor: mysql
    echo Usuario root: root
    echo Password root: root
    echo.

    echo MySQL desde PHP:
    echo Host: mysql
    echo Puerto: 3306
    echo Base de datos: devdb
    echo Usuario: devuser
    echo Password: devpass
    echo.

    echo MySQL desde Windows:
    echo Host: localhost
    echo Puerto: 3307
    echo Base de datos: devdb
    echo Usuario: devuser
    echo Password: devpass
    echo.

    echo Mailpit:
    echo Panel web: http://localhost:8025
    echo SMTP interno: mailpit:1025
    echo.

    echo Redis:
    echo Host desde Docker: redis
    echo Host desde Windows: localhost
    echo Puerto: 6379
    echo.

    echo ==============================================
    echo COMANDOS UTILES
    echo ==============================================
    echo.

    echo Parar entorno:
    echo docker compose down
    echo.

    echo Ver logs:
    echo docker compose logs -f
    echo.

    echo Entrar al contenedor PHP:
    echo docker exec -it DEV_apache_php bash
    echo.

    echo Entrar a MySQL:
    echo docker exec -it DEV_mysql mysql -u root -p
    echo.

    echo ==============================================
    echo ENTORNO DEV LISTO
    echo ==============================================
    echo.

    pause

    Para ejecutarlo, el alumno puede hacer doble clic sobre inicio.bat o ejecutarlo desde PowerShell.


    20. Script para parar el entorno

    Ubuntu y macOS

    Creamos:

    DEVLOCAL/_docker/parar.sh

    Contenido:

    #!/bin/bash

    cd "$(dirname "$0")" || exit 1

    echo "Parando entorno DEV..."
    docker compose down

    echo "Entorno detenido."

    Permisos:

    chmod +x parar.sh

    Ejecución:

    ./parar.sh

    Windows

    Creamos:

    DEVLOCAL/_docker/parar.bat

    Contenido:

    @echo off
    cls

    cd /d "%~dp0"

    echo Parando entorno DEV...
    docker compose down

    echo.
    echo Entorno detenido.
    pause

    21. Comandos básicos que debe conocer el alumno

    Levantar contenedores

    docker compose up -d

    Levantar y reconstruir

    docker compose up -d --build

    Parar contenedores

    docker compose down

    Ver contenedores activos

    docker ps

    Ver todos los contenedores

    docker ps -a

    Ver logs

    docker compose logs -f

    Ver logs de un servicio concreto

    docker compose logs -f apache_php

    Entrar al contenedor de PHP

    docker exec -it DEV_apache_php bash

    Entrar al contenedor de MySQL

    docker exec -it DEV_mysql mysql -u root -p

    Contraseña:

    root

    22. Crear varios proyectos web

    El alumno puede crear diferentes carpetas dentro de DEVLOCAL.

    Por ejemplo:

    DEVLOCAL/
    ├── pruebas/
    ├── tienda/
    ├── blog/
    ├── reservas/
    └── ejercicios_php/

    Cada proyecto se abrirá desde el navegador así:

    http://localhost:8080/pruebas/
    http://localhost:8080/tienda/
    http://localhost:8080/blog/
    http://localhost:8080/reservas/
    http://localhost:8080/ejercicios_php/

    23. Ejercicio práctico

    Parte 1: Comprobar PHP

    Crear una carpeta llamada:

    alumno_php

    Dentro crear un archivo:

    index.php

    Con este contenido:

    <?php

    $nombre = "Alumno";
    $curso = "Desarrollo Web con PHP";

    echo "<h1>Hola, $nombre</h1>";
    echo "<p>Estamos trabajando en el curso de $curso.</p>";

    Abrir:

    http://localhost:8080/alumno_php/

    Parte 2: Comprobar MySQL

    Crear un archivo:

    conexion.php

    Contenido:

    <?php

    $conexion = new mysqli("mysql", "devuser", "devpass", "devdb");

    if ($conexion->connect_error) {
    die("Error: " . $conexion->connect_error);
    }

    echo "Conexión correcta con MySQL";

    Abrir:

    http://localhost:8080/alumno_php/conexion.php

    Parte 3: Crear una tabla desde phpMyAdmin

    Entrar en:

    http://localhost:8081

    Seleccionar la base de datos:

    devdb

    Crear una tabla llamada:

    alumnos

    Con estos campos:

    CampoTipoExtra
    idINTAUTO_INCREMENT, PRIMARY KEY
    nombreVARCHAR(100)
    emailVARCHAR(150)
    fecha_altaDATETIME

    SQL:

    CREATE TABLE alumnos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(150) NOT NULL,
    fecha_alta DATETIME DEFAULT CURRENT_TIMESTAMP
    );

    Insertar datos:

    INSERT INTO alumnos (nombre, email) VALUES
    ('Ana', 'ana@example.test'),
    ('Luis', 'luis@example.test'),
    ('Marta', 'marta@example.test');

    Parte 4: Leer datos desde PHP

    Crear:

    listar.php

    Contenido:

    <?php

    $conexion = new mysqli("mysql", "devuser", "devpass", "devdb");

    if ($conexion->connect_error) {
    die("Error: " . $conexion->connect_error);
    }

    $resultado = $conexion->query("SELECT * FROM alumnos");

    echo "<h1>Listado de alumnos</h1>";

    echo "<table border='1' cellpadding='8'>";
    echo "<tr>";
    echo "<th>ID</th>";
    echo "<th>Nombre</th>";
    echo "<th>Email</th>";
    echo "<th>Fecha de alta</th>";
    echo "</tr>";

    while ($fila = $resultado->fetch_assoc()) {
    echo "<tr>";
    echo "<td>" . $fila["id"] . "</td>";
    echo "<td>" . $fila["nombre"] . "</td>";
    echo "<td>" . $fila["email"] . "</td>";
    echo "<td>" . $fila["fecha_alta"] . "</td>";
    echo "</tr>";
    }

    echo "</table>";

    $conexion->close();

    Abrir:

    http://localhost:8080/alumno_php/listar.php

    24. Preguntas de reflexión para el alumno

    1. ¿Qué diferencia hay entre instalar Apache, PHP y MySQL directamente en el sistema y hacerlo con Docker?
    2. ¿Qué ventaja tiene que todos los servicios estén definidos en un archivo docker-compose.yml?
    3. ¿Por qué PHP se conecta a MySQL usando el host mysql y no localhost?
    4. ¿Qué es un volumen en Docker?
    5. ¿Dónde se guardan los datos de MySQL en este proyecto?
    6. ¿Qué pasaría si ejecutamos docker compose down -v?
    7. ¿Para qué sirve phpMyAdmin?
    8. ¿Para qué sirve Mailpit?
    9. ¿Por qué no deberíamos usar contraseñas como root o devpass en producción?
    10. ¿Qué diferencia hay entre un entorno de desarrollo y un entorno de producción?

    25. Problemas frecuentes

    Docker no arranca en Windows

    Posibles causas:

    • Docker Desktop no está abierto.
    • WSL 2 no está instalado o activado.
    • La virtualización está desactivada en BIOS/UEFI.
    • El equipo necesita reiniciarse.

    Docker recomienda usar el backend WSL 2 en Windows para Docker Desktop.


    El puerto 8080 ya está ocupado

    Puede ocurrir si otro servicio ya está usando ese puerto.

    Solución: cambiar en docker-compose.yml:

    ports:
    - "8080:80"

    Por ejemplo:

    ports:
    - "8090:80"

    Después habría que entrar por:

    http://localhost:8090

    El puerto 3307 ya está ocupado

    Cambiar:

    ports:
    - "3307:3306"

    Por ejemplo:

    ports:
    - "3308:3306"

    No aparecen los cambios en PHP

    Probar a recargar el navegador con:

    Ctrl + F5

    También se puede reiniciar el contenedor:

    docker compose restart apache_php

    Error de permisos en Ubuntu

    Si Docker pide sudo, puede faltar añadir el usuario al grupo docker.

    sudo usermod -aG docker $USER

    Después hay que cerrar sesión y volver a entrar.


    phpMyAdmin no conecta

    Comprobar que MySQL está activo:

    docker ps

    También se pueden ver los logs:

    docker compose logs -f mysql

    26. Cómo borrar todo y empezar de nuevo

    Para parar los contenedores:

    docker compose down

    Para parar y borrar también los datos de MySQL:

    docker compose down -v

    Cuidado: este comando borra el volumen de MySQL. Si hay bases de datos creadas, se perderán.


    29. Ampliaciones posibles

    Cuando el entorno básico funcione, se pueden añadir mejoras:

    • Crear dominios locales como tienda.localhost.
    • Añadir HTTPS local.
    • Añadir un contenedor de Node.js.
    • Añadir un contenedor específico para Laravel.
    • Crear plantillas para WordPress.
    • Añadir copias de seguridad automáticas de MySQL.
    • Crear un script para exportar e importar bases de datos.
    • Crear varios entornos: desarrollo, pruebas y producción.

    30. Conclusión

    Con esta práctica hemos creado un entorno de desarrollo web completo usando Docker. El resultado es similar a XAMPP o MAMP, pero con una ventaja importante: todo el entorno queda definido mediante archivos.

    Esto permite:

    • Repetir la instalación en diferentes sistemas operativos.
    • Compartir el entorno con otros compañeros.
    • Evitar conflictos con instalaciones locales.
    • Trabajar con varios servicios a la vez.
    • Preparar al alumno para entornos profesionales basados en contenedores.

    Docker Compose facilita levantar todos los servicios con un solo comando, lo que lo convierte en una herramienta especialmente útil para desarrollo web moderno.

    Script Cerrar

    Además del script inicio, vamos a crear un script llamado cerrar. Este script no solamente permite apagar el entorno, sino también eliminarlo de diferentes formas según lo que necesitemos. Esto es importante porque no siempre queremos borrar los datos. A veces solo queremos detener los contenedores, y otras veces queremos empezar desde cero.


    Diferencia entre las opciones

    OpciónComando¿Borra contenedores?¿Borra datos MySQL?¿Borra imágenes?
    Parardocker compose stopNoNoNo
    Eliminar contenedoresdocker compose downSíNoNo
    Eliminar con volúmenesdocker compose down -vSíSíNo
    Destruir completodocker compose down -v --rmi localSíSíSí

    docker compose stop

    Detiene los contenedores, pero no los elimina.

    Es como apagar una máquina virtual.


    docker compose down

    Detiene y elimina los contenedores, pero mantiene los volúmenes.

    Es decir, si teníamos una base de datos MySQL con datos, normalmente se conservará.


    docker compose down -v

    Detiene y elimina los contenedores, y también borra los volúmenes.

    En este proyecto significa que se borrarán los datos de MySQL.


    docker compose down -v --rmi local

    Además de eliminar contenedores y volúmenes, elimina imágenes locales creadas por el proyecto.

    En nuestro caso, puede borrar la imagen personalizada de Apache + PHP, por lo que en el siguiente arranque habrá que reconstruirla con:

    docker compose up -d --build

    Recomendación para uso diario

    Para el día a día, lo normal es usar la opción:

    1) Parar contenedores

    o:

    2) Parar y eliminar contenedores

    La opción 3 y 4 solo deberían usarse cuando queramos empezar desde cero.

    Especialmente la opción 3:

    docker compose down -v

    porque borra los datos de MySQL.

    Script para Ubuntu y macOS: cerrar.sh

    Crear el archivo:

    nano /Users/antoniooteroveiga/DEVLOCAL/_docker/cerrar.sh

    Contenido:

    #!/bin/bash

    clear

    echo "=============================================="
    echo " CERRAR ENTORNO DEVLOCAL"
    echo "=============================================="
    echo ""

    DEV_PATH="$(cd "$(dirname "$0")" && pwd)"
    cd "$DEV_PATH" || exit 1

    echo "Este script permite cerrar o destruir el entorno Docker DEV."
    echo ""
    echo "Elige una opcion:"
    echo ""
    echo " 1) Parar contenedores"
    echo " Equivale a: docker compose stop"
    echo ""
    echo " 2) Parar y eliminar contenedores"
    echo " Equivale a: docker compose down"
    echo ""
    echo " 3) Parar, eliminar contenedores y borrar volumenes"
    echo " Equivale a: docker compose down -v"
    echo " AVISO: se borraran las bases de datos de MySQL."
    echo ""
    echo " 4) Parar, eliminar contenedores, volumenes e imagenes del proyecto"
    echo " Equivale a: docker compose down -v --rmi local"
    echo " AVISO: se borraran las bases de datos y las imagenes creadas localmente."
    echo ""
    echo " 5) Ver contenedores DEV activos"
    echo ""
    echo " 6) Cancelar"
    echo ""

    read -p "Selecciona una opcion [1-6]: " opcion

    echo ""

    case "$opcion" in

    1)
    echo "Parando contenedores..."
    docker compose stop
    echo ""
    echo "Contenedores detenidos."
    ;;

    2)
    echo "Parando y eliminando contenedores..."
    docker compose down
    echo ""
    echo "Contenedores eliminados. Los volumenes se conservan."
    ;;

    3)
    echo "ATENCION: esta opcion borrara los volumenes."
    echo "Esto eliminara los datos persistentes de MySQL."
    echo ""
    read -p "Escribe BORRAR para confirmar: " confirmacion

    if [ "$confirmacion" = "BORRAR" ]; then
    echo ""
    echo "Eliminando contenedores y volumenes..."
    docker compose down -v
    echo ""
    echo "Entorno eliminado con volumenes."
    else
    echo ""
    echo "Operacion cancelada. No se ha borrado nada."
    fi
    ;;

    4)
    echo "ATENCION: esta opcion borrara contenedores, volumenes e imagenes locales."
    echo "Esto eliminara los datos de MySQL y obligara a reconstruir la imagen PHP."
    echo ""
    read -p "Escribe DESTRUIR para confirmar: " confirmacion

    if [ "$confirmacion" = "DESTRUIR" ]; then
    echo ""
    echo "Eliminando contenedores, volumenes e imagenes locales..."
    docker compose down -v --rmi local
    echo ""
    echo "Entorno destruido."
    else
    echo ""
    echo "Operacion cancelada. No se ha borrado nada."
    fi
    ;;

    5)
    echo "Mostrando contenedores DEV..."
    echo ""
    docker ps -a --filter "name=DEV_"
    ;;

    6)
    echo "Operacion cancelada."
    ;;

    *)
    echo "Opcion no valida."
    ;;

    esac

    echo ""
    echo "=============================================="
    echo " PROCESO FINALIZADO"
    echo "=============================================="
    echo ""

    Dar permisos de ejecución:

    chmod +x /Users/antoniooteroveiga/DEVLOCAL/_docker/cerrar.sh

    Ejecutarlo:

    cd /Users/antoniooteroveiga/DEVLOCAL/_docker
    ./cerrar.sh

    Versión más portable

    Si quieres que funcione sin depender de tu usuario concreto, es mejor decirles que creen el archivo dentro de _docker y que lo ejecuten desde ahí.

    cd ~/DEVLOCAL/_docker
    nano cerrar.sh
    chmod +x cerrar.sh
    ./cerrar.sh

    El script ya detecta automáticamente la carpeta donde está, gracias a esta línea:

    DEV_PATH="$(cd "$(dirname "$0")" && pwd)"

    Por eso vale tanto para Ubuntu como para macOS.


    Script para Windows: cerrar.bat

    Crear el archivo:

    C:\DEVLOCAL\_docker\cerrar.bat

    Contenido:

    @echo off
    cls

    cd /d "%~dp0"

    :menu
    cls
    echo ==============================================
    echo CERRAR ENTORNO DEVLOCAL
    echo ==============================================
    echo.
    echo Este script permite cerrar o destruir el entorno Docker DEV.
    echo.
    echo Elige una opcion:
    echo.
    echo 1) Parar contenedores
    echo Equivale a: docker compose stop
    echo.
    echo 2) Parar y eliminar contenedores
    echo Equivale a: docker compose down
    echo.
    echo 3) Parar, eliminar contenedores y borrar volumenes
    echo Equivale a: docker compose down -v
    echo AVISO: se borraran las bases de datos de MySQL.
    echo.
    echo 4) Parar, eliminar contenedores, volumenes e imagenes del proyecto
    echo Equivale a: docker compose down -v --rmi local
    echo AVISO: se borraran las bases de datos y las imagenes creadas localmente.
    echo.
    echo 5) Ver contenedores DEV activos
    echo.
    echo 6) Cancelar
    echo.

    set /p opcion=Selecciona una opcion [1-6]:

    if "%opcion%"=="1" goto parar
    if "%opcion%"=="2" goto eliminar
    if "%opcion%"=="3" goto volumenes
    if "%opcion%"=="4" goto destruir
    if "%opcion%"=="5" goto ver
    if "%opcion%"=="6" goto cancelar

    echo.
    echo Opcion no valida.
    pause
    goto menu

    :parar
    cls
    echo Parando contenedores...
    echo.
    docker compose stop
    echo.
    echo Contenedores detenidos.
    pause
    goto fin

    :eliminar
    cls
    echo Parando y eliminando contenedores...
    echo.
    docker compose down
    echo.
    echo Contenedores eliminados. Los volumenes se conservan.
    pause
    goto fin

    :volumenes
    cls
    echo ATENCION: esta opcion borrara los volumenes.
    echo Esto eliminara los datos persistentes de MySQL.
    echo.
    set /p confirmacion=Escribe BORRAR para confirmar:

    if "%confirmacion%"=="BORRAR" (
    echo.
    echo Eliminando contenedores y volumenes...
    docker compose down -v
    echo.
    echo Entorno eliminado con volumenes.
    ) else (
    echo.
    echo Operacion cancelada. No se ha borrado nada.
    )

    pause
    goto fin

    :destruir
    cls
    echo ATENCION: esta opcion borrara contenedores, volumenes e imagenes locales.
    echo Esto eliminara los datos de MySQL y obligara a reconstruir la imagen PHP.
    echo.
    set /p confirmacion=Escribe DESTRUIR para confirmar:

    if "%confirmacion%"=="DESTRUIR" (
    echo.
    echo Eliminando contenedores, volumenes e imagenes locales...
    docker compose down -v --rmi local
    echo.
    echo Entorno destruido.
    ) else (
    echo.
    echo Operacion cancelada. No se ha borrado nada.
    )

    pause
    goto fin

    :ver
    cls
    echo Mostrando contenedores DEV...
    echo.
    docker ps -a --filter "name=DEV_"
    echo.
    pause
    goto menu

    :cancelar
    cls
    echo Operacion cancelada.
    pause
    goto fin

    :fin
    cls
    echo ==============================================
    echo PROCESO FINALIZADO
    echo ==============================================
    echo.

  • Montar Google Drive como una unidad en Ubuntu sin sincronizar archivos

    Montar Google Drive como una unidad en Ubuntu sin sincronizar archivos

    1. Introducción

    En esta práctica vamos a aprender a montar Google Drive en Ubuntu como si fuera una unidad del sistema, pero sin sincronizar todos los archivos en el disco duro.

    Esto significa que podremos acceder a los archivos de Google Drive desde el explorador de archivos de Ubuntu, o desde la terminal, pero los archivos no se descargarán todos automáticamente. Se irán leyendo bajo demanda cuando los abramos o los copiemos.

    Para conseguirlo usaremos una herramienta llamada rclone.


    2. Objetivos de la práctica

    Al finalizar esta práctica, el alumno será capaz de:

    • Instalar rclone en Ubuntu.
    • Configurar una cuenta de Google Drive en rclone.
    • Montar Google Drive como una carpeta local.
    • Acceder a los archivos de Drive desde Ubuntu sin sincronizarlos.
    • Desmontar la unidad cuando sea necesario.
    • Crear un servicio para montar Google Drive automáticamente al iniciar sesión.

    3. Requisitos previos

    Para realizar esta práctica necesitas:

    • Un equipo con Ubuntu instalado.
    • Conexión a Internet.
    • Una cuenta de Google Drive.
    • Acceso a la terminal.
    • Permisos de administrador en el sistema.

    4. ¿Qué es rclone?

    rclone es una herramienta de línea de comandos que permite trabajar con servicios de almacenamiento en la nube como:

    • Google Drive.
    • OneDrive.
    • Dropbox.
    • Mega.
    • Amazon S3.
    • Nextcloud.
    • WebDAV.

    En esta práctica lo usaremos para montar Google Drive como una unidad, no para sincronizarlo.

    La diferencia es importante:

    OpciónQué hace
    SincronizarDescarga y mantiene copias locales de los archivos
    MontarMuestra los archivos como si estuvieran en local, pero se accede a ellos bajo demanda

    5. Instalación de rclone

    Abrimos una terminal y actualizamos la lista de paquetes:

    sudo apt update

    Instalamos rclone y fuse3:

    sudo apt install rclone fuse3

    fuse3 permite que un usuario pueda montar sistemas de archivos virtuales, como en este caso Google Drive.

    Comprobamos que rclone se ha instalado correctamente:

    rclone version

    Deberíamos ver una salida parecida a esta:

    rclone v1.xx.x
    - os/version: ubuntu
    - os/kernel: linux
    - os/type: linux

    6. Configuración de Google Drive en rclone

    Ahora vamos a configurar la conexión con nuestra cuenta de Google Drive.

    Ejecutamos:

    rclone config

    Aparecerá un menú en la terminal.

    Como todavía no tenemos ninguna conexión configurada, elegimos la opción:

    n

    Esta opción significa:

    New remote

    Es decir, crear una nueva conexión remota.


    6.1. Nombre de la conexión

    Cuando pregunte por el nombre, escribimos:

    gdrive

    Podríamos poner otro nombre, pero para esta práctica usaremos gdrive.

    Este nombre será el identificador que usaremos más adelante para referirnos a nuestro Google Drive.


    6.2. Selección del tipo de almacenamiento

    Después aparecerá una lista con muchos servicios de almacenamiento.

    Buscamos la opción correspondiente a:

    Google Drive

    El número puede cambiar según la versión de rclone, así que no copies el número de otra guía sin mirar la lista.

    Escribe el número que corresponda a Google Drive y pulsa Enter.


    6.3. Client ID y Client Secret

    rclone nos preguntará por:

    client_id
    client_secret

    Para una práctica normal podemos dejarlos vacíos pulsando Enter.

    Esto usará la configuración genérica de rclone.

    Si te pregunta algo mas que no sea scope dale a enter.


    6.4. Scope o permisos de acceso

    Cuando pregunte por el tipo de permisos o scope, elegimos la opción que permita acceso completo a Google Drive.

    Normalmente aparece como:

    drive

    Esto permite leer y escribir archivos en Google Drive desde Ubuntu.


    6.5. Configuración avanzada

    Cuando pregunte si queremos configuración avanzada:

    Edit advanced config?

    Respondemos:

    n

    6.6. Autenticación con Google

    Cuando pregunte:

    Use auto config?

    Si estamos en un Ubuntu con entorno gráfico, respondemos:

    y

    Se abrirá el navegador web para iniciar sesión con nuestra cuenta de Google.

    Aceptamos los permisos solicitados por rclone.

    Cuando el proceso termine, volvemos a la terminal.


    6.7. Guardar configuración

    Al final, rclone mostrará un resumen de la configuración.

    Cuando pregunte si queremos guardar la conexión, respondemos:

    y

    Después salimos del menú de configuración con:

    q

    7. Comprobar que la conexión funciona

    Para comprobar que podemos acceder a Google Drive, ejecutamos:

    rclone lsd gdrive:

    Este comando lista las carpetas principales de Google Drive.

    Si todo ha ido bien, veremos algo parecido a:

              -1 2026-05-25 10:30:00        -1 Documentos
    -1 2026-05-25 10:30:00 -1 Copias
    -1 2026-05-25 10:30:00 -1 Fotos

    Si aparece un error de autenticación, probablemente haya que repetir la configuración con:

    rclone config

    8. Crear la carpeta de montaje

    Ahora vamos a crear una carpeta local donde aparecerá Google Drive.

    Ejecutamos:

    mkdir -p ~/GoogleDrive

    Esta carpeta estará en nuestro directorio personal.

    La ruta completa será algo parecido a:

    /home/usuario/GoogleDrive

    Por ejemplo, si el usuario se llama antonio, la ruta será:

    /home/antonio/GoogleDrive

    9. Montar Google Drive como unidad

    Ahora montamos Google Drive dentro de esa carpeta:

    rclone mount gdrive: ~/GoogleDrive \
    --vfs-cache-mode writes \
    --daemon

    Explicación del comando:

    ParteSignificado
    rclone mountMonta un almacenamiento remoto
    gdrive:Nombre de la conexión configurada
    ~/GoogleDriveCarpeta local donde se montará
    --vfs-cache-mode writesUsa caché para operaciones de escritura
    --daemonEjecuta el montaje en segundo plano

    9.1. Comprobar el montaje

    Podemos comprobarlo desde la terminal:

    ls ~/GoogleDrive

    También podemos abrir el explorador de archivos de Ubuntu y entrar en la carpeta:

    GoogleDrive

    Ahí deberían aparecer los archivos y carpetas de nuestra cuenta de Google Drive.


    10. Importante: esto no sincroniza todos los archivos

    Este punto es clave.

    Con este sistema, Google Drive aparece como una carpeta local, pero no se descargan todos los archivos al ordenador.

    Los archivos se consultan cuando se necesitan.

    Por ejemplo:

    • Si abres un documento, se descargará temporalmente para poder leerlo.
    • Si copias un archivo desde Google Drive a otra carpeta local, entonces sí se descargará.
    • Si copias un archivo desde Ubuntu hacia ~/GoogleDrive, se subirá a Google Drive.
    • Si no abres un archivo, no ocupa espacio real en tu disco como una sincronización completa.

    11. Desmontar Google Drive

    Cuando queramos desmontar la unidad, usamos:

    fusermount3 -u ~/GoogleDrive

    Después de esto, la carpeta ~/GoogleDrive seguirá existiendo, pero ya no mostrará el contenido de Google Drive.

    Para comprobarlo:

    ls ~/GoogleDrive

    Si está desmontada, normalmente aparecerá vacía.


    12. Montaje automático al iniciar sesión

    Hasta ahora hemos montado Google Drive manualmente.

    Si queremos que se monte automáticamente al iniciar sesión en Ubuntu, podemos crear un servicio de usuario con systemd.


    12.1. Crear carpeta para servicios de usuario

    Ejecutamos:

    mkdir -p ~/.config/systemd/user

    12.2. Crear el archivo del servicio

    Creamos el archivo:

    nano ~/.config/systemd/user/rclone-gdrive.service

    Pegamos el siguiente contenido.

    Importante: cambia antonio por el nombre real de tu usuario en Ubuntu.

    [Unit]
    Description=Montar Google Drive con rclone
    After=network-online.target

    [Service]
    Type=simple
    ExecStart=/usr/bin/rclone mount gdrive: /home/antonio/GoogleDrive --vfs-cache-mode writes
    ExecStop=/bin/fusermount3 -u /home/antonio/GoogleDrive
    Restart=on-failure
    RestartSec=10

    [Install]
    WantedBy=default.target

    Guardamos el archivo:

    • Ctrl + O
    • Enter
    • Ctrl + X

    12.3. Activar el servicio

    Recargamos los servicios de usuario:

    systemctl --user daemon-reload

    Activamos el servicio para que arranque automáticamente:

    systemctl --user enable rclone-gdrive

    Iniciamos el servicio:

    systemctl --user start rclone-gdrive

    12.4. Comprobar el estado del servicio

    Ejecutamos:

    systemctl --user status rclone-gdrive

    Si todo está bien, deberíamos ver algo parecido a:

    Active: active (running)

    Para salir de la pantalla de estado, pulsamos:

    q

    13. Detener el montaje automático

    Si queremos parar el servicio temporalmente:

    systemctl --user stop rclone-gdrive

    Si queremos desactivarlo para que no arranque automáticamente:

    systemctl --user disable rclone-gdrive

    14. Posibles errores y soluciones

    Error: la carpeta parece vacía

    Primero comprueba si el montaje está activo:

    systemctl --user status rclone-gdrive

    O prueba a montar manualmente:

    rclone mount gdrive: ~/GoogleDrive --vfs-cache-mode writes --daemon

    Error: transport endpoint is not connected

    A veces puede quedarse un montaje bloqueado.

    Solución:

    fusermount3 -u ~/GoogleDrive

    Si no funciona:

    fusermount3 -uz ~/GoogleDrive

    Después vuelve a montar:

    rclone mount gdrive: ~/GoogleDrive --vfs-cache-mode writes --daemon

    Error: command not found

    Si aparece:

    rclone: command not found

    Significa que rclone no está instalado.

    Instálalo con:

    sudo apt install rclone

    Error: fusermount3 no encontrado

    Instala fuse3:

    sudo apt install fuse3

    Error en el servicio por el nombre de usuario

    Si el servicio no arranca, revisa esta parte:

    ExecStart=/usr/bin/rclone mount gdrive: /home/antonio/GoogleDrive --vfs-cache-mode writes
    ExecStop=/bin/fusermount3 -u /home/antonio/GoogleDrive

    Debes cambiar antonio por tu usuario real.

    Puedes saber tu usuario con:

    whoami

    Y tu ruta personal con:

    echo $HOME

    15. Comandos principales de la práctica

    AcciónComando
    Instalar rclonesudo apt install rclone fuse3
    Configurar Google Driverclone config
    Ver carpetas de Driverclone lsd gdrive:
    Crear carpeta de montajemkdir -p ~/GoogleDrive
    Montar Driverclone mount gdrive: ~/GoogleDrive --vfs-cache-mode writes --daemon
    Desmontar Drivefusermount3 -u ~/GoogleDrive
    Ver estado del serviciosystemctl --user status rclone-gdrive
    Iniciar serviciosystemctl --user start rclone-gdrive
    Parar serviciosystemctl --user stop rclone-gdrive

    16. Ejercicio propuesto para el alumno

    Realiza los siguientes pasos y documenta el proceso:

    1. Instala rclone en Ubuntu.
    2. Configura tu cuenta de Google Drive.
    3. Crea la carpeta ~/GoogleDrive.
    4. Monta Google Drive usando rclone mount.
    5. Comprueba que puedes ver tus archivos desde el explorador.
    6. Crea una carpeta de prueba dentro de ~/GoogleDrive.
    7. Comprueba desde la web de Google Drive que la carpeta se ha creado.
    8. Desmonta Google Drive.
    9. Vuelve a montarlo manualmente.
    10. Configura el montaje automático con systemd.

    17. Preguntas de repaso

    1. ¿Qué diferencia hay entre sincronizar Google Drive y montarlo como unidad?
    2. ¿Para qué sirve rclone?
    3. ¿Qué significa gdrive: en los comandos usados?
    4. ¿Dónde se monta Google Drive en esta práctica?
    5. ¿Qué comando se usa para desmontar la unidad?
    6. ¿Qué ventaja tiene usar systemd en esta práctica?
    7. ¿Se descargan todos los archivos de Google Drive al montar la unidad?
    8. ¿Qué ocurre si copiamos un archivo dentro de ~/GoogleDrive?

    18. Conclusión

    En esta práctica hemos montado Google Drive en Ubuntu como una carpeta local usando rclone.

    La ventaja principal de este método es que podemos trabajar con nuestros archivos de Google Drive desde Ubuntu sin tener que sincronizar todo el contenido en el disco duro.

    Esto resulta muy útil en equipos con poco almacenamiento, máquinas virtuales, portátiles o entornos de laboratorio donde necesitamos acceso a archivos en la nube sin ocupar espacio innecesario.

    Además, al configurar el montaje automático mediante systemd, conseguimos que Google Drive esté disponible cada vez que iniciemos sesión en Ubuntu.

  • Instalación de Ollama en Windows paso a paso

    Instalación de Ollama en Windows paso a paso

    1. Introducción

    En esta práctica vamos a instalar Ollama en un equipo con Windows para poder ejecutar modelos de inteligencia artificial de forma local.

    Ollama permite descargar y ejecutar modelos como Llama, Qwen, Mistral u otros modelos compatibles directamente en nuestro ordenador, sin depender necesariamente de servicios externos.

    Esto es especialmente interesante en clase porque nos permite trabajar conceptos como:

    • Inteligencia artificial local.
    • Modelos de lenguaje.
    • Uso de la terminal.
    • Consumo de CPU, RAM y GPU.
    • Servicios accesibles desde red local.
    • Integración con herramientas como Open WebUI, OpenCode o aplicaciones propias.

    2. Requisitos previos

    Para realizar esta práctica necesitaremos:

    • Un equipo con Windows 10 o Windows 11.
    • Conexión a Internet.
    • Permisos para instalar programas.
    • PowerShell o Terminal de Windows.
    • Espacio suficiente en disco para descargar modelos.

    Según la página oficial de Ollama, la versión para Windows requiere Windows 10 o posterior.


    3. ¿Qué vamos a instalar?

    Vamos a instalar:

    Ollama para Windows

    Una vez instalado, podremos ejecutar comandos como:

    ollama --version
    ollama pull llama3.1:8b
    ollama run llama3.1:8b

    Paso 1. Descargar Ollama para Windows

    Entramos en la página oficial de descarga de Ollama:

    https://ollama.com/download/windows

    En la página oficial aparece la opción de descarga para Windows y también una instalación mediante PowerShell.

    Podemos instalarlo de dos formas:

    Opción A: Instalación con instalador gráfico

    Esta es la opción más sencilla para alumnos que empiezan.

    1. Entramos en la web oficial de Ollama.
    2. Seleccionamos la versión de Windows.
    3. Descargamos el instalador.
    4. Ejecutamos el archivo descargado.
    5. Seguimos el asistente de instalación.

    Cuando termine, Ollama debería quedar instalado en el sistema.


    Opción B: Instalación desde PowerShell

    También podemos instalar Ollama usando PowerShell.

    Abrimos PowerShell y ejecutamos:

    irm https://ollama.com/install.ps1 | iex

    Este comando aparece actualmente en la página oficial de descarga de Ollama para Windows.


    Paso 2. Comprobar que Ollama está instalado

    Abrimos una terminal de PowerShell y ejecutamos:

    ollama --version

    Si todo está correcto, veremos una salida parecida a esta:

    ollama version x.x.x

    La versión puede cambiar según el momento en el que se instale.


    Paso 3. Comprobar que el servicio responde

    Ollama funciona como una aplicación/servicio local que escucha normalmente en el puerto:

    11434

    Podemos comprobarlo con:

    curl http://localhost:11434/api/tags

    Si todavía no tenemos modelos instalados, puede aparecer una lista vacía o una respuesta JSON sin modelos.

    Ejemplo aproximado:

    {
    "models": []
    }

    Paso 4. Descargar un primer modelo

    Ahora vamos a descargar un modelo pequeño o mediano para probar.

    Para empezar podemos usar:

    ollama pull llama3.1:8b

    También podríamos usar un modelo orientado a programación, por ejemplo:

    ollama pull qwen2.5-coder:7b

    La descarga puede tardar varios minutos, dependiendo de la conexión a Internet y del tamaño del modelo.


    Paso 5. Ejecutar un modelo

    Una vez descargado el modelo, podemos ejecutarlo con:

    ollama run llama3.1:8b

    Aparecerá un prompt interactivo donde podremos escribir preguntas.

    Por ejemplo:

    >>> Explícame qué es una dirección IP como si fuera para un alumno de primero de ASIR.

    Ollama responderá usando el modelo cargado en local.

    Para salir del chat podemos escribir:

    /bye

    Paso 6. Ver los modelos instalados

    Para ver qué modelos tenemos descargados:

    ollama list

    La salida será parecida a:

    NAME              ID              SIZE      MODIFIED
    llama3.1:8b xxxxxxxx 4.7 GB ...
    qwen2.5-coder:7b xxxxxxxx 4.7 GB ...

    Paso 7. Ver qué modelos están cargados en memoria

    Este paso es importante, especialmente si el ordenador también se usa para jugar, editar vídeo o realizar tareas pesadas.

    Ejecutamos:

    ollama ps

    Este comando muestra los modelos que están cargados en ese momento.

    Ejemplo:

    NAME              ID              SIZE      PROCESSOR    UNTIL
    llama3.1:8b xxxxxxxx 4.7 GB 100% GPU 4 minutes from now

    Paso 8. Detener un modelo cargado

    Si queremos liberar memoria, podemos detener un modelo con:

    ollama stop llama3.1:8b

    O, si estamos usando otro modelo:

    ollama stop qwen2.5-coder:7b

    Esto es útil para liberar RAM o VRAM de la tarjeta gráfica.


    Paso 9. Borrar un modelo

    Si queremos eliminar un modelo descargado para liberar espacio en disco:

    ollama rm llama3.1:8b

    Después podemos comprobar de nuevo:

    ollama list

    Paso 10. Probar Ollama desde el navegador

    Podemos abrir en el navegador:

    http://localhost:11434

    También podemos probar directamente la API:

    http://localhost:11434/api/tags

    El endpoint /api/tags permite comprobar los modelos disponibles en la instalación local.


    Paso 11. Crear una petición a la API de Ollama

    Ollama no solo sirve para usar modelos desde terminal. También podemos hacer peticiones HTTP a su API local.

    Ejemplo desde PowerShell:

    curl http://localhost:11434/api/generate `
    -Method POST `
    -Body '{"model":"llama3.1:8b","prompt":"Explica qué es Linux en pocas palabras.","stream":false}' `
    -ContentType "application/json"

    Esto nos devolverá una respuesta en formato JSON.


    Paso 12. Configurar Ollama para acceder desde otro equipo de la red

    Por defecto, Ollama suele estar pensado para usarse desde el propio equipo. Si queremos acceder desde otro ordenador de la red local, por ejemplo desde un portátil, podemos configurar la variable de entorno:

    OLLAMA_HOST

    La documentación oficial de Ollama indica que en Windows las variables de entorno se pueden configurar desde las opciones de entorno del usuario o del sistema. Para ello recomienda cerrar primero Ollama desde la barra de tareas, abrir la configuración de variables de entorno, crear o editar la variable correspondiente y volver a iniciar Ollama.

    Configuración recomendada para red local

    Creamos la variable:

    OLLAMA_HOST

    Con el valor:

    0.0.0.0:11434

    También se puede hacer desde PowerShell como administrador:

    setx OLLAMA_HOST "0.0.0.0:11434" /M

    Después debemos cerrar Ollama y volver a abrirlo, o reiniciar el equipo.


    Paso 13. Abrir el puerto en el firewall de Windows

    Si queremos acceder a Ollama desde otro ordenador de la misma red, puede ser necesario abrir el puerto 11434.

    Abrimos PowerShell como administrador y ejecutamos:

    New-NetFirewallRule `
    -DisplayName "Ollama 11434" `
    -Direction Inbound `
    -Protocol TCP `
    -LocalPort 11434 `
    -Action Allow

    Paso 14. Averiguar la IP del equipo Windows

    En el equipo donde está instalado Ollama ejecutamos:

    ipconfig

    Buscamos la dirección IPv4 de la tarjeta de red que estamos usando.

    Ejemplo:

    Dirección IPv4. . . . . . . . . . . . . . : 192.168.1.50

    En este ejemplo, la IP del equipo sería:

    192.168.1.50

    Paso 15. Probar Ollama desde otro ordenador

    Desde otro equipo de la misma red, por ejemplo un portátil, probamos:

    curl http://192.168.1.50:11434/api/tags

    Cambiando 192.168.1.50 por la IP real del equipo Windows.

    Si todo está correcto, veremos la lista de modelos disponibles.


    Paso 16. Conectar Open WebUI u otra herramienta

    Si instalamos una interfaz web como Open WebUI, podremos conectarla a la dirección del servidor Ollama.

    Si Open WebUI está en el mismo equipo:

    http://localhost:11434

    Si Open WebUI está en otro ordenador de la red:

    http://192.168.1.50:11434

    Paso 17. Recomendaciones de modelos para empezar

    Para equipos normales:

    ollama pull llama3.1:8b
    ollama pull qwen2.5-coder:7b

    Para equipos con buena GPU:

    ollama pull qwen2.5-coder:14b
    ollama pull qwen3:14b

    Para programación:

    ollama run qwen2.5-coder:7b

    O, si el equipo tiene más potencia:

    ollama run qwen2.5-coder:14b

    Para uso general:

    ollama run llama3.1:8b

    Paso 18. Buenas prácticas si el equipo también se usa para jugar

    Si el ordenador tiene una tarjeta gráfica potente y también se usa para jugar, conviene tener cuidado.

    Ollama puede usar:

    • RAM.
    • CPU.
    • GPU.
    • VRAM de la tarjeta gráfica.
    • Disco.

    Instalar Ollama no debería reducir el rendimiento de los juegos por sí solo. El problema aparece cuando tenemos un modelo cargado mientras jugamos.

    Antes de jugar podemos comprobar:

    ollama ps

    Si aparece algún modelo cargado, podemos detenerlo:

    ollama stop nombre_del_modelo

    Por ejemplo:

    ollama stop qwen2.5-coder:14b

    También podemos cerrar Ollama desde el icono de la bandeja del sistema.


    Paso 19. Seguridad básica

    No debemos exponer Ollama directamente a Internet.

    Una cosa es usarlo dentro de nuestra red local y otra muy distinta dejarlo accesible públicamente desde fuera.

    Recomendaciones:

    • No abrir el puerto 11434 en el router.
    • No hacer redirección de puertos hacia Ollama.
    • Usarlo solo en red local.
    • Si se necesita acceso remoto, usar VPN o una solución segura.
    • No permitir que cualquier persona de la red use modelos grandes sin control.

    Paso 20. Comandos principales de Ollama

    AcciónComando
    Ver versiónollama --version
    Descargar modeloollama pull nombre_modelo
    Ejecutar modeloollama run nombre_modelo
    Ver modelos instaladosollama list
    Ver modelos cargadosollama ps
    Detener modeloollama stop nombre_modelo
    Borrar modeloollama rm nombre_modelo
    Probar API localcurl http://localhost:11434/api/tags

    Actividad propuesta

    Instalar Ollama en Windows, descargar un modelo, ejecutarlo y comprobar que funciona correctamente desde terminal y desde la API local.

    Tareas

    1. Instalar Ollama en Windows.
    2. Comprobar la versión instalada.
    3. Descargar un modelo.
    4. Ejecutar el modelo desde terminal.
    5. Hacerle al modelo una pregunta técnica.
    6. Listar los modelos instalados.
    7. Comprobar qué modelos están cargados.
    8. Probar la API local con curl.
    9. Documentar el proceso con capturas.
    10. Explicar qué recursos del equipo consume Ollama.

    Cargar modelos de ollama en el disco duro secundario

    Importante:
    Los modelos de Ollama pueden ocupar muchos gigabytes. En equipos con un disco principal pequeño, es recomendable guardar los modelos en un disco secundario usando la variable de entorno OLLAMA_MODELS. Así evitamos llenar la unidad C: y mantenemos el sistema operativo con espacio libre suficiente.

    la forma correcta para Windows, que es mover la carpeta de modelos usando la variable OLLAMA_MODELS. También voy a incluir una alternativa con enlace simbólico, útil si ya tienes modelos descargados.Pensó durante 4s

    Sí. En Windows tienes que cambiar la ubicación de los modelos con la variable de entorno OLLAMA_MODELS. Ollama indica oficialmente que esta variable sirve para elegir dónde se guardan los modelos descargados.

    Por ejemplo, si quieres que los modelos estén en el disco D:, puedes crear esta carpeta:

    D:\Ollama\Models

    Opción recomendada: usar OLLAMA_MODELS

    1. Crea la carpeta en el disco secundario

    En PowerShell:

    mkdir D:\Ollama\Models

    Puedes cambiar D: por la letra real de tu disco.


    2. Cierra Ollama

    Antes de cambiar la ruta:

    1. Ve al icono de Ollama en la bandeja del sistema, junto al reloj.
    2. Clic derecho.
    3. Pulsa Quit o Salir.

    También puedes cerrar el proceso desde PowerShell:

    taskkill /IM ollama.exe /F

    3. Crea la variable de entorno

    Abre PowerShell como administrador y ejecuta:

    setx OLLAMA_MODELS "D:\Ollama\Models" /M

    Esto crea la variable a nivel de sistema.

    También podrías crearla solo para tu usuario:

    setx OLLAMA_MODELS "D:\Ollama\Models"

    Para un PC personal, yo usaría la opción con /M.


    4. Reinicia Ollama

    Después de crear la variable:

    • Cierra todas las terminales.
    • Vuelve a abrir PowerShell.
    • Abre Ollama de nuevo desde el menú de inicio.

    Para ir a lo seguro, reinicia Windows.


    5. Comprueba que la variable existe

    En una nueva ventana de PowerShell:

    echo $env:OLLAMA_MODELS

    Debería aparecer:

    D:\Ollama\Models

    6. Descarga un modelo de prueba

    Por ejemplo:

    ollama pull llama3.1:8b

    Después comprueba que se han creado archivos dentro de:

    D:\Ollama\Models

    Si ya tenías modelos descargados

    Por defecto, Ollama suele guardar los modelos en la carpeta del usuario, dentro de:

    C:\Users\TU_USUARIO\.ollama

    La documentación oficial también indica que %HOMEPATH%\.ollama contiene modelos y configuración.

    Puedes moverlos así:

    1. Cierra Ollama

    taskkill /IM ollama.exe /F

    2. Copia la carpeta antigua al nuevo disco

    Por ejemplo:

    robocopy "$env:USERPROFILE\.ollama\models" "D:\Ollama\Models" /E

    3. Crea la variable OLLAMA_MODELS

    setx OLLAMA_MODELS "D:\Ollama\Models" /M

    4. Reinicia Windows

    Después prueba:

    ollama list

    Si los modelos aparecen correctamente, ya está usando la nueva ubicación.


  • Instalación y uso de OpenCode en Windows con WSL Ubuntu

    Instalación y uso de OpenCode en Windows con WSL Ubuntu

    1. Introducción

    OpenCode es una herramienta de asistencia al desarrollo mediante inteligencia artificial. Permite trabajar con proyectos de programación desde la terminal, hacer preguntas sobre el código, pedir cambios, generar archivos, revisar errores y trabajar con diferentes proveedores de modelos de IA.

    Aunque OpenCode puede instalarse de varias formas, en Windows la opción más recomendable para trabajar de manera seria es usarlo dentro de WSL, es decir, el Subsistema de Windows para Linux. De esta forma trabajaremos con un entorno Linux real dentro de Windows, algo muy útil para programación, servidores, automatización y administración de sistemas.

    OpenCode está disponible como herramienta de terminal, aplicación de escritorio e incluso extensiones para editores como Visual Studio Code. La versión de escritorio está disponible en beta para Windows x64.


    Objetivos de la práctica

    Al finalizar esta práctica, el alumno será capaz de:

    • Instalar WSL en Windows.
    • Instalar Ubuntu dentro de WSL.
    • Instalar OpenCode en el entorno Linux.
    • Abrir un proyecto con Visual Studio Code usando Remote WSL.
    • Ejecutar OpenCode desde la terminal del proyecto.
    • Conectar OpenCode con un proveedor de IA.
    • Comprender la diferencia entre usar OpenCode en terminal, escritorio o navegador.

    Requisitos previos

    Para realizar esta práctica necesitaremos:

    • Un equipo con Windows 10 o Windows 11.
    • Conexión a Internet.
    • Permisos de administrador en Windows.
    • Visual Studio Code instalado.
    • Una cuenta o clave de acceso para algún proveedor de IA compatible, por ejemplo OpenAI, GitHub Copilot, OpenRouter, Anthropic, Google Gemini u otro proveedor compatible.

    OpenCode necesita un proveedor de modelos de lenguaje para funcionar. La herramienta puede ser gratuita, pero el uso del modelo puede depender del proveedor elegido.


    Paso 1. Instalar WSL en Windows

    Abrimos PowerShell como administrador.

    Para ello:

    1. Pulsamos en el menú de inicio.
    2. Escribimos PowerShell.
    3. Hacemos clic derecho.
    4. Seleccionamos Ejecutar como administrador.

    Después ejecutamos:

    wsl --install

    Este comando instalará WSL y, normalmente, también una distribución Ubuntu por defecto.

    Cuando finalice la instalación, reiniciamos el equipo si Windows lo solicita.


    Paso 2. Comprobar que WSL funciona

    Después del reinicio, abrimos una terminal de Ubuntu desde el menú de inicio.

    Buscamos:

    Ubuntu

    Si no lo vemos, probamos lo siguiente:

    Ejecuta:

    wsl --list --online

    Esto te mostrará las distribuciones disponibles.


    Instala Ubuntu manualmente

    Ejecuta:

    wsl --install -d Ubuntu

    Reinicia el ordenador si te lo pide.

    Si al reinicar aun no aparece, vuelve a poner

    wsl --install -d Ubuntu

    Comprueba qué distribuciones tienes instaladas

    Después abre PowerShell normal y ejecuta:

    wsl -l -v

    Deberías ver algo parecido a:

      NAME      STATE           VERSION
    * Ubuntu Stopped 2

    Si aparece Ubuntu, puedes abrirlo con:

    wsl

    o también:

    ubuntu

    La primera vez que se abra, Ubuntu nos pedirá crear un usuario y una contraseña.

    Por ejemplo:

    Enter new UNIX username: alumno
    New password:
    Retype new password:

    La contraseña no se verá mientras la escribimos. Esto es normal en Linux.


    Paso 3. Actualizar Ubuntu

    Dentro de la terminal de Ubuntu ejecutamos:

    sudo apt update
    sudo apt upgrade -y

    Con esto actualizamos la lista de paquetes y los programas instalados.

    Es importante hacer este paso antes de instalar herramientas de desarrollo.


    Paso 4. Instalar herramientas básicas

    Instalamos algunas herramientas útiles:

    sudo apt install -y curl git unzip

    Estas herramientas nos permitirán descargar archivos, trabajar con repositorios y preparar proyectos.


    Paso 5. Instalar OpenCode dentro de WSL

    La documentación oficial de OpenCode recomienda instalarlo en WSL para trabajar desde Windows. Una vez dentro de la terminal de Ubuntu, ejecutamos el instalador oficial:

    curl -fsSL https://opencode.ai/install | bash

    Cuando termine la instalación, cerramos y volvemos a abrir la terminal de Ubuntu.

    Después comprobamos que OpenCode está instalado:

    opencode --version

    Si aparece una versión, la instalación ha funcionado correctamente.


    Paso 6. Crear una carpeta de trabajo

    Vamos a crear una carpeta para nuestros proyectos dentro del entorno Linux.

    mkdir -p ~/proyectos
    cd ~/proyectos

    Ahora podemos crear un proyecto de prueba:

    mkdir prueba-opencode
    cd prueba-opencode

    Creamos un archivo simple:

    nano index.html

    Pegamos este contenido:

    <!DOCTYPE html>
    <html lang="es">
    <head>
    <meta charset="UTF-8">
    <title>Prueba OpenCode</title>
    </head>
    <body>
    <h1>Hola desde OpenCode</h1>
    <p>Este es mi primer proyecto usando OpenCode en WSL.</p>
    </body>
    </html>

    Guardamos con:

    CTRL + O
    Enter
    CTRL + X

    Paso 7. Ejecutar OpenCode en el proyecto

    Desde la carpeta del proyecto ejecutamos:

    opencode

    OpenCode se iniciará en la terminal.

    La primera vez puede solicitar configuración o conexión con un proveedor de IA.

    Dentro de OpenCode podemos usar comandos como:

    /connect

    Este comando permite conectar OpenCode con un proveedor de modelos de IA.

    Dependiendo del proveedor elegido, puede pedir una clave API, iniciar sesión o configurar una cuenta.


    Paso 8. Primeras pruebas con OpenCode

    Una vez abierto OpenCode dentro del proyecto, podemos pedirle tareas sencillas.

    Por ejemplo:

    Explícame qué hace este proyecto.

    También podemos pedirle mejoras:

    Mejora el archivo index.html añadiendo una estructura básica con cabecera, contenido principal y pie de página.

    O podemos pedirle que cree nuevos archivos:

    Crea un archivo styles.css y enlázalo desde index.html.

    OpenCode puede ayudar a escribir, modificar y revisar código, pero el alumno siempre debe revisar los cambios antes de aceptarlos. La IA puede equivocarse, generar código innecesario o modificar partes que no debería.


    Paso 9. Instalar Visual Studio Code

    Si no lo tenemos instalado, descargamos e instalamos Visual Studio Code desde su página oficial.

    Una vez instalado, abrimos Visual Studio Code.


    Paso 10. Instalar la extensión Remote WSL

    En Visual Studio Code:

    1. Vamos a la sección Extensions.
    2. Buscamos:
    WSL
    1. Instalamos la extensión oficial de Microsoft llamada WSL.

    Esta extensión permite abrir carpetas de Linux/WSL directamente desde Visual Studio Code.


    Paso 11. Abrir el proyecto desde WSL en Visual Studio Code

    Volvemos a la terminal de Ubuntu y nos situamos en nuestro proyecto:

    cd ~/proyectos/prueba-opencode

    Ejecutamos:

    code .

    La primera vez puede tardar un poco, porque Visual Studio Code instalará algunos componentes dentro de WSL.

    Cuando se abra Visual Studio Code, veremos que estamos trabajando dentro del entorno WSL.

    Esto es importante: aunque estamos usando una aplicación gráfica de Windows, el proyecto realmente está dentro de Ubuntu.


    Paso 12. Abrir una terminal integrada en Visual Studio Code

    Dentro de Visual Studio Code:

    1. Vamos al menú Terminal.
    2. Pulsamos New Terminal.
    3. Comprobamos que la terminal abierta es de Ubuntu/WSL.

    Podemos probarlo ejecutando:

    pwd

    Debería aparecer una ruta parecida a:

    /home/alumno/proyectos/prueba-opencode

    Ahora podemos lanzar OpenCode directamente desde la terminal integrada:

    opencode

    Esta forma de trabajar es muy cómoda porque tenemos:

    • El editor Visual Studio Code.
    • El proyecto abierto.
    • La terminal Linux.
    • OpenCode trabajando sobre la carpeta real del proyecto.

    Paso 13. Acceder a proyectos de Windows desde WSL

    Aunque es recomendable guardar los proyectos dentro de Linux, también podemos acceder a carpetas de Windows desde WSL.

    Por ejemplo, el disco C: de Windows aparece en WSL como:

    /mnt/c/

    Ejemplo:

    cd /mnt/c/Users/TuUsuario/Desktop

    El disco D: aparecería como:

    /mnt/d/

    Y así sucesivamente.

    La documentación oficial de OpenCode indica que desde WSL podemos navegar a proyectos de Windows usando rutas como /mnt/c/Users/YourName/project y ejecutar allí opencode.

    Aun así, para proyectos de programación es mejor trabajar dentro de:

    /home/usuario/proyectos

    Suele dar menos problemas de permisos, rendimiento y compatibilidad.


    Paso 14. Usar la versión web de OpenCode desde WSL

    OpenCode también puede ejecutarse como interfaz web en el navegador. La documentación oficial recomienda que, en Windows, se ejecute desde WSL para mantener buen acceso al sistema de archivos y a la terminal.

    Desde la carpeta del proyecto podemos ejecutar:

    opencode web --hostname 0.0.0.0

    OpenCode mostrará una dirección local en la terminal.

    Después podremos abrirla desde el navegador de Windows.

    Normalmente será algo parecido a:

    http://localhost:PUERTO

    donde PUERTO será el número que indique OpenCode.


    Paso 15. Usar la aplicación de escritorio de OpenCode

    OpenCode también tiene una aplicación de escritorio en beta para Windows x64.

    La idea sería:

    1. Instalar OpenCode Desktop para Windows.
    2. Ejecutar el servidor de OpenCode dentro de WSL.
    3. Conectar la aplicación de escritorio al servidor local.

    Desde WSL podemos lanzar el servidor con:

    opencode serve --hostname 0.0.0.0 --port 4096

    Después, desde la aplicación de escritorio, nos conectaríamos a:

    http://localhost:4096

    Si localhost no funciona, podemos obtener la IP de WSL con:

    hostname -I

    Y conectarnos usando una dirección parecida a:

    http://IP-DE-WSL:4096

    La documentación oficial advierte que, si usamos --hostname 0.0.0.0, conviene proteger el servidor con contraseña mediante la variable OPENCODE_SERVER_PASSWORD.

    Ejemplo:

    OPENCODE_SERVER_PASSWORD=clave-segura opencode serve --hostname 0.0.0.0 --port 4096

    Paso 16. Recomendación para clase

    Para esta práctica vamos a trabajar principalmente de esta forma:

    Windows + WSL Ubuntu + Visual Studio Code + OpenCode en terminal

    Es decir:

    1. Usamos Windows como sistema principal.
    2. Usamos Ubuntu mediante WSL como entorno de desarrollo.
    3. Abrimos los proyectos con Visual Studio Code usando Remote WSL.
    4. Ejecutamos OpenCode desde la terminal del proyecto.

    Esta forma de trabajo es muy parecida a un entorno profesional real, porque muchos servidores, herramientas de desarrollo y sistemas de despliegue funcionan sobre Linux.


    Paso 17. Comandos resumen

    Instalar WSL

    wsl --install

    Actualizar Ubuntu

    sudo apt update
    sudo apt upgrade -y

    Instalar herramientas básicas

    sudo apt install -y curl git unzip

    Instalar OpenCode

    curl -fsSL https://opencode.ai/install | bash

    Comprobar versión

    opencode --version

    Crear proyecto

    mkdir -p ~/proyectos/prueba-opencode
    cd ~/proyectos/prueba-opencode

    Abrir Visual Studio Code desde WSL

    code .

    Ejecutar OpenCode

    opencode

    Ejecutar OpenCode Web

    opencode web --hostname 0.0.0.0

    Ejecutar servidor para escritorio

    opencode serve --hostname 0.0.0.0 --port 4096

    Problemas frecuentes

    El comando opencode no se reconoce

    Cerramos y volvemos a abrir la terminal de Ubuntu.

    También podemos probar:

    source ~/.bashrc

    Después:

    opencode --version

    El comando code . no funciona

    Puede deberse a que Visual Studio Code no está instalado correctamente o no está disponible en el PATH.

    Solución:

    1. Abrir Visual Studio Code en Windows.
    2. Instalar la extensión WSL.
    3. Volver a abrir Ubuntu.
    4. Probar de nuevo:
    code .

    OpenCode no puede conectarse al proveedor de IA

    Revisamos:

    • Que tenemos conexión a Internet.
    • Que la clave API es correcta.
    • Que hemos usado el comando /connect.
    • Que el proveedor elegido está disponible.
    • Que la cuenta tiene saldo, suscripción o permisos suficientes.

    Estoy en PowerShell y no en Ubuntu

    OpenCode para esta práctica debe ejecutarse dentro de Ubuntu/WSL.

    Si estamos en PowerShell, veremos algo parecido a:

    PS C:\Users\Alumno>

    Si estamos en Ubuntu/WSL, veremos algo parecido a:

    alumno@equipo:~$

    Para entrar en Ubuntu, abrimos la aplicación Ubuntu desde el menú de inicio.


    Actividad propuesta para el alumno

    Crea un pequeño proyecto web con estos archivos:

    index.html
    styles.css
    script.js

    El proyecto debe tener:

    • Una página principal con título.
    • Un párrafo explicando qué es OpenCode.
    • Un botón creado con HTML.
    • Estilos básicos en CSS.
    • Un pequeño mensaje en JavaScript al pulsar el botón.

    Después abre OpenCode desde la carpeta del proyecto y pídele:

    Revisa este proyecto y propón mejoras sencillas para un alumno que está empezando.

    A continuación, el alumno deberá entregar:

    • Captura de WSL funcionando.
    • Captura de opencode --version.
    • Captura de Visual Studio Code abierto con Remote WSL.
    • Captura de OpenCode ejecutándose en la terminal del proyecto.
    • Breve explicación de qué ventajas tiene trabajar con WSL en Windows.

    Conclusión

    OpenCode puede utilizarse directamente en Windows, pero para un entorno de clase y de desarrollo técnico es más recomendable utilizarlo dentro de WSL con Ubuntu.

    De esta manera conseguimos un entorno más parecido al que encontraremos en servidores reales, con mejor integración con herramientas Linux y con una forma de trabajo muy útil para programación, administración de sistemas, ciberseguridad y automatización.

    La combinación recomendada para el curso será:

    Windows + WSL Ubuntu + Visual Studio Code Remote WSL + OpenCode