Deploy, Configuración, Mantenimiento e Integración con Superset
Ing. Marcelo Denis
Líder de Proyecto
mdenis@codelab.com.py
odelab S.A
24 de enero del 2025
1. Introducción
2. Arquitectura del Sistema
3. Estructura del Código
4. Proceso de Implementación
6. Mantenimiento
5. Configuración de Servidores
7. Integración con SuperSet
10. Resumen
9. Características Adicionales
Sistema empleado para el registro y monitoreo de pacientes diagnosticadas con cáncer de mama.
Arquitectura del Sistema
Descripción de la estructura del backend, frontend y la comunicación entre ambos componentes.
Estructura del Código
Localización de la definición de los componentes y la organización de los proyectos.
Proceso de Despliegue
Procedimientos detallados para la configuración y despliegue del sistema en un servidor.
Configuración de Servidores
Requerimientos mínimos de servidores en ambientes de test, demo y producción.
Características Adicionales
Reportes del sistema.
Dashboard. Configuración, integración y versionamiento.
Encriptación de datos.
Interoperabilidad.
Mantenimiento
Copias de seguridad de datos, gestión de cambios en BD, gestión de logs y auditoría.
Se basa en el patrón de arquitectura orientada a servicios web, donde se tiene:
Manual Técnico PY, pág. 3
Manual Técnico PY, pág. 4
Manual Técnico PY, pág. 4
Repositorio
https://gitlab.com/citycancerdigital/backend-service
Directorios principales:
models: Representación de las tablas de la base de datos.resources: Lógica de negocio.swagger: Documentación de la API.dao: Acceso a los datos.database: Scripts SQL iterativos.logs: Ficheros físicos diarios del log del servidor.translations: Traducciones.Archivos clave:
app.py: Arranque principal del backend.requirements.txt: Dependencias del sistema.Dockerfile: Definición para el despliegue y construcción de imágenes..gitlab-ci.yml: configuración de procesos automatizados de CI/CD.Manual Técnico PY, pág. 5
Repositorio
https://gitlab.com/citycancerdigital/frontend-service
Carpetas principales:
src: Código fuente del frontend (Vue.js y Quasar).public: Recursos estáticos (imágenes e íconos)Archivos clave:
quasar.config.js: Configuración del framework.Dockerfile: Despliegue del frontend.index.html: Punto de entrada principal.package.json: Dependencias y scripts del proyecto..gitlab-ci.yml: configuración de procesos automatizados de CI/CD.Manual Técnico PY, pág. 5, 6
El servidor debe tener instalado mínimamente las siguientes herramientas:
Opcionalmente, para ambientes de desarrollo y testing:
Manual Técnico PY, pág. 7
Una vez seleccionado el directorio de trabajo en el servidor destino (test|demo|prod), se debe seguir los siguientes pasos para arrancar el sistema:
1. Generar Nombre de Dominio Para el Acceso al Sistema.
EJemplo: http://ccan_py_test.citycancerchallenge.org
Manual Técnico PY, pág. 7
2. Generar Imágenes Docker de Backend y Frontend.
1. Backend
> git clone https://gitlab.com/citycancerdigital/backend-service.git
> pip install - requirements.txt
> docker build -t registry.gitlab.com/citycancerdigital/backend-service:<RELEASE|BRANCH>
Si se tiene configurado CI/CD en el gitlab, este proceso es automático.
En cambio, si no lo tenemos configurado, debemos realizar el proceso de generación de imágenes de forma manual, para ello, se debe clonar los repositorios, restaurar las dependencias y generar las imágenes docker localmente.
2. Frontend
> git clone https://gitlab.com/citycancerdigital/frontend-service.git
> npm install
> docker build -t registry.gitlab.com/citycancerdigital/frontend-service:<RELEASE|BRANCH>
Manual Técnico PY, pág. 8
3. Disponibilización de las Imágenes Docker en el Servidor
Si no se tiene configurado CI/CD en gitlab, se debe realizar lo siguiente para disponibilizar las imágenes docker en el servidor de forma manual.
GitLab provee un mecanismo para automatizar la creación de las imágenes docker empleando el repositorio. Mas detalle de como utilizarlo en:
https://docs.gitlab.com/ee/ci/
Tipos de Generación de Imágenes
Manual Técnico PY, pág. 8
Backend
> docker save -o ccan_backend.tar registry.gitlab.com/citycancerdigital/backend-service:<RELEASE|BRANCH>
> scp ccan_backend.tar <USER>@<IP_O_DNS_SERVER>:/home/docker_images
> ssh <USER>@<IP_O_DNS_SERVER>
> sudo docker load -i /home/docker_images/ccan_backend.tar
Frontend
> docker save -o ccan_backend.tar registry.gitlab.com/citycancerdigital/frontend-service:<RELEASE|BRANCH>
> scp ccan_frontend.tar <USER>@<IP_O_DNS_SERVER>:/home/docker_images
> ssh <USER>@<IP_O_DNS_SERVER>
> sudo docker load -i /home/docker_images/ccan_frontend.tar
4. Generación Externa de las Imágenes Docker
Manual Técnico PY, pág. 9
services:
db-test:
image: postgres:14
environment:
POSTGRES_PASSWORD: <pass>
POSTGRES_DB: <database_name>
TZ: America/Asuncion
ports:
- "5433:5432"
volumes:
- ./postgres:/var/lib/postgresql/data
- ./database:/docker-entrypoint-initdb.d/:ro
networks:
- db-network
networks:
db-network:
driver: bridge
5. Configurar docker compose
En el fichero docker-compose.yml...
Manual Técnico PY, pág. 9
services:
backend-py-test:
image: registry.gitlab.com/citycancerdigital/backend-service:develop
environment:
DB_USERNAME: 'postgres'
DB_PASSWORD: '<pass>'
DB_HOST: '10.1.0.4'
DB_DATABASE: '<database_name>'
DB_SCHEMA: 'public'
DB_PORT: '5433'
DB_INTEROPERABILIDAD: "postgresql://<user>:<pass>@192.168.1.22:5432/MSPSBS_REDINET"
TZ: America/Asuncion
SUPERSET_URL: 'http://ccan_dashboards_test.citycancerchallenge.org'
SUPERSET_DASHBOARD_ID: 'bbb054f0-85ae-48d9-a179-0e6b625a7798'
# Let's not expose our backend port outside the docker network
ports:
- "5001:5000"
networks:
- backend-py-test
- frontend-py-test
volumes:
- readiness_files:/usr/src/app/static/uploaded_files
- ./logs:/usr/src/app/logs
5. Configurar docker compose
Manual Técnico PY, pág. 10
frontend-py-test:
image: registry.gitlab.com/citycancerdigital/frontend-service:develop
environment:
TZ: America/Asuncion
BACKEND_URL: http://ccan_py_test.citycancerchallenge.org/api
# Port 9000 mapped to the web server to avoid conflicts with the host's
# nginx with the certbot configuration
ports:
- "9001:80"
networks:
- frontend-py-test
depends_on:
- backend-py-test
volumes:
- "./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro"
- "./bin/40-process-envars.sh:/docker-entrypoint.d/40-process-envars.sh:ro"
5. Configurar docker compose
Manual Técnico PY, pág. 10
en el fichero ./nginx/default.conf...
server {
listen 80;
server_name localhost;
client_body_buffer_size 50M;
client_max_body_size 50M;
large_client_header_buffers 4 16k;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /api {
proxy_pass http://backend:5000/api;
}
}
5. Configurar docker compose
Manual Técnico PY, pág. 11
en el fichero ./bin/40-process-envars.sh...
#!/bin/bash
if [ "$STATIC_BACKEND_URL" != "" ]; then
sed -i 's|STATIC_BACKEND_URL:"[a-zA-Z0-9\/:-_.&?]\+",|STATIC_BACKEND_URL:"'"$STATIC_BACKEND_URL"'",|g' /usr/share/nginx/html/assets/auth.*.js
fi
if [ "$BACKEND_URL" != "" ]; then
sed -i 's|BACKEND_URL:"[a-zA-Z0-9\/:-_.&?]\+",|BACKEND_URL:"'"$BACKEND_URL"'",|g' /usr/share/nginx/html/assets/auth.*.js
fi
5. Configurar docker compose
Manual Técnico PY, pág. 11
networks:
frontend-py-test:
backend-py-test:
volumes:
readiness_files:
driver: local
driver_opts:
type: none
o: bind
device: /home/readiness/ccan-deploy-tests/ccan/py/uploaded_files
5. Configurar docker compose
Manual Técnico PY, pág. 10
en el fichero /etc/nginx/sites-available/ccan_py_test.citycancerchallenge.org
server {
listen 80;
listen [::]:80;
server_name ccan_py_test.citycancerchallenge.org;
location / {
proxy_pass http://localhost:9001;
}
}
Obs: Se debe crear el enlace simbólico al directorio /etc/nginx/sites-enabled para habilitar el sitio web.
6. Configurar NGINX del servidor
Manual Técnico PY, pág. 11, 12
Una vez verificadas las configuraciones deseadas se debe iniciar los contenedores con el siguiente comando:
> docker compose up -d
El servicio podrá ser accedido a través de: http://<IP_SERVIDOR|NOMBRE_DOMINIO>:80
Los contenedores se pueden detener con el siguiente comando:
> docker compose down
7. Administración de Contenedores
Manual Técnico PY, pág. 12
| Test|Demo | Producción | |
|---|---|---|
| Memoria | 4GB | 8GB |
| CPU | Compartido 2 núcleos | Dedicado 4 nucleos |
| Almacenamiento | 250GB | 1TB |
| Sistema Operativo | Linux compatible con Docker | Linux compatible con Docker |
| Dominios | 2 dominios (aplicación y superset) | 2 dominios (aplicación y superset) |
| Certificado SSL | 2 | 2 |
Manual Técnico PY, pág. 4, 5
Se recomienda realizar un backup diario de los datos, esto incluye:
Se puede implementar mediante un script que se ejecute periódicamente vía crontab.
> sudo su
> crontab -e
> 0 3 * * * /home/readiness/ccan-deploy-tests/ccan/py/script_backup_db_files.sh
Manual Técnico PY, pág. 22
#!/bin/bash
# Variables del fichero destino
DATE=$(date +%Y%m%d)
BACKUP_PATH="/home/readiness/ccan-deploy-tests/ccan/py/backups/${DATE}/"
# Ejecutar creación del directorio
mkdir -p $BACKUP_PATH
# Variables para el backup de BD
CONTAINER_NAME="database-db-test-1"
DB_USER="postgres"
DB_NAME="ccan_py_test"
BACKUP_FILE="${BACKUP_PATH}backup_${DB_NAME}_${DATE}.sql"
# Variables para la compresión del directorio
DIRECTORY_PATH="/home/readiness/ccan-deploy-tests/ccan/py/uploaded_files"
DIRECTORY_BACKUP_FILE="${BACKUP_PATH}directory_backup_py_test_${DATE}.tar.gz"
# Ejecutar copia de seguridad
docker exec $CONTAINER_NAME pg_dump -U $DB_USER -d $DB_NAME > $BACKUP_FILE
# Verificar el Éxito de la copia de seguridad
if [ $? -eq 0 ]; then
echo "Copia de seguridad de BD exitosa. Archivo: $BACKUP_FILE"
else
echo "¡Error! La copia de seguridad de BD ha fallado."
fi
# Comprimir el directorio
tar -czvf $DIRECTORY_BACKUP_FILE $DIRECTORY_PATH
# Verificar el éxito de la compresión del directorio
if [ $? -eq 0 ]; then
echo "Compresión del directorio exitosa. Archivo: $DIRECTORY_BACKUP_FILE"
else
echo "¡Error! La compresión del directorio ha fallado."
fi
Manual Técnico PY, pág. 23
Para continuar con el desarrollo de la aplicación, se debe poder:
1. Restaurar la BD.
2. Actualizar la BD de producción con cambios incrementales provenientes de desarrollos.
Manual Técnico PY, pág. 21, 22
El proyecto del backend cuenta con un directorio denominado database, el cual contiene los siguientes subdirectorios:
Manual Técnico PY, pág. 21, 22
Se pueden visualizar a través de los logs de cada contenedor mediante:
Frontend
> docker logs -f <NOMBRE_CONTENEDOR_FRONTEND>
Backend
> docker logs -f <NOMBRE_CONTENEDOR_BACKEND>
El backend almacena los logs en un fichero creado diariamente en el directorio "logs". El mismo se halla en la ubicación donde se encuentra el fichero docker-compose.yml utilizado para levantar el backend y frontend.
Manual Técnico PY, pág. 23
Para la auditoría se ha creado el esquema audit.
Cada tabla de cada esquema, tiene su correspondiente tabla de auditoría en el esquema audit siguiendo la nomenclatura:
<nombre del esquema>_<nombre de la tabla>
Manual Técnico PY, pág. 18
Estructura de las Tablas de Auditoría
Las tablas de auditoría registran información clave sobre cada evento realizado en la base de datos, incluyendo:
Este esquema permite un control detallado de los cambios en la base de datos, facilitando auditorías y análisis de seguridad.
Manual Técnico PY, pág. 18
Mecanismo de Auditoría
Los registros de auditoría se insertan automáticamente mediante un trigger AFTER en cada operación Insert, Delete o Update.
Los nombres de los triggers siguen la nomenclatura audit_<nombre_de_la_tabla>_all
Cada trigger ejecuta una función cuyo nombre sigue la nomeclatura audit_<nombre_de_la_tabla>
Este sistema garantiza un seguimiento detallado de los cambios en la base de datos.
Manual Técnico PY, pág. 19
La función de trigger se construye de la siguiente manera:
CREATE FUNCTION audit_patient() RETURNS trigger SECURITY DEFINER LANGUAGE plpgsql AS $$ DECLARE __DELETE char(6); __INSERT char(6); __UPDATE char(6); BEGIN __DELETE := 'DELETE'; __INSERT := 'INSERT'; __UPDATE := 'UPDATE'; if ( TG_OP = __DELETE ) then INSERT INTO audit.public_patient SELECT substr(TG_OP ,1,1),session_user,current_timestamp,inet_client_addr(),OLD.*; return OLD; else INSERT INTO audit.public_patient SELECT substr(TG_OP ,1,1),session_user,current_timestamp,inet_client_addr(),NEW.*; return NEW; end if; END $$;
Manual Técnico PY, pág. 19
Entonces la sentencia de create del trigger para public.patient es:
CREATE TRIGGER audit_patient_all AFTER INSERT OR UPDATE OR DELETE ON patient FOR EACH ROW EXECUTE PROCEDURE audit_patient();
Manual Técnico PY, pág. 20
services:
superset:
build:
context: ./superset
dockerfile: dockerfile
environment:
- ADMIN_USERNAME=<user>
- ADMIN_EMAIL=admin@superset.com
- ADMIN_PASSWORD=<pass>
ports:
- '8088:8088'
volumes:
- superset:/app/superset_home
volumes:
superset:
external: false
En el fichero docker-compose.yml...
Manual Técnico PY, pág. 12
En el directorio superset se encuentran los siguientes ficheros:
Manual Técnico PY, pág. 12
En el fichero dockerfile...
FROM apache/superset:3.1.0
USER root
ENV ADMIN_USERNAME $ADMIN_USERNAME
ENV ADMIN_EMAIL $ADMIN_EMAIL
ENV ADMIN_PASSWORD $ADMIN_PASSWORD
COPY ./superset-init.sh /superset-init.sh
RUN chmod +x /superset-init.sh
COPY superset_config.py /app/
ENV SUPERSET_CONFIG_PATH /app/superset_config.py
USER superset
ENTRYPOINT [ "/superset-init.sh" ]
Manual Técnico PY, pág. 12
En el fichero superset-init.sh...
#!/bin/bash
# create Admin user, you can read these values from env or anywhere else possible
superset fab create-admin --username "$ADMIN_USERNAME" --firstname Superset --lastname Admin --email "$ADMIN_EMAIL" --password "$ADMIN_PASSWORD"
# Upgrading Superset metastore
superset db upgrade
# Load examples
superset load_examples
# setup roles and permissions
superset superset init
# Starting server
/bin/sh -c /usr/bin/run-server.sh
Manual Técnico PY, pág. 12
En el fichero superset_config.py...
SESSION_COOKIE_SAMESITE = None
ENABLE_PROXY_FIX = True
PUBLIC_ROLE_LIKE_GAMMA = True
FEATURE_FLAGS = {
"EMBEDDED_SUPERSET": True,
"HORIZONTAL_FILTER_BAR": True,
"SSH_TUNNELING": True,
"ENABLE_TEMPLATE_PROCESSING": True
}
CORS_OPTIONS = {
'supports_credentials': True,
'allow_headers': ['*'],
'resources':['*'],
'origins': ['*']
}
SECRET_KEY='q8FfRww3uCBD/XI/qdAw3JwNJfkLsK1CqXTNEJx3fRYj3b+Nz6TfKIJa'
CSRF_ENABLED = False
WTF_CSRF_ENABLED = False
TALISMAN_ENABLED = False
LANGUAGES = {
"en": {"flag": "us", "name": "English"},
"es": {"flag": "es", "name": "Spanish"},
}
Manual Técnico PY, pág. 12
# Variables de entorno desde el backend (app.py)
SUPERSET_URL: http://ccan_dashboards_test.citycancerchallenge.org
SUPERSET_USERNAME: <user>
SUPERSET_PASSWORD: <pass>
SUPERSET_DASHBOARD_ID: bbb054f0-85ae-48d9-a179-0e6b625a7798
Manual Técnico PY, pág. 12
Los reportes en PDF son diseñados en JasperReports.
Las fuentes de los reportes se encuentran en el directorio "reports" dentro de las fuentes del backend.
Los reportes son generados por el proyecto del backend en tiempo de ejecución y son invocados mediante servicios. No posees un servidor de reportes.
Nuevos reportes deberán ser agregados en esa ubicación.
Manual Técnico PY, pág. 25
El sistema también permite generar reportes de formato Excel o CSV.
Las mismas son generadas a partir de consultas SQL, procesadas y transformadas utilizando la biblioteca pandas, antes de ser exportadas en los formatos mencionados.
Manual Técnico PY, pág. 25
Los dashboard deberán ser diseñados en la herramienta de SuperSet.
Manual Técnico PY, pág. 46
Cada dashboard genera un código identificador que deberá ser proveído al backend de C/CAN Soft mediante invocación de servicios estos datos son enviados al frontend para renderizar el dashboad.
Manual Técnico PY, pág. 46
Se mantiene la encriptación de los siguientes campos de la tabla public.patient:
Para el efecto se posee de 2 funciones en BD:
encrypt_data(data text, key text): Realiza la encriptación del dato proveído utilizando un clave.
decrypt_data(data text, key text): Realiza el proceso inverso de la función de encriptación.
La clave de encriptación se encuentra el archivo app.py del backend como variable de entorno.
Manual Técnico PY, pág. 20
La encriptación se realiza mediante una función en BD que se compone de 2 partes:
Cifra el contenido de data utilizando una clave simétrica key mediante PGP (Pretty Good Privacy).
Esta función forma parte de pgcrypto, una extensión de PostgreSQL para criptografía.
Convierte el resultado en un formato legible utilizando caracteres de escape en lugar de binarios.
Esto facilita el almacenamiento y manipulación en bases de datos y consultas SQL.
Manual Técnico PY, pág. 20
El sistema realiza un recolección de datos de forma diaria de diferentes fuentes:
Para el efecto se dispone de la conexión a una BD remota la cual es actualizada constantemente.
IP: 192.168.1.22
Puerto: 5432
Nombre de la BD: MSPSBS_REDINET
User: <user>
Pass: <pass>
Manual Técnico PY, pág. 13
Las tablas proveídas por la BD MSPSBS_REDINET son:
Manual Técnico PY, pág. 13
[redinet@readiness ~]$ crontab -l
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
0 1 * * * curl -X GET --header 'Accept: application/json' http://localhost:5000/api/interoperabilidad/tasks/task_manager
Manual Técnico PY, pág. 18
Manuales Técnicos
Instancia de PY
https://docs.google.com/document/d/1XFC66etrfU8PMXHAu1bJo3LpC7qZ7uKd8jrLZ6KLweQ/edit?usp=sharing
Instancia de CO
https://docs.google.com/document/d/1aMgSlXBy2mpke8ZpB9Nzm91Q1hJM9H5WcqIlBMVQOTA/edit?usp=sharing
Instancia de GH
Manuales de Usuario
Instancia de PY
https://docs.google.com/document/d/16fyX-8a2sqlmmhhSe6erDqnzErlzgn5aUNtv6Px8b6o/edit?usp=sharing
Instancia de CO
https://docs.google.com/document/d/1vq9e4Qtvddy0SJAC4hB7Faqi9ZP-vlhjP7cBBHFPVBU/edit?usp=sharing
Instancia de GH
Diccionario de Datos
Instancia de [PY|CO|GH]
https://docs.google.com/spreadsheets/d/1Saehbnj3McbKodFafXfdzBjksmp2gjrRgoN3TFdW-qE/edit?usp=sharing
Inventario
Instancia de [PY|CO|GH]
https://drive.google.com/drive/folders/1i2G5D5AE-4PGe_JOtLqEhFTRVbJcEzan?usp=sharing