Skip to content

Commit 28945a6

Browse files
committed
Add exercises for Docker bind mounts and volumes to README.md; create index.html and styles.css for confetti application
1 parent c2de38e commit 28945a6

File tree

3 files changed

+266
-0
lines changed

3 files changed

+266
-0
lines changed

01-contenedores/contenedores-iv/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,9 +623,37 @@ Existen varias extensiones de Docker que nos permiten monitorizar nuestros conte
623623

624624
### 🎯 Ejercicios sugeridos
625625

626+
#### 1️⃣ **Bind Mount con Hot-Reload** 🔥
627+
**Objetivo:** Entender cómo sincronizar cambios en tiempo real entre tu máquina y el contenedor.
626628

629+
Tenemos una aplicación web estática en la carpeta `ejercicio/` que muestra confeti animado. Los colores del confeti se modifican fácilmente en el CSS.
627630

628631

632+
Tu tarea es crear un contenedor nginx que sirva este contenido utilizando un bind mount para que cualquier cambio en el archivo `styles.css` se refleje inmediatamente en el contenedor.
633+
634+
**Concepto clave:** El bind mount vincula directamente tu directorio local con la ruta del contenedor. Cualquier cambio que hagas en `ejercicio/styles.css` se refleja inmediatamente en `/usr/share/nginx/html/styles.css` del contenedor.
635+
636+
**Ventajas para desarrollo:**
637+
- ✅ Sin necesidad de rebuild de imagen
638+
- ✅ Cambios instantáneos
639+
- ✅ Perfecto para desarrollo ágil
640+
- ✅ Mayor productividad
641+
642+
---
643+
644+
#### 2️⃣ **Persistencia con Volúmenes** 💾
645+
646+
**Objetivo:** El mismo contenido que ya estás sirviendo para el confeti ahora vamos a montarlo en un volumen y que se comparta tanto en un nginx como en un httpd.
647+
648+
649+
Tu tarea es crear un volumen llamado `confetti-data` y montar este volumen en dos contenedores diferentes: uno con nginx y otro con httpd. Asegúrate de copiar el contenido inicial del directorio `ejercicio/` al volumen para que ambos contenedores puedan servir el mismo contenido.
650+
651+
652+
653+
**Concepto clave:** Los volúmenes son almacenamiento administrado por Docker. Los datos en un volumen **persisten** incluso si eliminas el contenedor que lo usa. Con este ejercicio, aprenderás a compartir datos entre múltiples contenedores utilizando volúmenes, aunque estos estén basados en diferentes imágenes.
654+
655+
---
656+
629657

