Primera prueba de código de microservicios
This commit is contained in:
30
.gitea/workflows/build.yml
Normal file
30
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [k8s, kaniko]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Login a Nexus
|
||||
env:
|
||||
NEXUS_USER: ${{ secrets.NEXUS_USER }}
|
||||
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
|
||||
run: |
|
||||
echo "$NEXUS_PASSWORD" | docker login nexus.rancherk3.duckdns.org -u "$NEXUS_USER" --password-stdin
|
||||
|
||||
- name: Build & Push image with Kaniko
|
||||
image: gcr.io/kaniko-project/executor:latest
|
||||
command:
|
||||
- /kaniko/executor
|
||||
args:
|
||||
- --context=.
|
||||
- --dockerfile=./Dockerfile
|
||||
- --destination=nexus.rancherk3.duckdns.org/tfm/microserviciospython:latest
|
||||
8
.gitea/workflows/test.yml
Normal file
8
.gitea/workflows/test.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
name: test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
echo:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- run: echo "Gitea Actions funcionando!"
|
||||
72
Dockerfile
Normal file
72
Dockerfile
Normal file
@@ -0,0 +1,72 @@
|
||||
# =========================
|
||||
# Etapa de Build
|
||||
# =========================
|
||||
FROM python:3.12-slim AS build
|
||||
|
||||
# Instalar dependencias de build mínimas
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
python3-venv \
|
||||
pip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
# Crear un entorno virtual Python para build
|
||||
RUN python -m venv /opt/venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# Actualizar pip y herramientas de build
|
||||
RUN pip install --upgrade pip setuptools wheel build pipenv
|
||||
|
||||
# Copiar código y preparar build
|
||||
ADD src /app
|
||||
WORKDIR /app
|
||||
|
||||
# Instalar dependencias y construir la rueda
|
||||
RUN pipenv install --deploy --system || true
|
||||
RUN python -m build -w
|
||||
|
||||
# =========================
|
||||
# Etapa de Runtime
|
||||
# =========================
|
||||
FROM python:3.12-slim
|
||||
|
||||
ARG BUILD_DATE
|
||||
LABEL org.label-schema.maintainer="Diego Fernández Carvajal (diego.fdezcarvajal@emtmadrid.es)" \
|
||||
org.label-schema.build-dockerfile="11/12/2025" \
|
||||
org.label-schema.build=$BUILD_DATE
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Europe/Madrid
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Instalar dependencias mínimas de runtime
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3-venv \
|
||||
supervisor \
|
||||
tzdata \
|
||||
locales \
|
||||
&& ln -fs /usr/share/zoneinfo/$TZ /etc/localtime \
|
||||
&& echo $TZ > /etc/timezone \
|
||||
&& echo "es_ES.UTF-8 UTF-8" > /etc/locale.gen \
|
||||
&& echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen \
|
||||
&& locale-gen \
|
||||
&& apt-get clean && apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
# Copiar supervisord
|
||||
COPY /man/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
# Copiar ruedas desde build
|
||||
COPY --from=build /app/dist/*.whl /app/
|
||||
|
||||
# Crear entorno virtual para runtime
|
||||
RUN python -m venv /opt/venv
|
||||
|
||||
# Instalar ruedas en el entorno virtual
|
||||
RUN pip install --upgrade pip setuptools wheel
|
||||
RUN pip install /app/*.whl -t /app
|
||||
|
||||
CMD ["/usr/bin/supervisord","-c","/etc/supervisor/conf.d/supervisord.conf"]
|
||||
23
man/supervisord.conf
Normal file
23
man/supervisord.conf
Normal file
@@ -0,0 +1,23 @@
|
||||
[supervisord]
|
||||
logfile=/tmp/supervisord.log ; supervisord log file
|
||||
logfile_maxbytes=10MB ; maximum size of logfile before rotation
|
||||
logfile_backups=2 ; number of backed up logfiles
|
||||
loglevel=error ; info, debug, warn, trace
|
||||
pidfile=/var/run/supervisord.pid ; pidfile location
|
||||
nodaemon=true ; run supervisord as a daemon
|
||||
minfds=1024 ; number of startup file descriptors
|
||||
minprocs=200 ; number of process descriptors
|
||||
user=root ; default user
|
||||
|
||||
[unix_http_server]
|
||||
file = /tmp/supervisor.sock ; supervisor unix http server sock file
|
||||
|
||||
[program:microservicios]
|
||||
command=python3 flask.py
|
||||
directory=/app
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/fd/2
|
||||
stderr_logfile_maxbytes=0
|
||||
autostart=true
|
||||
autorestart=true
|
||||
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
85
src/flask.py
Normal file
85
src/flask.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from flask import Flask, jsonify, request
|
||||
import os
|
||||
import psycopg2
|
||||
import pandas as pd
|
||||
import xgboost as xgb
|
||||
import joblib
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Configuración de la base de datos
|
||||
DB_CONFIG = {
|
||||
'dbname': os.getenv('DB_NAME', 'postgres'),
|
||||
'user': os.getenv('DB_USER', 'postgres'),
|
||||
'password': os.getenv('DB_PASSWORD', 'tfmuocdfcarvajal'),
|
||||
'host': os.getenv('DB_HOST', '10.10.5.32'),
|
||||
'port': os.getenv('DB_PORT', '5432')
|
||||
}
|
||||
|
||||
# Ruta para entrenar o cargar el modelo
|
||||
MODEL_PATH = 'modelo_xgb.joblib'
|
||||
|
||||
def get_db_connection():
|
||||
conn = psycopg2.connect(**DB_CONFIG)
|
||||
return conn
|
||||
|
||||
def fetch_data():
|
||||
conn = get_db_connection()
|
||||
query = """
|
||||
SELECT h3, hour, dow, total_events, total_nulos, nulo_rate
|
||||
FROM demanda_h3_hour
|
||||
"""
|
||||
df = pd.read_sql(query, conn)
|
||||
conn.close()
|
||||
return df
|
||||
|
||||
def train_model(df):
|
||||
# Variables predictoras
|
||||
X = df[['hour', 'dow', 'total_events']]
|
||||
# Variable objetivo: nulo_rate > 0 -> 1, else 0
|
||||
y = (df['nulo_rate'] > 0).astype(int)
|
||||
|
||||
model = xgb.XGBClassifier(
|
||||
max_depth=4, n_estimators=100, learning_rate=0.1, use_label_encoder=False, eval_metric='logloss'
|
||||
)
|
||||
model.fit(X, y)
|
||||
joblib.dump(model, MODEL_PATH)
|
||||
return model
|
||||
|
||||
def load_model():
|
||||
try:
|
||||
model = joblib.load(MODEL_PATH)
|
||||
return model
|
||||
except:
|
||||
df = fetch_data()
|
||||
return train_model(df)
|
||||
|
||||
model = load_model()
|
||||
|
||||
@app.route('/predict', methods=['GET'])
|
||||
def predict():
|
||||
# Parámetros de la consulta
|
||||
hour = int(request.args.get('hour'))
|
||||
dow = int(request.args.get('dow'))
|
||||
total_events = int(request.args.get('total_events', 1)) # valor por defecto si no se pasa
|
||||
|
||||
X_pred = pd.DataFrame([[hour, dow, total_events]], columns=['hour', 'dow', 'total_events'])
|
||||
prob = model.predict_proba(X_pred)[0][1] # Probabilidad de nulo
|
||||
return jsonify({
|
||||
'hour': hour,
|
||||
'dow': dow,
|
||||
'total_events': total_events,
|
||||
'predicted_nulo_prob': float(prob)
|
||||
})
|
||||
|
||||
@app.route('/demand', methods=['GET'])
|
||||
def demand():
|
||||
df = fetch_data()
|
||||
X_pred = df[['hour', 'dow', 'total_events']]
|
||||
df['predicted_nulo_prob'] = model.predict_proba(X_pred)[:,1].astype(float)
|
||||
# Convertimos a JSON
|
||||
result = df.to_dict(orient='records')
|
||||
return jsonify(result)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
33
src/pyproject.toml
Normal file
33
src/pyproject.toml
Normal file
@@ -0,0 +1,33 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "microservicios"
|
||||
version = "1.0.1"
|
||||
requires-python = ">= 3.8"
|
||||
description = "Microservicios para Estimaciones"
|
||||
license={text = "EULA"}
|
||||
|
||||
authors = [
|
||||
{name = "Diego Fernández", email = "carvajal.diego@gmail.com"}
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
'flask',
|
||||
'psycopg2-binary',
|
||||
'pandas',
|
||||
'xgboost',
|
||||
'scikit-learn',
|
||||
'joblib'
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = true
|
||||
py-modules = ["flask"]
|
||||
|
||||
[tool.setuptools.packages]
|
||||
find = {}
|
||||
|
||||
[project.urls]
|
||||
Documentation='https://https://gitea.tfmuocdfcarvajal.duckdns.org/TFM/microservicios_python'
|
||||
Reference in New Issue
Block a user