From e7aea83bdbd3fb434559533fe0362e5fac5716b7 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 30 Jan 2026 11:36:16 +0100 Subject: [PATCH] =?UTF-8?q?Subida=20del=20docker=20a=20producci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 63 +++++++++++++--- .env.example | 8 +++ Dockerfile | 59 +++++++++++++++ docker-compose.yml | 22 ++++++ scripts/build-docker.sh | 140 ++++++++++++++++++++++++++++++++++++ setup.cfg | 2 +- src/signing_service/main.py | 2 +- 7 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 docker-compose.yml create mode 100755 scripts/build-docker.sh diff --git a/.dockerignore b/.dockerignore index 8eb47dd..5376c8f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,12 +1,59 @@ -.venv -__pycache__ +# ---------------------------- +# Python +# ---------------------------- +__pycache__/ *.pyc *.pyo *.pyd -.pytest_cache -.mypy_cache -.ruff_cache -.git +*.py[cod] +*.egg-info/ +.eggs/ +.pytest_cache/ +.mypy_cache/ + +# Virtual environments (local only) +.venv/ +venv/ +env/ + +# ---------------------------- +# Environment & secrets +# ---------------------------- +.env +.env.* +*.pem +*.key +*.crt +*.pfx +*.p12 + +# ---------------------------- +# Git +# ---------------------------- +.git/ .gitignore -tests -*.log + +# ---------------------------- +# Docker +# ---------------------------- +Dockerfile +docker-compose.yml + +# ---------------------------- +# Editor / OS junk +# ---------------------------- +.vscode/ +.idea/ +.DS_Store +Thumbs.db + +# ---------------------------- +# Docs & local files +# ---------------------------- +docs/ +*.md + +# ---------------------------- +# Tests & tooling (si no se usan en runtime) +# ---------------------------- +tests/ diff --git a/.env.example b/.env.example index ab5e117..66ccf16 100644 --- a/.env.example +++ b/.env.example @@ -3,11 +3,19 @@ # Copy this file to `.env` and fill the real values # ============================================================ +# -------------------- +# UVICORN +# -------------------- +UVICORN_RELOAD=false +UVICORN_WORKERS=2 + # -------------------- # Application # -------------------- APP_ENV=local # local | prod LOG_LEVEL=INFO +STATE_PATH = ./ +LOCAL_TZ = Europe/Madrid # -------------------- # Secret manager diff --git a/Dockerfile b/Dockerfile index e69de29..1ef7c9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -0,0 +1,59 @@ +# ---------- Base image ---------- +FROM python:3.12-slim AS base + +# Evita pyc, mejora logs +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# Dependencias del sistema necesarias para crypto / PDFs +RUN apt-get update && apt-get install -y \ + build-essential \ + libssl-dev \ + libffi-dev \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + + +# ---------- Dependencies ---------- +FROM base AS deps + +# Copiamos solo metadatos para aprovechar cache +COPY ./pyproject.toml ./ + +# Instalamos dependencias +RUN pip install --upgrade pip \ + && pip install . + + +# ---------- Runtime ---------- +FROM base AS runtime + +# Copiamos dependencias instaladas +COPY --from=deps /usr/local /usr/local + +# Copiamos código fuente +COPY src/ ./ + +# Puerto FastAPI +EXPOSE 8000 + +# Usuario no-root (buena práctica) +RUN useradd -m appuser +USER appuser + +# Comando de arranque +CMD ["sh", "-c", "\ + if [ \"$UVICORN_RELOAD\" = \"true\" ]; then \ + uvicorn signing_service.main:app --host 0.0.0.0 --port 8000 --reload; \ + else \ + uvicorn signing_service.main:app --host 0.0.0.0 --port 8000 --workers ${UVICORN_WORKERS:-1}; \ + fi"] + +# Healthcheck + +#RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* + +#HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ +# CMD curl -f http://127.0.0.1:8000/health || exit 1 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dedf816 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +services: + signing-service: + image: factuges-document-signing-service:latest + container_name: factuges-document-signing-service + restart: unless-stopped + env_file: + - .env + ports: + - "8000:8000" + networks: + - internal + - edge + volumes: + - ./volumes:/var/log + +networks: + edge: + name: edge + external: true + internal: + name: factuges_rodax_internal + external: true diff --git a/scripts/build-docker.sh b/scripts/build-docker.sh new file mode 100755 index 0000000..de7c75a --- /dev/null +++ b/scripts/build-docker.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_VERSION="0.0.3" + +# ===================================================== +# FACTUGES Document Signing Service Build Script +# ----------------------------------------------------- +# Build + Subida a producción de la imagen Docker +# ===================================================== +# Uso: +# ./build_docker.sh [--load] +# +# ===================================================== + +# --- Configuración base --- +COMPANY="" +LOAD=false + +# --- Validar que el primer argumento existe y no es un flag --- +if [[ $# -eq 0 || "$1" == --* ]]; then + echo "❌ ERROR: Falta el parámetro " + echo "Uso: ./build_docker.sh [--load]" + echo "Ejemplos:" + echo " ./build_docker.sh acme --load" + exit 1 +fi + +COMPANY="$1" + +SSH_USER="rodax" +SSH_HOST="vps-2.rodax-software.com" +SSH_PORT="49152" + +# Override por compañía específica +if [[ "$COMPANY" == "rodax" ]]; then + SSH_USER="rodax" + SSH_HOST="factuges.rodax-software.local" + SSH_PORT="22" +fi + +# --- Parseo de flags --- +shift # quitamos el , ahora solo quedan flags + +for arg in "$@"; do + case "$arg" in + --load) LOAD=true ;; + *) echo "⚠️ Argumento desconocido: $arg" ;; + esac +done + +# --- Paths base --- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(realpath "${SCRIPT_DIR}/..")" +OUT_DIR="${PROJECT_DIR}/build/" + +if [[ -z "$COMPANY" ]]; then + echo "❌ Error: debes indicar la compañía. Ejemplo: ./build_docker.sh acme [--load]" + exit 1 +fi + +if [[ $COMPANY =~ --.* ]]; then + echo "❌ Error: debes indicar la compañía. Ejemplo: ./build_docker.sh acme [--load]" + exit 1 +fi + +# --- Detectar nombre y versión de la API --- +IMAGE_NAME=$(node -p "require('${PROJECT_DIR}/setup.cfg').name" 2>/dev/null || echo "factuges-document-signing-service") +IMAGE_VERSION=$(node -p "require('${PROJECT_DIR}/setup.cfg').version" 2>/dev/null || echo "0.1.2") + +PORT="8000" # valor por defecto + +# --- 3. Etiquetas e información --- +DATE=$(date +'%Y%m%d-%H%M%S') +ISO_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +USER_NAME=$(whoami) +GIT_HASH=$(git -C "$PROJECT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown") +GIT_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") + +IMAGE_TAG_V="${IMAGE_NAME}:v${IMAGE_VERSION}" +IMAGE_TAG_LATEST="${IMAGE_NAME}:latest" + +mkdir -p "$OUT_DIR" +rm -rf "${OUT_DIR:?}/"* + +echo "" +echo "-------------------------------------------------------" +echo " FACTUGES Document Signing Service Build Script v${SCRIPT_VERSION}" +echo " Compañía: ${COMPANY}" +echo " Proyecto: ${IMAGE_NAME}" +echo " Versión: ${IMAGE_VERSION}" +echo " Puerto: ${PORT}" +echo " Etiquetas: ${IMAGE_TAG_V}, ${IMAGE_TAG_LATEST}" +echo " Dir. out: ${OUT_DIR}" +echo " Cargar: ${LOAD}" +echo "-------------------------------------------------------" + +cd "${PROJECT_DIR}" +echo "🐳 Construyendo imagen Docker..." + +docker build \ +-t "${IMAGE_TAG_V}" -t "${IMAGE_TAG_LATEST}" \ +--build-arg PORT="${PORT}" \ +-f "${PROJECT_DIR}/Dockerfile" "${PROJECT_DIR}" + +echo "✅ Imagen Docker construida correctamente" + + +TAR_FILE_V="${OUT_DIR}/${IMAGE_NAME}-v${IMAGE_VERSION}-${DATE}.tar" +echo "🐳 Guardando imagen Docker ${TAR_FILE_V} ..." +docker save "${IMAGE_TAG_V}" "${IMAGE_TAG_LATEST}" -o "${TAR_FILE_V}" + +echo "📦 Imagen guardada:" +echo " ${TAR_FILE_V}" + +if [[ "$LOAD" == true ]]; then + echo "📥 Cargando imagen en producción ${SSH_HOST}..." + scp -r -P "${SSH_PORT}" "${OUT_DIR}" "${SSH_USER}@${SSH_HOST}:/opt/factuges-document-signing-service/" + + RESULT=$(ssh -p "${SSH_PORT}" "${SSH_USER}@${SSH_HOST}" \ + "docker load -i /opt/factuges-document-signing-service/build/$(basename "${TAR_FILE_V}") && \ + docker tag ${IMAGE_TAG_V} ${IMAGE_TAG_LATEST} " 2>&1) + + echo "${RESULT}" + echo "✅ Imagen cargada en producción" +fi + + +# ===================================================== +# 3️⃣ Resumen +# ===================================================== +echo "-------------------------------------------------------" +echo "🎯 Resultado final para '${COMPANY}'" +echo " 🐳 v${IMAGE_VERSION} → ${OUT_DIR}" +echo "🧩 Script version: ${SCRIPT_VERSION} - FIN" +echo "-------------------------------------------------------" +echo "" +echo "" +echo "" + diff --git a/setup.cfg b/setup.cfg index b3ba5ae..48de64a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = "factuges-document-signing-service" -version = "0.1.1" +version = "0.1.2" description = "FastAPI service for signing PDF documents using external secret managers" author = Rodax Software author_email = info@rodax-software.com diff --git a/src/signing_service/main.py b/src/signing_service/main.py index baec930..1c877e0 100644 --- a/src/signing_service/main.py +++ b/src/signing_service/main.py @@ -19,7 +19,7 @@ local_tz = tz.gettz(settings.local_tz) state_path = Path(settings.state_path) # Logging -log_dir = state_path / "logs" +log_dir = state_path log_dir.mkdir(parents=True, exist_ok=True) logger = create_logger( name="factuges-document-signing-service",