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:
- A Python 3.10+ container: An isolated environment running Python 3.10 or newer
- VS Code configured for remote development: The ability to edit code in VS Code while it runs inside the container
- Git integration: Version control within the container
- Basic project structure for a web application: A template for Python web development
- Documentation: Clear instructions on how to use the setup
Let's break down what each component means:
- Python Container: A Docker container that runs Python 3.10+ and includes necessary development tools
- VS Code Remote Development: Configuration that allows VS Code to connect to and work within the container
- Git Integration: Git installed in the container and configured for version control
- Project Structure: A standard directory layout for web applications
- Documentation: README files and other documentation explaining the setup
The main challenges we need to address are:
- Creating a Dockerfile that includes all necessary tools
- Configuring VS Code to work with the container
- Setting up proper volume mounts to persist code and Git history
- Creating a sensible project structure that follows best practices
- Documenting everything clearly for future reference
Step 2: Devise a Plan
Here's our step-by-step approach to building this containerized environment:
- Create a project directory structure
- Initialize Git repository
- Create a Dockerfile for Python 3.10+ with development tools
- Create docker-compose.yml for easy container management
- Configure VS Code for remote development with devcontainer.json
- Set up a basic Python web application structure
- Create documentation with README.md
- 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:
app/: Main application packageapp/static/: Static files (CSS, JavaScript, images)app/templates/: HTML templatesapp/tests/: Unit and integration tests.devcontainer/: Configuration for VS Code remote development.vscode/: VS Code settings specific to this project
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:
- Uses Python 3.10 as the base image
- Sets environment variables to improve Python behavior in containers
- Installs system dependencies including Git and build tools
- Sets the working directory to /workspace
- Installs Python packages for web development and code quality
- Sets the default command to bash to keep the container running
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:
- Defines a service called "app" that builds using our Dockerfile
- Mounts the current directory to /workspace in the container
- Maps port 5000 from the container to the host
- Sets environment variables for Flask development
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:
- Tell VS Code to connect to the Docker container
- Configure Python tools like linters, formatters, and testing
- Install useful VS Code extensions in the container
- Set up debugging configurations for Flask
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 %}
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:
- A main application file (app.py)
- Templates with inheritance (base.html, index.html, about.html)
- Static CSS (style.css)
- Basic tests (test_app.py)
- Requirements file listing dependencies
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:
- An overview of the environment's features
- Prerequisites for using it
- Step-by-step instructions for getting started
- Explanation of the project structure
- Guidance for customization
- Troubleshooting tips
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:
- Make sure Docker is running on your system
- Open VS Code in the project directory:
code . - When VS Code prompts about a dev container configuration, click "Reopen in Container"
- If not prompted, click the green icon in the bottom-left corner and select "Remote-Containers: Reopen in Container"
- Wait for the container to build and VS Code to connect (this may take a few minutes the first time)
- Once connected, open a terminal in VS Code (Terminal > New Terminal)
- Run
python --versionto verify Python is available - Run
git --versionto verify Git is available - Run
flask run --host=0.0.0.0to start the Flask application - Open a web browser and navigate to
http://localhost:5000to see the application - Open a new terminal and run
pytest app/teststo verify the tests pass
Reflection: What We've Accomplished
Let's review what we've built and why it matters:
- Consistency: This environment works the same on any system with Docker, eliminating "works on my machine" problems
- Isolation: The containerized environment doesn't interfere with other projects or system configurations
- Version Control: Git integration enables tracking changes and collaboration
- IDE Integration: VS Code connects seamlessly to the container, providing a full-featured development experience
- Good Practices: The structure follows Python and web development best practices
- Documentation: Clear documentation ensures others (or you in the future) can use the environment
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:
- A Python 3.10+ container with all necessary development tools
- VS Code configured for remote development
- Git integration for version control
- A basic project structure for web applications
- Comprehensive documentation on how to use the setup
This environment provides several key benefits:
- Portability: Your development environment works identically on any machine with Docker
- Reproducibility: New team members can get the exact same setup with minimal effort
- Isolation: Dependencies and configurations are contained, avoiding conflicts
- Consistency: Development, testing, and production environments can use the same base configuration
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.