Docker Practice: Real-World Projects

🎯 Learning by Doing: These hands-on exercises will help you practice Docker with real applications. Each exercise includes step-by-step solutions and explanations.

Exercise 1: Python Flask API with PostgreSQL

Build a RESTful API with Python Flask that connects to a PostgreSQL database, all containerized!

Step 1: Create Project Structure

$ mkdir python-flask-docker && cd python-flask-docker
$ mkdir app
# Create the following structure:
python-flask-docker/
├── app/
│ ├── app.py
│ ├── requirements.txt
│ └── Dockerfile
├── docker-compose.yml
└── .env

Step 2: Create Flask Application

app/app.py from flask import Flask, jsonify, request from flask_sqlalchemy import SQLAlchemy import os app = Flask(__name__) # Database configuration app.config[ 'SQLALCHEMY_DATABASE_URI' ] = ( f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASS')}" f"@{os.getenv('DB_HOST')}/{os.getenv('DB_NAME')}" ) db = SQLAlchemy(app) # Model class Task (db.Model): id = db.Column(db.Integer, primary_key= True ) title = db.Column(db.String( 100 ), nullable= False ) completed = db.Column(db.Boolean, default= False ) @app.route ( '/tasks' , methods=[ 'GET' ]) def get_tasks (): tasks = Task.query.all() return jsonify([{ 'id' : t.id, 'title' : t.title} for t in tasks]) if __name__ == '__main__' : with app.app_context(): db.create_all() app.run(host= '0.0.0.0' , port= 5000 )

Step 3: Create Requirements File

# app/requirements.txt
Flask==3.0.0
Flask-SQLAlchemy==3.1.1
psycopg2-binary==2.9.9
python-dotenv==1.0.0

Step 4: Create Dockerfile

# app/Dockerfile
FROM
python:3.11-slim

WORKDIR
/app

COPY
requirements.txt .
RUN
pip install --no-cache-dir -r requirements.txt

COPY
. .

EXPOSE
5000
CMD
["python", "app.py"]

Step 5: Create Docker Compose File

# docker-compose.yml
version:
'3.8'

services:

api:

build: ./app
ports:
- "5000:5000"
environment:
- DB_HOST=postgres
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
depends_on:
- postgres
networks:
- api-network

postgres:

image: postgres:15-alpine
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASS}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- api-network

adminer:

image: adminer
ports:
- "8080:8080"
networks:
- api-network

networks:

api-network:

driver: bridge

volumes:

postgres_data:

Step 6: Environment Variables

# .env
DB_NAME=flask_api_db
DB_USER=flask_user
DB_PASS=flask_password

Step 7: Run the Application

$ docker-compose up --build

# Test the API
$ curl http://localhost:5000/tasks

# Access database manager
Open browser: http://localhost:8080
System: PostgreSQL
Server: postgres
Username: flask_user
Password: flask_password

Exercise 2: React Application with Node.js Backend

Create a full-stack application with React frontend and Express backend, all containerized!

graph LR A[React Frontend
Port 3000] --> B[Express API
Port 5000] B --> C[MongoDB
Port 27017] B --> D[Redis Cache
Port 6379] style A fill:#61dafb style B fill:#68a063 style C fill:#4db33d style D fill:#dc382d

Step 1: Project Structure

react-node-docker/
├── frontend/
│ ├── src/
│ ├── public/
│ ├── package.json
│ └── Dockerfile
├── backend/
│ ├── server.js
│ ├── package.json
│ └── Dockerfile
├── docker-compose.yml
└── .env

Step 2: Backend Express Server

# backend/server.js
const
express = require('express');
const
mongoose = require('mongoose');
const
redis = require('redis');
const
cors = require('cors');

const
app = express();
app.use(cors());
app.use(express.json());

// MongoDB connection
mongoose.connect('mongodb://mongo:27017/todoapp');

