Dockerize backend — replace systemd service with docker-compose
- Add backend/Dockerfile (python:3.12-slim) - Add docker-compose.yml (backend :5137 + mongo:7) - Add backend/.dockerignore - Rewrite deploy.sh: build frontend locally, rsync dist/, docker compose up --build - Remove semblance.service (no longer needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8d532a50f0
commit
4a6b4d6fe0
5 changed files with 91 additions and 110 deletions
13
backend/.dockerignore
Normal file
13
backend/.dockerignore
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
.env
|
||||
uploads/
|
||||
temp/
|
||||
tests/
|
||||
*.md
|
||||
*.pdf
|
||||
*.txt
|
||||
*.png
|
||||
*.sh
|
||||
22
backend/Dockerfile
Normal file
22
backend/Dockerfile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system deps for bcrypt, pymongo, etc.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Python dependencies first (layer caching)
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create upload/temp directories
|
||||
RUN mkdir -p /app/uploads /app/temp
|
||||
|
||||
EXPOSE 5137
|
||||
|
||||
CMD ["python", "run.py"]
|
||||
92
deploy.sh
92
deploy.sh
|
|
@ -3,7 +3,7 @@ set -e
|
|||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Semblance — deploy script
|
||||
# Builds frontend locally, rsyncs dist/ + backend to server, manages services.
|
||||
# Builds frontend locally, rsyncs dist/ to server, restarts Docker services.
|
||||
#
|
||||
# Server: optical-dev.oliver.solution (Ubuntu 24.04, Apache2, Docker)
|
||||
# SSH alias: optical-dev (see ~/.ssh/config)
|
||||
|
|
@ -12,16 +12,14 @@ set -e
|
|||
# ssh optical-dev
|
||||
# sudo mkdir -p /opt/semblance && sudo chown vadym.samoilenko:vadym.samoilenko /opt/semblance
|
||||
# git clone git@bitbucket.org:zlalani/semblance-dev.git /opt/semblance
|
||||
# cp /opt/semblance/backend/.env.example /opt/semblance/backend/.env && nano /opt/semblance/backend/.env
|
||||
# sudo cp /opt/semblance/semblance.service /etc/systemd/system/
|
||||
# sudo systemctl daemon-reload && sudo systemctl enable semblance.service
|
||||
# cp /opt/semblance/backend/.env.example /opt/semblance/backend/.env
|
||||
# nano /opt/semblance/backend/.env # fill in real values
|
||||
# cd /opt/semblance && docker compose up -d --build
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
SSH_HOST="optical-dev"
|
||||
DEPLOY_DIR="/opt/semblance"
|
||||
FRONTEND_DEST="/var/www/html/semblance"
|
||||
BACKEND_DIR="$DEPLOY_DIR/backend"
|
||||
PYTHON_CMD="python3"
|
||||
|
||||
echo "======================================"
|
||||
echo "Semblance — starting deployment"
|
||||
|
|
@ -30,18 +28,18 @@ echo "======================================"
|
|||
# ── Pre-flight: backend/.env must exist on server ────────────────────────────
|
||||
echo ""
|
||||
echo "[pre-flight] Checking backend/.env on server..."
|
||||
if ! ssh "$SSH_HOST" "test -f $BACKEND_DIR/.env"; then
|
||||
if ! ssh "$SSH_HOST" "test -f $DEPLOY_DIR/backend/.env"; then
|
||||
echo ""
|
||||
echo "ERROR: $BACKEND_DIR/.env not found on server."
|
||||
echo "ERROR: $DEPLOY_DIR/backend/.env not found on server."
|
||||
echo "Run on server:"
|
||||
echo " cp $BACKEND_DIR/.env.example $BACKEND_DIR/.env"
|
||||
echo " nano $BACKEND_DIR/.env"
|
||||
echo " cp $DEPLOY_DIR/backend/.env.example $DEPLOY_DIR/backend/.env"
|
||||
echo " nano $DEPLOY_DIR/backend/.env"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for VAR in SECRET_KEY JWT_SECRET_KEY OPENAI_API_KEY GEMINI_API_KEY; do
|
||||
if ! ssh "$SSH_HOST" "grep -q '^${VAR}=.\+' $BACKEND_DIR/.env 2>/dev/null"; then
|
||||
echo "ERROR: $VAR is not set in $BACKEND_DIR/.env on server"
|
||||
if ! ssh "$SSH_HOST" "grep -q '^${VAR}=.\+' $DEPLOY_DIR/backend/.env 2>/dev/null"; then
|
||||
echo "ERROR: $VAR is not set in backend/.env on server"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
|
@ -49,12 +47,12 @@ echo "✓ backend/.env present and required vars set"
|
|||
|
||||
# ── Step 1: Pull latest code on server ───────────────────────────────────────
|
||||
echo ""
|
||||
echo "[1/7] Pulling latest code on server..."
|
||||
echo "[1/5] Pulling latest code on server..."
|
||||
ssh "$SSH_HOST" "cd $DEPLOY_DIR && git pull"
|
||||
|
||||
# ── Step 2: Build frontend locally ───────────────────────────────────────────
|
||||
echo ""
|
||||
echo "[2/7] Building frontend locally..."
|
||||
echo "[2/5] Building frontend locally..."
|
||||
cp .env.production .env
|
||||
npm install --silent
|
||||
npm run build
|
||||
|
|
@ -62,48 +60,15 @@ echo "✓ Frontend built (dist/)"
|
|||
|
||||
# ── Step 3: Deploy frontend to server ────────────────────────────────────────
|
||||
echo ""
|
||||
echo "[3/7] Deploying frontend to server..."
|
||||
ssh "$SSH_HOST" "sudo mkdir -p $FRONTEND_DEST && sudo chown vadym.samoilenko:www-data $FRONTEND_DEST && sudo chmod 755 $FRONTEND_DEST"
|
||||
echo "[3/5] Deploying frontend to server..."
|
||||
ssh "$SSH_HOST" "sudo mkdir -p $FRONTEND_DEST"
|
||||
rsync -az --delete dist/ "$SSH_HOST:$FRONTEND_DEST/"
|
||||
ssh "$SSH_HOST" "sudo chown -R www-data:www-data $FRONTEND_DEST"
|
||||
echo "✓ Frontend deployed to $FRONTEND_DEST"
|
||||
|
||||
# ── Step 4: Set up Python virtual environment on server ──────────────────────
|
||||
# ── Step 4: Ensure Apache proxy config for semblance ─────────────────────────
|
||||
echo ""
|
||||
echo "[4/7] Setting up Python virtual environment..."
|
||||
ssh "$SSH_HOST" "
|
||||
cd $BACKEND_DIR
|
||||
if [ ! -d venv ]; then
|
||||
echo 'Creating new virtual environment...'
|
||||
$PYTHON_CMD -m venv venv
|
||||
fi
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt --quiet
|
||||
echo '✓ Python dependencies installed'
|
||||
"
|
||||
|
||||
# ── Step 5: Ensure MongoDB is running in Docker ───────────────────────────────
|
||||
echo ""
|
||||
echo "[5/7] Ensuring MongoDB is running..."
|
||||
ssh "$SSH_HOST" "
|
||||
if ! docker ps --format '{{.Names}}' | grep -q '^semblance-mongo\$'; then
|
||||
echo 'Starting MongoDB container...'
|
||||
docker run -d \
|
||||
--name semblance-mongo \
|
||||
--restart unless-stopped \
|
||||
-p 127.0.0.1:27017:27017 \
|
||||
-v semblance-mongo-data:/data/db \
|
||||
mongo:7
|
||||
sleep 3
|
||||
echo '✓ MongoDB started'
|
||||
else
|
||||
echo '✓ MongoDB already running'
|
||||
fi
|
||||
"
|
||||
|
||||
# ── Step 6: Ensure Apache proxy config for semblance ─────────────────────────
|
||||
echo ""
|
||||
echo "[6/7] Ensuring Apache proxy config..."
|
||||
echo "[4/5] Ensuring Apache proxy config..."
|
||||
ssh "$SSH_HOST" "
|
||||
CONF='/etc/apache2/sites-enabled/optical-dev.oliver.solutions.conf'
|
||||
|
||||
|
|
@ -111,31 +76,22 @@ ssh "$SSH_HOST" "
|
|||
echo '✓ Apache semblance config already present'
|
||||
else
|
||||
echo 'Adding semblance blocks to Apache config...'
|
||||
|
||||
# Insert before the closing </VirtualHost> tag
|
||||
sudo sed -i 's|</VirtualHost>| # ----------------------------------------------------------------\n # Semblance — Quart/Hypercorn backend at :5137\n # ----------------------------------------------------------------\n\n # WebSocket (Socket.IO)\n RewriteCond %{HTTP:Upgrade} websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule ^/semblance_back/socket.io/(.*) ws://127.0.0.1:5137/socket.io/\$1 [P,L]\n\n # REST API\n ProxyPass /semblance_back/api/ http://127.0.0.1:5137/api/\n ProxyPassReverse /semblance_back/api/ http://127.0.0.1:5137/api/\n\n # Socket.IO HTTP polling fallback\n ProxyPass /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n ProxyPassReverse /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n\n # Semblance SPA — served at /semblance/\n Alias /semblance $FRONTEND_DEST\n <Directory $FRONTEND_DEST>\n Options -Indexes +FollowSymLinks\n AllowOverride None\n Require all granted\n RewriteEngine On\n RewriteBase /semblance/\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteRule ^ index.html [L]\n </Directory>\n\n</VirtualHost>|' \"\$CONF\"
|
||||
|
||||
sudo apache2ctl configtest && sudo systemctl reload apache2
|
||||
echo '✓ Apache config updated and reloaded'
|
||||
fi
|
||||
"
|
||||
|
||||
# ── Step 7: Install / restart backend service ────────────────────────────────
|
||||
# ── Step 5: Rebuild and restart Docker services ───────────────────────────────
|
||||
echo ""
|
||||
echo "[7/7] Restarting backend service..."
|
||||
echo "[5/5] Rebuilding and restarting Docker services..."
|
||||
ssh "$SSH_HOST" "
|
||||
# Install or update service file
|
||||
sudo cp $DEPLOY_DIR/semblance.service /etc/systemd/system/semblance.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable semblance.service
|
||||
|
||||
# Ensure upload/temp dirs exist with correct ownership
|
||||
sudo mkdir -p $BACKEND_DIR/uploads $BACKEND_DIR/temp
|
||||
sudo chown -R www-data:www-data $BACKEND_DIR/uploads $BACKEND_DIR/temp
|
||||
|
||||
sudo systemctl restart semblance.service
|
||||
sleep 2
|
||||
systemctl status semblance.service --no-pager || true
|
||||
cd $DEPLOY_DIR
|
||||
docker compose pull mongo 2>/dev/null || true
|
||||
docker compose up -d --build
|
||||
echo ''
|
||||
echo 'Container status:'
|
||||
docker compose ps
|
||||
"
|
||||
|
||||
echo ""
|
||||
|
|
|
|||
32
docker-compose.yml
Normal file
32
docker-compose.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
services:
|
||||
mongo:
|
||||
image: mongo:7
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:27017:27017"
|
||||
volumes:
|
||||
- mongo-data:/data/db
|
||||
healthcheck:
|
||||
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:5137:5137"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
environment:
|
||||
MONGO_URI: mongodb://mongo:27017/semblance_db
|
||||
volumes:
|
||||
- ./backend/uploads:/app/uploads
|
||||
- ./backend/temp:/app/temp
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
mongo-data:
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
[Unit]
|
||||
Description=Semblance back end service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
User=www-data
|
||||
Group=www-data
|
||||
WorkingDirectory=/opt/semblance/backend
|
||||
EnvironmentFile=/opt/semblance/backend/.env
|
||||
Environment=PATH=/opt/semblance/backend/venv/bin
|
||||
ExecStart=/opt/semblance/backend/venv/bin/python /opt/semblance/backend/run.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Output to journal
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=semblance
|
||||
|
||||
# Security settings (adjusted for file uploads)
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=false
|
||||
ProtectHome=yes
|
||||
|
||||
# Allow access to temp directories
|
||||
PrivateTmp=no
|
||||
|
||||
# Writable directories for uploads and temp files
|
||||
ReadWritePaths=/opt/semblance/backend/uploads
|
||||
ReadWritePaths=/opt/semblance/backend/temp
|
||||
ReadWritePaths=/tmp
|
||||
ReadWritePaths=/var/tmp
|
||||
|
||||
# Create necessary directories
|
||||
ExecStartPre=/bin/mkdir -p /opt/semblance/backend/uploads
|
||||
ExecStartPre=/bin/mkdir -p /opt/semblance/backend/temp
|
||||
ExecStartPre=/bin/chown -R www-data:www-data /opt/semblance/backend/uploads
|
||||
ExecStartPre=/bin/chown -R www-data:www-data /opt/semblance/backend/temp
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Add table
Reference in a new issue