630658
> [!TIP]
631659
> 💡 **Consejo final**: En producción, siempre usa volúmenes para datos críticos, bind mounts solo para desarrollo, y tmpfs para datos temporales que requieren alto rendimiento.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!DOCTYPE html>
2+
<html lang="es">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Confetti Celebration</title>
7+
<link rel="stylesheet" href="styles.css">
8+
</head>
9+
<body>
10+
<div class="container">
11+
<div class="confetti-container" id="confetti-container"></div>
12+
<div class="content">
13+
<h1>🎉 ¡Celebración!</h1>
14+
<p>Bind Mount Docker en Acción</p>
15+
<button onclick="launchConfetti()">Lanzar Confeti</button>
16+
<div class="color-panel">
17+
<h3>Personaliza los colores en styles.css</h3>
18+
<p>Modifica la variable CSS en la raíz del archivo:</p>
19+
<code> --confetti-color: #00FF00;</code>
20+
</div>
21+
</div>
22+
</div>
23+
24+
<script>
25+
function launchConfetti() {
26+
const container = document.getElementById('confetti-container');
27+
28+
// Limpiar confeti anterior
29+
container.innerHTML = '';
30+
31+
// Crear 80 confetis para más efecto
32+
for (let i = 0; i < 80; i++) {
33+
const confetti = document.createElement('div');
34+
confetti.className = 'confetti';
35+
confetti.style.left = Math.random() * 100 + '%';
36+
confetti.style.top = '-10px';
37+
confetti.style.backgroundColor = getRandomColor();
38+
confetti.style.animationDelay = (Math.random() * 0.8) + 's';
39+
confetti.style.animationDuration = (2 + Math.random() * 1) + 's';
40+
container.appendChild(confetti);
41+
}
42+
43+
// Limpiar después de la animación
44+
setTimeout(() => {
45+
container.innerHTML = '';
46+
}, 4000);
47+
}
48+
49+
function getRandomColor() {
50+
// Todos los confetis usan el mismo color definido en CSS
51+
return getComputedStyle(document.documentElement).getPropertyValue('--confetti-color').trim();
52+
}
53+
54+
// Lanzar confeti automáticamente al cargar
55+
window.addEventListener('load', () => {
56+
setTimeout(launchConfetti, 500);
57+
});
58+
</script>
59+
</body>
60+
</html>
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
:root {
2+
/* 🎨 MODIFICABLE: Cambia este color en styles.css y recarga el navegador para ver el cambio instantáneo */
3+
--confetti-color: #00FF00; /* Verde BRILLANTE */
4+
}
5+
6+
* {
7+
margin: 0;
8+
padding: 0;
9+
box-sizing: border-box;
10+
}
11+
12+
body {
13+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
14+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
15+
margin: 0;
16+
padding: 0;
17+
overflow: hidden;
18+
}
19+
20+
.container {
21+
position: relative;
22+
width: 100%;
23+
height: 100vh;
24+
overflow: hidden;
25+
display: flex;
26+
align-items: center;
27+
justify-content: center;
28+
}
29+
30+
.confetti-container {
31+
position: absolute;
32+
top: 0;
33+
left: 0;
34+
width: 100%;
35+
height: 100%;
36+
pointer-events: none;
37+
}
38+
39+
.confetti {
40+
position: absolute;
41+
width: 10px;
42+
height: 10px;
43+
/* 🎨 Los colores se asignan dinámicamente desde JavaScript */
44+
animation: fall linear forwards;
45+
transform: rotateZ(45deg);
46+
border-radius: 50%;
47+
}
48+
49+
.confetti:nth-child(even) {
50+
width: 8px;
51+
height: 8px;
52+
}
53+
54+
.confetti:nth-child(3n) {
55+
width: 12px;
56+
height: 12px;
57+
border-radius: 0%;
58+
}
59+
60+
.confetti:nth-child(4n) {
61+
width: 6px;
62+
height: 6px;
63+
}
64+
65+
@keyframes fall {
66+
0% {
67+
opacity: 1;
68+
transform: translateY(0) rotateZ(0deg);
69+
}
70+
90% {
71+
opacity: 1;
72+
}
73+
100% {
74+
opacity: 0;
75+
transform: translateY(100vh) rotateZ(720deg);
76+
}
77+
}
78+
79+
.content {
80+
position: relative;
81+
z-index: 10;
82+
text-align: center;
83+
color: white;
84+
display: flex;
85+
flex-direction: column;
86+
gap: 20px;
87+
}
88+
89+
.content h1 {
90+
font-size: 4rem;
91+
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
92+
animation: bounce 0.6s ease-in-out;
93+
}
94+
95+
.content p {
96+
font-size: 1.5rem;
97+
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
98+
}
99+
100+
button {
101+
padding: 15px 40px;
102+
font-size: 1.1rem;
103+
background-color: #FF6B6B; /* 🎨 MODIFICABLE: Este es el color del botón */
104+
color: white;
105+
border: none;
106+
border-radius: 50px;
107+
cursor: pointer;
108+
transition: all 0.3s ease;
109+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
110+
max-width: 250px;
111+
margin: 0 auto;
112+
font-weight: bold;
113+
}
114+
115+
button:hover {
116+
background-color: #FF5252;
117+
transform: scale(1.05);
118+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
119+
}
120+
121+
button:active {
122+
transform: scale(0.98);
123+
}
124+
125+
.color-panel {
126+
margin-top: 30px;
127+
padding: 20px;
128+
background: rgba(255, 255, 255, 0.1);
129+
border-radius: 10px;
130+
backdrop-filter: blur(10px);
131+
border: 1px solid rgba(255, 255, 255, 0.2);
132+
max-width: 400px;
133+
font-size: 0.9rem;
134+
}
135+
136+
.color-panel h3 {
137+
font-size: 1.1rem;
138+
margin-bottom: 10px;
139+
}
140+
141+
.color-panel p {
142+
font-size: 0.9rem;
143+
margin-bottom: 10px;
144+
}
145+
146+
.color-panel code {
147+
background: rgba(0, 0, 0, 0.3);
148+
padding: 8px 12px;
149+
border-radius: 5px;
150+
display: block;
151+
font-family: 'Courier New', monospace;
152+
font-size: 0.85rem;
153+
}
154+
155+
@keyframes bounce {
156+
0%, 100% {
157+
transform: translateY(0);
158+
}
159+
50% {
160+
transform: translateY(-20px);
161+
}
162+
}
163+
164+
/* Responsive */
165+
@media (max-width: 768px) {
166+
.content h1 {
167+
font-size: 2.5rem;
168+
}
169+
170+
.content p {
171+
font-size: 1.1rem;
172+
}
173+
174+
button {
175+
padding: 12px 30px;
176+
font-size: 1rem;
177+
}
178+
}

0 commit comments

Comments
 (0)