]> git.giorgioravera.it Git - network-manager.git/commitdiff
migrated to alpine docker image
authorGiorgio Ravera <giorgio.ravera@gmail.com>
Wed, 11 Mar 2026 18:38:38 +0000 (19:38 +0100)
committerGiorgio Ravera <giorgio.ravera@gmail.com>
Wed, 11 Mar 2026 18:38:38 +0000 (19:38 +0100)
Dockerfile
backend/bootstrap.py [new file with mode: 0644]
backend/main.py [new file with mode: 0644]
backend/server.py [new file with mode: 0644]
entrypoint.py [deleted file]

index c46c04ca4c9a45913c25d545c8150e378a78c1ec..db0c2611d875bf1e8c47d05ba20fb32a21ed4b9a 100644 (file)
@@ -1,52 +1,45 @@
-# ---------- STAGE 1: BUILD ----------
-FROM python:3.12-slim AS builder
+# ---------- STAGE 1: Alpine Build ----------
+FROM python:3.12-alpine AS builder
 
 # Install build dependencies
-RUN apt-get update && \
-    apt-get install -y --no-install-recommends sqlite3 && \
-    apt-get clean && \
-    rm -rf /var/lib/apt/lists/*
+RUN apk add --no-cache build-base libffi-dev openssl-dev
 
+WORKDIR /app
+
+# Copy dependency list
 COPY requirements.txt .
-RUN pip install --prefix=/install -r requirements.txt
 
-WORKDIR /app
+# Build wheels to avoid building in final image
+RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
 
-# Copy backend, frontend, entrypoint
+# Copy full application
 COPY backend backend
 COPY frontend frontend
-COPY entrypoint.py entrypoint.py
 COPY log log
 COPY settings settings
-RUN chmod 755 entrypoint.py
 
-# ---------- STAGE 2: DISTROLESS ----------
-FROM gcr.io/distroless/base-debian13
+# ---------- STAGE 2: Alpine Runtime ----------
+FROM python:3.12-alpine
 
-# Copy Python runtime from builder
-COPY --from=builder /usr/local /usr/local
+ENV PYTHONDONTWRITEBYTECODE=1 \
+    PYTHONUNBUFFERED=1 \
+    LANG=C.UTF-8
 
-# Copy libs
-COPY --from=builder /lib/x86_64-linux-gnu/libsqlite3.so.0 /lib/x86_64-linux-gnu/
-COPY --from=builder /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/
-COPY --from=builder /lib/x86_64-linux-gnu/libbz2.so.1.0 /lib/x86_64-linux-gnu/
-COPY --from=builder /lib/x86_64-linux-gnu/liblzma.so.5 /lib/x86_64-linux-gnu/
-COPY --from=builder /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/
-
-# Copy installed Python packages
-COPY --from=builder /install /usr/local
+# librerie runtime
+RUN apk add --no-cache libffi openssl sqlite-libs
 
 WORKDIR /app
 
-# Copy application
-COPY --from=builder /app/backend backend
-COPY --from=builder /app/frontend frontend
-COPY --from=builder /app/entrypoint.py entrypoint.py
-COPY --from=builder /app/log log
-COPY --from=builder /app/settings settings
+# Copy application and deps
+COPY --from=builder /app /app
+COPY --from=builder /wheels /wheels
+
+# Install dependencies inside distroless environment
+RUN pip install --no-cache-dir --no-compile /wheels/* && \
+    rm -rf /wheels && \
+    find /usr/local/lib/python3.12/site-packages -regex '.*\(tests\|test\|docs\|examples\).*' -type d -prune -exec rm -rf {} + && \
+    rm -rf /root/.cache
 
-# Ensure Python sees the installed packages
-ENV PYTHONPATH="/usr/local/lib/python3.12/site-packages"
+RUN find /usr/local/lib/python3.12 -name '__pycache__' -type d -exec rm -rf {} +
 
-ENTRYPOINT ["/app/entrypoint.py"]
-CMD ["python3", "-u", "-m", "backend.main"]
+ENTRYPOINT ["python", "-u", "-m", "backend.main"]
diff --git a/backend/bootstrap.py b/backend/bootstrap.py
new file mode 100644 (file)
index 0000000..ca3261e
--- /dev/null
@@ -0,0 +1,114 @@
+# backend/bootstrap.py
+
+# Import standard modules
+import logging
+import os
+# Import backend modules
+from backend.db.db import init_db
+import backend.db.users
+import backend.db.hosts
+import backend.db.aliases
+# Import Settings & Logging
+from settings.settings import settings
+from log.log import setup_logging, get_logger
+
+# ------------------------------------------------------------------------------
+# Welcome log
+# ------------------------------------------------------------------------------
+def print_welcome(logger):
+    masked_secret = "****" if settings.SECRET_KEY else "undefined"
+    masked_admin_pwd = "****" if settings.ADMIN_PASSWORD else "undefined"
+    masked_admin_hash = "****" if settings.ADMIN_PASSWORD_HASH else "undefined"
+
+    logger.info(
+        "%s starting | app_version=%s",
+        settings.APP_NAME, settings.APP_VERSION
+    )
+    logger.info(
+        "App settings: frontend=%s | host=%s | port=%d | secret=%s",
+        settings.FRONTEND_DIR, settings.HTTP_HOST, settings.HTTP_PORT, masked_secret
+    )
+    logger.info(
+        "Database: file=%s | reset=%s",
+        settings.DB_FILE, settings.DB_RESET
+    )
+    logger.info(
+        "Log: level=%s, to_file=%s, file=%s",
+        settings.LOG_LEVEL, settings.LOG_TO_FILE, settings.LOG_FILE
+    )
+    logger.info(
+        "Users: admin=%s | password=%s | hash=%s | hash_file=%s",
+        settings.ADMIN_USER, masked_admin_pwd, masked_admin_hash, settings.ADMIN_PASSWORD_HASH_FILE
+    )
+    logger.info(
+        "DNS: host file=%s | alias file=%s | reverse file=%s",
+        settings.DNS_HOST_FILE, settings.DNS_ALIAS_FILE, settings.DNS_REVERSE_FILE
+    )
+    logger.info(
+        "DHCP: ipv4 host file=%s | ipv4 leases file=%s | ipv6 host file=%s | ipv6 leases file=%s",
+        settings.DHCP4_HOST_FILE, settings.DHCP4_LEASES_FILE, settings.DHCP6_HOST_FILE, settings.DHCP6_LEASES_FILE
+    )
+
+# ------------------------------------------------------------------------------
+# Shutdown log
+# ------------------------------------------------------------------------------
+def print_goodbye(logger):
+    logger.info(
+        "Application %s shutting down | app_version=%s",
+        settings.APP_NAME, settings.APP_VERSION
+    )
+
+# ================================
+# Create DB if needed
+# ================================
+def docker_create_db(logger):
+    # Reset database if requested
+    if settings.DB_RESET and os.path.exists(settings.DB_FILE):
+        logger.info("Removing existing database: %s", settings.DB_FILE)
+        os.remove(settings.DB_FILE)
+
+    # Skip creation if DB already exists
+    if os.path.exists(settings.DB_FILE):
+        logger.info("Database already exists. Nothing to do.")
+        return
+
+    logger.info("Creating database: %s", settings.DB_FILE)
+
+    # Ensure directory exists
+    os.makedirs(os.path.dirname(settings.DB_FILE) or ".", exist_ok=True)
+
+    # Initialize DB tables
+    init_db()
+
+# ------------------------------------------------------------------------------
+# Bootstrap: setup logging, print welcome, create DB, etc.
+# ------------------------------------------------------------------------------
+def bootstrap():
+
+    # Log Setup
+    setup_logging(level=settings.LOG_LEVEL, to_file=settings.LOG_TO_FILE, log_file=settings.LOG_FILE, log_access_file=settings.LOG_ACCESS_FILE)
+    logger = get_logger(__name__)
+
+    print_welcome(logger)
+
+    # Create or update database
+    docker_create_db(logger)
+
+    #os.makedirs(DATA_DIR, exist_ok=True)
+    #os.makedirs(BIND_DIR, exist_ok=True)
+    #os.makedirs(KEA_DIR, exist_ok=True)
+
+    #if not os.path.exists(DB_PATH):
+    #    conn = sqlite3.connect(DB_PATH)
+    #    # eventuale executescript(schema) qui
+    #    conn.close()
+
+    #named_conf = os.path.join(BIND_DIR, "named.conf")
+    #if not os.path.exists(named_conf):
+    #    with open(named_conf, "w") as f:
+    #        f.write("// generated by network-manager\n")
+
+    #kea_conf = os.path.join(KEA_DIR, "kea-dhcp4.conf")
+    #if not os.path.exists(kea_conf):
+    #    with open(kea_conf, "w") as f:
+    #        f.write("{\n  // generated by network-manager\n}\n")
diff --git a/backend/main.py b/backend/main.py
new file mode 100644 (file)
index 0000000..67d407a
--- /dev/null
@@ -0,0 +1,25 @@
+# backend/main.py
+
+# Import standard modules
+import os
+# Import backend modules
+from backend.bootstrap import bootstrap
+from backend.app import create_app
+from backend.server import run_server
+
+# ------------------------------------------------------------------------------
+# Main: entry point of the application
+# ------------------------------------------------------------------------------
+def main():
+
+    # 1) System Initialization (Settings, Logging, DB, etc.)
+    bootstrap()
+
+    # 2) Costruzione app FastAPI
+    app = create_app()
+
+    # 4) Uvicorn Start
+    run_server(app)
+
+if __name__ == "__main__":
+    main()
diff --git a/backend/server.py b/backend/server.py
new file mode 100644 (file)
index 0000000..3b73281
--- /dev/null
@@ -0,0 +1,39 @@
+# backend/server.py
+
+# import standard modules
+import uvicorn
+
+# Import Settings & Logging
+from settings.settings import settings
+from log.log import get_logger
+
+# Logger initialization
+logger = get_logger(__name__)
+
+# ------------------------------------------------------------------------------
+# Starting the server with Uvicorn
+# ------------------------------------------------------------------------------
+def run_server(app):
+
+    # Uvicorn config da settings with fallback
+    host=(settings.HTTP_HOST or "0.0.0.0")
+    port=int(settings.HTTP_PORT or 8000)
+    log_level=(settings.LOG_LEVEL or "info").lower()
+    workers = 1 # GRGR in prod valuta gunicorn+uvicorn workers
+    #reload = os.getenv("UVICORN_RELOAD", "false").lower() == "true"
+    reload = bool(getattr(settings, "DEV_RELOAD", False))
+
+    logger.info(f"Server running on http://{host}:{port} (reload={reload}, log_level={log_level})")
+
+    uvicorn.run(
+        app,
+        host=host,
+        port=port,
+        proxy_headers=True,
+        forwarded_allow_ips="*",
+        log_level=log_level,
+        #access_log=True,
+        log_config=None,
+        workers=(1 if reload else workers),
+        reload=reload,
+    )
diff --git a/entrypoint.py b/entrypoint.py
deleted file mode 100755 (executable)
index 4a99c65..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/local/bin/python3
-
-# Import standard modules
-import logging
-import os
-import sys
-import argparse
-# Import local modules
-from backend.db.db import init_db
-import backend.db.users
-import backend.db.hosts
-import backend.db.aliases
-# Import Settings
-from settings.settings import settings
-# Import Log
-from log.log import setup_logging, get_logger
-
-# ================================
-# Parse CLI arguments
-# ================================
-def parse_args():
-    parser = argparse.ArgumentParser(add_help=False)
-    parser.add_argument("--reset", action="store_true")
-    parser.add_argument("--domain")
-    parser.add_argument("--external-name")
-    parser.add_argument("cmd", nargs=argparse.REMAINDER)
-    return parser.parse_args()
-
-# ================================
-# Create DB if needed
-# ================================
-def docker_create_db(logger):
-    # Reset database if requested
-    if settings.DB_RESET and os.path.exists(settings.DB_FILE):
-        logger.info("Removing existing database: %s", settings.DB_FILE)
-        os.remove(settings.DB_FILE)
-
-    # Skip creation if DB already exists
-    if os.path.exists(settings.DB_FILE):
-        logger.info("Database already exists. Nothing to do.")
-        return
-
-    logger.info("Creating database: %s", settings.DB_FILE)
-
-    # Ensure directory exists
-    os.makedirs(os.path.dirname(settings.DB_FILE) or ".", exist_ok=True)
-
-    # Initialize DB tables
-    init_db()
-
-# ================================
-# Entry Point
-# ================================
-def main():
-    # Enable logging
-    setup_logging()
-    logger = get_logger("baseimg")
-
-    # Log startup docker image
-    logger.info("Starting docker image %s version %s", settings.BASEIMG_NAME, settings.BASEIMG_VERSION)
-
-    # Parse arguments
-    args = parse_args()
-
-    # Apply arguments into settings
-    if args.reset:
-        settings.DB_RESET = True
-    if args.domain:
-        settings.DOMAIN = args.domain
-    if args.external_name:
-        settings.EXTERNAL_NAME = args.EXTERNAL_NAME
-
-    # Create or update database
-    docker_create_db(logger)
-
-    # If no command provided -> error
-    if not args.cmd:
-        logger.error("No command provided. Exiting.")
-        sys.exit(1)
-
-    cmd = args.cmd[0]
-    rest = args.cmd[1:]
-
-    logger.info("Docker image initialization completed — executing: %s %s", cmd, " ".join(rest))
-
-    try:
-        os.execvp(cmd, [cmd, *rest])
-    except FileNotFoundError:
-        logger.critical("Command not found: %s", cmd)
-        sys.exit(1)
-
-if __name__ == "__main__":
-    main()