Weekend Project: Containerized Python Environment Setup

Creating a Complete Development Environment for Python Web Applications

Project Overview

In this weekend project, you'll create a fully containerized Python development environment that you can use throughout the course and in your future development work. This environment will include a Python 3.10+ container, VS Code configured for remote development, Git integration, and a basic web application structure.

Think of this project as building your own professional developer workshop—a consistent, portable space with all the tools you need, organized exactly how you want them. Just as a chef prepares their kitchen before cooking or a carpenter sets up their workshop before building furniture, we'll set up our development environment before diving into more complex programming tasks.

By the end of this project, you'll have a powerful, isolated development environment that runs identically on any machine with Docker installed, eliminating the "it works on my machine" problem and ensuring consistent experiences across different computers and operating systems.

Problem-Solving Approach

Step 1: Understand the Problem

Our goal is to create a containerized Python environment that includes:

Let's break down what each component means:

The main challenges we need to address are:

Step 2: Devise a Plan

Here's our step-by-step approach to building this containerized environment:

  1. Create a project directory structure
  2. Initialize Git repository
  3. Create a Dockerfile for Python 3.10+ with development tools
  4. Create docker-compose.yml for easy container management
  5. Configure VS Code for remote development with devcontainer.json
  6. Set up a basic Python web application structure
  7. Create documentation with README.md
  8. Test the complete environment

This plan follows a logical progression, starting with the basic structure and git initialization, then setting up the containerization, followed by editor configuration, and finally adding documentation.

Step 3: Execute the Plan

Now we'll implement our plan step by step. I'll provide detailed instructions and code for each component.

Step 4: Review/Reflect

After completing the implementation, we'll test the environment, verify that it meets all requirements, and review what we've learned.

Implementation

Step 1: Create Project Directory Structure

First, let's create a directory for our project and set up the basic structure:

mkdir python_web_container
cd python_web_container

# Create directories for our web application
mkdir -p app/static app/templates app/tests

# Create directories for container configuration
mkdir -p .devcontainer .vscode

Here's what each directory will be used for:

Step 2: Initialize Git Repository

Next, let's initialize a Git repository and create a basic .gitignore file:

# Initialize Git repository
git init

# Create .gitignore file
cat > .gitignore << EOL
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Virtual Environment
venv/
ENV/

# VS Code (excluding settings we want to commit)
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# Environment Variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Docker
.docker/

# Logs
logs/
*.log

# Tests
.coverage
htmlcov/
.pytest_cache/
EOL

# Make initial commit
git add .gitignore
git commit -m "Initial commit: Add .gitignore"

The .gitignore file is important because it prevents unnecessary files from being tracked by Git. This keeps your repository clean and efficient.

Step 3: Create Dockerfile

Now, let's create a Dockerfile that defines our Python environment:

cat > Dockerfile << EOL
FROM python:3.10-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PYTHONPATH=/workspace

# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \\
    build-essential \\
    git \\
    curl \\
    vim \\
    && apt-get clean \\
    && rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /workspace

# Install Python development tools
RUN pip install --no-cache-dir \\
    flask \\
    pytest \\
    black \\
    flake8 \\
    mypy \\
    python-dotenv \\
    gunicorn

# Keep container running
CMD ["bash"]
EOL

Let's understand what this Dockerfile does:

Analogy: Think of this Dockerfile as a recipe for building your development environment. Just as a recipe lists ingredients and preparation steps, this file specifies what components to include and how to configure them. Anyone with this recipe can reproduce the exact same environment.

Step 4: Create docker-compose.yml

Docker Compose makes it easy to manage containers. Let's create a docker-compose.yml file:

cat > docker-compose.yml << EOL
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/workspace
    ports:
      - "5000:5000"  # Flask default port
    environment:
      - FLASK_APP=app/app.py
      - FLASK_ENV=development
      - FLASK_DEBUG=1
EOL

This docker-compose.yml file:

Real-world example: In professional development teams, docker-compose.yml files often define multiple services that work together—for example, a web server, database, cache, and background workers. This approach allows developers to spin up an entire application stack with a single command.

Step 5: Configure VS Code for Remote Development

Now, let's configure VS Code to connect to our container. First, create a devcontainer.json file:

cat > .devcontainer/devcontainer.json << EOL
{
    "name": "Python Web Development",
    "dockerComposeFile": ["../docker-compose.yml"],
    "service": "app",
    "workspaceFolder": "/workspace",
    "settings": {
        "python.defaultInterpreterPath": "/usr/local/bin/python",
        "python.linting.enabled": true,
        "python.linting.pylintEnabled": false,
        "python.linting.flake8Enabled": true,
        "python.formatting.provider": "black",
        "python.formatting.blackPath": "/usr/local/bin/black",
        "editor.formatOnSave": true,
        "editor.rulers": [88],
        "python.linting.flake8Args": ["--max-line-length=88"],
        "python.testing.pytestEnabled": true,
        "python.testing.unittestEnabled": false,
        "python.testing.nosetestsEnabled": false,
        "python.testing.pytestArgs": ["app/tests"]
    },
    "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "ms-azuretools.vscode-docker",
        "eamodio.gitlens",
        "streetsidesoftware.code-spell-checker",
        "njpwerner.autodocstring"
    ],
    "remoteUser": "root"
}
EOL

