Docker functionality
This commit is contained in:
63
.dockerignore
Normal file
63
.dockerignore
Normal 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
48
Dockerfile
Normal 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
176
README-Docker.md
Normal 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
41
deploy.sh
Normal 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
39
docker-compose.yml
Normal 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
35
docker-entrypoint.sh
Normal 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
38
gunicorn.conf.py
Normal 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"
|
||||||
Reference in New Issue
Block a user