Docker functionality

This commit is contained in:
georg.sinn-schirwitz
2025-09-08 14:51:04 +02:00
parent 042a196718
commit 5940e1f8b4
7 changed files with 440 additions and 0 deletions

63
.dockerignore Normal file
View File

@@ -0,0 +1,63 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
.venv/
venv/
ENV/
env/
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Git
.git/
.gitignore
# Logs
logs/
*.log
# Cache
cache/
# Testing
.pytest_cache/
.coverage
htmlcov/
# Documentation
docs/_build/
# Docker
Dockerfile*
docker-compose*.yml
.dockerignore
README-Docker.md
deploy.sh

48
Dockerfile Normal file
View File

@@ -0,0 +1,48 @@
# Use Python 3.11 slim image
FROM python:3.11-slim
# Set environment variables
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV FLASK_ENV=production
ENV FLASK_SECRET=production-secret-change-me
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
default-libmysqlclient-dev \
pkg-config \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create app directory
WORKDIR /app
# Copy requirements first for better caching
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create necessary directories
RUN mkdir -p cache logs
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/ || exit 1
# Copy entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Set entrypoint
ENTRYPOINT ["docker-entrypoint.sh"]
# Run gunicorn
CMD ["gunicorn", "--config", "gunicorn.conf.py", "web.app:app"]

176
README-Docker.md Normal file
View File

@@ -0,0 +1,176 @@
# Jobs App - Docker Deployment
This application is a Craigslist job scraper with a Flask web interface.
## Quick Start with Docker
### Prerequisites
- Docker
- Docker Compose
### Deployment
1. **Clone the repository and navigate to the project directory**
```bash
cd /path/to/jobs-app/jobs
```
2. **Start the application**
```bash
docker-compose up --build -d
```
3. **Wait for services to be ready** (about 30 seconds)
```bash
# Check if the app is running
curl http://localhost:8000
```
4. **Access the application**
- Main app: http://localhost:8000
- Admin interface: http://localhost:8000/admin/users
- Username: `admin`
- Password: `M11ffpgm.`
- Scraper interface: http://localhost:8000/scrape-page
## Docker Architecture
### Services
- **jobs-app**: Flask application with Gunicorn WSGI server
- **mysql**: MySQL 8.0 database
### Ports
- 8000: Flask application
- 3306: MySQL database (exposed for external access if needed)
### Volumes
- `mysql_data`: Persistent MySQL data storage
## Configuration
### Environment Variables
- `FLASK_ENV`: Set to `production`
- `FLASK_SECRET`: Secret key for Flask sessions
### Database Configuration
The database configuration is in `config/settings.json`:
```json
{
"database": {
"mysql": {
"host": "mysql",
"user": "jobs",
"password": "jobdb",
"database": "jobs",
"port": 3306
}
}
}
```
## Useful Commands
```bash
# View logs
docker-compose logs -f
# Stop services
docker-compose down
# Restart services
docker-compose restart
# Rebuild and restart
docker-compose up --build
# View running containers
docker-compose ps
# Execute commands in the app container
docker-compose exec jobs-app bash
# Check database
docker-compose exec mysql mysql -u jobs -p jobs
```
## Production Considerations
1. **Security**:
- Change default passwords in `docker-compose.yml`
- Use environment variables for secrets
- Configure proper firewall rules
2. **Scaling**:
- Adjust Gunicorn workers in `gunicorn.conf.py`
- Consider using a reverse proxy (nginx)
- Implement proper logging and monitoring
3. **Database**:
- Use external MySQL for production
- Configure backups
- Set up connection pooling
4. **Networking**:
- Use proper domain names
- Configure SSL/TLS
- Set up load balancing if needed
## Troubleshooting
### Common Issues
1. **Port conflicts**: Change ports in `docker-compose.yml`
2. **Database connection**: Ensure MySQL container is healthy
3. **Memory issues**: Increase Docker memory limits
4. **Permission issues**: Check file permissions in mounted volumes
### Logs
```bash
# Application logs
docker-compose logs jobs-app
# Database logs
docker-compose logs mysql
# All logs
docker-compose logs
```
## Development
For development with hot reload:
```bash
# Run in development mode
docker-compose -f docker-compose.dev.yml up --build
```
Create `docker-compose.dev.yml`:
```yaml
version: "3.8"
services:
jobs-app:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=development
volumes:
- .:/app
command: ["flask", "run", "--host", "0.0.0.0", "--port", "8000"]
```

