diff --git a/Dockerfile.minimal b/Dockerfile.minimal index 7de858d..bbaac67 100644 --- a/Dockerfile.minimal +++ b/Dockerfile.minimal @@ -17,27 +17,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # Set working directory WORKDIR /app -# Create minimal requirements inline to ensure we use Pydantic v1 -# which doesn't require Rust compilation -RUN cat > requirements.txt << 'EOF' -# Minimal requirements - no compilation needed -# Using Pydantic v1 which is pure Python (no Rust required) -fastapi==0.95.2 -pydantic==1.10.9 -uvicorn==0.22.0 -httpx==0.24.1 -icalendar==5.0.7 -jinja2==3.1.2 -apscheduler==3.10.1 -pytz==2023.3 -python-multipart==0.0.6 -starlette==0.27.0 -typing-extensions==4.6.3 -python-dateutil==2.8.2 -EOF - -# Install Python dependencies - all pure Python or pre-built wheels -RUN pip install --no-cache-dir -r requirements.txt +# Install Python dependencies directly +# Using Pydantic v1 which doesn't require Rust compilation +RUN pip install --no-cache-dir \ + fastapi==0.95.2 \ + pydantic==1.10.9 \ + uvicorn==0.22.0 \ + httpx==0.24.1 \ + icalendar==5.0.7 \ + jinja2==3.1.2 \ + apscheduler==3.10.1 \ + pytz==2023.3 \ + python-multipart==0.0.6 \ + starlette==0.27.0 \ + typing-extensions==4.6.3 \ + python-dateutil==2.8.2 # Copy application files COPY main.py . diff --git a/Dockerfile.pizero b/Dockerfile.pizero new file mode 100644 index 0000000..92bece9 --- /dev/null +++ b/Dockerfile.pizero @@ -0,0 +1,46 @@ +# Optimized Dockerfile for Raspberry Pi Zero (ARMv6) +# Minimal memory footprint and no compilation required +FROM python:3.11-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + TZ=Europe/Berlin \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 + +# Install only essential runtime dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + tzdata \ + && rm -rf /var/lib/apt/lists/* \ + && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \ + && echo $TZ > /etc/timezone + +# Set working directory +WORKDIR /app + +# Install Python dependencies one by one for better memory management on Pi Zero +# Using older stable versions that are known to work on ARMv6 +RUN pip install --no-cache-dir fastapi==0.95.2 +RUN pip install --no-cache-dir pydantic==1.10.9 +RUN pip install --no-cache-dir uvicorn==0.22.0 +RUN pip install --no-cache-dir httpx==0.24.1 +RUN pip install --no-cache-dir icalendar==5.0.7 +RUN pip install --no-cache-dir jinja2==3.1.2 +RUN pip install --no-cache-dir apscheduler==3.10.1 +RUN pip install --no-cache-dir pytz==2023.3 +RUN pip install --no-cache-dir python-multipart==0.0.6 + +# Copy application files +COPY main.py . +COPY Vektor-Logo.svg ./ + +# Create static directory and copy logo +RUN mkdir -p static && \ + cp Vektor-Logo.svg static/logo.svg + +# Expose port +EXPOSE 8000 + +# Run with limited workers and basic asyncio loop for Pi Zero +CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1", "--loop", "asyncio"] \ No newline at end of file diff --git a/build-pi.sh b/build-pi.sh new file mode 100755 index 0000000..29b83f6 --- /dev/null +++ b/build-pi.sh @@ -0,0 +1,257 @@ +#!/bin/bash + +# Build script for Raspberry Pi Zero/ARM devices +# Optimized for low memory and no compilation + +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 + +# Configuration +IMAGE_NAME="turmli-calendar" +CONTAINER_NAME="turmli-calendar" + +# Print functions +print_error() { + echo -e "${RED}❌ $1${NC}" +} + +print_success() { + echo -e "${GREEN}✅ $1${NC}" +} + +print_info() { + echo -e "${BLUE}ℹ️ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +# Detect system +detect_system() { + ARCH=$(uname -m) + OS=$(cat /etc/os-release | grep "^ID=" | cut -d= -f2 | tr -d '"') + VERSION=$(cat /etc/os-release | grep "^VERSION_CODENAME=" | cut -d= -f2 | tr -d '"') + + print_info "System: ${OS} ${VERSION} on ${ARCH}" + + # Check if we're on a Raspberry Pi + if [ -f /proc/device-tree/model ]; then + MODEL=$(tr -d '\0' < /proc/device-tree/model) + print_info "Raspberry Pi detected: ${MODEL}" + fi + + # Check available memory + if [ -f /proc/meminfo ]; then + TOTAL_MEM=$(grep MemTotal /proc/meminfo | awk '{print $2}') + TOTAL_MEM_MB=$((TOTAL_MEM / 1024)) + print_info "Available RAM: ${TOTAL_MEM_MB}MB" + + if [ $TOTAL_MEM_MB -lt 512 ]; then + print_warning "Low memory detected! Build may be slow." + print_info "Tip: Consider adding swap space if build fails" + fi + fi +} + +# Check for container runtime +check_runtime() { + if command -v podman &> /dev/null; then + RUNTIME="podman" + print_success "Using Podman" + elif command -v docker &> /dev/null; then + RUNTIME="docker" + print_success "Using Docker" + else + print_error "No container runtime found!" + print_info "Install Podman (recommended) or Docker:" + echo " sudo apt-get update" + echo " sudo apt-get install podman" + exit 1 + fi +} + +# Build the container +build_container() { + print_info "Building container image..." + + # Choose the right Dockerfile + if [ -f "Dockerfile.pizero" ] && [[ "$ARCH" == "armv6l" ]]; then + DOCKERFILE="Dockerfile.pizero" + print_info "Using Pi Zero optimized Dockerfile" + elif [ -f "Dockerfile.minimal" ]; then + DOCKERFILE="Dockerfile.minimal" + print_info "Using minimal Dockerfile (no compilation)" + elif [ -f "Dockerfile" ]; then + DOCKERFILE="Dockerfile" + print_warning "Using standard Dockerfile (may require compilation)" + else + print_error "No Dockerfile found!" + exit 1 + fi + + # Build with memory limits for Pi Zero + if [[ "$ARCH" == "armv6l" ]] && [ $TOTAL_MEM_MB -lt 512 ]; then + print_warning "Building with memory constraints..." + ${RUNTIME} build \ + --memory-swap -1 \ + --file "${DOCKERFILE}" \ + --tag "${IMAGE_NAME}" \ + . || { + print_error "Build failed!" + print_info "Try these solutions:" + echo " 1. Add swap space:" + echo " sudo dd if=/dev/zero of=/swapfile bs=1M count=1024" + echo " sudo mkswap /swapfile" + echo " sudo swapon /swapfile" + echo " 2. Use pre-built image:" + echo " ${RUNTIME} pull docker.io/yourusername/turmli-calendar:armv6" + exit 1 + } + else + ${RUNTIME} build \ + --file "${DOCKERFILE}" \ + --tag "${IMAGE_NAME}" \ + . || { + print_error "Build failed!" + exit 1 + } + fi + + print_success "Container image built successfully!" +} + +# Run the container +run_container() { + print_info "Starting container..." + + # Check if container already exists + if ${RUNTIME} ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + print_info "Container already exists, removing it..." + ${RUNTIME} stop ${CONTAINER_NAME} 2>/dev/null || true + ${RUNTIME} rm ${CONTAINER_NAME} 2>/dev/null || true + fi + + # Create cache file if it doesn't exist + if [ ! -f "calendar_cache.json" ]; then + echo "{}" > calendar_cache.json + print_info "Created cache file" + fi + + # Run with appropriate resource limits for Pi + if [[ "$ARCH" == "armv6l" ]] && [ $TOTAL_MEM_MB -lt 512 ]; then + print_info "Running with memory constraints..." + ${RUNTIME} run -d \ + --name ${CONTAINER_NAME} \ + --memory 256m \ + --memory-swap -1 \ + -p 8000:8000 \ + -v $(pwd)/calendar_cache.json:/app/calendar_cache.json:Z \ + -e TZ=Europe/Berlin \ + --restart unless-stopped \ + ${IMAGE_NAME} + else + ${RUNTIME} run -d \ + --name ${CONTAINER_NAME} \ + -p 8000:8000 \ + -v $(pwd)/calendar_cache.json:/app/calendar_cache.json:Z \ + -e TZ=Europe/Berlin \ + --restart unless-stopped \ + ${IMAGE_NAME} + fi + + print_success "Container started!" + print_info "Access the calendar at: http://$(hostname -I | awk '{print $1}'):8000" + print_info "or http://localhost:8000" +} + +# Check container status +check_status() { + print_info "Checking container status..." + + if ${RUNTIME} ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -q ${CONTAINER_NAME}; then + ${RUNTIME} ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "NAMES|${CONTAINER_NAME}" + + # Test if the app is responding + print_info "Testing application..." + sleep 3 + if curl -s -f http://localhost:8000/api/events > /dev/null 2>&1; then + print_success "Application is running and healthy!" + else + print_warning "Application not responding yet, checking logs..." + ${RUNTIME} logs --tail 20 ${CONTAINER_NAME} + fi + else + print_warning "Container is not running" + fi +} + +# Show logs +show_logs() { + ${RUNTIME} logs -f ${CONTAINER_NAME} +} + +# Main script +case "${1:-build}" in + build) + detect_system + check_runtime + build_container + ;; + run) + detect_system + check_runtime + run_container + check_status + ;; + start) + detect_system + check_runtime + build_container + run_container + check_status + ;; + status) + check_runtime + check_status + ;; + logs) + check_runtime + show_logs + ;; + stop) + check_runtime + print_info "Stopping container..." + ${RUNTIME} stop ${CONTAINER_NAME} + print_success "Container stopped" + ;; + help) + echo "Raspberry Pi Build Script for Turmli Calendar" + echo "" + echo "Usage: $0 [COMMAND]" + echo "" + echo "Commands:" + echo " build - Build the container image (default)" + echo " run - Run the container" + echo " start - Build and run" + echo " status - Check container status" + echo " logs - Show container logs" + echo " stop - Stop the container" + echo " help - Show this help" + echo "" + echo "This script is optimized for Raspberry Pi Zero and other" + echo "low-resource ARM devices. It uses Pydantic v1 to avoid" + echo "Rust compilation requirements." + ;; + *) + echo "Unknown command: $1" + echo "Run '$0 help' for usage" + exit 1 + ;; +esac \ No newline at end of file diff --git a/requirements-pizero.txt b/requirements-pizero.txt new file mode 100644 index 0000000..e153a9c --- /dev/null +++ b/requirements-pizero.txt @@ -0,0 +1,25 @@ +# Requirements for Raspberry Pi Zero (ARMv6) +# All pure Python packages - NO compilation required +# Uses Pydantic v1 to avoid Rust dependency + +fastapi==0.95.2 +pydantic==1.10.9 +uvicorn==0.22.0 +httpx==0.24.1 +icalendar==5.0.7 +jinja2==3.1.2 +apscheduler==3.10.1 +pytz==2023.3 +python-multipart==0.0.6 + +# These will be installed as dependencies but listing for clarity: +# starlette==0.27.0 +# typing-extensions==4.6.3 +# python-dateutil==2.8.2 +# six==1.16.0 +# h11==0.14.0 +# click==8.1.3 +# anyio==3.7.0 +# sniffio==1.3.0 +# certifi +# idna \ No newline at end of file