#!/bin/bash ################################################################################ # Script LITE de instalación de VOLO con Docker # Para sistemas con Docker, Ollama, Kiwix y ZIM ya instalados # Autor: Script generado para instalación simplificada # Fecha: Noviembre 2025 ################################################################################ set -e # Colores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Puertos Volo (solo verificamos estos) VOLO_UI_PORT=3002 VOLO_API_PORT=1255 # Puertos de servicios existentes OPENWEBUI_PORT=8380 OLLAMA_PORT=11434 KIWIX_PORT=8888 # Directorios INSTALL_DIR="/opt/volo" ZIM_FILE_PATH="" echo -e "${BLUE}╔═══════════════════════════════════════╗${NC}" echo -e "${BLUE}║ Instalador LITE de VOLO con Docker ║${NC}" echo -e "${BLUE}║ Usa infraestructura existente ║${NC}" echo -e "${BLUE}╚═══════════════════════════════════════╝${NC}" echo "" ################################################################################ # Función: Verificar si el script se ejecuta como root ################################################################################ check_root() { if [ "$EUID" -ne 0 ]; then echo -e "${RED}[ERROR]${NC} Este script debe ejecutarse como root o con sudo" exit 1 fi } ################################################################################ # Función: Verificar disponibilidad de puerto ################################################################################ check_port() { local port=$1 local service=$2 if ss -tuln | grep -q ":$port "; then echo -e "${RED}[✗]${NC} Puerto $port ($service) está en uso" ss -tulnp | grep ":$port " return 1 else echo -e "${GREEN}[✓]${NC} Puerto $port ($service) disponible" return 0 fi } ################################################################################ # Función: Verificar puertos de Volo ################################################################################ check_volo_ports() { echo -e "\n${BLUE}[INFO]${NC} Verificando puertos de Volo...\n" local all_available=true check_port $VOLO_UI_PORT "Volo UI" || all_available=false check_port $VOLO_API_PORT "Volo API" || all_available=false if [ "$all_available" = false ]; then echo -e "\n${RED}[ERROR]${NC} Puertos de Volo en uso. ¿Continuar de todos modos? (s/N)" read -r response if [[ ! "$response" =~ ^[Ss]$ ]]; then exit 1 fi else echo -e "\n${GREEN}[✓]${NC} Puertos de Volo disponibles\n" fi } ################################################################################ # Función: Verificar servicios existentes ################################################################################ check_existing_services() { echo -e "${BLUE}[INFO]${NC} Verificando servicios existentes...\n" # Verificar Docker if ! command -v docker &> /dev/null; then echo -e "${RED}[ERROR]${NC} Docker no está instalado" exit 1 fi echo -e "${GREEN}[✓]${NC} Docker instalado: $(docker --version)" # Verificar Docker Compose if ! docker compose version &> /dev/null; then echo -e "${RED}[ERROR]${NC} Docker Compose no está disponible" exit 1 fi echo -e "${GREEN}[✓]${NC} Docker Compose instalado: $(docker compose version)" # Verificar Ollama if ss -tuln | grep -q ":$OLLAMA_PORT "; then echo -e "${GREEN}[✓]${NC} Ollama detectado en puerto $OLLAMA_PORT" else echo -e "${YELLOW}[WARN]${NC} Ollama no detectado en puerto $OLLAMA_PORT" echo -e "${YELLOW} ${NC}Volo necesita Ollama para funcionar" fi # Verificar Kiwix if ss -tuln | grep -q ":$KIWIX_PORT "; then echo -e "${GREEN}[✓]${NC} Kiwix detectado en puerto $KIWIX_PORT" else echo -e "${YELLOW}[WARN]${NC} Kiwix no detectado en puerto $KIWIX_PORT" echo -e "${YELLOW} ${NC}Volo puede funcionar con Kiwix integrado o externo" fi # Verificar OpenWebUI if ss -tuln | grep -q ":$OPENWEBUI_PORT "; then echo -e "${GREEN}[✓]${NC} OpenWebUI detectado en puerto $OPENWEBUI_PORT" fi echo "" } ################################################################################ # Función: Solicitar ruta de archivos ZIM ################################################################################ ask_zim_path() { echo -e "${BLUE}[INFO]${NC} Especifica la ruta COMPLETA al archivo ZIM de Wikipedia" echo -e "${YELLOW}Ejemplo:${NC} /mnt/data/zim/wikipedia_en_all_nopic_2024-06.zim" read -e -p "Ruta al archivo .zim: " ZIM_FILE_PATH if [ -z "$ZIM_FILE_PATH" ]; then echo -e "${RED}[ERROR]${NC} Debes especificar la ruta al archivo ZIM" exit 1 fi if [ ! -f "$ZIM_FILE_PATH" ]; then echo -e "${YELLOW}[WARN]${NC} El archivo no existe: $ZIM_FILE_PATH" echo -e "${YELLOW} ${NC}¿Continuar de todos modos? (s/N)" read -r response if [[ ! "$response" =~ ^[Ss]$ ]]; then exit 1 fi else echo -e "${GREEN}[✓]${NC} Archivo ZIM encontrado: $ZIM_FILE_PATH" fi } ################################################################################ # Función: Preguntar modo de Kiwix ################################################################################ ask_kiwix_mode() { echo -e "\n${BLUE}[INFO]${NC} ¿Cómo deseas usar Kiwix?" echo -e " 1) Usar Kiwix externo existente (puerto $KIWIX_PORT)" echo -e " 2) Integrar Kiwix dentro del contenedor de Volo" read -p "Opción [1-2]: " kiwix_choice if [ "$kiwix_choice" = "1" ]; then USE_EXTERNAL_KIWIX=true echo -e "${GREEN}[✓]${NC} Usando Kiwix externo en http://localhost:$KIWIX_PORT" else USE_EXTERNAL_KIWIX=false echo -e "${GREEN}[✓]${NC} Kiwix se integrará en el contenedor de Volo" fi } ################################################################################ # Función: Crear estructura de directorios ################################################################################ create_directories() { echo -e "\n${BLUE}[INFO]${NC} Creando directorios..." mkdir -p "$INSTALL_DIR" echo -e "${GREEN}[✓]${NC} Directorios creados" } ################################################################################ # Función: Crear Dockerfile optimizado ################################################################################ create_dockerfile() { echo -e "${BLUE}[INFO]${NC} Creando Dockerfile..." if [ "$USE_EXTERNAL_KIWIX" = true ]; then # Dockerfile sin Kiwix (usa el externo) cat > "$INSTALL_DIR/Dockerfile" << 'EOF' FROM python:3.11-slim-bookworm # Instalar dependencias mínimas RUN apt-get update && apt-get install -y \ git \ curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Clonar Volo RUN git clone https://github.com/AdyTech99/volo.git . # Instalar dependencias Python RUN pip install --no-cache-dir -r requirements.txt # Crear directorios RUN mkdir -p /app/data # Exponer puertos EXPOSE 3002 1255 COPY entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh ENTRYPOINT ["/app/entrypoint.sh"] EOF else # Dockerfile con Kiwix integrado cat > "$INSTALL_DIR/Dockerfile" << 'EOF' FROM python:3.11-slim-bookworm # Instalar dependencias RUN apt-get update && apt-get install -y \ git \ wget \ curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Clonar Volo RUN git clone https://github.com/AdyTech99/volo.git . # Instalar dependencias Python RUN pip install --no-cache-dir -r requirements.txt # Descargar Kiwix tools RUN mkdir -p /app/kiwix_tools && \ wget https://download.kiwix.org/release/kiwix-tools/kiwix-tools_linux-x86_64-3.7.0.tar.gz && \ tar -xzf kiwix-tools_linux-x86_64-3.7.0.tar.gz -C /app/kiwix_tools --strip-components=1 && \ rm kiwix-tools_linux-x86_64-3.7.0.tar.gz && \ chmod +x /app/kiwix_tools/* # Crear directorios RUN mkdir -p /app/data # Exponer puertos EXPOSE 3002 1255 821 COPY entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh ENTRYPOINT ["/app/entrypoint.sh"] EOF fi echo -e "${GREEN}[✓]${NC} Dockerfile creado" } ################################################################################ # Función: Crear entrypoint ################################################################################ create_entrypoint() { echo -e "${BLUE}[INFO]${NC} Creando script de entrada..." cat > "$INSTALL_DIR/entrypoint.sh" << 'EOF' #!/bin/bash set -e echo "Iniciando Volo..." # Generar config.ini si no existe if [ ! -f /app/config.ini ]; then echo "Generando config.ini..." timeout 10 python3 /app/start.py || true fi # Aplicar configuración if [ -f /app/config.ini ]; then # Configurar paths según el config.ini montado echo "Configuración cargada desde /app/config.ini" fi echo "Volo iniciado correctamente" exec python3 /app/start.py EOF chmod +x "$INSTALL_DIR/entrypoint.sh" echo -e "${GREEN}[✓]${NC} Script de entrada creado" } ################################################################################ # Función: Crear config.ini ################################################################################ create_config() { echo -e "${BLUE}[INFO]${NC} Creando configuración..." if [ "$USE_EXTERNAL_KIWIX" = true ]; then # Configuración con Kiwix externo cat > "$INSTALL_DIR/config.ini" << EOF [PATHS] zim_file_path = $ZIM_FILE_PATH [SERVER] port = 1255 ui_port = 3002 kiwix_serve_url = http://host.docker.internal:$KIWIX_PORT heading_count = 64 ai_model = qwen2.5:3b ollama_api_url = http://host.docker.internal:$OLLAMA_PORT/api/chat [RAG] max_results = 5 context_window = 4096 EOF else # Configuración con Kiwix integrado cat > "$INSTALL_DIR/config.ini" << EOF [PATHS] kiwix_search_path = /app/kiwix_tools/kiwix-search kiwix_serve_path = /app/kiwix_tools/kiwix-serve zim_file_path = $ZIM_FILE_PATH [SERVER] port = 1255 ui_port = 3002 kiwix_serve_url = http://localhost:821 heading_count = 64 ai_model = qwen2.5:3b ollama_api_url = http://host.docker.internal:$OLLAMA_PORT/api/chat [RAG] max_results = 5 context_window = 4096 EOF fi echo -e "${GREEN}[✓]${NC} Configuración creada" } ################################################################################ # Función: Crear docker-compose.yml ################################################################################ create_docker_compose() { echo -e "${BLUE}[INFO]${NC} Creando docker-compose.yml..." local expose_kiwix="" if [ "$USE_EXTERNAL_KIWIX" = false ]; then expose_kiwix=" - \"821:821\" # Kiwix Server interno" fi # Obtener directorio del archivo ZIM local zim_dir=$(dirname "$ZIM_FILE_PATH") local zim_file=$(basename "$ZIM_FILE_PATH") cat > "$INSTALL_DIR/docker-compose.yml" << EOF version: '3.8' services: volo: build: . container_name: volo restart: unless-stopped ports: - "${VOLO_UI_PORT}:3002" # UI Web - "${VOLO_API_PORT}:1255" # API REST $expose_kiwix volumes: - ${INSTALL_DIR}/config.ini:/app/config.ini:rw - ${INSTALL_DIR}/data:/app/data - ${zim_dir}:/zim:ro extra_hosts: - "host.docker.internal:host-gateway" environment: - TZ=Europe/Madrid - PYTHONUNBUFFERED=1 networks: - volo-network networks: volo-network: driver: bridge EOF # Actualizar config.ini con la ruta correcta dentro del contenedor sed -i "s|zim_file_path = .*|zim_file_path = /zim/$zim_file|g" "$INSTALL_DIR/config.ini" echo -e "${GREEN}[✓]${NC} docker-compose.yml creado" } ################################################################################ # Función: Crear archivo systemd service (opcional) ################################################################################ create_systemd_service() { echo -e "\n${BLUE}[INFO]${NC} ¿Deseas crear un servicio systemd para iniciar Volo automáticamente? (S/n)" read -r response if [[ ! "$response" =~ ^[Nn]$ ]]; then cat > /etc/systemd/system/volo.service << EOF [Unit] Description=Volo RAG Wikipedia Service Requires=docker.service After=docker.service [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=$INSTALL_DIR ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down User=root [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable volo.service echo -e "${GREEN}[✓]${NC} Servicio systemd creado y habilitado" echo -e "${YELLOW}Comandos:${NC}" echo -e " sudo systemctl start volo" echo -e " sudo systemctl stop volo" echo -e " sudo systemctl status volo" fi } ################################################################################ # Función: Construir e iniciar ################################################################################ build_and_start() { echo -e "\n${BLUE}[INFO]${NC} Construyendo imagen de Volo..." cd "$INSTALL_DIR" docker compose build echo -e "${GREEN}[✓]${NC} Imagen construida" echo -e "\n${BLUE}[INFO]${NC} ¿Deseas iniciar Volo ahora? (S/n)" read -r response if [[ ! "$response" =~ ^[Nn]$ ]]; then docker compose up -d sleep 3 echo -e "\n${GREEN}[✓]${NC} Volo iniciado" echo -e "\n${BLUE}[INFO]${NC} Verificando estado..." docker compose ps echo -e "\n${YELLOW}Ver logs:${NC} cd $INSTALL_DIR && docker compose logs -f" fi } ################################################################################ # Función: Mostrar información final ################################################################################ show_final_info() { echo -e "\n${GREEN}╔════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ Instalación LITE completada exitosamente ║${NC}" echo -e "${GREEN}╚════════════════════════════════════════════════════════╝${NC}" echo -e "\n${BLUE}🎯 Acceso a Volo:${NC}\n" echo -e " 🌐 UI Web: ${YELLOW}http://localhost:$VOLO_UI_PORT${NC}" echo -e " 🔌 API REST: ${YELLOW}http://localhost:$VOLO_API_PORT/v1${NC}" echo -e "\n${BLUE}🔗 Integración con OpenWebUI (puerto $OPENWEBUI_PORT):${NC}\n" echo -e " 1. Accede a OpenWebUI: ${YELLOW}http://localhost:$OPENWEBUI_PORT${NC}" echo -e " 2. Ve a Admin Panel > Settings > Connections" echo -e " 3. Añade esta URL: ${YELLOW}http://host.docker.internal:$VOLO_API_PORT/v1${NC}" echo -e " 4. ${RED}IMPORTANTE:${NC} Desactiva streaming en OpenWebUI" echo -e " 5. Selecciona el modelo ${YELLOW}volo-workflow${NC}" echo -e "\n${BLUE}📁 Directorios:${NC}\n" echo -e " Instalación: ${YELLOW}$INSTALL_DIR${NC}" echo -e " Configuración: ${YELLOW}$INSTALL_DIR/config.ini${NC}" echo -e " Archivo ZIM: ${YELLOW}$ZIM_FILE_PATH${NC}" echo -e "\n${BLUE}🛠️ Comandos útiles:${NC}\n" echo -e " Iniciar: ${YELLOW}cd $INSTALL_DIR && docker compose up -d${NC}" echo -e " Detener: ${YELLOW}cd $INSTALL_DIR && docker compose down${NC}" echo -e " Logs: ${YELLOW}cd $INSTALL_DIR && docker compose logs -f${NC}" echo -e " Reiniciar: ${YELLOW}cd $INSTALL_DIR && docker compose restart${NC}" echo -e " Estado: ${YELLOW}cd $INSTALL_DIR && docker compose ps${NC}" echo -e "\n${BLUE}🧪 Prueba rápida:${NC}\n" echo -e " ${YELLOW}curl -X POST http://localhost:$VOLO_API_PORT/v1/chat/completions \\${NC}" echo -e " ${YELLOW} -H \"Content-Type: application/json\" \\${NC}" echo -e " ${YELLOW} -d '{\"model\": \"volo-workflow\", \"messages\": [{\"role\": \"user\", \"content\": \"What is Linux?\"}]}'${NC}" echo -e "" } ################################################################################ # MAIN - Ejecución principal ################################################################################ main() { check_root check_existing_services check_volo_ports ask_zim_path ask_kiwix_mode create_directories create_dockerfile create_entrypoint create_config create_docker_compose create_systemd_service build_and_start show_final_info } # Ejecutar script principal main