220 lines
8.9 KiB
Markdown
220 lines
8.9 KiB
Markdown
# Docker Deployment Guide
|
||
|
||
Deploy **dashboard-cpsp** with Docker Compose. Layout mirrors the legacy `dashboard/` stack (Postgres + API + Nginx frontend), adapted for Django + Gunicorn and a separate cron container.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ Host Server │
|
||
│ │
|
||
│ ┌─────────────┐ /api/* ┌──────────────┐ │
|
||
│ │ frontend │────────────►│ api │ │
|
||
│ │ (Nginx) │ │ (Django) │ │
|
||
│ │ port 80 │ │ port 8000 │ │
|
||
│ └─────────────┘ └──────┬───────┘ │
|
||
│ │ │
|
||
│ ┌─────────────┐ ▼ │
|
||
│ │ cron │────────────► ┌──────────────┐ │
|
||
│ │ (same image)│ │ database │ │
|
||
│ └─────────────┘ │ (PostgreSQL) │ │
|
||
│ │ port 15432* │ │
|
||
│ └──────────────┘ │
|
||
└──────────────────────────────────────────────────────────────┘
|
||
* 15432 on host → 5432 in container (SSH tunnel / DBeaver)
|
||
```
|
||
|
||
| Container | Image / build | Host port | Role |
|
||
| --------- | ----------------------- | ------------ | ----------------------------- |
|
||
| frontend | `docker/Dockerfile.web` | 80 → 80 | React SPA + Nginx API proxy |
|
||
| api | `docker/Dockerfile.api` | 18000 → 8000 | Gunicorn, migrations, static |
|
||
| cron | same as api | — | Scheduled management commands |
|
||
| database | postgres:16-alpine | 15432 → 5432 | PostgreSQL |
|
||
|
||
## Prerequisites
|
||
|
||
- Docker 20.10+
|
||
- Docker Compose v2+
|
||
- Ports available: **80** (UI), **18000** (direct API), **15432** (Postgres on localhost)
|
||
|
||
```bash
|
||
docker --version
|
||
docker compose version
|
||
```
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
cd dashboard-cpsp
|
||
cp docker/.env.example .env
|
||
# Edit .env — set SECRET_KEY and DB_PASSWORD
|
||
|
||
docker compose up -d --build
|
||
```
|
||
|
||
**Production** (empty database, no demo data):
|
||
|
||
```bash
|
||
docker compose exec api python manage.py bootstrap_admin
|
||
```
|
||
|
||
Uses `BOOTSTRAP_ADMIN_USER` / `BOOTSTRAP_ADMIN_PASSWORD` from `.env` (default in `docker/.env.example`: `admin` / `Pr04dm1n`). Optionally set `BOOTSTRAP_STAFF_USER` / `BOOTSTRAP_STAFF_PASSWORD` to create a staff login at the same time. Then log in and create sites, kandang, and cycles in Pengaturan.
|
||
|
||
**Dev / demo** (Sukawarna sample cycles):
|
||
|
||
```bash
|
||
docker compose exec api python manage.py seed_demo
|
||
```
|
||
|
||
Logins after seed: `admin` / `admin123`, `staff` / `staff123`. Do **not** run `seed_demo` on production.
|
||
|
||
### Verify
|
||
|
||
```bash
|
||
docker compose ps
|
||
docker compose logs -f
|
||
|
||
curl http://localhost/health
|
||
curl http://localhost/api/v1/health/
|
||
```
|
||
|
||
Open **http://localhost** in a browser.
|
||
|
||
## Configuration files (`docker/`)
|
||
|
||
| File | Purpose |
|
||
| -------------------------------- | ----------------------------------------------------------------- |
|
||
| `Dockerfile.web` | Frontend image (Vite + Nginx) |
|
||
| `Dockerfile.api` | Backend image (Django + Gunicorn) |
|
||
| `nginx.conf` | Nginx proxy config for frontend |
|
||
| `entrypoint-api.sh` | API container startup |
|
||
| `entrypoint-cron.sh` | Cron container startup |
|
||
| `crontab` | Fallback schedule; runtime regenerated from `DASHBOARD_PUBLISH_*` |
|
||
| `.env.example` | Compose env template → copy to project root `.env` |
|
||
| `compose.override.local.example` | Optional local Postgres port override |
|
||
|
||
## Configuration
|
||
|
||
Compose reads variables from a root `.env` file. Template: `docker/.env.example`.
|
||
|
||
Important production values:
|
||
|
||
| Variable | Purpose |
|
||
| --------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||
| `SECRET_KEY` | Django secret — use a long random string |
|
||
| `DB_PASSWORD` | PostgreSQL password |
|
||
| `CSRF_TRUSTED_ORIGINS` | Must include your public UI origin (e.g. `https://dashboard.example.com`) |
|
||
| `CORS_ALLOWED_ORIGINS` | Same as above if the SPA is on a different origin |
|
||
| `KARUNG_WEB_ADMIN_BASE_URL` | External karung service; default `http://host.docker.internal:5000` reaches the host from containers |
|
||
| `BOOTSTRAP_ADMIN_USER` | Superadmin username for `bootstrap_admin` (default `admin`) |
|
||
| `BOOTSTRAP_ADMIN_PASSWORD` | Superadmin password for `bootstrap_admin` (required for production bootstrap) |
|
||
| `BOOTSTRAP_STAFF_USER` | Optional staff username; leave empty to skip staff creation |
|
||
| `BOOTSTRAP_STAFF_PASSWORD` | Staff password (required when `BOOTSTRAP_STAFF_USER` is set) |
|
||
| `SITE_API_KEY` | Optional fixed API key for scripts (hashed at rest) |
|
||
|
||
Wagtail admin is proxied at **http://localhost/admin/** (through Nginx → Django).
|
||
|
||
## Management
|
||
|
||
```bash
|
||
# Start / stop
|
||
docker compose start
|
||
docker compose stop
|
||
docker compose restart
|
||
|
||
# Logs
|
||
docker compose logs -f api
|
||
docker compose logs -f frontend
|
||
docker compose logs -f cron
|
||
|
||
# Django shell
|
||
docker compose exec api python manage.py shell
|
||
|
||
# Database (psql)
|
||
docker compose exec database psql -U newdashboard -d newdashboard
|
||
|
||
# From host (port 15432)
|
||
psql -h localhost -p 15432 -U newdashboard -d newdashboard
|
||
```
|
||
|
||
### Update after code changes
|
||
|
||
```bash
|
||
git pull
|
||
docker compose down
|
||
docker compose up -d --build
|
||
```
|
||
|
||
Rolling update (less downtime):
|
||
|
||
```bash
|
||
docker compose build
|
||
docker compose up -d --no-deps --build api
|
||
docker compose up -d --no-deps --build cron
|
||
docker compose up -d --no-deps --build frontend
|
||
```
|
||
|
||
### Local DBeaver on Mac
|
||
|
||
```bash
|
||
cp docker/compose.override.local.example docker-compose.override.yml
|
||
```
|
||
|
||
Adds `127.0.0.1:5432:5432` while keeping the server’s `15432` mapping.
|
||
|
||
## Cron jobs
|
||
|
||
The `cron` service runs:
|
||
|
||
- **Every 10 min** — `sync_iot_from_api` (IoT panel, real-time — not gated by publish cutoff)
|
||
- **At `DASHBOARD_PUBLISH_HOUR`:`DASHBOARD_PUBLISH_MINUTE` daily** — `sync_karung_from_web_admin` then `recompute_kpi_rollups` (entrypoint renders cron from these env vars; default 17:00)
|
||
|
||
Dashboard reads hide today's KPI/counting/weight/karung data until the configured publish time WIB. **IoT panel is excluded** and updates live every 10 minutes.
|
||
|
||
Logs: `docker compose exec cron tail -f /var/log/cron.log`
|
||
|
||
## Troubleshooting
|
||
|
||
**API unhealthy**
|
||
|
||
```bash
|
||
docker compose logs api
|
||
docker compose exec api python manage.py migrate --plan
|
||
```
|
||
|
||
**Frontend 502 on /api**
|
||
|
||
Ensure `api` is healthy and both services share `newdashboard-network`:
|
||
|
||
```bash
|
||
docker compose ps
|
||
curl http://127.0.0.1:18000/api/v1/health/
|
||
```
|
||
|
||
**Database connection errors**
|
||
|
||
Check Postgres is healthy and credentials in `.env` match `docker-compose.yml` defaults.
|
||
|
||
**Port conflicts**
|
||
|
||
Change mappings in `docker-compose.yml`, e.g. `"8080:80"` for frontend.
|
||
|
||
**Reset database** (destructive)
|
||
|
||
```bash
|
||
docker compose down -v
|
||
docker compose up -d --build
|
||
docker compose exec api python manage.py bootstrap_admin
|
||
```
|
||
|
||
## Security notes
|
||
|
||
- Do not commit `.env` with real secrets.
|
||
- Use strong `SECRET_KEY` and `DB_PASSWORD` in production.
|
||
- Restrict database port `15432` to localhost (already bound to `127.0.0.1`).
|
||
- Put HTTPS in front of port 80 (reverse proxy + Let's Encrypt) for public deployment.
|
||
|
||
---
|
||
|
||
Docker deployment guide — dashboard-cpsp
|