ai-engine / Dockerfile
Severian's picture
Update Dockerfile
073e2d1 verified
raw
history blame
9.27 kB
# Base image
FROM python:3.12-slim-bookworm AS base
# Set shared environment variables
ENV POETRY_VERSION=1.8.4 \
POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_CREATE=true \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_CACHE_DIR=/tmp/poetry_cache \
PYTHONDONTWRITEBYTECODE=1 \
LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \
PORT=7860 \
NODE_ENV=production
# Install system dependencies and set up locales
RUN apt-get update && apt-get install -y \
curl \
git \
gcc \
python3-dev \
libgmp-dev \
libmpfr-dev \
libmpc-dev \
nodejs \
npm \
postgresql \
postgresql-contrib \
locales \
nginx \
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir "poetry==${POETRY_VERSION}" \
&& sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
&& locale-gen
# Configure nginx
RUN rm /etc/nginx/sites-enabled/default || true
COPY <<EOF /etc/nginx/nginx.conf
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 120;
# Added buffer size configurations
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# Added timeout configurations
proxy_connect_timeout 120s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
upstream frontend {
server 127.0.0.1:3000;
}
upstream backend {
server 127.0.0.1:5001;
}
server {
listen 7860;
server_name _;
client_max_body_size 100M;
# Increased buffer sizes
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade "$http_upgrade";
proxy_set_header Connection "upgrade";
proxy_set_header Host "$host";
proxy_cache_bypass "$http_upgrade";
proxy_set_header X-Real-IP "$remote_addr";
proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for";
proxy_set_header X-Forwarded-Proto "$scheme";
proxy_read_timeout 300s;
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
}
location /api {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade "$http_upgrade";
proxy_set_header Connection "upgrade";
proxy_set_header Host "$host";
proxy_cache_bypass "$http_upgrade";
proxy_set_header X-Real-IP "$remote_addr";
proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for";
proxy_set_header X-Forwarded-Proto "$scheme";
proxy_read_timeout 300s;
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log debug;
}
}
EOF
# Set up directories and permissions
RUN mkdir -p /var/log/nginx /var/run/postgresql /var/lib/postgresql/data /var/log/postgresql /app/api /app/web /data/storage && \
chown -R root:root /etc/nginx && \
chmod -R 755 /etc/nginx && \
touch /run/nginx.pid && \
chown -R root:root /run/nginx.pid && \
chmod -R 755 /var/lib/nginx && \
chmod -R 777 /run && \
chmod 700 /var/lib/postgresql/data && \
chmod 777 /data /app
# Create and configure entrypoint script
COPY <<-'EOT' /app/entrypoint.sh
#!/bin/bash
set -e
echo "===== Application Startup at $(date "+%Y-%m-%d %H:%M:%S") ====="
# Function to check if a service is ready
check_service() {
local service=$1
local url=$2
local max_attempts=$3
local wait_time=$4
echo "Checking $service..."
for i in $(seq 1 $max_attempts); do
if curl -s "$url" >/dev/null; then
echo "$service is ready"
return 0
fi
echo "Waiting for $service (attempt $i/$max_attempts)..."
sleep $wait_time
done
echo "$service failed to start"
return 1
}
# Initialize PostgreSQL database if not already initialized
if [ ! -f "$PGDATA/PG_VERSION" ]; then
echo "Initializing PostgreSQL database..."
initdb --username=user --pwfile=<(echo "$DB_PASSWORD") --auth=md5 --encoding=UTF8
# Configure PostgreSQL
echo "local all all trust" > "$PGDATA/pg_hba.conf"
echo "host all all 127.0.0.1/32 md5" >> "$PGDATA/pg_hba.conf"
echo "host all all ::1/128 md5" >> "$PGDATA/pg_hba.conf"
echo "host all all 0.0.0.0/0 md5" >> "$PGDATA/pg_hba.conf"
echo "listen_addresses = '*'" >> "$PGDATA/postgresql.conf"
echo "max_connections = 100" >> "$PGDATA/postgresql.conf"
echo "shared_buffers = 128MB" >> "$PGDATA/postgresql.conf"
echo "work_mem = 16MB" >> "$PGDATA/postgresql.conf"
echo "maintenance_work_mem = 128MB" >> "$PGDATA/postgresql.conf"
echo "effective_cache_size = 512MB" >> "$PGDATA/postgresql.conf"
fi
# Start PostgreSQL with detailed logging
echo "Starting PostgreSQL server..."
pg_ctl start -D "$PGDATA" -l /var/log/postgresql/postgresql.log -o "-c logging_collector=on -c log_directory='/var/log/postgresql' -c log_filename='postgresql-%Y-%m-%d_%H%M%S.log' -c log_statement='all'" -w
# Wait for PostgreSQL to start and show logs if there are issues
max_tries=30
count=0
echo "Checking database connection..."
until pg_isready -h localhost -p 5432; do
if [ $count -eq 0 ]; then
echo "PostgreSQL logs:"
tail -n 50 /var/log/postgresql/postgresql.log
fi
echo "Waiting for database connection... (${count}/${max_tries})"
sleep 2
count=$((count+1))
if [ $count -gt $max_tries ]; then
echo "Failed to connect to database after ${max_tries} attempts"
echo "Last 100 lines of PostgreSQL logs:"
tail -n 100 /var/log/postgresql/postgresql.log
exit 1
fi
done
# Create database if it doesn't exist
if ! psql -lqt | cut -d \| -f 1 | grep -qw dify; then
echo "Creating database dify..."
createdb -U user dify
fi
echo "Database connection successful"
# Start application services
cd /app/api && poetry run python -m flask db upgrade
# Start API server
cd /app/api && poetry run python -m gunicorn app:app \
--bind ${DIFY_BIND_ADDRESS:-127.0.0.1}:${DIFY_PORT:-5001} \
--worker-class gevent \
--workers 1 \
--timeout 300 \
--preload \
--access-logfile - \
--error-logfile - &
# Wait for API to be ready
echo "Waiting for API server to be ready..."
check_service "API" "http://127.0.0.1:5001/api/health" 30 2
# Start frontend server
cd /app/web && PORT=3000 node server.js &
# Wait for frontend to be ready
echo "Waiting for frontend server to be ready..."
check_service "Frontend" "http://127.0.0.1:3000" 30 2
# Start nginx with debug logging
echo "Starting nginx..."
nginx -g "daemon off; error_log /var/log/nginx/error.log debug;" &
# Monitor all processes
while true; do
if ! pgrep -f "gunicorn" > /dev/null; then
echo "API server died"
exit 1
fi
if ! pgrep -f "node server.js" > /dev/null; then
echo "Frontend server died"
exit 1
fi
if ! pgrep -f "nginx" > /dev/null; then
echo "Nginx died"
exit 1
fi
sleep 30
done
EOT
# Set permissions for entrypoint script
RUN chmod +x /app/entrypoint.sh && \
chown user:user /app/entrypoint.sh
# Create user after setting up all files and permissions
RUN useradd -m -u 1000 user && \
chown -R user:user /var/run/postgresql /var/lib/postgresql/data /var/log/postgresql /app /data /var/log/nginx
# Switch to user for remaining operations
USER user
# Set environment for user
ENV HOME=/home/user \
PATH=/usr/lib/postgresql/15/bin:/home/user/.local/bin:$PATH \
PGDATA=/var/lib/postgresql/data
# Pull official images
FROM langgenius/dify-web:latest AS web
FROM langgenius/dify-api:latest AS api
# Final stage
FROM base
# Set up directory structure
WORKDIR /app
RUN mkdir -p api web /data/storage
# Copy from official images
COPY --from=web --chown=user:user /app/web /app/web/
COPY --from=api --chown=user:user /app/api /app/api/
# Install API dependencies using Poetry
WORKDIR /app/api
COPY --from=api --chown=user /app/api/pyproject.toml /app/api/poetry.lock /app/api/poetry.toml ./
RUN poetry install --no-root --no-dev
# Create symlink for persistent storage
RUN ln -s /data/storage /app/api/storage
# Set environment variables
ENV FLASK_APP=app.py \
EDITION=SELF_HOSTED \
DEPLOY_ENV=PRODUCTION \
MODE=api \
LOG_LEVEL=INFO \
DEBUG=false \
FLASK_DEBUG=false \
SECRET_KEY=sk-9f73s3ljTXVcMT3Blb3ljTqtsKiGHXVcMT3BlbkFJLK7U \
CONSOLE_API_URL=https://${SPACE_ID}.hf.space \
CONSOLE_WEB_URL=https://${SPACE_ID}.hf.space \
SERVICE_API_URL=https://${SPACE_ID}.hf.space \
APP_WEB_URL=https://${SPACE_ID}.hf.space \
DIFY_PORT=5001 \
DIFY_BIND_ADDRESS=127.0.0.1 \
DB_USERNAME=user \
DB_PASSWORD=difyai123456 \
DB_HOST=localhost \
DB_PORT=5432 \
DB_DATABASE=dify \
PYTHONPATH=/app/api \
STORAGE_PATH=/data/storage
EXPOSE 7860
WORKDIR /app
CMD ["./entrypoint.sh"]