// Redis client
const
redisClient = redis.createClient({
  url: 'redis://redis:6379'
});
redisClient.connect();

// Todo model
const
Todo = mongoose.model('Todo', {
  title: String,
  completed: Boolean
});

// Routes
app.get('/api/todos', async (req, res) => {
  
const
todos = await Todo.find();
  res.json(todos);
});

app.post('/api/todos', async (req, res) => {
  
const
todo = new Todo(req.body);
  await todo.save();
  // Invalidate cache
  await redisClient.del('todos');
  res.json(todo);
});

app.listen(5000, () => {
  console.log('Server running on port 5000');
});

Step 3: React Frontend

# frontend/src/App.js
import
React, { useState, useEffect } from 'react';
import
axios from 'axios';

function
App() {
  
const
[todos, setTodos] = useState([]);
  
const
[newTodo, setNewTodo] = useState('');

  useEffect(() => {
    fetchTodos();
  }, []);

  
const
fetchTodos = async () => {
    
const
res = await axios.get('http://localhost:5000/api/todos');
    setTodos(res.data);
  };

  
const
addTodo = async () => {
    await axios.post('http://localhost:5000/api/todos', {
      title: newTodo,
      completed: false
    });
    setNewTodo('');
    fetchTodos();
  };

  return (
    <div className="App">
      <h1>Todo App</h1>
      <input
        value={newTodo}
        onChange={(e) => setNewTodo(e.target.value)}
      />
      <button onClick={addTodo}>Add Todo</button>
      <ul>
        {todos.map(todo => (
          <li key={todo._id}>{todo.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Step 4: Dockerfiles

# backend/Dockerfile
FROM
node:18-alpine
WORKDIR
/app
COPY
package*.json ./
RUN
npm ci --only=production
COPY
. .
EXPOSE
5000
CMD
["node", "server.js"]

# frontend/Dockerfile
# Build stage
FROM
node:18-alpine AS builder
WORKDIR
/app
COPY
package*.json ./
RUN
npm ci
COPY
. .
RUN
npm run build

# Production stage
FROM
nginx:alpine
COPY
--from=builder /app/build /usr/share/nginx/html
EXPOSE
80

Step 5: Docker Compose

# docker-compose.yml
version:
'3.8'

services:

frontend:

build: ./frontend
ports:
- "3000:80"
depends_on:
- backend

backend:

build: ./backend
ports:
- "5000:5000"
environment:
- MONGO_URL=mongodb://mongo:27017/todoapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis

mongo:

image: mongo:7
volumes:
- mongo_data:/data/db

redis:

image: redis:alpine

volumes:

mongo_data:

Exercise 3: WordPress with Custom Theme

Deploy WordPress with MySQL, phpMyAdmin, and a custom theme development environment!

Complete Docker Compose Setup

# docker-compose.yml
version:
'3.8'

services:

wordpress:

image: wordpress:latest
ports:
- "8000:80"
environment:
- WORDPRESS_DB_HOST=mysql:3306
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_PASSWORD=wordpress
- WORDPRESS_DB_NAME=wordpress
- WORDPRESS_DEBUG=true
volumes:
- wordpress_data:/var/www/html
- ./themes:/var/www/html/wp-content/themes
- ./plugins:/var/www/html/wp-content/plugins
- ./uploads:/var/www/html/wp-content/uploads
depends_on:
- mysql
networks:
- wp_network

mysql:

image: mysql:8.0
command: '--default-authentication-plugin=mysql_native_password'
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=wordpress
volumes:
- mysql_data:/var/lib/mysql
networks:
- wp_network

phpmyadmin:

image: phpmyadmin/phpmyadmin
ports:
- "8080:80"
environment:
- PMA_HOST=mysql
- PMA_USER=root
- PMA_PASSWORD=rootpassword
depends_on:
- mysql
networks:
- wp_network

mailhog:

image: mailhog/mailhog
ports:
- "1025:1025" # SMTP server
- "8025:8025" # Web UI
networks:
- wp_network

networks:

wp_network:

driver: bridge

volumes:

wordpress_data:

mysql_data:

Custom WordPress Theme Development

# Create theme structure
$ mkdir -p themes/mytheme

# themes/mytheme/style.css
/*
Theme Name: My Custom Theme
Author: Your Name
Description: Docker WordPress Theme
Version: 1.0
*/

# themes/mytheme/index.php
<?php get_header(); ?>
<main>
  <h1>Welcome to Docker WordPress!</h1>
  <?php if (have_posts()) : while (have_posts()) : the_post(); ?>
    <article>
      <h2><?php the_title(); ?></h2>
      <?php the_content(); ?>
    </article>
  <?php endwhile; endif; ?>
</main>
<?php get_footer(); ?>

Running the Stack

$ docker-compose up -d

# Access URLs:
WordPress: http://localhost:8000
phpMyAdmin: http://localhost:8080
MailHog: http://localhost:8025

# Watch logs
$ docker-compose logs -f wordpress

# Import database
$ docker exec -i $(docker-compose ps -q mysql) mysql -uroot -prootpassword wordpress < backup.sql

Bonus Exercise: Microservices Architecture

Build a complete microservices application with multiple languages and services!

graph TB A[Nginx Gateway
Port 80] --> B[React Frontend
Port 3000] A --> C[User Service
Node.js - Port 3001] A --> D[Product Service
Python - Port 3002] A --> E[Order Service
Go - Port 3003] C --> F[PostgreSQL
Users DB] D --> G[MongoDB
Products DB] E --> H[Redis
Order Cache] I[RabbitMQ
Message Queue] --> C I --> D I --> E style A fill:#85c1e9 style B fill:#61dafb style C fill:#68a063 style D fill:#3776ab style E fill:#00add8

Complete Microservices Setup

Key Learning Points:

  • Service discovery using container names
  • API Gateway pattern with Nginx
  • Different databases for different services
  • Message queue for async communication
  • Health checks for each service
  • Centralized logging
  • Service isolation and scaling

Files needed:

  • docker-compose.yml (main orchestration)
  • nginx/nginx.conf (API gateway configuration)
  • Each service with its own Dockerfile
  • .env for environment variables

Practice Tips

🎓 Best Practices for Learning

1. Start Small: Begin with single containers before moving to multi-container setups

2. Debug Systematically:

  • Check logs: docker logs container-name
  • Inspect running processes: docker exec container-name ps aux
  • Test networking: docker exec container-name ping other-container

3. Version Control: Always commit your Docker configurations to Git

4. Documentation: Document your setup and any custom configurations

5. Clean Up: Regularly clean unused images and containers: docker system prune -a

Challenge Yourself

🚀 Advanced Challenges

  1. Add CI/CD: Integrate GitHub Actions to build and push images automatically
  2. Implement Monitoring: Add Prometheus and Grafana to monitor your containers
  3. Add Authentication: Implement JWT authentication across services
  4. Scale Services: Use Docker Swarm to scale your applications
  5. Add Testing: Create test containers that run automated tests
  6. Implement Backups: Automate database backups with cron containers
  7. Add SSL: Use Let's Encrypt with Nginx for HTTPS
  8. Create Development vs Production: Use override files for different environments

Resources and Next Steps

🎉 Congratulations on completing these exercises!

You've now practiced with:
✅ Python Flask APIs
✅ React with Node.js backends
✅ WordPress development
✅ Multi-database architectures
✅ Docker Compose orchestration
✅ Container networking
✅ Volume management

Keep Learning:
• Experiment with different technologies
• Try deploying to cloud providers
• Explore Kubernetes for larger scale
• Join Docker communities and forums
• Contribute to open source projects

Remember: The best way to learn Docker is by doing. Keep building, keep experimenting, and keep containerizing!