diff --git a/app/Dockerfile.prod b/app/Dockerfile.prod index 6d5fc2a..d772e1f 100644 --- a/app/Dockerfile.prod +++ b/app/Dockerfile.prod @@ -90,36 +90,34 @@ FROM linuxserver/blender:4.4.3 AS blender_builder # ------------------------------ # Stage 4: Production Runtime # ------------------------------ -FROM ubuntu:22.04 AS final - -RUN echo 'Acquire::ForceIPv4 "true";' > /etc/apt/apt.conf.d/99force-ipv4 - -ENV TZ=UTC -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN sed -i'' 's/archive\.ubuntu\.com/us\.archive\.ubuntu\.com/' /etc/apt/sources.list +FROM alpine:3.22 AS final # Create non-root user -RUN groupadd -r appuser && useradd -r -g appuser -s /bin/false appuser - -ENV DEBIAN_FRONTEND=noninteractive -ARG DEBIAN_FRONTEND=noninteractive - -# Install only runtime dependencies -# Seperate for cache issues -RUN apt-get -y update -RUN apt-get install -y --no-install-recommends \ - libcgal-qt5-dev libceres2 libboost-system1.74.0 libboost-filesystem1.74.0 \ - libboost-program-options1.74.0 libboost-serialization1.74.0 \ - libopencv-core4.5d libopencv-imgproc4.5d libopencv-imgcodecs4.5d \ - libjpeg8 libpng16-16 libtiff5 libglu1-mesa libglew2.2 \ - libglfw3 libgomp1 ca-certificates curl wget libboost-all-dev libopencv-dev \ - xorg && \ - apt-get clean && rm -rf /var/lib/apt/lists/* +RUN addgroup -S appuser && adduser -S -G appuser -s /sbin/nologin appuser + +# Install runtime dependencies +RUN apk add --no-cache \ + boost-dev \ + cgal \ + # ceres \ + curl \ + glew \ + glfw \ + glu \ + jpeg \ + libgomp \ + libpng \ + libstdc++ \ + mesa \ + opencv \ + tiff \ + tzdata \ + wget +# xorg-server # Copy OpenMVG/OpenMVS binaries and libraries -COPY --from=cv_builder /usr/local/bin/ /usr/local/bin/ -COPY --from=cv_builder /usr/local/lib/ /usr/local/lib/ +COPY --from=cv_builder /usr/local/bin/open* /usr/local/bin/ +COPY --from=cv_builder /usr/local/lib/open* /usr/local/lib/ # Copy Blender (only essential parts) COPY --from=blender_builder /blender /opt/blender @@ -134,7 +132,7 @@ WORKDIR /app COPY ./bin /app/bin # Update library cache -RUN ldconfig +RUN ldconfig || true # Create app directory with proper permissions RUN mkdir -p /app /app/data /app/logs && \ @@ -142,20 +140,20 @@ RUN mkdir -p /app /app/data /app/logs && \ chown -R appuser:appuser /usr/local/bin # Copy entrypoint script -COPY --chown=appuser:appuser entrypoint.sh /app/entrypoint.sh -RUN chmod +x /app/entrypoint.sh +COPY --chown=appuser:appuser ./entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh # Set environment variables -ENV PATH="/usr/local/bin:/usr/local/bin/OpenMVS:$PATH" \ +ENV PATH="/usr/local/bin:/usr/local/bin/OpenMVS:/opt/blender:$PATH" \ PORT=3333 \ GIN_MODE=release \ LOG_LEVEL=info \ - BLENDER_PATH=/usr/local/bin/blender + BLENDER_PATH=/usr/local/bin/blender \ + TZ=UTC # Switch to non-root user USER appuser - # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ CMD curl -f http://localhost:${PORT}/health || exit 1 @@ -164,5 +162,5 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ EXPOSE 3333 # Use proper entrypoint -ENTRYPOINT ["/app/entrypoint.sh"] -CMD ["server"] \ No newline at end of file +ENTRYPOINT ["/entrypoint.sh"] +CMD ["/usr/local/bin/server"] \ No newline at end of file diff --git a/app/entrypoint.sh b/app/entrypoint.sh index cf0d8d7..c82d401 100755 --- a/app/entrypoint.sh +++ b/app/entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh set -e # Production entrypoint script @@ -11,21 +11,21 @@ YELLOW='\033[1;33m' NC='\033[0m' # No Color log() { - echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] INFO:${NC} $1" + printf "%s[%s] INFO:%s %s\n" "$GREEN" "$(date +'%Y-%m-%d %H:%M:%S')" "$NC" "$1" } warn() { - echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARN:${NC} $1" + printf "%s[%s] WARN:%s %s\n" "$YELLOW" "$(date +'%Y-%m-%d %H:%M:%S')" "$NC" "$1" } error() { - echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" + printf "%s[%s] ERROR:%s %s\n" "$RED" "$(date +'%Y-%m-%d %H:%M:%S')" "$NC" "$1" } # Function to handle graceful shutdown graceful_shutdown() { log "Received shutdown signal, performing graceful shutdown..." - if [ ! -z "$SERVER_PID" ]; then + if [ -n "$SERVER_PID" ]; then kill -TERM "$SERVER_PID" 2>/dev/null || true wait "$SERVER_PID" 2>/dev/null || true fi @@ -34,37 +34,39 @@ graceful_shutdown() { } # Set up signal handlers -trap graceful_shutdown SIGTERM SIGINT +trap graceful_shutdown TERM INT # Validate required environment variables validate_env() { - local required_vars=("PORT") - local missing_vars=() - - for var in "${required_vars[@]}"; do - if [ -z "${!var}" ]; then - missing_vars+=("$var") + required_vars="PORT" + missing_vars="" + + for var in $required_vars; do + eval "value=\$$var" + if [ -z "$value" ]; then + missing_vars="$missing_vars $var" fi done - - if [ ${#missing_vars[@]} -ne 0 ]; then - error "Missing required environment variables: ${missing_vars[*]}" + + if [ -n "$missing_vars" ]; then + error "Missing required environment variables:$missing_vars" exit 1 fi } # Initialize Google Cloud credentials if provided init_gcp_credentials() { - if [ ! -z "$GOOGLE_CREDENTIALS" ]; then + if [ -n "$GOOGLE_CREDENTIALS" ]; then log "Setting up Google Cloud credentials..." - echo "$GOOGLE_CREDENTIALS" > /tmp/service-account-key.json + printf "%s" "$GOOGLE_CREDENTIALS" > /tmp/service-account-key.json export GOOGLE_APPLICATION_CREDENTIALS="/tmp/service-account-key.json" - - # Validate credentials format - if ! command -v jq >/dev/null || ! echo "$GOOGLE_CREDENTIALS" | jq empty 2>/dev/null; then - warn "Google credentials may not be valid JSON" + + if command -v jq >/dev/null 2>&1; then + echo "$GOOGLE_CREDENTIALS" | jq empty 2>/dev/null || warn "Google credentials may not be valid JSON" + else + warn "jq not installed, cannot validate JSON" fi - elif [ ! -z "$GOOGLE_APPLICATION_CREDENTIALS" ] && [ -f "$GOOGLE_APPLICATION_CREDENTIALS" ]; then + elif [ -n "$GOOGLE_APPLICATION_CREDENTIALS" ] && [ -f "$GOOGLE_APPLICATION_CREDENTIALS" ]; then log "Using existing Google Cloud credentials file: $GOOGLE_APPLICATION_CREDENTIALS" else warn "No Google Cloud credentials provided" @@ -73,22 +75,22 @@ init_gcp_credentials() { # Health check function health_check() { - local max_attempts=30 - local attempt=1 - + max_attempts=30 + attempt=1 + log "Waiting for application to be ready..." - - while [ $attempt -le $max_attempts ]; do + + while [ "$attempt" -le "$max_attempts" ]; do if curl -sf "http://localhost:${PORT}/health" >/dev/null 2>&1; then log "Application is ready and healthy" return 0 fi - + log "Health check attempt $attempt/$max_attempts failed, retrying in 2s..." sleep 2 attempt=$((attempt + 1)) done - + error "Application failed to become healthy after $max_attempts attempts" return 1 } @@ -96,24 +98,22 @@ health_check() { # Pre-flight checks preflight_checks() { log "Running pre-flight checks..." - - # Check if required binaries exist - local required_bins=("server") - for bin in "${required_bins[@]}"; do + + required_bins="server" + for bin in $required_bins; do if ! command -v "$bin" >/dev/null 2>&1; then error "Required binary not found: $bin" exit 1 fi done - - # Check OpenMVG/OpenMVS binaries - local openmvg_bins=("openMVG_main_SfMInit_ImageListing" "openMVG_main_ComputeFeatures") - for bin in "${openmvg_bins[@]}"; do + + openmvg_bins="openMVG_main_SfMInit_ImageListing openMVG_main_ComputeFeatures" + for bin in $openmvg_bins; do if ! command -v "$bin" >/dev/null 2>&1; then warn "OpenMVG binary not found: $bin" fi done - + log "Pre-flight checks completed successfully" } @@ -123,41 +123,34 @@ main() { log "Running as user: $(whoami)" log "Working directory: $(pwd)" log "Environment: ${GIN_MODE:-development}" - + # Run initialization steps validate_env init_gcp_credentials preflight_checks - - # Start the application based on the command + case "${1:-server}" in "server") log "Starting Go server on port $PORT..." - exec /usr/local/bin/server & + /usr/local/bin/server & SERVER_PID=$! - - # Wait a moment for server to start + sleep 5 - - # Optional: Run health check in background + if command -v curl >/dev/null 2>&1; then health_check & fi - - # Wait for the server process - wait $SERVER_PID + + wait "$SERVER_PID" ;; "health") - # Health check command curl -f "http://localhost:${PORT}/health" ;; *) - # Execute any other command passed log "Executing command: $*" exec "$@" ;; esac } -# Execute main function with all arguments -main "$@" \ No newline at end of file +main "$@"