refactor: Single image serving both API (8081) and Streamlit (3000)
All checks were successful
armco-org/visual-search-engine/pipeline/head This commit was not built

- Combine API and Streamlit into single Dockerfile
- Add entrypoint.sh to run both services in parallel
- Reduces Harbor storage from 2.66GB to 1.33GB
- Single build instead of two
This commit is contained in:
2026-01-03 14:29:52 +05:30
parent 8f7e5f639b
commit 906b06dc3e
6 changed files with 38 additions and 114 deletions

View File

@@ -1,5 +1,5 @@
# Reverse Image Search API
# Multi-stage build for optimized image size
# Visual Search Engine - API (8081) + Streamlit UI (3000)
# Single image serving both endpoints
FROM python:3.11-slim as builder
@@ -19,12 +19,12 @@ FROM python:3.11-slim
WORKDIR /app
# Install runtime dependencies (libgomp for OpenMP parallelism)
# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
# FAISS uses OpenMP for parallel search - set threads for Xeon
# FAISS OpenMP threads
ENV OMP_NUM_THREADS=8
# Copy installed packages from builder
@@ -36,29 +36,30 @@ COPY api/ ./api/
COPY config.py .
COPY reverse_icon_search.py .
COPY run_api_server.py .
COPY main_streamlit.py .
COPY entrypoint.sh .
# Copy pre-built index and filenames for inference
# Option 1: Bundle in image (~530 MB)
# Copy pre-built index and filenames
COPY index.faiss .
COPY filenames.pkl .
# Option 2: Mount as volume at runtime (uncomment above, use docker run -v)
# Create necessary directories
RUN mkdir -p uploads logs
# Create necessary directories and make entrypoint executable
RUN mkdir -p uploads logs && chmod +x entrypoint.sh
# Environment variables
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV API_PORT=5002
ENV API_PORT=8081
ENV DEBUG=false
ENV STREAMLIT_SERVER_PORT=3000
ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0
# Expose port
EXPOSE 5002
# Expose both ports
EXPOSE 8081 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5002/api/health')" || exit 1
# Health check on API
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8081/api/health')" || exit 1
# Run the application
CMD ["python", "run_api_server.py"]
# Run both services
CMD ["./entrypoint.sh"]

View File

@@ -1,33 +0,0 @@
# Reverse Image Search API
# Uses pre-built base image with TensorFlow to avoid repeated downloads
FROM harbor.armco.dev/library/visualsearch-base:latest
WORKDIR /app
# Copy application code
COPY api/ ./api/
COPY config.py .
COPY reverse_icon_search.py .
COPY run_api_server.py .
# Copy pre-built index and filenames for inference
COPY index.faiss .
COPY filenames.pkl .
# Create necessary directories
RUN mkdir -p uploads logs
# Environment variables
ENV API_PORT=8081
ENV DEBUG=false
# Expose port
EXPOSE 8081
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8081/api/health')" || exit 1
# Run the application
CMD ["python", "run_api_server.py"]

View File

@@ -1,26 +0,0 @@
# Base image with TensorFlow and heavy dependencies pre-installed
# Build once, reuse for API and Streamlit builds
# Tag: harbor.armco.dev/library/visualsearch-base:latest
FROM python:3.11-slim
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*
# Copy and install requirements
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# FAISS uses OpenMP for parallel search
ENV OMP_NUM_THREADS=8
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Clean up build dependencies to reduce image size
RUN apt-get purge -y build-essential && apt-get autoremove -y

View File

@@ -1,32 +0,0 @@
# Reverse Image Search Streamlit UI
# Uses pre-built base image with TensorFlow to avoid repeated downloads
FROM harbor.armco.dev/library/visualsearch-base:latest
WORKDIR /app
# Copy application code
COPY config.py .
COPY reverse_icon_search.py .
COPY main_streamlit.py .
# Copy pre-built index and filenames for inference
COPY index.faiss .
COPY filenames.pkl .
# Create necessary directories
RUN mkdir -p uploads logs
# Environment variables
ENV STREAMLIT_SERVER_PORT=3000
ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:3000/_stcore/health')" || exit 1
# Run Streamlit
CMD ["streamlit", "run", "main_streamlit.py", "--server.port=3000", "--server.address=0.0.0.0"]

7
Jenkinsfile vendored
View File

@@ -1,14 +1,11 @@
@Library('jenkins-shared@main') _
// NOTE: Build base image first when requirements.txt changes:
// [imageName: 'visualsearch-base', dockerfile: 'Dockerfile.base']
// Then rebuild API and Streamlit images
// Single image serves both API (8081) and Streamlit UI (3000)
kanikoPipeline(
repoName: 'visual-search-engine',
branch: env.BRANCH_NAME ?: 'main',
builds: [
[imageName: 'visualsearch', dockerfile: 'Dockerfile.api'],
[imageName: 'visualsearch-stl', dockerfile: 'Dockerfile.streamlit']
[imageName: 'visualsearch', dockerfile: 'Dockerfile']
]
)

17
entrypoint.sh Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Run both API server (8081) and Streamlit UI (3000) in parallel
# Start API server in background
python run_api_server.py &
API_PID=$!
# Start Streamlit in background
streamlit run main_streamlit.py --server.port=3000 --server.address=0.0.0.0 &
STL_PID=$!
# Wait for either to exit
wait -n $API_PID $STL_PID
# If one exits, kill the other and exit
kill $API_PID $STL_PID 2>/dev/null
exit 1