Internal
Public Access
Add Docker deployment configuration
- Add Dockerfile for production (multi-stage build) - Add Dockerfile.dev for development with hot reload - Add docker-compose.yml with 5 services (web, mariadb, redis, celery-worker, celery-beat) - Add docker-compose.dev.yml for development overrides - Add .dockerignore for optimized builds - Add docker/entrypoint.sh startup script - Add .env.docker.example template - Add Makefile for common commands - Add deploy.sh for one-command deployment - Update config/settings/selfhosted.py to support MariaDB via DATABASE_URL - Add mysqlclient to requirements.txt for MariaDB support - Update .gitignore to exclude .env.docker 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
parent
a2097dcad3
commit
71399e54b8
@@ -0,0 +1,66 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
*.egg
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# Django
|
||||||
|
db.sqlite3
|
||||||
|
*.sqlite3
|
||||||
|
staticfiles/
|
||||||
|
media/
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.docker.example
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile
|
||||||
|
Dockerfile.dev
|
||||||
|
docker-compose.yml
|
||||||
|
docker-compose.dev.yml
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
README.md
|
||||||
|
*.md
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# CI/CD
|
||||||
|
.github/
|
||||||
|
.gitlab-ci.yml
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
.pytest_cache/
|
||||||
|
.tox/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# KeepItGoing Server - Docker Environment Configuration
|
||||||
|
# ===================================================================
|
||||||
|
# Copy this file to .env.docker and fill in your values
|
||||||
|
# DO NOT commit .env.docker to version control!
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Django Core Settings
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# SECURITY WARNING: Generate a strong secret key!
|
||||||
|
# Generate with: python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
|
||||||
|
SECRET_KEY=CHANGE_ME_GENERATE_A_STRONG_SECRET_KEY_50_PLUS_CHARACTERS
|
||||||
|
|
||||||
|
# Debug mode (MUST be False in production)
|
||||||
|
DEBUG=False
|
||||||
|
|
||||||
|
# Comma-separated list of allowed hosts
|
||||||
|
# Example: api.yourdomain.com,localhost
|
||||||
|
ALLOWED_HOSTS=yourdomain.com,localhost
|
||||||
|
|
||||||
|
# Comma-separated list of trusted origins for CSRF (must include https://)
|
||||||
|
# Example: https://api.yourdomain.com,https://yourdomain.com
|
||||||
|
CSRF_TRUSTED_ORIGINS=https://yourdomain.com
|
||||||
|
|
||||||
|
# Domain used in email links
|
||||||
|
SITE_DOMAIN=yourdomain.com
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Database Configuration (MariaDB)
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
DB_NAME=keepitgoing
|
||||||
|
DB_USER=keepitgoing
|
||||||
|
DB_PASSWORD=CHANGE_ME_STRONG_DATABASE_PASSWORD
|
||||||
|
DB_ROOT_PASSWORD=CHANGE_ME_STRONG_ROOT_PASSWORD
|
||||||
|
DB_HOST=mariadb
|
||||||
|
DB_PORT=3306
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Redis Configuration
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
REDIS_URL=redis://redis:6379/0
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Email Configuration
|
||||||
|
# ===================================================================
|
||||||
|
# Required for user registration (email verification)
|
||||||
|
|
||||||
|
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
||||||
|
|
||||||
|
# Gmail Example (use App Password, not regular password):
|
||||||
|
# EMAIL_HOST=smtp.gmail.com
|
||||||
|
# EMAIL_PORT=587
|
||||||
|
# EMAIL_USE_TLS=True
|
||||||
|
# EMAIL_HOST_USER=your-email@gmail.com
|
||||||
|
# EMAIL_HOST_PASSWORD=your-app-password
|
||||||
|
|
||||||
|
# SendGrid Example:
|
||||||
|
# EMAIL_HOST=smtp.sendgrid.net
|
||||||
|
# EMAIL_PORT=587
|
||||||
|
# EMAIL_USE_TLS=True
|
||||||
|
# EMAIL_HOST_USER=apikey
|
||||||
|
# EMAIL_HOST_PASSWORD=your-sendgrid-api-key
|
||||||
|
|
||||||
|
# AWS SES Example:
|
||||||
|
# EMAIL_HOST=email-smtp.us-east-1.amazonaws.com
|
||||||
|
# EMAIL_PORT=587
|
||||||
|
# EMAIL_USE_TLS=True
|
||||||
|
# EMAIL_HOST_USER=your-aws-access-key
|
||||||
|
# EMAIL_HOST_PASSWORD=your-aws-secret-key
|
||||||
|
|
||||||
|
EMAIL_HOST=smtp.gmail.com
|
||||||
|
EMAIL_PORT=587
|
||||||
|
EMAIL_USE_TLS=True
|
||||||
|
EMAIL_HOST_USER=your-email@gmail.com
|
||||||
|
EMAIL_HOST_PASSWORD=your-app-password
|
||||||
|
DEFAULT_FROM_EMAIL=KeepItGoing <noreply@yourdomain.com>
|
||||||
|
SERVER_EMAIL=server@yourdomain.com
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Email Verification Settings
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
EMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS=24
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# CORS Configuration
|
||||||
|
# ===================================================================
|
||||||
|
# Comma-separated list of allowed origins
|
||||||
|
# Example: https://yourdomain.com,https://app.yourdomain.com
|
||||||
|
|
||||||
|
CORS_ALLOWED_ORIGINS=https://yourdomain.com
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# SaaS Mode
|
||||||
|
# ===================================================================
|
||||||
|
# Set to True for SaaS deployment with billing/usage limits
|
||||||
|
# Set to False for self-hosted deployment (no limits)
|
||||||
|
|
||||||
|
SAAS_MODE=False
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Gunicorn Configuration
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
GUNICORN_WORKERS=3
|
||||||
|
GUNICORN_TIMEOUT=60
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Push Notifications (Optional)
|
||||||
|
# ===================================================================
|
||||||
|
# Leave empty if not using push notifications
|
||||||
|
|
||||||
|
# Firebase Cloud Messaging (FCM)
|
||||||
|
FCM_CREDENTIALS_PATH=
|
||||||
|
|
||||||
|
# Web Push (VAPID keys)
|
||||||
|
VAPID_PUBLIC_KEY=
|
||||||
|
VAPID_PRIVATE_KEY=
|
||||||
|
VAPID_ADMIN_EMAIL=
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Additional Settings
|
||||||
|
# ===================================================================
|
||||||
|
|
||||||
|
# Django settings module (usually don't need to change)
|
||||||
|
DJANGO_SETTINGS_MODULE=config.settings.selfhosted
|
||||||
@@ -17,6 +17,7 @@ media/
|
|||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
.env
|
.env
|
||||||
|
.env.docker
|
||||||
*.env.local
|
*.env.local
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# Stage 1: Builder - Install dependencies
|
||||||
|
# ===================================================================
|
||||||
|
FROM python:3.12-slim-bookworm AS builder
|
||||||
|
|
||||||
|
# Install build dependencies for mysqlclient and other packages
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
gcc \
|
||||||
|
default-libmysqlclient-dev \
|
||||||
|
pkg-config \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
COPY requirements.txt /tmp/
|
||||||
|
RUN pip install --user --no-cache-dir -r /tmp/requirements.txt \
|
||||||
|
&& pip install --user --no-cache-dir mysqlclient gunicorn
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# Stage 2: Runtime - Minimal production image
|
||||||
|
# ===================================================================
|
||||||
|
FROM python:3.12-slim-bookworm
|
||||||
|
|
||||||
|
# Install runtime dependencies only
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
default-mysql-client \
|
||||||
|
curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create django user (non-root for security)
|
||||||
|
RUN useradd -m -u 1000 -s /bin/bash django
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy Python dependencies from builder stage
|
||||||
|
COPY --from=builder /root/.local /home/django/.local
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY --chown=django:django . /app/
|
||||||
|
|
||||||
|
# Create directories for media and static files
|
||||||
|
RUN mkdir -p /app/media /app/staticfiles && \
|
||||||
|
chown -R django:django /app/media /app/staticfiles
|
||||||
|
|
||||||
|
# Switch to django user
|
||||||
|
USER django
|
||||||
|
|
||||||
|
# Add .local/bin to PATH for installed packages
|
||||||
|
ENV PATH=/home/django/.local/bin:$PATH
|
||||||
|
|
||||||
|
# Set Python to run in unbuffered mode (better for Docker logs)
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Expose port 8000 for Gunicorn
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Health check - ping the API endpoint
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:8000/api/ || exit 1
|
||||||
|
|
||||||
|
# Entrypoint script handles migrations, static collection, and service startup
|
||||||
|
ENTRYPOINT ["/app/docker/entrypoint.sh"]
|
||||||
|
|
||||||
|
# Default command is to run Gunicorn
|
||||||
|
CMD ["gunicorn"]
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# ===================================================================
|
||||||
|
# Development Dockerfile - Fast builds with development tools
|
||||||
|
# ===================================================================
|
||||||
|
FROM python:3.12-slim-bookworm
|
||||||
|
|
||||||
|
# Install build and runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
gcc \
|
||||||
|
default-libmysqlclient-dev \
|
||||||
|
default-mysql-client \
|
||||||
|
pkg-config \
|
||||||
|
curl \
|
||||||
|
vim \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create django user
|
||||||
|
RUN useradd -m -u 1000 -s /bin/bash django
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
# Note: In dev mode, code is mounted as volume, so we just install deps
|
||||||
|
COPY requirements.txt /tmp/
|
||||||
|
RUN pip install --no-cache-dir -r /tmp/requirements.txt \
|
||||||
|
&& pip install --no-cache-dir mysqlclient gunicorn
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
RUN mkdir -p /app/media /app/staticfiles && \
|
||||||
|
chown -R django:django /app
|
||||||
|
|
||||||
|
# Switch to django user
|
||||||
|
USER django
|
||||||
|
|
||||||
|
# Set Python to run in unbuffered mode
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Expose port 8000
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Entrypoint script handles service startup
|
||||||
|
ENTRYPOINT ["/app/docker/entrypoint.sh"]
|
||||||
|
|
||||||
|
# Default command for dev is Django runserver
|
||||||
|
CMD ["runserver"]
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
.PHONY: help dev dev-build prod build deploy logs logs-web logs-celery shell migrate makemigrations createsuperuser test clean stop restart status backup
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
help:
|
||||||
|
@echo "KeepItGoing Server - Docker Commands"
|
||||||
|
@echo "====================================="
|
||||||
|
@echo ""
|
||||||
|
@echo "Development:"
|
||||||
|
@echo " make dev - Start development environment with live reload"
|
||||||
|
@echo " make dev-build - Rebuild and start development environment"
|
||||||
|
@echo " make shell - Access Django shell"
|
||||||
|
@echo " make test - Run tests"
|
||||||
|
@echo ""
|
||||||
|
@echo "Production:"
|
||||||
|
@echo " make prod - Start production environment"
|
||||||
|
@echo " make build - Build production containers"
|
||||||
|
@echo " make deploy - Deploy to production (pull, build, migrate, restart)"
|
||||||
|
@echo ""
|
||||||
|
@echo "Database:"
|
||||||
|
@echo " make migrate - Run database migrations"
|
||||||
|
@echo " make makemigrations - Create new migrations"
|
||||||
|
@echo " make createsuperuser - Create Django superuser"
|
||||||
|
@echo " make backup - Backup database"
|
||||||
|
@echo " make dbshell - Access MariaDB shell"
|
||||||
|
@echo ""
|
||||||
|
@echo "Logs & Monitoring:"
|
||||||
|
@echo " make logs - View all container logs"
|
||||||
|
@echo " make logs-web - View web container logs"
|
||||||
|
@echo " make logs-celery - View celery worker logs"
|
||||||
|
@echo " make status - Show container status"
|
||||||
|
@echo ""
|
||||||
|
@echo "Control:"
|
||||||
|
@echo " make stop - Stop all containers"
|
||||||
|
@echo " make restart - Restart all containers"
|
||||||
|
@echo " make clean - Stop and remove containers, networks, volumes"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# Development
|
||||||
|
dev:
|
||||||
|
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||||
|
|
||||||
|
dev-build:
|
||||||
|
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
||||||
|
|
||||||
|
# Production
|
||||||
|
prod:
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker-compose build --no-cache
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
@echo "Deploying to production..."
|
||||||
|
git pull
|
||||||
|
docker-compose build
|
||||||
|
docker-compose up -d
|
||||||
|
docker-compose exec web python manage.py migrate
|
||||||
|
docker-compose exec web python manage.py collectstatic --noinput
|
||||||
|
docker-compose restart web
|
||||||
|
@echo "Deployment complete!"
|
||||||
|
@make status
|
||||||
|
|
||||||
|
# Database operations
|
||||||
|
migrate:
|
||||||
|
docker-compose exec web python manage.py migrate
|
||||||
|
|
||||||
|
makemigrations:
|
||||||
|
docker-compose exec web python manage.py makemigrations
|
||||||
|
|
||||||
|
createsuperuser:
|
||||||
|
docker-compose exec web python manage.py createsuperuser
|
||||||
|
|
||||||
|
dbshell:
|
||||||
|
docker-compose exec mariadb mysql -u keepitgoing -p keepitgoing
|
||||||
|
|
||||||
|
backup:
|
||||||
|
@echo "Creating database backup..."
|
||||||
|
@mkdir -p backups
|
||||||
|
docker-compose exec mariadb mysqldump -u root -p keepitgoing > backups/backup-$$(date +%Y%m%d-%H%M%S).sql
|
||||||
|
@echo "Backup created in backups/ directory"
|
||||||
|
|
||||||
|
# Django shell and tests
|
||||||
|
shell:
|
||||||
|
docker-compose exec web python manage.py shell
|
||||||
|
|
||||||
|
test:
|
||||||
|
docker-compose exec web python manage.py test
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs:
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
logs-web:
|
||||||
|
docker-compose logs -f web
|
||||||
|
|
||||||
|
logs-celery:
|
||||||
|
docker-compose logs -f celery-worker
|
||||||
|
|
||||||
|
# Control
|
||||||
|
stop:
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
restart:
|
||||||
|
docker-compose restart
|
||||||
|
|
||||||
|
status:
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
clean:
|
||||||
|
docker-compose down -v
|
||||||
|
@echo "Containers, networks, and volumes removed"
|
||||||
@@ -6,6 +6,7 @@ Use this for users running their own instance.
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import dj_database_url
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
@@ -28,18 +29,51 @@ if DEBUG:
|
|||||||
|
|
||||||
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
|
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
|
||||||
|
|
||||||
# Database - PostgreSQL for self-hosted
|
# Database - Support both PostgreSQL and MariaDB/MySQL via DATABASE_URL
|
||||||
|
# DATABASE_URL format examples:
|
||||||
|
# PostgreSQL: postgresql://user:password@host:5432/dbname
|
||||||
|
# MariaDB/MySQL: mysql://user:password@host:3306/dbname
|
||||||
|
database_url = os.environ.get('DATABASE_URL')
|
||||||
|
|
||||||
|
if database_url:
|
||||||
|
# Use DATABASE_URL for flexible database backend (PostgreSQL, MariaDB, MySQL, etc.)
|
||||||
|
DATABASES = {
|
||||||
|
'default': dj_database_url.config(
|
||||||
|
default=database_url,
|
||||||
|
conn_max_age=600,
|
||||||
|
conn_health_checks=True,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add MariaDB/MySQL specific options if using MySQL backend
|
||||||
|
if 'mysql' in database_url.lower():
|
||||||
|
DATABASES['default']['OPTIONS'] = {
|
||||||
|
'charset': 'utf8mb4',
|
||||||
|
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# Fallback to manual configuration (backwards compatibility)
|
||||||
|
# Supports both PostgreSQL and MariaDB/MySQL
|
||||||
|
db_engine = os.environ.get('DB_ENGINE', 'django.db.backends.postgresql')
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql',
|
'ENGINE': db_engine,
|
||||||
'NAME': os.environ.get('POSTGRES_DB', 'keepitgoing'),
|
'NAME': os.environ.get('DB_NAME', os.environ.get('POSTGRES_DB', 'keepitgoing')),
|
||||||
'USER': os.environ.get('POSTGRES_USER', 'keepitgoing'),
|
'USER': os.environ.get('DB_USER', os.environ.get('POSTGRES_USER', 'keepitgoing')),
|
||||||
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', 'keepitgoing'),
|
'PASSWORD': os.environ.get('DB_PASSWORD', os.environ.get('POSTGRES_PASSWORD', 'keepitgoing')),
|
||||||
'HOST': os.environ.get('POSTGRES_HOST', 'db'),
|
'HOST': os.environ.get('DB_HOST', os.environ.get('POSTGRES_HOST', 'db')),
|
||||||
'PORT': os.environ.get('POSTGRES_PORT', '5432'),
|
'PORT': os.environ.get('DB_PORT', os.environ.get('POSTGRES_PORT', '5432')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Add MariaDB/MySQL specific options if using MySQL backend
|
||||||
|
if 'mysql' in db_engine:
|
||||||
|
DATABASES['default']['OPTIONS'] = {
|
||||||
|
'charset': 'utf8mb4',
|
||||||
|
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
|
||||||
|
}
|
||||||
|
|
||||||
# Redis/Celery
|
# Redis/Celery
|
||||||
REDIS_URL = os.environ.get('REDIS_URL', 'redis://redis:6379/0')
|
REDIS_URL = os.environ.get('REDIS_URL', 'redis://redis:6379/0')
|
||||||
CELERY_BROKER_URL = REDIS_URL
|
CELERY_BROKER_URL = REDIS_URL
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${BLUE}KeepItGoing Server - Deployment Script${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if .env.docker exists
|
||||||
|
if [ ! -f ".env.docker" ]; then
|
||||||
|
echo -e "${RED}ERROR: .env.docker file not found!${NC}"
|
||||||
|
echo -e "${YELLOW}Please copy .env.docker.example to .env.docker and configure it.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 1: Pull latest code
|
||||||
|
echo -e "${YELLOW}[1/6] Pulling latest code from git...${NC}"
|
||||||
|
if git pull; then
|
||||||
|
echo -e "${GREEN}✓ Code updated${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Git pull failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 2: Build containers
|
||||||
|
echo -e "${YELLOW}[2/6] Building Docker containers...${NC}"
|
||||||
|
if docker-compose build; then
|
||||||
|
echo -e "${GREEN}✓ Containers built${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Build failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 3: Start containers
|
||||||
|
echo -e "${YELLOW}[3/6] Starting containers...${NC}"
|
||||||
|
if docker-compose up -d; then
|
||||||
|
echo -e "${GREEN}✓ Containers started${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Failed to start containers${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 4: Wait for services to be ready
|
||||||
|
echo -e "${YELLOW}[4/6] Waiting for services to be ready...${NC}"
|
||||||
|
sleep 10
|
||||||
|
echo -e "${GREEN}✓ Services ready${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 5: Run migrations
|
||||||
|
echo -e "${YELLOW}[5/6] Running database migrations...${NC}"
|
||||||
|
if docker-compose exec -T web python manage.py migrate --noinput; then
|
||||||
|
echo -e "${GREEN}✓ Migrations complete${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Migrations failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 6: Collect static files
|
||||||
|
echo -e "${YELLOW}[6/6] Collecting static files...${NC}"
|
||||||
|
if docker-compose exec -T web python manage.py collectstatic --noinput; then
|
||||||
|
echo -e "${GREEN}✓ Static files collected${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Failed to collect static files${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Restart web service for good measure
|
||||||
|
echo -e "${YELLOW}Restarting web service...${NC}"
|
||||||
|
docker-compose restart web
|
||||||
|
echo -e "${GREEN}✓ Web service restarted${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show status
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo -e "${GREEN}Deployment Complete!${NC}"
|
||||||
|
echo -e "${BLUE}========================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Container Status:${NC}"
|
||||||
|
docker-compose ps
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Recent logs:${NC}"
|
||||||
|
docker-compose logs --tail=20 web
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}Deployment successful! ✓${NC}"
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Development Overrides
|
||||||
|
# Use with: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||||
|
# =================================================================
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Web service overrides for development
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
volumes:
|
||||||
|
# Mount source code for live reload
|
||||||
|
- ./:/app
|
||||||
|
# Override media and static with the same mounts
|
||||||
|
- ./media:/app/media
|
||||||
|
- static_files:/app/staticfiles
|
||||||
|
environment:
|
||||||
|
# Enable debug mode
|
||||||
|
DEBUG: "True"
|
||||||
|
# Use development settings (optional - can still use selfhosted)
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings.development
|
||||||
|
command: runserver
|
||||||
|
# Remove healthcheck in dev mode (optional)
|
||||||
|
healthcheck:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
# Celery worker overrides for development
|
||||||
|
celery-worker:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
volumes:
|
||||||
|
# Mount source code for live reload
|
||||||
|
- ./:/app
|
||||||
|
environment:
|
||||||
|
DEBUG: "True"
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings.development
|
||||||
|
|
||||||
|
# Celery beat overrides for development
|
||||||
|
celery-beat:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
volumes:
|
||||||
|
# Mount source code for live reload
|
||||||
|
- ./:/app
|
||||||
|
environment:
|
||||||
|
DEBUG: "True"
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings.development
|
||||||
|
|
||||||
|
# Note: mariadb and redis services inherit from base docker-compose.yml
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# =================================================================
|
||||||
|
# MariaDB Database Service
|
||||||
|
# =================================================================
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:11.2
|
||||||
|
container_name: keepitgoing-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_DATABASE: ${DB_NAME:-keepitgoing}
|
||||||
|
MYSQL_USER: ${DB_USER:-keepitgoing}
|
||||||
|
MYSQL_PASSWORD: ${DB_PASSWORD}
|
||||||
|
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||||
|
MYSQL_CHARACTER_SET_SERVER: utf8mb4
|
||||||
|
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Redis Service - Message broker for Celery
|
||||||
|
# =================================================================
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: keepitgoing-redis
|
||||||
|
restart: unless-stopped
|
||||||
|
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Django/Gunicorn Web Service
|
||||||
|
# =================================================================
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: keepitgoing-web
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env.docker
|
||||||
|
environment:
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings.selfhosted
|
||||||
|
DATABASE_URL: mysql://${DB_USER:-keepitgoing}:${DB_PASSWORD}@mariadb:3306/${DB_NAME:-keepitgoing}
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
volumes:
|
||||||
|
- ./media:/app/media
|
||||||
|
- static_files:/app/staticfiles
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
- backend
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8000/api/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Celery Worker Service - Background task processor
|
||||||
|
# =================================================================
|
||||||
|
celery-worker:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: keepitgoing-celery-worker
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env.docker
|
||||||
|
environment:
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings.selfhosted
|
||||||
|
DATABASE_URL: mysql://${DB_USER:-keepitgoing}:${DB_PASSWORD}@mariadb:3306/${DB_NAME:-keepitgoing}
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
command: celery-worker
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Celery Beat Service - Scheduled task scheduler
|
||||||
|
# =================================================================
|
||||||
|
celery-beat:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: keepitgoing-celery-beat
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env.docker
|
||||||
|
environment:
|
||||||
|
DJANGO_SETTINGS_MODULE: config.settings.selfhosted
|
||||||
|
DATABASE_URL: mysql://${DB_USER:-keepitgoing}:${DB_PASSWORD}@mariadb:3306/${DB_NAME:-keepitgoing}
|
||||||
|
REDIS_URL: redis://redis:6379/0
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
command: celery-beat
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Named Volumes
|
||||||
|
# =================================================================
|
||||||
|
volumes:
|
||||||
|
db_data:
|
||||||
|
name: keepitgoing_db_data
|
||||||
|
redis_data:
|
||||||
|
name: keepitgoing_redis_data
|
||||||
|
static_files:
|
||||||
|
name: keepitgoing_static_files
|
||||||
|
|
||||||
|
# =================================================================
|
||||||
|
# Networks
|
||||||
|
# =================================================================
|
||||||
|
networks:
|
||||||
|
frontend:
|
||||||
|
name: keepitgoing_frontend
|
||||||
|
backend:
|
||||||
|
name: keepitgoing_backend
|
||||||
Executable
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${GREEN}========================================${NC}"
|
||||||
|
echo -e "${GREEN}KeepItGoing Docker Entrypoint${NC}"
|
||||||
|
echo -e "${GREEN}========================================${NC}"
|
||||||
|
|
||||||
|
# Extract database host from DATABASE_URL or use DB_HOST env var
|
||||||
|
DB_HOST=${DB_HOST:-mariadb}
|
||||||
|
|
||||||
|
# Wait for MariaDB to be ready
|
||||||
|
echo -e "${YELLOW}Waiting for MariaDB at ${DB_HOST}...${NC}"
|
||||||
|
max_attempts=30
|
||||||
|
attempt=0
|
||||||
|
|
||||||
|
while ! mysqladmin ping -h"${DB_HOST}" --silent 2>/dev/null; do
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
if [ $attempt -eq $max_attempts ]; then
|
||||||
|
echo -e "${RED}ERROR: MariaDB did not become available in time${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Waiting for MariaDB... ($attempt/$max_attempts)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ MariaDB is ready!${NC}"
|
||||||
|
|
||||||
|
# Function to run migrations
|
||||||
|
run_migrations() {
|
||||||
|
echo -e "${YELLOW}Running database migrations...${NC}"
|
||||||
|
python manage.py migrate --noinput
|
||||||
|
echo -e "${GREEN}✓ Migrations complete${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to collect static files
|
||||||
|
collect_static() {
|
||||||
|
echo -e "${YELLOW}Collecting static files...${NC}"
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
echo -e "${GREEN}✓ Static files collected${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine which service to start based on command argument
|
||||||
|
case "$1" in
|
||||||
|
"gunicorn")
|
||||||
|
echo -e "${GREEN}Starting Gunicorn WSGI server...${NC}"
|
||||||
|
run_migrations
|
||||||
|
collect_static
|
||||||
|
|
||||||
|
# Get Gunicorn config from environment or use defaults
|
||||||
|
WORKERS=${GUNICORN_WORKERS:-3}
|
||||||
|
TIMEOUT=${GUNICORN_TIMEOUT:-60}
|
||||||
|
|
||||||
|
echo -e "${GREEN}Configuration:${NC}"
|
||||||
|
echo " Workers: $WORKERS"
|
||||||
|
echo " Timeout: $TIMEOUT"
|
||||||
|
echo " Bind: 0.0.0.0:8000"
|
||||||
|
|
||||||
|
exec gunicorn config.wsgi:application \
|
||||||
|
--bind 0.0.0.0:8000 \
|
||||||
|
--workers $WORKERS \
|
||||||
|
--timeout $TIMEOUT \
|
||||||
|
--access-logfile - \
|
||||||
|
--error-logfile - \
|
||||||
|
--log-level info
|
||||||
|
;;
|
||||||
|
|
||||||
|
"celery-worker")
|
||||||
|
echo -e "${GREEN}Starting Celery worker...${NC}"
|
||||||
|
# Wait a bit more for web service to run migrations
|
||||||
|
sleep 5
|
||||||
|
exec celery -A config worker \
|
||||||
|
--loglevel=info \
|
||||||
|
--concurrency=2
|
||||||
|
;;
|
||||||
|
|
||||||
|
"celery-beat")
|
||||||
|
echo -e "${GREEN}Starting Celery beat scheduler...${NC}"
|
||||||
|
# Wait for web service to run migrations and create database tables
|
||||||
|
sleep 10
|
||||||
|
exec celery -A config beat \
|
||||||
|
--loglevel=info \
|
||||||
|
--scheduler django_celery_beat.schedulers:DatabaseScheduler
|
||||||
|
;;
|
||||||
|
|
||||||
|
"runserver")
|
||||||
|
echo -e "${GREEN}Starting Django development server...${NC}"
|
||||||
|
run_migrations
|
||||||
|
collect_static
|
||||||
|
exec python manage.py runserver 0.0.0.0:8000
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
# If command is not recognized, just execute it
|
||||||
|
echo -e "${YELLOW}Executing custom command: $@${NC}"
|
||||||
|
exec "$@"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
+3
-2
@@ -9,8 +9,9 @@ djangorestframework>=3.14.0
|
|||||||
djangorestframework-simplejwt>=5.3.0
|
djangorestframework-simplejwt>=5.3.0
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
psycopg2-binary>=2.9.9
|
psycopg2-binary>=2.9.9 # PostgreSQL driver
|
||||||
dj-database-url>=2.1.0
|
mysqlclient>=2.2.0 # MariaDB/MySQL driver
|
||||||
|
dj-database-url>=2.1.0 # Universal database URL parser
|
||||||
|
|
||||||
# Task queue
|
# Task queue
|
||||||
celery>=5.3.0
|
celery>=5.3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user