Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 32 additions & 34 deletions app/Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -134,28 +132,28 @@ 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 && \
chown -R appuser:appuser /app && \
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
Expand All @@ -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"]
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/local/bin/server"]
99 changes: 46 additions & 53 deletions app/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
set -e

# Production entrypoint script
Expand All @@ -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
Expand All @@ -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"
Expand All @@ -73,47 +75,45 @@ 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
}

# 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"
}

Expand All @@ -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 "$@"
main "$@"
Loading