From 92ce7400e7f7acab6e4487139396b8d8aff33707 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Fri, 20 Mar 2026 02:11:48 +0000 Subject: [PATCH] feat: make Nomad fully composable --- Dockerfile | 9 ++++++++- admin/config/logger.ts | 2 +- install/entrypoint.sh | 6 ++---- install/install_nomad.sh | 29 ----------------------------- install/management_compose.yaml | 29 +++++++++++++++++------------ 5 files changed, 28 insertions(+), 47 deletions(-) diff --git a/Dockerfile b/Dockerfile index 27f3aed..c91f9ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,7 +45,14 @@ COPY --from=production-deps /app/node_modules /app/node_modules COPY --from=build /app/build /app # Copy root package.json for version info COPY package.json /app/version.json + +# Copy docs and README for access within the container COPY admin/docs /app/docs COPY README.md /app/README.md + +# Copy entrypoint script and ensure it's executable +COPY install/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + EXPOSE 8080 -CMD ["node", "./bin/server.js"] \ No newline at end of file +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] \ No newline at end of file diff --git a/admin/config/logger.ts b/admin/config/logger.ts index 59aa141..981e167 100644 --- a/admin/config/logger.ts +++ b/admin/config/logger.ts @@ -18,7 +18,7 @@ const loggerConfig = defineConfig({ targets: targets() .pushIf(!app.inProduction, targets.pretty()) - .pushIf(app.inProduction, targets.file({ destination: "/app/storage/logs/admin.log" })) + .pushIf(app.inProduction, targets.file({ destination: "/app/storage/logs/admin.log", mkdir: true })) .toArray(), }, }, diff --git a/install/entrypoint.sh b/install/entrypoint.sh index 17bdc95..4361903 100644 --- a/install/entrypoint.sh +++ b/install/entrypoint.sh @@ -3,11 +3,9 @@ set -e echo "Starting entrypoint script..." -echo "Running wait-for-it.sh to ensure MySQL is ready..." -# Use wait-for-it.sh to wait for MySQL to be available -# wait-for-it.sh : [-t timeout] [-- command args] -/usr/local/bin/wait-for-it.sh ${DB_HOST}:${DB_PORT} -t 60 -- echo "MySQL is up and running!" +# Ensure required storage directories exist (volume may be freshly mounted) +mkdir -p /app/storage/logs /app/storage/kb_uploads # Run AdonisJS migrations echo "Running AdonisJS migrations..." diff --git a/install/install_nomad.sh b/install/install_nomad.sh index b7ac85e..4a15d76 100644 --- a/install/install_nomad.sh +++ b/install/install_nomad.sh @@ -31,14 +31,11 @@ GREEN='\033[1;32m' # Light Green. WHIPTAIL_TITLE="Project N.O.M.A.D Installation" NOMAD_DIR="/opt/project-nomad" MANAGEMENT_COMPOSE_FILE_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/management_compose.yaml" -ENTRYPOINT_SCRIPT_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/entrypoint.sh" SIDECAR_UPDATER_DOCKERFILE_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/sidecar-updater/Dockerfile" SIDECAR_UPDATER_SCRIPT_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/sidecar-updater/update-watcher.sh" START_SCRIPT_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/start_nomad.sh" STOP_SCRIPT_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/stop_nomad.sh" UPDATE_SCRIPT_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/update_nomad.sh" -WAIT_FOR_IT_SCRIPT_URL="https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh" - script_option_debug='true' accepted_terms='false' local_ip_address='' @@ -406,30 +403,6 @@ download_management_compose_file() { echo -e "${GREEN}#${RESET} Docker compose file configured successfully.\\n" } -download_wait_for_it_script() { - local wait_for_it_script_path="${NOMAD_DIR}/wait-for-it.sh" - - echo -e "${YELLOW}#${RESET} Downloading wait-for-it script...\\n" - if ! curl -fsSL "$WAIT_FOR_IT_SCRIPT_URL" -o "$wait_for_it_script_path"; then - echo -e "${RED}#${RESET} Failed to download the wait-for-it script. Please check the URL and try again." - exit 1 - fi - chmod +x "$wait_for_it_script_path" - echo -e "${GREEN}#${RESET} wait-for-it script downloaded successfully to $wait_for_it_script_path.\\n" -} - -download_entrypoint_script() { - local entrypoint_script_path="${NOMAD_DIR}/entrypoint.sh" - - echo -e "${YELLOW}#${RESET} Downloading entrypoint script...\\n" - if ! curl -fsSL "$ENTRYPOINT_SCRIPT_URL" -o "$entrypoint_script_path"; then - echo -e "${RED}#${RESET} Failed to download the entrypoint script. Please check the URL and try again." - exit 1 - fi - chmod +x "$entrypoint_script_path" - echo -e "${GREEN}#${RESET} entrypoint script downloaded successfully to $entrypoint_script_path.\\n" -} - download_sidecar_files() { # Create sidecar-updater directory if it doesn't exist if [[ ! -d "${NOMAD_DIR}/sidecar-updater" ]]; then @@ -580,8 +553,6 @@ ensure_docker_installed setup_nvidia_container_toolkit get_local_ip create_nomad_directory -download_wait_for_it_script -download_entrypoint_script download_sidecar_files download_helper_scripts download_management_compose_file diff --git a/install/management_compose.yaml b/install/management_compose.yaml index 5cf344e..6522efa 100644 --- a/install/management_compose.yaml +++ b/install/management_compose.yaml @@ -1,3 +1,11 @@ +# Project N.O.M.A.D. management services Docker Compose configuration +# +# This compose file defines the admin server, database, and other supporting services required to run Project N.O.M.A.D. +# You can use this with `docker-compose up -d` to start all the necessary services with a single command after installation. +# +# Note: we recommend leaving all of the environment variables as-is except for any "replaceme" values, +# which must be updated for the admin server to start successfully. The default values are optimized for ease of installation and use, +# but you can customize them as needed (e.g. changing ports, database connection details, log level, etc.) name: project-nomad services: admin: @@ -12,38 +20,35 @@ services: volumes: - /opt/project-nomad/storage:/app/storage - /var/run/docker.sock:/var/run/docker.sock # Allows the admin service to communicate with the Host's Docker daemon - - ./entrypoint.sh:/usr/local/bin/entrypoint.sh - - ./wait-for-it.sh:/usr/local/bin/wait-for-it.sh - nomad-update-shared:/app/update-shared # Shared volume for update communication environment: - NODE_ENV=production - PORT=8080 - LOG_LEVEL=info - - APP_KEY=replaceme - - HOST=0.0.0.0 - - URL=replaceme + - APP_KEY=replaceme # Needs to be at least 16 chars or will fail validation and container won't start! + - HOST=0.0.0.0 # Leave this as is so the admin server listens all interfaces within the container - this doesn't affect how you access it from the host, it's just for internal container networking + - URL=replaceme # Should be set to the URL you will access the admin interface at (e.g. http://localhost:8080 or http://192.168.1.x:8080) - DB_HOST=mysql - - DB_PORT=3306 + - DB_PORT=3306 # If you change the MySQL port, make sure to update this accordingly - DB_DATABASE=nomad - DB_USER=nomad_user - - DB_PASSWORD=replaceme + - DB_PASSWORD=replaceme # Needs to match the MYSQL_PASSWORD in the mysql service! - DB_NAME=nomad - DB_SSL=false - REDIS_HOST=redis - - REDIS_PORT=6379 + - REDIS_PORT=6379 # If you change the Redis port, make sure to update this accordingly depends_on: mysql: condition: service_healthy redis: condition: service_healthy - entrypoint: ["/usr/local/bin/entrypoint.sh"] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] interval: 30s timeout: 10s retries: 3 dozzle: - image: amir20/dozzle:v10.0 + image: amir20/dozzle:v10.0 # Dozzle is optional, but note that the "Service Logs & Metrics" link in Settings points to it. We recommend including it unless you have a specific reason not to container_name: nomad_dozzle restart: unless-stopped ports: @@ -61,7 +66,7 @@ services: - MYSQL_ROOT_PASSWORD=replaceme - MYSQL_DATABASE=nomad - MYSQL_USER=nomad_user - - MYSQL_PASSWORD=replaceme + - MYSQL_PASSWORD=replaceme # Needs to match DB_PASSWORD in the admin service! volumes: - /opt/project-nomad/mysql:/var/lib/mysql healthcheck: @@ -80,7 +85,7 @@ services: interval: 30s timeout: 10s retries: 3 - updater: + updater: # Updater & disk-collector are lightweight sidecar containers that run alongside the admin container to handle updates and host disk usage collection, respectively. image: ghcr.io/crosstalk-solutions/project-nomad-sidecar-updater:latest pull_policy: always container_name: nomad_updater