41
deploy.sh Normal file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
# Deployment script for Jobs App
set -e
echo "🚀 Starting Jobs App deployment..."
# Check if Docker is installed
if ! command -v docker &> /dev/null; then
echo "❌ Docker is not installed. Please install Docker first."
exit 1
fi
# Check if docker-compose is installed
if ! command -v docker-compose &> /dev/null; then
echo "❌ docker-compose is not installed. Please install docker-compose first."
exit 1
fi
echo "📦 Building and starting services..."
docker-compose up --build -d
echo "⏳ Waiting for services to be ready..."
sleep 30
echo "🔍 Checking service health..."
if curl -f http://localhost:8000/ &> /dev/null; then
echo "✅ Jobs App is running successfully!"
echo "🌐 Access the application at: http://localhost:8000"
echo "📊 Admin interface: http://localhost:8000/admin/users (login: admin/M11ffpgm.)"
echo "🔄 Scraper interface: http://localhost:8000/scrape-page"
else
echo "❌ Jobs App failed to start. Check logs with: docker-compose logs"
exit 1
fi
echo "📋 Useful commands:"
echo " View logs: docker-compose logs -f"
echo " Stop services: docker-compose down"
echo " Restart: docker-compose restart"
echo " Rebuild: docker-compose up --build"

39
docker-compose.yml Normal file
View File

@@ -0,0 +1,39 @@
version: "3.8"
services:
jobs-app:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=production
- FLASK_SECRET=production-secret-change-me
depends_on:
- mysql
networks:
- jobs-network
restart: unless-stopped
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=jobs
- MYSQL_USER=jobs
- MYSQL_PASSWORD=jobdb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./mysql-init:/docker-entrypoint-initdb.d
networks:
- jobs-network
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
volumes:
mysql_data:
networks:
jobs-network:
driver: bridge

35
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Docker entrypoint script for Jobs App
set -e
echo "🚀 Starting Jobs App container..."
# Wait for MySQL to be ready
echo "⏳ Waiting for MySQL to be ready..."
while ! mysqladmin ping -h mysql -u jobs -pjobdb --silent; do
echo "MySQL is not ready, waiting..."
sleep 2
done
echo "✅ MySQL is ready!"
# Run database setup
echo "🗄️ Setting up database..."
python setup.py mysql-init
# Seed initial data if needed
echo "🌱 Seeding initial data..."
python -c "
from web.db import db_init
from web.utils import initialize_users_from_settings
db_init()
try:
initialize_users_from_settings()
print('✅ Users seeded successfully')
except Exception as e:
print(f'⚠️ User seeding failed: {e}')
"
echo "🎯 Starting Gunicorn server..."
exec "$@"

38
gunicorn.conf.py Normal file
View File

@@ -0,0 +1,38 @@
# Gunicorn configuration file
import multiprocessing
# Server socket
bind = "0.0.0.0:8000"
backlog = 2048
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 30
keepalive = 2
# Logging
loglevel = "info"
accesslog = "-"
errorlog = "-"
# Process naming
proc_name = "jobs_app"
# Server mechanics
daemon = False
pidfile = "/tmp/gunicorn.pid"
user = None
group = None
tmp_upload_dir = None
# SSL (if needed)
keyfile = None
certfile = None
# Application
wsgi_module = "web.app:app"
callable = "app"