Pràctiques (reptes) i projecte de Docker i Apache HTTP Server
ÍNDEX
- Introducció i Objectius
- Part 1: Fonaments de Docker
- Part 2: Dockerfile - Creant les Nostres Pròpies Imatges
- Part 3: Apache HTTP Server - Configuració Bàsica
- Part 4: Configuració Avançada d'Apache
- Part 5: Docker Compose - Orquestració de Contenidors
- Part 6: Projecte Final
1. INTRODUCCIÓ I OBJECTIUS
Què aprendràs en aquesta pràctica?
En aquesta pràctica, aprendràs a utilitzar Docker com a plataforma de contenidorització per desplegar i gestionar servidors web Apache. Docker és una tecnologia fonamental en el món DevOps actual que permet empaquetar aplicacions i les seves dependències en contenidors portables i reproducibles.
Objectius d'aprenentatge
Al finalitzar aquesta pràctica, hauràs adquirit coneixements sobre:
- Docker: Gestió de contenidors, imatges, volums i xarxes
- Dockerfile: Creació d'imatges personalitzades amb instruccions declaratives
- Docker Compose: Orquestració de múltiples contenidors amb fitxers YAML
- Apache HTTP Server 2.4: Configuració de servidors web professionals
- Mòduls d'Apache (SSL, rewrite, proxy, headers)
- Virtual Hosts (múltiples llocs web en un mateix servidor)
- HTTPS amb certificats SSL/TLS
- URL Rewriting amb mod_rewrite
- Logging personalitzat i redirecció de logs
Prerequisits
- Docker Desktop instal·lat i funcionant al vostre ordinador
- Editor de text (VS Code, Sublime Text, Notepad++, etc.)
- Coneixements bàsics de línia de comandes (terminal/cmd)
- Coneixements bàsics d'HTTP i servidors web
Verificació de l'entorn
Abans de començar, verifica que Docker està correctament instal·lat executant aquestes comandes al terminal:
# Verifica la versió de Docker
docker --version
# Verifica que el daemon de Docker està funcionant
docker ps
# Comprova la versió de Docker Compose
docker compose version
Resultat esperat:
- Docker version 24.0.0 o superior
- Docker Compose version 2.20.0 o superior
- La comanda
docker pshauria de mostrar una taula buida (sense errors)
2. PART 1: FONAMENTS DE DOCKER (45 minuts)
2.1. Conceptes bàsics de Docker
Docker utilitza el concepte de contenidors, que són entorns aïllats que comparteixen el kernel del sistema operatiu però tenen els seus propis processos, sistema de fitxers i xarxa. Això és diferent de les màquines virtuals, que emulen un sistema complet amb el seu propi sistema operatiu.
Conceptes clau:
- Imatge: Plantilla de només lectura que conté el sistema de fitxers i la configuració necessària per executar una aplicació. És com una "fotografia" d'un sistema en un moment determinat.
- Contenidor: Instància en execució d'una imatge. És com un "procés" que s'executa de forma aïllada.
- Dockerfile: Fitxer de text amb instruccions per construir una imatge personalitzada.
- Docker Hub: Registre públic d'imatges Docker (com GitHub però per a imatges).
- Volum: Mecanisme per persistir dades més enllà del cicle de vida d'un contenidor.
- Xarxa: Permet la comunicació entre contenidors i amb l'exterior.
2.2. Primer contenidor: Apache bàsic
Anem a descarregar i executar el nostre primer contenidor amb Apache HTTP Server.
# Descarrega la imatge oficial d'Apache (httpd) versió 2.4.65
docker pull httpd:2.4.65
# Verifica que la imatge s'ha descarregat correctament
docker images
# Executa un contenidor amb Apache
docker run -d -p 8080:80 --name primer-apache-nomcognom httpd:2.4.65
Explicació de les opcions:
-d: Executa el contenidor en segon pla (detached mode)-p 8080:80: Mapeja el port 8080 de l'amfitrió al port 80 del contenidor--name primer-apache-nomcognom: Assigna un nom al contenidor (més fàcil que usar l'ID)httpd:2.4.65: La imatge a utilitzar amb la seva etiqueta (tag) específica
Ara obre el navegador i accedeix a: http://localhost:8080
Hauries de veure la pàgina per defecte d'Apache: "It works!"
Fes una captura conforme funciona i on es vegi com has llançat la comanda docker
2.3. Comandes bàsiques per gestionar contenidors
# Llista els contenidors en execució
docker ps
# Llista tots els contenidors (inclosos els aturats)
docker ps -a
# Atura el contenidor
docker stop primer-apache-nomcognom
# Inicia el contenidor aturat
docker start primer-apache-nomcognom
# Reinicia el contenidor
docker restart primer-apache-nomcognom
# Veure els logs del contenidor
docker logs primer-apache-nomcognom
# Seguir els logs en temps real (com tail -f)
docker logs -f primer-apache-nomcognom
# Executar una comanda dins del contenidor
docker exec primer-apache-nomcognom ls /usr/local/apache2/htdocs
# Accedir a una shell interactiva dins del contenidor
docker exec -it primer-apache-nomcognom /bin/bash
Exercici pràctic 1.1:
Dins del contenidor, explora l'estructura de directoris d'Apache:
# Executa aquesta comanda per entrar al contenidor
docker exec -it primer-apache-nomcognom /bin/bash
# Dins del contenidor, explora aquests directoris
ls -la /usr/local/apache2/
ls -la /usr/local/apache2/conf/
cat /usr/local/apache2/conf/httpd.conf | grep DocumentRoot
2.4. Personalitzant el contingut web
Anem a modificar la pàgina per defecte utilitzant un volum per muntar contingut local dins del contenidor.
# Atura i elimina el contenidor anterior
docker stop primer-apache-nomcognom
docker rm primer-apache-nomcognom
# Crea un directori per al contingut web
mkdir ~/docker-apache-lab
cd ~/docker-apache-lab
mkdir html
# Crea una pàgina HTML personalitzada
Crea una carpeta que es digui html i dintre un fitxer index.html amb el següent contingut:
'''html
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>El Meu Primer Apache amb Docker</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: center;
background: rgba(255, 255, 255, 0.1);
padding: 40px;
border-radius: 15px;
backdrop-filter: blur(10px);
}
h1 { font-size: 3em; margin: 0; }
p { font-size: 1.2em; }
</style>
</head>
<body>
<div class="container">
<h1> Apache + Docker</h1>
<p>Pràctica de laboratori ASIX 2n</p>
<p>Servidor Apache funcionant en un contenidor Docker!</p>
</div>
</body>
</html>
'''
# Executa el contenidor amb un volum muntat
docker run -d -p 8080:80 --name apache-custom-nomcognom \
-v "./html:/usr/local/apache2/htdocs" \
httpd:2.4.65
Concepte important - Volums:
Quan utilitzem -v estem creant un bind mount que connecta un directori del nostre sistema amb un directori del contenidor. Qualsevol canvi que facis al directori local es reflectirà immediatament dins del contenidor, i viceversa.
Refresca el navegador a http://localhost:8080 i veuràs la teva pàgina personalitzada!
2.5. Inspecció de contenidors
Docker proporciona informació detallada sobre els contenidors en execució:
# Informació detallada del contenidor (JSON)
docker inspect apache-custom-nomcognom
# Estadístiques d'ús de recursos en temps real
docker stats apache-custom-nomcognom
# Processos en execució dins del contenidor
docker top apache-custom-nomcognom
Repte 1
- Crea una segona pàgina HTML anomenada
about.htmldins del directorihtmlamb informació sobre tu (nom, curs, data). - Hauries de poder accedir-hi a
http://localhost:8080/about.htmlsense reiniciar el contenidor. - Adjunta captures de pantalla de com ho has fet.
3. PART 2: DOCKERFILE - CREANT LES NOSTRES PRÒPIES IMATGES
3.1. Introducció al Dockerfile
Un Dockerfile és un fitxer de text que conté una sèrie d'instruccions per construir una imatge Docker. És com una recepta que especifica exactament com ha de ser el teu contenidor.
Instruccions principals d'un Dockerfile:
FROM: Especifica la imatge base (punt de partida)RUN: Executa comandes durant la construcció de la imatgeCOPY/ADD: Copia fitxers del sistema host a la imatgeWORKDIR: Estableix el directori de treball dins del contenidorENV: Defineix variables d'entornEXPOSE: Documenta els ports que el contenidor utilitzaCMD: Comanda per defecte que s'executa quan s'inicia el contenidorENTRYPOINT: Punt d'entrada fix per al contenidorLABEL: Afegeix metadades a la imatge
3.2. El nostre primer Dockerfile
Crea un directori nou per a aquesta part:
Crea un fitxer anomenat Dockerfile (sense extensió) amb aquest contingut:
# Utilitzem Apache 2.4.65 com a imatge base (Alpine és una versió lightweight)
FROM httpd:2.4.65-alpine
# Informació sobre qui manté aquesta imatge
LABEL maintainer="elteuemail@sapalomera.cat"
LABEL description="Apache HTTP Server personalitzat per a pràctiques ASIX"
LABEL version="1.0"
# Copia el contingut web personalitzat
COPY ./web-content/ /usr/local/apache2/htdocs/
# Exposa el port 80 (documentació, no fa res funcionalment)
EXPOSE 80
# La imatge base ja té definit el CMD per iniciar Apache
# CMD ["httpd-foreground"]
Crea el subdirectori web-content amb el contingut web i crea un fitxer index.html amb el següent contingut
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Imatge Docker Personalitzada</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #2c3e50;
color: #ecf0f1;
padding: 50px;
text-align: center;
}
.badge {
background: #e74c3c;
padding: 10px 20px;
border-radius: 20px;
display: inline-block;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>🚀 Imatge Docker Personalitzada</h1>
<div class="badge">Construïda amb Dockerfile</div>
<p>Aquesta pàgina s'ha copiat automàticament durant la construcció de la imatge!</p>
</body>
</html>
EOF
3.3. Construir la imatge
Ara construïm la nostra pròpia imatge Docker:
# Construcció de la imatge amb un tag (etiqueta)
docker build -t apache-custom:v1.0 .
# Llista les imatges per veure la nova imatge
docker images
# Executa un contenidor amb la nostra imatge
docker run -d -p 8081:80 --name apache-dockerfile apache-custom:v1.0
Explicació del procés de construcció:
Quan executes docker build, Docker llegeix el Dockerfile línia per línia i crea capes (layers) per a cada instrucció. Cada capa és immutable i es pot reutilitzar (cache), la qual cosa fa que les construccions posteriors siguin molt més ràpides.
Accedeix a http://localhost:8081 i veuràs la pàgina que es va copiar durant la construcció.
3.4. Multi-stage builds (construccions en múltiples etapes)
Les multi-stage builds són una tècnica avançada que permet crear imatges més petites i segures. S'utilitzen quan necessites eines de construcció que no vols en la imatge final.
Crea un nou directori:
Crea aquest Dockerfile:
# ETAPA 1: Construcció del contingut
FROM node:18-alpine AS builder
# Instal·la dependències per "construir" el lloc web
WORKDIR /build
# En una aplicació real, aquí compilaries TypeScript, minificaries CSS, etc.
# Per simplicitat, només crearem fitxers HTML
RUN mkdir -p dist && \
echo '<!DOCTYPE html>' > dist/index.html && \
echo '<html><head><title>Multi-stage Build</title></head>' >> dist/index.html && \
echo '<body style="font-family: Arial; background: #34495e; color: white; padding: 50px; text-align: center;">' >> dist/index.html && \
echo '<h1> Multi-Stage Build</h1>' >> dist/index.html && \
echo '<p>Aquesta imatge s'\''ha creat utilitzant construcció en múltiples etapes</p>' >> dist/index.html && \
echo '<p style="background: #e67e22; padding: 10px; border-radius: 5px;">Imatge final: només Apache + fitxers estàtics (sense Node.js)</p>' >> dist/index.html && \
echo '</body></html>' >> dist/index.html
# ETAPA 2: Imatge final (només runtime)
FROM httpd:2.4.65-alpine
# Metadades
LABEL maintainer="elteuemail@sapalomera.cat"
LABEL description="Apache amb multi-stage build"
# Copia NOMÉS els fitxers construïts de l'etapa anterior
# No inclou Node.js ni les eines de construcció
COPY --from=builder /build/dist/ /usr/local/apache2/htdocs/
EXPOSE 80
Construeix i executa:
docker build -t apache-multistage:v1.0 .
docker run -d -p 8082:80 --name apache-multistage apache-multistage:v1.0
Avantatge clau: La imatge final és molt més petita perquè no inclou Node.js ni les eines de construcció, només Apache i els fitxers HTML resultants.
Comprova les mides:
3.5. Best practices per a Dockerfile (2025)
Segons les recomanacions actuals de Docker (referències: Docker Documentation i community best practices 2025):
1. Utilitza imatges base oficials i específiques:
# MALAMENT: tag "latest" pot canviar amb el temps
FROM httpd:latest
# CORRECTE: versió específica i variant lightweight
FROM httpd:2.4.65-alpine
2. Minimitza el nombre de capes:
# MALAMENT: Cada RUN crea una nova capa
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
# CORRECTE: Combina comandes relacionades
RUN apt-get update && apt-get install -y \
curl \
vim \
&& rm -rf /var/lib/apt/lists/*
3. Utilitza .dockerignore:
Crea un fitxer .dockerignore per excloure fitxers innecessaris:
4. Ordena les instruccions per aprofitar la cache:
Les instruccions que canvien més freqüentment han d'anar al final del Dockerfile.
5. No executis contenidors com a root (seguretat):
FROM httpd:2.4.65-alpine
# Crea un usuari no privilegiat
RUN addgroup -g 1001 apache-user && \
adduser -D -u 1001 -G apache-user apache-user
# Canvia el propietari dels fitxers necessaris
RUN chown -R apache-user:apache-user /usr/local/apache2/
# Canvia a l'usuari no privilegiat
USER apache-user
EXPOSE 80
CMD ["httpd-foreground"]
Repte 2
- Crea un Dockerfile des de zero
- Utilitza la imatge base
httpd:2.4.65-alpine - Copia un directori
static-siteamb almenys 3 pàgines HTML (index.html, services.html, contact.html) - Inclou metadades apropiades (LABEL)
- Segueix les best practices mencionades
- Construeix la imatge amb el tag
apache-exercise-nomcognom:v1.0i executa-la al port 8083. - Adjunta les captures de pantalla on es vegi clarament el funcionament.
4. PART 3: APACHE HTTP SERVER - CONFIGURACIÓ BÀSICA (45 minuts)
4.1. Estructura de configuració d'Apache
Apache HTTP Server 2.4.65 organitza la seva configuració en diversos fitxers i directoris. El fitxer principal és httpd.conf, però també es poden utilitzar fitxers .htaccess per a configuracions específiques de directori.
Directoris importants dins del contenidor oficial:
/usr/local/apache2/
├── conf/ # Fitxers de configuració
│ ├── httpd.conf # Configuració principal
│ ├── extra/ # Configuracions addicionals
│ └── mime.types # Tipus MIME
├── htdocs/ # Document root (contingut web)
├── logs/ # Logs d'accés i errors
└── modules/ # Mòduls d'Apache (.so)
4.2. Mòduls d'Apache
Apache utilitza una arquitectura modular. Els mòduls més importants que treballarem són:
- mod_rewrite: Reescriptura d'URLs
- mod_ssl: Suport HTTPS/SSL/TLS
- mod_headers: Manipulació de capçaleres HTTP
- mod_proxy: Proxy i proxy invers
- mod_log_config: Configuració de logging personalitzat
Verifica els mòduls carregats:
4.3. Configuració personalitzada d'Apache
Crea un directori per treballar amb configuracions:
Crea una configuració personalitzada (conf/httpd-custom.conf):
# Carrega els mòduls necessaris
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule dir_module modules/mod_dir.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule headers_module modules/mod_headers.so
# Configuració del servidor
ServerName localhost
Listen 80
# Usuari i grup (per defecte en la imatge oficial)
User daemon
Group daemon
# Document Root
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
# Permet seguir enllaços simbòlics
Options Indexes FollowSymLinks
# Permet l'ús de .htaccess
AllowOverride All
# Permet accés a tothom
Require all granted
# Activa el mod_rewrite per aquest directori
RewriteEngine On
</Directory>
# Index per defecte
DirectoryIndex index.html index.htm
# Tipus MIME
TypesConfig conf/mime.types
# Configuració de logging
ErrorLog "logs/error.log"
LogLevel warn
# Format de log personalitzat (Combined Log Format + temps de resposta)
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_with_time
CustomLog "logs/access.log" combined_with_time
# Capçalera de seguretat (exemple)
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Crea un fitxer Dockerfile:
FROM httpd:2.4.65-alpine
LABEL maintainer="elseu-email@exemple.cat"
LABEL description="Apache amb configuració personalitzada"
# Copia la configuració personalitzada
COPY conf/httpd-custom.conf /usr/local/apache2/conf/httpd.conf
# Copia el contingut web
COPY html/ /usr/local/apache2/htdocs/
EXPOSE 80
CMD ["httpd-foreground"]
Crea contingut HTML de prova (html/index.html):
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Apache Configurat</title>
<style>
body {
font-family: 'Courier New', monospace;
background: #1a1a2e;
color: #eee;
padding: 40px;
}
.config-box {
background: #16213e;
border-left: 5px solid #0f3460;
padding: 20px;
margin: 20px 0;
border-radius: 5px;
}
code {
background: #0f3460;
padding: 2px 8px;
border-radius: 3px;
color: #feca57;
}
</style>
</head>
<body>
<h1>Apache amb Configuració Personalitzada</h1>
<div class="config-box">
<h3>Funcionalitats actives:</h3>
<ul>
<li>Mòdul Rewrite activat</li>
<li>Capçaleres de seguretat configurades</li>
<li>Logging personalitzat amb temps de resposta</li>
<li>.htaccess permès (AllowOverride All)</li>
</ul>
</div>
<p>Comprova els logs amb: <code>docker logs apache-configured</code></p>
</body>
</html>
Construeix i executa:
docker build -t apache-configured:v1.0 .
docker run -d -p 8084:80 --name apache-configured \
-v "$(pwd)/logs:/usr/local/apache2/logs" \
apache-configured:v1.0
Nota important sobre volums: Hem muntat el directori logs per poder veure els logs d'Apache fora del contenidor.
Accedeix a http://localhost:8084 i després comprova els logs:
4.4. Fitxers .htaccess
Els fitxers .htaccess permeten configuracions específiques per directori sense modificar httpd.conf.
Crea html/.htaccess:
# Activar el motor de rewrite
RewriteEngine On
# Exemple: Redirigir .htm a .html
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^(.*)$ $1.html [L]
# Protegir fitxers de configuració
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Capçalera personalitzada per indicar que s'està utilitzant .htaccess
Header set X-Powered-By "Apache-ASIX-Lab"
# Desactivar listatge de directoris (si no hi ha index.html)
Options -Indexes
Reconstrueix la imatge per incloure el .htaccess:
docker stop apache-configured
docker rm apache-configured
docker build -t apache-configured:v1.1 .
docker run -d -p 8084:80 --name apache-configured \
-v "$(pwd)/logs:/usr/local/apache2/logs" \
apache-configured:v1.1
Repte 3
-
Modifica el fitxer
.htaccessper:- Crea una regla que redirigeixi qualsevol petició a
/old-pagecap a/new-page.html - Afegeix una capçalera personalitzada
X-Lab-Version: 1.0a totes les respostes
- Crea una regla que redirigeixi qualsevol petició a
-
Reconstrueix la imatge i prova que funciona accedint a
http://localhost:8084/old-page. - Adjunta captures de pantalla de tot el procés
5. PART 4: CONFIGURACIÓ AVANÇADA D'APACHE (60 minuts)
5.1. Virtual Hosts - Múltiples llocs web
Els Virtual Hosts permeten allotjar múltiples llocs web en un mateix servidor Apache. Hi ha dos tipus:
- Name-based: Múltiples dominis en una mateixa IP (més comú)
- IP-based: Cada domini té la seva pròpia IP
Crea un nou directori:
cd ~/docker-apache-lab
mkdir apache-vhosts
cd apache-vhosts
mkdir -p conf sites/site1 sites/site2 logs
Crea el contingut per al lloc 1 (sites/site1/index.html):
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Lloc 1 - Empresa Tech</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 50px;
text-align: center;
}
.logo { font-size: 4em; }
</style>
</head>
<body>
<div class="logo">🚀</div>
<h1>Empresa Tech Solutions</h1>
<p>Virtual Host 1: site1.local</p>
<p>Desenvolupament de software i consultoria IT</p>
</body>
</html>
Crea el contingut per al lloc 2 (sites/site2/index.html):
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Lloc 2 - Disseny Web</title>
<style>
body {
font-family: 'Georgia', serif;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
padding: 50px;
text-align: center;
}
.logo { font-size: 4em; }
</style>
</head>
<body>
<div class="logo">🎨</div>
<h1>Creative Design Studio</h1>
<p>Virtual Host 2: site2.local</p>
<p>Disseny gràfic i experiència d'usuari</p>
</body>
</html>
Crea la configuració d'Apache amb Virtual Hosts (conf/httpd-vhosts.conf):
# Carrega mòduls necessaris
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule dir_module modules/mod_dir.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule headers_module modules/mod_headers.so
LoadModule vhost_alias_module modules/mod_vhost_alias.so
# Configuració bàsica del servidor
ServerName localhost
Listen 80
User daemon
Group daemon
TypesConfig conf/mime.types
DirectoryIndex index.html
# Format de log
LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_combined
ErrorLog "logs/error.log"
# Virtual Host per defecte (quan no coincideix cap altre)
<VirtualHost *:80>
ServerName localhost
DocumentRoot "/usr/local/apache2/htdocs"
ErrorLog "logs/default-error.log"
CustomLog "logs/default-access.log" vhost_combined
<Directory "/usr/local/apache2/htdocs">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# Virtual Host 1: site1.local
<VirtualHost *:80>
ServerName site1.local
ServerAlias www.site1.local
DocumentRoot "/usr/local/apache2/sites/site1"
ErrorLog "logs/site1-error.log"
CustomLog "logs/site1-access.log" vhost_combined
<Directory "/usr/local/apache2/sites/site1">
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Capçalera personalitzada per identificar el virtual host
Header always set X-VHost "Site1"
</VirtualHost>
# Virtual Host 2: site2.local
<VirtualHost *:80>
ServerName site2.local
ServerAlias www.site2.local
DocumentRoot "/usr/local/apache2/sites/site2"
ErrorLog "logs/site2-error.log"
CustomLog "logs/site2-access.log" vhost_combined
<Directory "/usr/local/apache2/sites/site2">
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Header always set X-VHost "Site2"
</VirtualHost>
Crea el Dockerfile:
FROM httpd:2.4.65-alpine
LABEL maintainer="elteuemail@sapalomera.cat"
LABEL description="Apache amb Virtual Hosts"
# Copia la configuració
COPY conf/httpd-vhosts.conf /usr/local/apache2/conf/httpd.conf
# Copia els llocs web
COPY sites/ /usr/local/apache2/sites/
EXPOSE 80
CMD ["httpd-foreground"]
Construeix i executa:
docker build -t apache-vhosts:v1.0 .
docker run -d -p 8085:80 --name apache-vhosts \
-v "$(pwd)/logs:/usr/local/apache2/logs" \
apache-vhosts:v1.0
Configuració del sistema per provar els Virtual Hosts:
Per accedir als virtual hosts utilitzant noms de domini locals, has d'editar el fitxer /etc/hosts (Linux/Mac) o C:\Windows\System32\drivers\etc\hosts (Windows):
Ara pots accedir a:
- http://site1.local:8085
- http://site2.local:8085
Comprova els logs separats:
5.2. HTTPS amb SSL/TLS
Ara configurarem HTTPS utilitzant certificats auto-signats (per entorns de desenvolupament).
Crea un nou directori:
Generació de certificats SSL auto-signats:
Crearem un Dockerfile que generi els certificats durant la construcció:
FROM httpd:2.4.65-alpine
LABEL maintainer="elseu-email@exemple.cat"
LABEL description="Apache amb HTTPS/SSL"
# Instal·la OpenSSL (ja ve a Alpine, però assegurem dependències)
RUN apk add --no-cache openssl
# Crea el directori per als certificats
RUN mkdir -p /usr/local/apache2/certs
# Genera un certificat auto-signat
# Atenció: Això és NOMÉS per a desenvolupament! En producció usa Let's Encrypt o un CA oficial
RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /usr/local/apache2/certs/server.key \
-out /usr/local/apache2/certs/server.crt \
-subj "/C=ES/ST=Catalunya/L=Barcelona/O=ASIX Lab/OU=IT/CN=localhost"
# Ajusta permisos dels certificats
RUN chmod 600 /usr/local/apache2/certs/server.key && \
chmod 644 /usr/local/apache2/certs/server.crt
# Copia la configuració d'Apache amb SSL
COPY conf/httpd-ssl.conf /usr/local/apache2/conf/httpd.conf
# Copia el contingut web
COPY html/ /usr/local/apache2/htdocs/
# Exposa els ports HTTP i HTTPS
EXPOSE 80 443
CMD ["httpd-foreground"]
Crea la configuració SSL (conf/httpd-ssl.conf):
# Mòduls necessaris
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule dir_module modules/mod_dir.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule headers_module modules/mod_headers.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule rewrite_module modules/mod_rewrite.so
ServerName localhost
User daemon
Group daemon
TypesConfig conf/mime.types
DirectoryIndex index.html
# Configuració de logging
ErrorLog "logs/error.log"
LogLevel warn
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog "logs/access.log" combined
# Cache SSL per millorar el rendiment
SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300
# Virtual Host HTTP (port 80) - Redirigeix a HTTPS
<VirtualHost *:80>
ServerName localhost
DocumentRoot "/usr/local/apache2/htdocs"
# Redirigeix tot el tràfic HTTP a HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
ErrorLog "logs/http-error.log"
CustomLog "logs/http-access.log" combined
</VirtualHost>
# Virtual Host HTTPS (port 443)
<VirtualHost *:443>
ServerName localhost
DocumentRoot "/usr/local/apache2/htdocs"
# Activa SSL
SSLEngine on
# Rutes als certificats
SSLCertificateFile "/usr/local/apache2/certs/server.crt"
SSLCertificateKeyFile "/usr/local/apache2/certs/server.key"
# Protocols SSL/TLS moderns (desactiva versions antigues insegures)
# Seguint les recomanacions de seguretat 2025
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# Cipher suites segures (prioritza algoritmes moderns)
SSLCipherSuite HIGH:!aNULL:!MD5:!RC4
SSLHonorCipherOrder on
# Capçaleres de seguretat HTTP
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
<Directory "/usr/local/apache2/htdocs">
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog "logs/https-error.log"
CustomLog "logs/https-access.log" combined
</VirtualHost>
Crea el contingut web (html/index.html):
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Connexió Segura HTTPS</title>
<style>
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
color: white;
padding: 50px;
text-align: center;
}
.secure-badge {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
padding: 30px;
border-radius: 20px;
display: inline-block;
margin: 30px 0;
}
.lock { font-size: 5em; margin: 20px 0; }
.info {
background: rgba(0, 0, 0, 0.2);
padding: 20px;
border-radius: 10px;
margin: 20px auto;
max-width: 600px;
text-align: left;
}
</style>
</head>
<body>
<div class="secure-badge">
<div class="lock"></div>
<h1>Connexió Segura HTTPS</h1>
<p>Apache 2.4.65 amb SSL/TLS activat</p>
</div>
<div class="info">
<h3>Informació SSL/TLS:</h3>
<ul>
<li>Protocol: TLS 1.2 o superior</li>
<li>Certificat: Auto-signat (només desenvolupament)</li>
<li>HSTS: Activat (31536000 segons)</li>
<li>Redirecció HTTP → HTTPS: Activa</li>
</ul>
</div>
<p><small>El navegador mostrarà un avís perquè el certificat és auto-signat.<br>
En producció, utilitza certificats d'una CA reconeguda (Let's Encrypt, etc.)</small></p>
<script>
// Mostra informació sobre la connexió (només funciona en HTTPS)
if (location.protocol === 'https:') {
console.log('Connexió segura HTTPS establerta');
} else {
console.log('Hauries de ser redirigit a HTTPS...');
}
</script>
</body>
</html>
Construeix i executa:
docker build -t apache-ssl:v1.0 .
docker run -d -p 8086:80 -p 8443:443 --name apache-ssl \
-v "$(pwd)/logs:/usr/local/apache2/logs" \
apache-ssl:v1.0
Proves:
- Accedeix a
http://localhost:8086→ Hauries de ser redirigit ahttps://localhost:8443 - El navegador mostrarà un avís de certificat no segur (normal amb certificats auto-signats)
- Accepta l'avís i veuràs la pàgina amb HTTPS
Inspecciona els certificats:
# Veure informació del certificat
docker exec apache-ssl openssl x509 -in /usr/local/apache2/certs/server.crt -text -noout
5.3. Mod_rewrite avançat - Reescriptura d'URLs
El mod_rewrite és un dels mòduls més potents d'Apache per manipular URLs. Utilitzarem regular expressions i condicions.
Crea html/.htaccess amb regles avançades:
RewriteEngine On
# 1. Redirecció de www a no-www (o viceversa)
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
# 2. URLs amigables: /product/123 → /product.php?id=123
RewriteRule ^product/([0-9]+)$ product.php?id=$1 [L,QSA]
# 3. Eliminar l'extensió .php de les URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+)$ $1.php [L]
# 4. Redirecció d'URLs antigues a noves
RewriteRule ^old-blog/(.*)$ /new-blog/$1 [R=301,L]
# 5. Forçar HTTPS per a certes pàgines
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} ^/admin [OR]
RewriteCond %{REQUEST_URI} ^/login
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
# 6. Bloquejar accés basat en User-Agent (anti-bots)
RewriteCond %{HTTP_USER_AGENT} ^.*(bot|spider|crawler).* [NC]
RewriteRule ^api/ - [F,L]
# 7. Redirecció basada en la IP del client (geolocalització bàsica)
# Exemple: Si la IP comença per 192.168, redirigeix a versió local
RewriteCond %{REMOTE_ADDR} ^192\.168\.
RewriteRule ^$ /local-version.html [L]
5.4. Configuració de logging personalitzat
Apache permet configurar formats de log molt detallats. Modifica conf/httpd-ssl.conf:
# Format de log estàndard
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
# Format de log amb temps de resposta en microsegons
LogFormat "%h %l %u %t \"%r\" %>s %b %D" with_response_time
# Format de log amb informació SSL
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{SSL_PROTOCOL}x\" \"%{SSL_CIPHER}x\"" ssl_combined
# Format JSON per a processament automàtic
LogFormat "{ \"timestamp\": \"%{%Y-%m-%dT%H:%M:%S}t\", \"client\": \"%h\", \"request\": \"%r\", \"status\": %>s, \"bytes\": %b, \"referer\": \"%{Referer}i\", \"user_agent\": \"%{User-Agent}i\", \"response_time_us\": %D }" json_log
# Logging condicional: només registra errors 4xx i 5xx
CustomLog "logs/errors-only.log" combined expr=%{REQUEST_STATUS} >= 400
# Logging per tipus de petició
SetEnvIf Request_URI "\.jpg$" image-request
SetEnvIf Request_URI "\.png$" image-request
SetEnvIf Request_URI "\.gif$" image-request
CustomLog "logs/images.log" combined env=image-request
# Excloure certes peticions del log (com healthchecks)
SetEnvIf Request_URI "^/health$" dontlog
CustomLog "logs/access.log" combined env=!dontlog
Redirecció de logs a stdout/stderr (best practice per a contenidors):
En entorns Docker, és recomanable enviar els logs a stdout/stderr perquè Docker els pugui capturar:
# Envia els logs a la sortida estàndard del contenidor
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 combined
Repte 4
- Crea un Virtual Host amb les següents característiques:
- Nom de domini:
secure-site-nomcognoms.local - HTTPS obligatori (redirigeix HTTP a HTTPS)
- Un fitxer
.htaccessque:- Bloqueja l'accés al directori
/private - Reescriu les URLs per eliminar
.html - Afegeix una capçalera
X-Secure-Site: true
- Bloqueja l'accés al directori
- Nom de domini:
- Logs separats per a aquest virtual host 3, Adjunta les captures de pantalla on es vegi clarament la configuració.
6. PART 5: DOCKER COMPOSE - ORQUESTRACIÓ DE CONTENIDORS (45 minuts)
6.1. Introducció a Docker Compose
Docker Compose és una eina per definir i executar aplicacions Docker multi-contenidor. Utilitza fitxers YAML per configurar els serveis, xarxes i volums de l'aplicació.
Avantatges de Docker Compose:
- Definició declarativa de tota l'arquitectura
- Un sol fitxer
docker-compose.ymlper gestionar múltiples contenidors - Facilita el desplegament, l'escalat i la gestió d'aplicacions complexes
- Xarxes automàtiques entre contenidors
- Gestió de dependències entre serveis
6.2. Estructura bàsica d'un docker-compose.yml
services:
# Cada servei és un contenidor
nom-servei:
image: imatge:tag # Imatge a utilitzar
# o
build: ./directori # Construir des d'un Dockerfile
ports:
- "host:container" # Mapatge de ports
volumes:
- ./local:/container # Volums
environment:
- VAR=valor # Variables d'entorn
depends_on:
- altre-servei # Dependències
networks:
- nom-xarxa # Xarxes
networks:
nom-xarxa: # Definició de xarxes personalitzades
volumes:
nom-volum: # Definició de volums amb nom
6.3. Exemple pràctic: Apache + MySQL + phpMyAdmin
Crearem una aplicació completa amb 3 contenidors que es comuniquen entre ells.
Crea un nou directori:
cd ~/docker-apache-lab
mkdir docker-compose-stack
cd docker-compose-stack
mkdir -p apache/html apache/conf logs mysql-data
Estructura del projecte:
docker-compose-stack/
├── docker-compose.yml
├── apache/
│ ├── Dockerfile
│ ├── conf/
│ │ └── httpd.conf
│ └── html/
│ └── index.php
├── logs/
└── mysql-data/
Crea el Dockerfile per a Apache amb PHP (apache/Dockerfile):
FROM php:8.2-apache
LABEL maintainer="elteuemail@sapalomera.cat"
LABEL description="Apache amb PHP i extensió MySQL"
# Instal·la l'extensió mysqli per connectar a MySQL
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
# Activa mod_rewrite
RUN a2enmod rewrite
# Copia el contingut web
COPY html/ /var/www/html/
# Exposa el port 80
EXPOSE 80
Crea una pàgina PHP que es connecta a MySQL (apache/html/index.php):
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Stack LAMP amb Docker Compose</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px;
margin: 0;
}
.container {
max-width: 800px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
padding: 40px;
border-radius: 20px;
}
.status {
padding: 20px;
margin: 20px 0;
border-radius: 10px;
background: rgba(255, 255, 255, 0.2);
}
.success { border-left: 5px solid #2ecc71; }
.error { border-left: 5px solid #e74c3c; }
code {
background: rgba(0, 0, 0, 0.3);
padding: 2px 8px;
border-radius: 3px;
}
table {
width: 100%;
margin: 20px 0;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
th { background: rgba(0, 0, 0, 0.3); }
</style>
</head>
<body>
<div class="container">
<h1>🐳 Docker Compose: Apache + MySQL + phpMyAdmin</h1>
<?php
// Configuració de la connexió a MySQL
$host = 'mysql'; // Nom del servei en docker-compose.yml
$user = 'root';
$pass = 'rootpassword';
$db = 'testdb';
// Intenta connectar a MySQL
$conn = @new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
echo '<div class="status error">';
echo '<h3>❌ Error de connexió a MySQL</h3>';
echo '<p>' . $conn->connect_error . '</p>';
echo '</div>';
} else {
echo '<div class="status success">';
echo '<h3>✅ Connexió a MySQL establerta correctament</h3>';
echo '<p><strong>Servidor:</strong> ' . $host . '</p>';
echo '<p><strong>Base de dades:</strong> ' . $db . '</p>';
// Mostra informació del servidor MySQL
$version = $conn->query("SELECT VERSION() as version")->fetch_assoc();
echo '<p><strong>Versió MySQL:</strong> ' . $version['version'] . '</p>';
// Crea una taula de prova i insereix dades
$conn->query("CREATE TABLE IF NOT EXISTS visits (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME,
ip_address VARCHAR(50)
)");
$ip = $_SERVER['REMOTE_ADDR'];
$conn->query("INSERT INTO visits (timestamp, ip_address) VALUES (NOW(), '$ip')");
// Mostra les últimes visites
echo '<h3>📊 Últimes 5 visites registrades:</h3>';
$result = $conn->query("SELECT * FROM visits ORDER BY timestamp DESC LIMIT 5");
if ($result->num_rows > 0) {
echo '<table>';
echo '<tr><th>ID</th><th>Data i hora</th><th>IP</th></tr>';
while($row = $result->fetch_assoc()) {
echo '<tr>';
echo '<td>' . $row['id'] . '</td>';
echo '<td>' . $row['timestamp'] . '</td>';
echo '<td>' . $row['ip_address'] . '</td>';
echo '</tr>';
}
echo '</table>';
}
echo '</div>';
$conn->close();
}
?>
<div class="status">
<h3>🔗 Enllaços útils:</h3>
<p><a href="http://localhost:8081" style="color: #fff;">📊 phpMyAdmin</a> (usuari: root, contrasenya: rootpassword)</p>
<p><strong>Info del sistema:</strong></p>
<ul>
<li>PHP versió: <?php echo phpversion(); ?></li>
<li>Apache versió: <?php echo apache_get_version(); ?></li>
<li>Contenidor: <?php echo gethostname(); ?></li>
</ul>
</div>
<div class="status">
<h3>📚 Conceptes demostrats:</h3>
<ul>
<li>✅ Multi-contenidor amb Docker Compose</li>
<li>✅ Comunicació entre contenidors via xarxa Docker</li>
<li>✅ Volums persistents per a MySQL</li>
<li>✅ Variables d'entorn per a configuració</li>
<li>✅ Dependències entre serveis (depends_on)</li>
</ul>
</div>
</div>
</body>
</html>
Crea el fitxer docker-compose.yml:
# Versió de Docker Compose (opcional en versions recents)
version: '3.9'
# Definició dels serveis (contenidors)
services:
# Servei Apache amb PHP
apache:
# Construeix la imatge des del Dockerfile
build:
context: ./apache
dockerfile: Dockerfile
# Nom del contenidor
container_name: lamp-apache
# Reinicia automàticament si el contenidor falla
restart: unless-stopped
# Mapatge de ports: host:container
ports:
- "8090:80"
# Volums: persistència i desenvolupament
volumes:
- ./apache/html:/var/www/html # Codi PHP (bind mount)
- ./logs:/var/log/apache2 # Logs d'Apache
# Variables d'entorn
environment:
- TZ=Europe/Madrid
# Depèn del servei MySQL (s'inicia després)
depends_on:
- mysql
# Xarxa per comunicar-se amb altres serveis
networks:
- lamp-network
# Límits de recursos (opcional però recomanat)
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
# Servei MySQL
mysql:
# Imatge oficial de MySQL (versió específica, no "latest")
image: mysql:8.0.35
container_name: lamp-mysql
restart: unless-stopped
# Variables d'entorn per configurar MySQL
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=testdb
- MYSQL_USER=appuser
- MYSQL_PASSWORD=apppassword
- TZ=Europe/Madrid
# Volum amb nom per persistir dades de MySQL
volumes:
- mysql-data:/var/lib/mysql
# Exposa el port MySQL (opcional, només si vols accedir des de l'host)
ports:
- "3306:3306"
networks:
- lamp-network
# Healthcheck per verificar que MySQL està llest
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-prootpassword"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
# Servei phpMyAdmin (interfície web per gestionar MySQL)
phpmyadmin:
image: phpmyadmin:5.2-apache
container_name: lamp-phpmyadmin
restart: unless-stopped
ports:
- "8081:80"
environment:
- PMA_HOST=mysql # Nom del servei MySQL
- PMA_PORT=3306
- PMA_USER=root
- PMA_PASSWORD=rootpassword
- UPLOAD_LIMIT=50M
depends_on:
- mysql
networks:
- lamp-network
# Definició de xarxes
networks:
lamp-network:
driver: bridge # Xarxa bridge per defecte (permet comunicació entre contenidors)
# Definició de volums amb nom (persistents)
volumes:
mysql-data:
driver: local # Volum local gestionat per Docker
6.4. Executar l'stack amb Docker Compose
# Construeix i inicia tots els serveis en segon pla
docker compose up -d
# Veure els logs de tots els serveis
docker compose logs
# Seguir els logs en temps real
docker compose logs -f
# Veure només els logs d'un servei
docker compose logs -f apache
# Llista els contenidors de l'stack
docker compose ps
# Veure l'estat dels serveis
docker compose ps --format json
# Aturar tots els serveis (sense eliminar contenidors)
docker compose stop
# Iniciar els serveis aturats
docker compose start
# Reiniciar un servei específic
docker compose restart apache
# Aturar i eliminar tots els contenidors, xarxes (però manté els volums)
docker compose down
# Eliminar també els volums (⚠️ elimina les dades de MySQL!)
docker compose down -v
# Reconstruir les imatges i iniciar
docker compose up -d --build
# Executar una comanda en un servei
docker compose exec apache bash
# Escalar un servei (crear múltiples instàncies)
docker compose up -d --scale apache=3
Prova l'aplicació:
- Accedeix a
http://localhost:8090→ Veuràs la pàgina PHP connectada a MySQL - Accedeix a
http://localhost:8081→ phpMyAdmin per gestionar la base de dades - Refresca diverses vegades
http://localhost:8090per veure com es registren les visites
6.5. Xarxes i comunicació entre contenidors
Dins de Docker Compose, els contenidors poden comunicar-se entre ells utilitzant el nom del servei com a hostname.
# Accedeix al contenidor d'Apache
docker compose exec apache bash
# Des de dins d'Apache, pots fer ping a MySQL
ping mysql
# Pots connectar-te a MySQL des del contenidor d'Apache
mysql -h mysql -u root -prootpassword -e "SHOW DATABASES;"
Concepte important: Docker Compose crea automàticament una xarxa bridge privada on tots els serveis es poden comunicar. Els noms dels serveis es resolen automàticament com a hostnames via DNS intern.
6.6. Volums persistents vs Bind mounts
Bind mount (./apache/html:/var/www/html):
- Connecta un directori de l'host amb el contenidor
- Útil per a desenvolupament (canvis en temps real)
- El directori existeix al sistema de fitxers de l'host
Volum amb nom (mysql-data:/var/lib/mysql):
- Gestionat completament per Docker
- Millor rendiment (especialment en Windows/Mac)
- Persistent però independent del sistema de fitxers de l'host
- Ideal per a dades de producció
Gestió de volums:
# Llista tots els volums
docker volume ls
# Inspecciona un volum específic
docker volume inspect docker-compose-stack_mysql-data
# Elimina volums no utilitzats
docker volume prune
# Copia dades d'un volum (backup)
docker run --rm -v docker-compose-stack_mysql-data:/data -v $(pwd):/backup alpine tar czf /backup/mysql-backup.tar.gz -C /data .
6.7. Variables d'entorn i fitxers .env
Docker Compose suporta fitxers .env per centralitzar la configuració:
Crea un fitxer .env:
# Configuració de MySQL
MYSQL_ROOT_PASSWORD=supersecret2025
MYSQL_DATABASE=production_db
MYSQL_USER=webapp
MYSQL_PASSWORD=webapp_secret_2025
# Ports
APACHE_PORT=8090
PHPMYADMIN_PORT=8081
MYSQL_PORT=3306
# Timezone
TZ=Europe/Madrid
Modifica docker-compose.yml per utilitzar variables:
version: '3.9'
services:
apache:
# ...
ports:
- "${APACHE_PORT}:80"
environment:
- TZ=${TZ}
# ...
mysql:
# ...
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=${MYSQL_DATABASE}
- MYSQL_USER=${MYSQL_USER}
- MYSQL_PASSWORD=${MYSQL_PASSWORD}
- TZ=${TZ}
ports:
- "${MYSQL_PORT}:3306"
# ...
phpmyadmin:
# ...
ports:
- "${PHPMYADMIN_PORT}:80"
# ...
Important: Afegeix .env al .gitignore per no pujar credencials al repositori!
Repte 5
-
Modifica el
docker-compose.ymlper afegir un quart servei:- Redis (base de dades en memòria per a cache):
- Imatge:
redis:7-alpine - Port: 6379
- Volum persistent per a les dades
- Modifica
index.phpper utilitzar Redis com a comptador de visites. Ha de sortir el teu nom i cognom + nombre visites - Utilitza la llibreria PHP Redis:
pecl install redis
-
Adjunta captures de pantalla on es vegi clarament la configuració i el resultat.
7. PART 6: PROJECTE FINAL
PROJECTE FINAL D'INTEGRACIÓ
Ara que has après tots els conceptes, és hora de demostrar els teus coneixements creant una aplicació completa des de zero.
Objectiu: Crear un stack Docker Compose amb els següents requisits:
Especificacions tècniques:
1. Servei Apache (Frontend)
- Utilitza un Dockerfile personalitzat basat en
httpd:2.4.65-alpine - Configura 2 Virtual Hosts:
frontend.local→ Lloc web principal amb HTML/CSS/JSapi.local→ API REST (pot ser un mock amb fitxers JSON)- Activa i configura mod_rewrite per:
- URLs amigables (eliminar extensions .html)
- Redirigir HTTP a HTTPS
- Configura HTTPS amb certificats auto-signats utilitzant let's encrypt
- Implementa logging personalitzat amb format JSON
- Capçaleres de seguretat (HSTS, X-Frame-Options, CSP)
2. Servei MySQL (Backend Database)
- Versió específica:
mysql:8.0.35 - Crea una base de dades amb almenys 2 taules:
users(id, username, email, created_at)articles(id, user_id, title, content, published_at)- Volum persistent per a les dades
- Healthcheck configurat
- Script d'inicialització amb dades de prova (utilitza volum amb fitxer
.sql)
3. Servei Redis (Cache)
- Imatge:
redis:7-alpine - Volum persistent
- Configuració de límits de memòria
4. Servei phpMyAdmin (Eina d'administració)
- Interfície web per gestionar MySQL
- Port personalitzat
Requisits addicionals:
Seguretat:
- Utilitza variables d'entorn per a credencials (fitxer
.env) - No hardcodegis contrasenyes al codi
- Implementa restriccions d'accés via .htaccess per a certes rutes
Xarxes:
- Crea dues xarxes separades:
frontend-network: només Apachebackend-network: Apache, MySQL, Redis- Això simula una arquitectura amb capes de seguretat
Volums:
- Utilitza volums amb nom per a MySQL i Redis
- Utilitza bind mounts per al codi i configuracions d'Apache
- Munta els logs d'Apache fora del contenidor
Documentació:
- Crea un fitxer
README.mdamb: - Arquitectura del sistema (diagrama de text o descripció)
- Instruccions per desplegar l'aplicació
- URLs d'accés i credencials
- Explicació de les funcionalitats implementades
Funcionalitat aplicació web:
- Pàgina principal (
frontend.local): - Mostra estadístiques (nombre de visites utilitzant Redis)
- Llista els últims 5 articles de la base de dades MySQL
- Formulari per crear nous articles
- API REST (
api.local): - Endpoint GET
/api/articles→ Retorna JSON amb tots els articles - Endpoint POST
/api/articles→ Crea un nou article - Endpoint GET
/api/stats→ Retorna estadístiques (visites, usuaris, articles)
Estructura esperada del projecte
projecte-final/
├── docker-compose.yml
├── .env
├── .gitignore
├── README.md
├── apache/
│ ├── Dockerfile
│ ├── conf/
│ │ ├── httpd.conf
│ │ └── vhosts/
│ │ ├── frontend.conf
│ │ └── api.conf
│ ├── certs/
│ │ ├── (generats durant build)
│ ├── sites/
│ │ ├── frontend/
│ │ │ ├── index.php
│ │ │ ├── .htaccess
│ │ │ └── assets/ (css, js, images)
│ │ └── api/
│ │ ├── index.php
│ │ └── .htaccess
├── mysql/
│ └── init/
│ └── 01-schema.sql
├── logs/
└── (altres fitxers si cal)
Criteris d'avaluació
Has de realitzar totes aquestes accions més dos del bonus per considerar el projecte superat.
- L'aplicació s'inicia correctament amb
docker compose up -d(1p) - Es pot accedir als 2 Virtual Hosts configurant
/etc/hosts(1p) - HTTPS funciona correctament (amb avís de certificat auto-signat) (1p)
- Les peticions HTTP es redirigeixen a HTTPS (1p)
- La base de dades MySQL conté les taules i dades inicials (1p)
- Redis està funcionant i registra visites (1p)
- phpMyAdmin permet administrar la base de dades (1p)
- L'API retorna JSON vàlid (1p)
- Els logs es poden consultar des de l'host (0,5p)
- El README documenta tot el projecte clarament (0,5p)
Bonus (opcional)
- Implementa un healthcheck personalitzat per a cada servei (1p)
- Afegeix Nginx com a reverse proxy davant d'Apache (1p)
- Implementa rate limiting amb mod_evasive (1p)
- Crea un script de backup automàtic per a MySQL (1p)
- Afegeix un servei de monitoring (Prometheus/Grafana o similar) (1p)
LLIURAMENT DEL PROJECTE
Què has de lliurar:
- Directori complet del projecte final (comprimit en .zip o .tar.gz)
- Captures de pantalla que demostrin:
docker compose psmostrant tots els serveis en execució- Navegador accedint a
https://frontend.localamb el certificat SSL - phpMyAdmin mostrant les taules de la base de dades
- Output de l'API en format JSON
- Logs d'Apache mostrant peticions registrades
- README.md amb tota la documentació
Format del nom del fitxer:
COGNOM_NOM_ASIX2_Docker_Apache.zip
ANNEX: COMANDES DE REFERÈNCIA RÀPIDA
Docker
# Imatges
docker images # Llista imatges
docker pull <imatge>:<tag> # Descarrega imatge
docker rmi <imatge> # Elimina imatge
docker build -t <nom>:<tag> . # Construeix imatge
docker tag <imatge>:<tag> <nou-nom> # Etiqueta imatge
# Contenidors
docker ps # Llista contenidors actius
docker ps -a # Llista tots els contenidors
docker run [opcions] <imatge> # Executa contenidor
docker start <contenidor> # Inicia contenidor
docker stop <contenidor> # Atura contenidor
docker restart <contenidor> # Reinicia contenidor
docker rm <contenidor> # Elimina contenidor
docker exec -it <contenidor> <cmd> # Executa comanda en contenidor
docker logs <contenidor> # Veure logs
docker logs -f <contenidor> # Seguir logs
docker inspect <contenidor> # Informació detallada
docker stats # Estadístiques d'ús
# Volums
docker volume ls # Llista volums
docker volume create <nom> # Crea volum
docker volume inspect <volum> # Informació del volum
docker volume rm <volum> # Elimina volum
docker volume prune # Elimina volums no utilitzats
# Xarxes
docker network ls # Llista xarxes
docker network create <nom> # Crea xarxa
docker network inspect <xarxa> # Informació de xarxa
docker network rm <xarxa> # Elimina xarxa
# Neteja
docker system prune # Neteja contenidors, imatges i xarxes no utilitzats
docker system prune -a # Neteja + imatges sense contenidors
docker system prune -a --volumes # Neteja tot incloent volums
Docker Compose
# Gestió de serveis
docker compose up # Inicia serveis (primer pla)
docker compose up -d # Inicia serveis (segon pla)
docker compose up -d --build # Reconstrueix i inicia
docker compose down # Atura i elimina contenidors
docker compose down -v # Atura, elimina contenidors i volums
docker compose start # Inicia serveis aturats
docker compose stop # Atura serveis
docker compose restart # Reinicia serveis
docker compose pause # Pausa serveis
docker compose unpause # Reprèn serveis
# Logs i informació
docker compose ps # Llista serveis
docker compose logs # Veure logs de tots els serveis
docker compose logs -f # Seguir logs
docker compose logs -f <servei> # Logs d'un servei específic
docker compose top # Processos en execució
# Execució de comandes
docker compose exec <servei> <cmd> # Executa comanda en servei
docker compose run <servei> <cmd> # Executa comanda en nou contenidor
# Escalat
docker compose up -d --scale <servei>=<n> # Escala servei a N instàncies
# Validació
docker compose config # Valida i mostra la configuració
docker compose config --services # Llista serveis
docker compose version # Versió de Docker Compose
Apache (dins del contenidor)
# Configuració
httpd -V # Informació de compilació
httpd -M # Mòduls carregats
httpd -t # Testa la configuració
httpd -S # Mostra virtual hosts configurats
apachectl configtest # Testa configuració (igual que httpd -t)
apachectl -k graceful # Reinicia gracefully (no talla connexions)
# Logs
tail -f /usr/local/apache2/logs/access.log
tail -f /usr/local/apache2/logs/error.log
RECURSOS I REFERÈNCIES
Documentació oficial:
-
Docker Documentation (2025)
https://docs.docker.com/
Documentació oficial de Docker amb best practices actualitzades -
Docker Compose Documentation
https://docs.docker.com/compose/
Guia completa de Docker Compose -
Apache HTTP Server 2.4 Documentation
https://httpd.apache.org/docs/2.4/
Documentació oficial d'Apache 2.4.65 -
Docker Hub - Official Images
https://hub.docker.com/_/httpd
Imatge oficial d'Apache httpd
Versions utilitzades en aquesta pràctica:
- Docker Desktop: 4.47.0 o superior
- Docker Engine: 27.3.1 o superior
- Docker Compose: v2.20.0 o superior
- Apache HTTP Server: 2.4.65 (juliol 2025)
- MySQL: 8.0.35
- PHP: 8.2
- Redis: 7-alpine
- phpMyAdmin: 5.2
Conceptes clau que has après:
- Contenidorització amb Docker
- Creació d'imatges personalitzades amb Dockerfile
- Multi-stage builds per optimitzar imatges
- Configuració completa d'Apache HTTP Server
- Virtual Hosts per múltiples llocs web
- HTTPS/SSL/TLS
- Mod_rewrite per URLs amigables
- Logging personalitzat
- Docker Compose per orquestrar múltiples contenidors
- Xarxes i volums en Docker
- Best practices de seguretat i optimització (2025)
Pròxims passos que et recomano:
- Explorar Kubernetes per orquestració a escala
- Aprendre CI/CD amb Docker (GitLab CI, GitHub Actions, Jenkins)
- Estudiar Docker Swarm per clustering
- Implementar monitoring i logging (Prometheus, Grafana, ELK Stack)
- Aprofundir en seguretat de contenidors (Docker Scout, Trivy)
Bones pràctiques a recordar:
Seguretat: Mai utilitzis :latest, executa com a usuari no-root, escaneja vulnerabilitats
Imatges: Utilitza imatges Alpine quan sigui possible, minimitza capes
Logging: Envia logs a stdout/stderr per facilitar la gestió
Configuració: Utilitza variables d'entorn i fitxers .env
Documentació: Sempre documenta la teva infraestructura
Data de creació d'aquesta pràctica: Novembre 2025
Versió: 1.0
Feedback: [fbarragan@sapalomera.cat]