Then, let's create VS Code settings:

cat > .vscode/settings.json << EOL
{
    "python.linting.enabled": true,
    "python.linting.pylintEnabled": false,
    "python.linting.flake8Enabled": true,
    "python.formatting.provider": "black",
    "editor.formatOnSave": true,
    "editor.rulers": [88],
    "files.exclude": {
        "**/__pycache__": true,
        "**/.pytest_cache": true,
        "**/*.pyc": true
    }
}
EOL

cat > .vscode/launch.json << EOL
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Flask",
            "type": "python",
            "request": "launch",
            "module": "flask",
            "env": {
                "FLASK_APP": "app/app.py",
                "FLASK_ENV": "development",
                "FLASK_DEBUG": "1"
            },
            "args": [
                "run",
                "--host=0.0.0.0",
                "--no-debugger"
            ],
            "jinja": true
        },
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        }
    ]
}
EOL

These configuration files:

Analogy: The devcontainer.json file acts like a bridge between your local VS Code and the container. It's similar to how a universal remote control connects to different devices—it creates a seamless connection that lets you work with remote code as if it were local.

Step 6: Set Up Basic Python Web Application

Now, let's create a basic Flask application structure:

cat > app/app.py << EOL
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html', title="Home")

@app.route('/about')
def about():
    return render_template('about.html', title="About")

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)
EOL

cat > app/templates/base.html << EOL



    
    
    {{ title }} - Python Web App
    


    

Python Web Application

{% block content %}{% endblock %}

© 2025 Python Web App

EOL cat > app/templates/index.html << EOL {% extends "base.html" %} {% block content %}

Welcome to the Python Web App

This is a simple Flask application running in a Docker container.

This application was created as part of the containerized Python environment weekend project.

{% endblock %} EOL cat > app/templates/about.html << EOL {% extends "base.html" %} {% block content %}

About This Application

