π Scenario 03 β Docker Networking Magic
π― Scenario Goal
Demonstrate real-world Docker networking problems and how to fix them by:
β
Running a Python Flask Voting App (WFO vs WFH)
β
Letting workshop attendees vote live via a public URL (ngrok or Cloudflare Tunnel)
β
Simulating networking failures:
- Missing database
- Containers in separate networks
β
Fixing networking issues β everything magically works!
This scenario creates an unforgettable AHA moment for learners.
β Directory Structure
scenario_03_networking/
β
βββ app/
β βββ app.py # Enhanced Flask app with error handling & beautiful UI
β βββ requirements.txt
β βββ Dockerfile
βββ scripts/
β βββ cleanup.sh
β βββ fix_network.sh
β βββ expose_ngrok.sh
β βββ expose_cloudflared.sh
β βββ [other utility scripts]
βββ demo_simple.sh # Automated 4-step demo
βββ demo_manual.sh # Interactive demo with explanations
βββ README.md
β How The Voting App Works
Enhanced Flask Voting App
Beautiful UI with real-time status indicators:
- β Vote WFH (Work From Home) - π‘
- β Vote WFO (Work From Office) - π’
- β Real-time Redis connectivity status
- β Success/error messages for educational clarity
- β Vote counts stored in Redis under keys:
votes:wfhvotes:wfo
β Total votes shown live with beautiful styling.
app/app.py (Enhanced Version)
from flask import Flask, render_template_string, request
import redis
import os
import logging
# Set up logging for debugging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = Flask(__name__)
REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
# Initialize Redis connection with error handling
try:
logger.info(f"Attempting to connect to Redis at {REDIS_HOST}:6379")
r = redis.Redis(host=REDIS_HOST, port=6379, decode_responses=True)
# Test the connection
r.ping()
redis_available = True
logger.info("β
Redis connection successful!")
except (redis.ConnectionError, redis.RedisError) as e:
redis_available = False
logger.error(f"β Redis connection failed: {e}")
# Beautiful HTML template with status indicators
TEMPLATE = """
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<title>π Docker Voting App</title>
<style>
/* Beautiful gradient background and modern styling */
body {
background: linear-gradient(135deg, #f8ffae 0%, #43c6ac 100%);
font-family: 'Segoe UI', 'Arial', sans-serif;
text-align: center;
padding: 0;
margin: 0;
min-height: 100vh;
}
/* Container styling with glassmorphism effect */
.container {
margin-top: 60px;
background: rgba(255,255,255,0.9);
border-radius: 20px;
box-shadow: 0 8px 32px 0 rgba(31,38,135,0.2);
display: inline-block;
padding: 40px 60px 30px 60px;
}
/* Status indicators for educational clarity */
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
}
.status-online { background: #4CAF50; }
.status-offline { background: #f44336; }
/* Error and success messages */
.error-message {
background: #ff6b6b;
color: white;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
font-size: 1.1em;
}
.success-message {
background: #4CAF50;
color: white;
padding: 15px;
border-radius: 10px;
margin: 20px 0;
font-size: 1.1em;
}
/* Beautiful voting buttons */
.vote-btn {
font-size: 1.5em;
padding: 20px 40px;
margin: 20px 30px;
border: none;
border-radius: 12px;
cursor: pointer;
transition: background 0.2s, transform 0.2s;
box-shadow: 0 2px 8px rgba(67,198,172,0.15);
}
.vote-btn.wfh {
background: linear-gradient(90deg, #f7971e 0%, #ffd200 100%);
color: #2d3a4b;
}
.vote-btn.wfo {
background: linear-gradient(90deg, #43c6ac 0%, #191654 100%);
color: #fff;
}
.vote-btn:hover {
transform: scale(1.07);
}
/* Vote count display */
.votes {
margin-top: 30px;
display: flex;
justify-content: center;
gap: 60px;
}
.vote-box {
background: #fff;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(67,198,172,0.10);
padding: 30px 40px;
min-width: 160px;
display: flex;
flex-direction: column;
align-items: center;
}
.vote-count {
font-size: 2.5em;
font-weight: bold;
color: #43c6ac;
}
</style>
</head>
<body>
<div class="container">
<h1>π Docker Voting App</h1>
<h2>Which do you prefer?</h2>
{% if show_error %}
<div class="error-message">
<span class="status-indicator status-offline"></span>
<strong>Voting Failed!</strong><br>
Cannot connect to Redis at {{ redis_host }}:6379<br>
This demonstrates what happens when containers can't communicate!
</div>
{% elif vote_success %}
<div class="success-message">
<span class="status-indicator status-online"></span>
<strong>Vote Recorded Successfully!</strong><br>
Your vote was saved to Redis at {{ redis_host }}:6379
</div>
{% elif redis_available %}
<div class="success-message">
<span class="status-indicator status-online"></span>
<strong>Database Connected</strong> - Redis is available at {{ redis_host }}:6379
</div>
{% endif %}
<form method="POST">
<button class="vote-btn wfh" name="vote" value="wfh">π‘ WFH (Work From Home)</button>
<button class="vote-btn wfo" name="vote" value="wfo">π’ WFO (Work From Office)</button>
</form>
<div class="votes">
<div class="vote-box">
<div class="emoji">π‘</div>
<div class="vote-label">WFH Votes</div>
<div class="vote-count">{{ wfh }}</div>
</div>
<div class="vote-box">
<div class="emoji">π’</div>
<div class="vote-label">WFO Votes</div>
<div class="vote-count">{{ wfo }}</div>
</div>
</div>
</div>
<div class="footer">
Made with β€οΈ for Today's Workshop for you!
</div>
</body>
</html>
"""
@app.route("/", methods=["GET", "POST"])
def index():
wfh = 0
wfo = 0
show_error = False
vote_success = False
if request.method == "POST":
logger.info("User attempted to vote")
# User tried to vote - check if Redis is available
if not redis_available:
logger.error("Voting failed - Redis not available")
show_error = True
else:
try:
vote = request.form["vote"]
logger.info(f"Recording vote for: {vote}")
r.incr(f"votes:{vote}")
logger.info(f"Vote recorded successfully for: {vote}")
vote_success = True
except (redis.ConnectionError, redis.RedisError) as e:
logger.error(f"Redis error during voting: {e}")
show_error = True
if redis_available and not show_error:
try:
logger.info("Reading vote counts from Redis")
wfh = r.get("votes:wfh") or 0
wfo = r.get("votes:wfo") or 0
logger.info(f"Vote counts - WFH: {wfh}, WFO: {wfo}")
except (redis.ConnectionError, redis.RedisError) as e:
logger.error(f"Redis error when reading votes: {e}")
# Redis connection failed when reading votes
pass
return render_template_string(TEMPLATE, wfh=wfh, wfo=wfo, redis_available=redis_available, redis_host=REDIS_HOST, show_error=show_error, vote_success=vote_success)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
app/requirements.txt
flask
redis
app/Dockerfile
FROM python:3.10
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
β Enhanced Demo Execution
π― 4-Step Educational Flow
The demo now follows a clear, educational progression:
STEP 1: App Without Database (Expected Failure)
- Build and run Flask app without Redis
- Demonstrate container isolation
- Show realistic error messages
STEP 2: Database in Wrong Network (Expected Failure)
- Add Redis but in separate network
- Demonstrate network isolation concepts
- Show hostname resolution failures
STEP 3: Fix the Network (Success!)
- Create custom Docker network
- Place both containers in same network
- Demonstrate successful communication
STEP 4: Production-Ready Setup
- Complete working microservices setup
- Real Redis connectivity and voting
- Network connectivity testing
β Running the Demo
Automated Demo (Recommended)
# Run the complete 4-step demo automatically
./demo_simple.sh
Features: - β Automated testing of each step - β Real vote count validation - β Network connectivity verification - β Beautiful colored output - β Educational success/failure messages
Interactive Demo
# Run with explanations and user interaction
./demo_manual.sh
Features: - β Step-by-step explanations - β User interaction prompts - β Educational commentary - β Manual verification steps
β Demo Scripts
demo_simple.sh (Automated)
Key Features:
- β
4 clear educational steps
- β
Real vote testing with awk parsing
- β
Network connectivity verification
- β
Beautiful colored output
- β
Comprehensive error handling
Sample Output:
π Docker Networking Magic - Automated Demo
========================================
π§Ή Initial Cleanup
β€ Cleaning up containers and networks...
β
Cleanup completed
π΄ STEP 1: App Without Database (Expected Failure)
β€ Building the Flask voting app...
β
App built successfully
β€ Running app without Redis database...
β
App container started
β
App is accessible at http://localhost:5000
β
Voting failed as expected - no database connection!
π΄ STEP 2: Database in Wrong Network (Expected Failure)
β
Voting failed as expected - network isolation!
π’ STEP 3: Fix the Network (Success!)
β
Custom network 'vote-net' created
β
Redis started in vote-net network
β
App started in vote-net network
π VOTING WORKS! The magic of Docker networking!
β
Network connectivity: App can reach Redis on port 6379
β
Redis connectivity: App can connect and ping Redis
π Demo Complete!
β
Container isolation and communication
β
Docker network concepts
β
How to fix common networking issues
β
Real-world microservices patterns
demo_manual.sh (Interactive)
Key Features: - β Educational explanations for each step - β User interaction with prompts - β Manual verification steps - β Detailed commentary on what's happening
β Making It Public
Option 1 β Expose Via ngrok
# Install ngrok
brew install ngrok/ngrok/ngrok
# Expose the app
ngrok http 5000
β
Share public URL like: https://glorious-bear-1234.ngrok.io
Option 2 β Expose Via Cloudflare Tunnel
# Install cloudflared
brew install cloudflared
# Expose the app
cloudflared tunnel --url http://localhost:5000
β
Get public URL like: https://vote.mydomain.com
β Clean Up
# Clean up all containers and networks
docker rm -f vote-app redis-server
docker network rm vote-net
Or use the cleanup script:
./scripts/cleanup.sh
β Key Improvements Made
π― Educational Clarity
- β 4-step progression instead of 8 confusing steps
- β Clear success/failure indicators
- β Realistic error messages only when voting fails
- β Beautiful UI with status indicators
π§ Technical Enhancements
- β Real Redis connectivity testing
- β
Proper vote count parsing with
awk - β Network connectivity verification
- β Comprehensive logging for debugging
- β Robust error handling
π Production-Ready Features
- β Beautiful modern UI with gradients
- β Real-time status indicators
- β Educational success/error messages
- β Comprehensive testing of each step
- β Network connectivity validation
β Learning Outcomes
What Attendees Learn:
- β Container isolation and communication
- β Docker network concepts and troubleshooting
- β Real-world microservices patterns
- β How to fix common networking issues
- β Production-ready application deployment
Key Takeaways:
- β Containers need explicit network configuration
- β Custom networks enable service communication
- β This pattern is used in production every day
- β Docker networking is powerful but requires understanding
β Troubleshooting
Common Issues:
App not responding:
# Check if containers are running
docker ps
# Check app logs
docker logs vote-app
# Check Redis logs
docker logs redis-server
Voting not working:
# Test Redis connectivity
docker exec vote-app python -c "import redis; r = redis.Redis(host='redis-server', port=6379); r.ping()"
# Check network connectivity
docker exec vote-app ping redis-server
Network issues:
# List networks
docker network ls
# Inspect network
docker network inspect vote-net
This enhanced scenario provides a memorable learning experience with real-world Docker networking challenges and solutions! π