Cetak Biru Deployment Server Produksi
Membawa aplikasi asinkron modern berbasis FastAPI + React dari laptop lokal ke server produksi enterprise menuntut perhatian infrastruktur yang tidak dapat diselesaikan hanya dengan optimasi kode semata:
- Timeout Proxy WebSocket: Nginx secara default memutus koneksi TCP idle setelah 60 detik. Di tengah pelaksanaan ujian sekolah, pemutusan sepihak ini akan mematikan layar telemetri pengawas guru secara tiba-tiba.
- Isolasi Sumber Daya pada Hardware Ekonomis: Institusi pendidikan umumnya menggunakan Virtual Private Server (VPS) dengan spesifikasi terjangkau (misal: 2 vCPU / 4GB RAM atau 4 vCPU / 8GB RAM). Seluruh stack (FastAPI, React, PostgreSQL 16, dan Redis 7) harus berjalan harmonis tanpa memicu kepanikan memori Out-Of-Memory (OOM) killer Linux.
- Pemulihan Otomatis (Self-Healing) & Peringatan Dini: Jika lonjakan konkurensi memicu crash pada kontainer backend pada pukul 08:00 WIB, sistem harus mampu melakukan restart otomatis dalam hitungan detik sembari mengirimkan alert webhook ke tim teknis.
Pada proyek mfarim/fastapi-elearning-react, kami merancang arsitektur produksi tangguh pada server VPS tunggal (single VPS).
1. Orkestrasi Multi-Kontainer dengan Docker Compose
Pada berkas docker-compose.yml, seluruh service didefinisikan lengkap beserta healthcheck, kebijakan restart, dan volume penyimpanan data persisten:
YAML
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
container_name: fastapi-elearning-postgres
restart: always
environment:
POSTGRES_DB: ${POSTGRES_DB:-elearning}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
ports:
- "127.0.0.1:5434:5432" # Ikat hanya ke localhost untuk keamanan
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- elearning-network
redis:
image: redis:7-alpine
container_name: fastapi-elearning-redis
restart: always
ports:
- "127.0.0.1:6380:6379" # Ikat hanya ke localhost
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
- elearning-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: fastapi-elearning-backend
restart: always
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-elearning}
REDIS_URL: redis://redis:6379/0
SECRET_KEY: ${SECRET_KEY}
ACCESS_TOKEN_EXPIRE_MINUTES: 120
ports:
- "127.0.0.1:8000:8000"
volumes:
- ./backend/storage:/app/storage
networks:
- elearning-network
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: fastapi-elearning-frontend
restart: always
ports:
- "127.0.0.1:3001:80"
networks:
- elearning-network
volumes:
postgres_data:
redis_data:
networks:
elearning-network:
driver: bridge
Praktik Keamanan Krusial:
- Binding Port Khusus Localhost (
127.0.0.1:5434): Port database dan Redis hanya mendengarkan request dari dalam mesin VPS, mencegah pemindaian port terbuka secara langsung dari internet publik. depends_ondenganservice_healthy: Kontainer backend menunda proses booting sampai PostgreSQL berhasil lolos pengecekanpg_isready, mengeliminasi error connection refused pada saat server restart.
2. Dockerfile Produksi: Python 3.12 Ramping & Efisien
Di backend/Dockerfile, kami membangun image multi-layer berbasis python:3.12-slim:
DOCKERFILE
# backend/Dockerfile
FROM python:3.12-slim
WORKDIR /app
# Pasang pustaka sistem yang dibutuhkan untuk kompilasi biner asyncpg & healthcheck
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# Cache dependensi Python
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Salin seluruh kode aplikasi
COPY . .
# Buat direktori penyimpanan berkas jika belum ada
RUN mkdir -p /app/storage/uploads
EXPOSE 8000
# Jalankan Uvicorn dengan konfigurasi worker proses teroptimasi
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
3. Host Nginx Reverse Proxy dengan Konfigurasi WSS & SSL
Pada sistem operasi host VPS, Nginx bertindak sebagai gerbang terdepan. Konfigurasi di bawah ini menyediakan terminasi SSL, header keamanan, dan yang paling utama: stabilitas koneksi persisten WebSocket:
NGINX
# /etc/nginx/sites-available/elearning.conf
# 1. Redirection Otomatis HTTP ke HTTPS
server {
listen 80;
server_name elearning.sekolah.id;
return 301 https://$host$request_uri;
}
# 2. Server HTTPS Produksi
server {
listen 443 ssl http2;
server_name elearning.sekolah.id;
# Sertifikat SSL dari Let's Encrypt via Certbot
ssl_certificate /etc/letsencrypt/live/elearning.sekolah.id/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/elearning.sekolah.id/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Batas ukuran upload untuk berkas Excel dan Tugas Siswa
client_max_body_size 30M;
# Header Keamanan Tingkat Lanjut
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# --- Proxy REST API ---
location /api/ {
proxy_pass http://127.0.0.1:8000/api/;
proxy_set_header Host $host;
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;
}
# --- Dokumentasi Interaktif API ---
location /docs {
proxy_pass http://127.0.0.1:8000/docs;
proxy_set_header Host $host;
}
location /openapi.json {
proxy_pass http://127.0.0.1:8000/openapi.json;
proxy_set_header Host $host;
}
# --- Tunneling WebSocket Telemetri Pengawas ---
location /ws/ {
proxy_pass http://127.0.0.1:8000/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# KRUSIAL: Mencegah Nginx memutuskan koneksi WebSocket pengawas setelah 60 detik idle
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
# --- Frontend SPA (React 19) ---
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Konfigurasi Krusial: proxy_read_timeout 86400s
Secara default, Nginx memiliki batas waktu baca 60 detik (proxy_read_timeout 60s). Jika seorang siswa sedang berkonsentrasi membaca soal esai yang rumit dan tidak ada event telemetri yang dikirimkan selama 60 detik, Nginx akan memutus koneksi socket secara sepihak.
Penyetelan proxy_read_timeout 86400s (24 jam) menjaga koneksi telemetri tetap hidup sepanjang hari ujian sekolah.
4. Skrip Monitoring Kesehatan & Pemulihan Otomatis (Auto-Recovery)
Bahkan aplikasi yang dirancang paling teliti sekalipun dapat mengalami kegagalan di luar kendali. Di scripts/health-monitor.sh, kami membangun skrip pengawas mandiri:
BASH
#!/usr/bin/env bash
# ==============================================================================
# Skrip Watchdog Monitoring Kesehatan & Auto-Recovery Produksi
# scripts/health-monitor.sh
# ==============================================================================
set -euo pipefail
TARGET_URL="${HEALTH_CHECK_URL:-http://localhost:8000/health}"
WEBHOOK_URL="${ALERT_WEBHOOK_URL:-https://discord.com/api/webhooks/your_webhook_here}"
TIMEOUT_SECONDS=5
PROJECT_DIR="/var/www/python-fastapi-react-elearning"
LOG_FILE="/var/log/elearning-health.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
send_alert() {
local message="$1"
if [ -n "$WEBHOOK_URL" ]; then
curl -s -X POST -H "Content-Type: application/json" \
-d "{\"content\": \"⚠️ **ALERT: Insiden Sistem E-Learning**\n$message\"}" \
"$WEBHOOK_URL" > /dev/null 2>&1 || true
fi
}
# 1. Pindai endpoint healthcheck backend
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT_SECONDS" "$TARGET_URL" || echo "000")
if [ "$HTTP_STATUS" -eq 200 ]; then
log "KONDISI NORMAL: API mengembalikan HTTP 200"
exit 0
else
log "KEGAGALAN SISTEM: API mengembalikan kode HTTP $HTTP_STATUS! Memulai prosedur auto-recovery..."
send_alert "Backend FastAPI tidak merespons (HTTP $HTTP_STATUS) di server $(hostname). Melakukan restart kontainer otomatis..."
# 2. Eksekusi restart kontainer otomatis
cd "$PROJECT_DIR"
docker compose restart backend
# 3. Tunggu dan verifikasi ulang status sistem
sleep 10
RETRY_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT_SECONDS" "$TARGET_URL" || echo "000")
if [ "$RETRY_STATUS" -eq 200 ]; then
log "PEMULIHAN BERHASIL: Backend kembali normal (HTTP 200)"
send_alert "✅ **PEMULIHAN BERHASIL**: Backend FastAPI berhasil pulih dan aktif kembali di server $(hostname)."
else
log "KRITIKAL: Auto-recovery gagal! Membutuhkan intervensi manual tim engineer."
send_alert "🚨 **PERINGATAN KRITIKAL**: Auto-recovery gagal! Backend tetap mati (HTTP $RETRY_STATUS)."
fi
fi
Penjadwalan Berkala via Cron Host
Daftarkan skrip ke crontab server VPS untuk berjalan setiap 5 menit:
BASH
# Tambahkan ke crontab host (crontab -e):
*/5 * * * * /var/www/python-fastapi-react-elearning/scripts/health-monitor.sh >> /var/log/elearning-cron.log 2>&1
Jika terjadi kendala pada akhir pekan atau malam hari, pengawas akan merestart kontainer dalam 5 menit, memulihkan layanan, dan mengirimkan ringkasan insiden ke kanal Discord/Slack tim engineering.
Rekapitulasi Lengkap 6 Bab Series Rebuild
Melalui rangkaian 6 bab komprehensif ini, kita telah mentransformasikan platform ujian monolitik Laravel menjadi arsitektur modern berkecepatan tinggi berbasis Python FastAPI:
| Part | Topik Pembahasan | Intisari Rekayasa Arsitektur |
|---|---|---|
| Part 1 | Arsitektur & Migrasi Monolit | Perbandingan runtime sinkron PHP-FPM vs asinkron ASGI, komparasi 3 stack (Laravel vs Java vs Python). |
| Part 2 | Async ORM & Type Safety | SQLAlchemy 2.0 Async, mitigasi query N+1 via selectinload, serta keamanan expire_on_commit=False. |
| Part 3 | Engine Ujian CBT Presisi Tinggi | Sisa waktu otoritatif server, ambang batas 5x pelanggaran curang, dan native ASGI WebSocket dengan Redis. |
| Part 4 | Penguatan Keamanan Enterprise | Stateless JWT, RBAC deklaratif, dan 1-Click admin impersonation dengan pemulihan sesi instan. |
| Part 5 | Pipeline Data & Jaminan Kualitas | Streaming openpyxl hemat memori (<15MB), transaksi batch atomik, serta pengujian otomatis Pytest-Asyncio. |
| Part 6 | Deployment VPS & Monitoring Produksi | Docker Compose multi-kontainer, Nginx SSL reverse proxy, tuning WebSocket, serta skrip monitoring auto-recovery. |
Seluruh kode sumber, konfigurasi Docker, dan pengujian otomatis tersedia secara open-source di GitHub: mfarim/fastapi-elearning-react.