This application demonstrates a basic Flask structure with:

  • Containerized Python environment
  • VS Code integration
  • Template inheritance
  • Static file serving
{% endblock %} EOL mkdir -p app/static/css cat > app/static/css/style.css << EOL * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; } header { margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #eee; } nav { margin-top: 10px; } nav a { margin-right: 15px; text-decoration: none; color: #0066cc; } h1, h2 { margin-bottom: 15px; } main { margin-bottom: 30px; } footer { margin-top: 20px; padding-top: 10px; border-top: 1px solid #eee; text-align: center; font-size: 0.8rem; color: #666; } EOL cat > app/tests/test_app.py << EOL import pytest from app.app import app @pytest.fixture def client(): app.config['TESTING'] = True with app.test_client() as client: yield client def test_home_page(client): """Test that the home page loads successfully.""" response = client.get('/') assert response.status_code == 200 assert b'Welcome to the Python Web App' in response.data def test_about_page(client): """Test that the about page loads successfully.""" response = client.get('/about') assert response.status_code == 200 assert b'About This Application' in response.data EOL cat > requirements.txt << EOL Flask==2.3.3 pytest==7.4.0 black==23.7.0 flake8==6.1.0 mypy==1.5.1 python-dotenv==1.0.0 gunicorn==21.2.0 EOL

This sets up a basic Flask application with:

Real-world application: This structure follows patterns used in production Flask applications. Template inheritance enables consistent layouts across pages, static files provide styling and client-side functionality, and tests ensure the application behaves as expected.

Step 7: Create Documentation

Let's create a comprehensive README.md file that documents our setup:

cat > README.md << EOL
# Containerized Python Web Development Environment

A complete development environment for Python web applications, using Docker containers and VS Code.

## Features

- Python 3.10+ development container
- VS Code integrated development environment with remote container support
- Git version control
- Basic Flask web application structure
- Code quality tools (Black, Flake8, MyPy)
- Testing framework (pytest)

## Prerequisites

- [Docker](https://www.docker.com/get-started)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Visual Studio Code](https://code.visualstudio.com/)
- VS Code [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension

## Getting Started

### Clone the Repository

\`\`\`bash
git clone 
cd python_web_container
\`\`\`

### Open in VS Code

\`\`\`bash
code .
\`\`\`

### Start Development in Container

1. When VS Code opens, you should see a notification that a Dev Container configuration file was detected.
2. Click "Reopen in Container" to start the container and connect VS Code to it.
3. If you don't see this notification, you can:
   - Click the green icon in the bottom-left corner of VS Code
   - Select "Remote-Containers: Reopen in Container"

### Running the Application

Once VS Code is connected to the container, you can:

1. Open a terminal in VS Code (Terminal > New Terminal)
2. Run the Flask application:
   \`\`\`bash
   flask run --host=0.0.0.0
   \`\`\`
3. Access the application at http://localhost:5000

### Running Tests

\`\`\`bash
pytest app/tests
\`\`\`

## Project Structure

\`\`\`
python_web_container/
├── .devcontainer/        # VS Code remote container configuration
│   └── devcontainer.json
├── .vscode/              # VS Code settings
│   ├── launch.json       # Debug configuration
│   └── settings.json     # Editor settings
├── app/                  # Application package
│   ├── static/           # Static files (CSS, JavaScript, images)
│   │   └── css/
│   │       └── style.css
│   ├── templates/        # HTML templates
│   │   ├── base.html
│   │   ├── index.html
│   │   └── about.html
│   ├── tests/            # Test directory
│   │   └── test_app.py
│   └── app.py            # Main application file
├── Dockerfile            # Docker configuration
├── docker-compose.yml    # Container composition
├── requirements.txt      # Python dependencies
└── README.md             # This file
\`\`\`

## Customizing

### Adding Python Packages

1. Add the package to \`requirements.txt\`
2. Rebuild the container:
   - Click the green icon in the bottom-left corner
   - Select "Remote-Containers: Rebuild Container"

### Changing Python Version

1. Update the first line in \`Dockerfile\` to specify a different Python version (e.g., \`FROM python:3.11-slim\`)
2. Rebuild the container as described above

## Troubleshooting

### Container Won't Start

- Ensure Docker is running
- Check Docker logs: \`docker-compose logs\`
- Try rebuilding: \`docker-compose build --no-cache\`

### VS Code Not Connecting to Container

- Ensure the Remote - Containers extension is installed
- Try reloading VS Code: \`Ctrl+R\` or \`Cmd+R\` on Mac
- Check the Remote - Containers extension output in VS Code

## License

This project is licensed under the MIT License - see the LICENSE file for details.
EOL

This README provides:

Step 8: Commit all files to Git

git add .
git commit -m "Create containerized Python environment with VS Code integration"

Testing and Review

Testing the Environment

Let's verify that our containerized environment works correctly by following these steps:

  1. Make sure Docker is running on your system
  2. Open VS Code in the project directory: code .
  3. When VS Code prompts about a dev container configuration, click "Reopen in Container"
  4. If not prompted, click the green icon in the bottom-left corner and select "Remote-Containers: Reopen in Container"
  5. Wait for the container to build and VS Code to connect (this may take a few minutes the first time)
  6. Once connected, open a terminal in VS Code (Terminal > New Terminal)
  7. Run python --version to verify Python is available
  8. Run git --version to verify Git is available
  9. Run flask run --host=0.0.0.0 to start the Flask application
  10. Open a web browser and navigate to http://localhost:5000 to see the application
  11. Open a new terminal and run pytest app/tests to verify the tests pass

Reflection: What We've Accomplished

Let's review what we've built and why it matters:

Real-world application: This approach mirrors how professional development teams create standardized environments. Companies like Spotify, Netflix, and many others use containerized development environments to ensure all developers have identical setups, reducing onboarding time and eliminating environment-related bugs.

Advanced Customizations (Optional)

Adding a Database

For web applications, you often need a database. You can add PostgreSQL to your environment by updating the docker-compose.yml file:

# Updated docker-compose.yml with PostgreSQL
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/workspace
    ports:
      - "5000:5000"
    environment:
      - FLASK_APP=app/app.py
      - FLASK_ENV=development
      - FLASK_DEBUG=1
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/devdb
    depends_on:
      - db
  
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_DB=devdb
    ports:
      - "5432:5432"

volumes:
  postgres_data:

You would also need to add database packages to your Python environment:

# Add to requirements.txt
psycopg2-binary==2.9.7
SQLAlchemy==2.0.20

Adding HTTPS with self-signed certificates

For more realistic development, you might want to add HTTPS support:

# Create a directory for certificates
mkdir -p certs

# Generate self-signed certificates (on Linux/Mac)
openssl req -x509 -newkey rsa:4096 -keyout certs/key.pem -out certs/cert.pem -days 365 -nodes -subj '/CN=localhost'

Then update your Flask application to use HTTPS:

# Updated app.py
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, ssl_context=('certs/cert.pem', 'certs/key.pem'))

Adding Docker Compose services for frontend development

If you're working on a project with a JavaScript frontend, you could add Node.js support:

# Extended docker-compose.yml with Node.js
version: '3.8'

services:
  app:
    # Python backend configuration (as before)
    
  db:
    # Database configuration (as before)
    
  frontend:
    image: node:16
    volumes:
      - ./frontend:/workspace
    working_dir: /workspace
    ports:
      - "3000:3000"
    command: bash -c "npm install && npm start"

Conclusion

Congratulations! You've created a professional-grade containerized Python development environment that includes:

This environment provides several key benefits:

As you progress through the course, you can extend this environment to include additional tools and services as needed. The foundation you've built will support increasingly complex web applications.

Final thought: Just as a chef's well-organized kitchen enables creativity in cooking, your containerized development environment enables creativity in programming by handling the technical details consistently. You're now ready to focus on the code rather than the environment.

Further Resources