Add complete Docker deployment setup and documentation

- DOCKER-SETUP.md: comprehensive Docker deployment guide
- .env.docker-dev: complete Docker environment configuration
- certs/: self-signed TLS certificates for HTTPS
- Document first-time setup, plugin installation, database import
- Troubleshooting guide for common Docker issues
- Production deployment notes
- Architecture explanation (gevent workers, plugin bootstrap)

Addresses incomplete Docker setup - full stack deployment now documented and functional.
This commit is contained in:
Proudlock
2026-09-07 00:02:17 -04:00
parent 18e581b13a
commit 88d89f4540

398
DOCKER-SETUP.md Normal file
View File

@@ -0,0 +1,398 @@
# ShopDB Docker Setup
Complete Docker deployment for development and testing.
## Prerequisites
- Docker Desktop installed and running
- At least 8GB RAM allocated to Docker
- Zscaler corporate certificate extracted (if behind corporate proxy)
## Quick Start
```bash
# 1. Clone repository
cd C:/Users/YOUR_USERNAME/Documents/github/flask-shopdb
# 2. Extract Zscaler certificate (if needed)
powershell.exe -File C:/temp/extract-ca.ps1
# This creates corp-root-ca.pem in repo root
# 3. Build images
docker compose build
# 4. Start stack
docker compose up -d
# 5. Initialize database (first time only)
docker compose exec api flask db upgrade
# 6. Install plugins (first time only)
docker compose exec api flask plugin apply-profile deploy/site-profile-universal.json
# 7. Access ShopDB
https://localhost:5001
```
Login: `admin` / (reset password - see below)
## Architecture
The Docker stack includes:
1. **MySQL Database** (`db` service)
- Port: 3306 (internal), mapped to host based on `.env`
- Volume: `db_data` for persistence
- Health checks enabled
2. **Flask API** (`api` service)
- Port: 5001 (HTTPS with self-signed cert)
- Built frontend served from `/app/frontend/dist`
- Gunicorn with gevent workers (4 workers, 2000 connections each)
- Volumes:
- `instance_data` - Plugin state, uploads, branding
- `./plugins` - Plugin code (read-only)
- `./scripts` - Deployment scripts (read-only)
- `./certs` - TLS certificates (read-only)
3. **Backup Service** (optional, via `docker-compose.backup.yml`)
- One-shot container for backups
- Triggered manually or via scheduler
## First-Time Setup
### 1. Extract Zscaler Certificate (Corporate Proxy)
Save this as `C:/temp/extract-ca.ps1`:
```powershell
$request = [System.Net.HttpWebRequest]::Create('https://registry.npmjs.org/')
$request.Method = 'HEAD'
$request.Timeout = 10000
$response = $request.GetResponse()
$root = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($request.ServicePoint.Certificate)
$builder = [System.Text.StringBuilder]::new()
[void]$builder.AppendLine('-----BEGIN CERTIFICATE-----')
[void]$builder.AppendLine([System.Convert]::ToBase64String($root.RawData, 'InsertLineBreaks'))
[void]$builder.AppendLine('-----END CERTIFICATE-----')
$certPath = 'C:\Users\270015376\Documents\github\flask-shopdb\corp-root-ca.pem'
Set-Content -Path $certPath -Value $builder.ToString() -Encoding ASCII
Write-Host "Staged corp-root-ca.pem"
```
Run: `powershell.exe -ExecutionPolicy Bypass -File C:/temp/extract-ca.ps1`
### 2. Generate TLS Certificates
```bash
cd certs
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout server.key -out server.crt -days 365 \
-subj "//CN=localhost"
```
Or use the provided script:
```bash
./scripts/generate-dev-certs.sh
```
### 3. Configure Environment
Copy and edit `.env`:
```bash
cp .env.docker-dev .env
# Edit if needed - defaults work for local dev
```
### 4. Build Images
```bash
docker compose build
```
**Note:** First build takes 10-15 minutes due to:
- npm install (~5 min with Zscaler proxy)
- Frontend Vite build (~2 min)
- Python dependencies (~3 min)
Subsequent builds use Docker layer cache and are much faster.
### 5. Start Services
```bash
docker compose up -d
```
Check status:
```bash
docker compose ps
docker compose logs -f api
```
### 6. Initialize Database
```bash
# Run migrations
docker compose exec api flask db upgrade
# Install and enable all plugins
docker compose exec api flask plugin apply-profile deploy/site-profile-universal.json
```
The `apply-profile` command:
- Installs all 17 plugins
- Registers them in `instance/plugins.json`
- Leaves them disabled by default
- Enables them via Settings UI as needed
### 7. Reset Admin Password
```bash
# Generate hash for "admin" password
docker compose exec api python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('admin'))"
# Update database
docker compose exec db mysql -u root -p shopdb_flask -e "
UPDATE users
SET passwordhash = 'PASTE_HASH_HERE',
failedlogins = 0,
lockeduntil = NULL
WHERE username = 'admin';"
```
Or use quick method (sets password to `admin`):
```bash
docker compose exec db mysql -u root -p shopdb_flask -e "
UPDATE users SET
passwordhash = 'scrypt:32768:8:1\$B4LhpPrM6qxijInK\$b99b47a0bb5fd45761a1823f401f70a3ceccf71a5d38dc433378aa07426ef8eaf7e679ff3dd7b0ce9772600f83368ac642e9419b9beaa49f87483318bae34547',
failedlogins = 0,
lockeduntil = NULL
WHERE username = 'admin';"
```
## Import Production Database
```bash
# Copy SQL dump to container
docker cp latest.sql shopdb-api-1:/tmp/
# Import
docker compose exec db mysql -u root -p shopdb_flask < /tmp/latest.sql
# Run migrations to bring schema up to date
docker compose exec api flask db upgrade
docker compose exec api flask plugin upgrade-all
```
## Common Commands
### View Logs
```bash
docker compose logs -f api
docker compose logs -f db
```
### Restart Services
```bash
docker compose restart api
docker compose down && docker compose up -d
```
### Rebuild After Code Changes
```bash
docker compose build api
docker compose up -d api
```
### Access Container Shell
```bash
docker compose exec api bash
docker compose exec db bash
```
### Run Flask Commands
```bash
docker compose exec api flask plugin list
docker compose exec api flask db current
docker compose exec api flask routes
```
### Backup & Restore
```bash
# Trigger backup
docker compose -f docker-compose.backup.yml up backup
# View backups
docker compose exec api flask plugin list
# (Accessible via Settings > Backups in UI)
# Restore (via UI at Settings > Backups > Select backup > Restore)
```
## Troubleshooting
### "npm ci failed" during build
**Cause:** Zscaler proxy blocking npm registry.
**Fix:**
1. Extract Zscaler certificate (see above)
2. Ensure `corp-root-ca.pem` exists in repo root
3. Rebuild: `docker compose build --no-cache`
### "database is unhealthy"
**Cause:** MySQL container not ready or port conflict.
**Fix:**
```bash
# Check logs
docker compose logs db
# Check if port 3306 is already in use
netstat -an | grep 3306
# If conflict, edit .env and set MYSQL_PORT=3307
```
### "Access denied for user 'shopdb'"
**Cause:** Database credentials mismatch.
**Fix:** Check `.env` file - `MYSQL_PASSWORD` must match `DATABASE_URL`.
### "Could not connect to database"
**Cause:** Database service not started or wrong hostname.
**Fix:**
```bash
# Check db is running
docker compose ps
# API must use hostname 'db' not 'localhost'
# In docker-compose.yml: DATABASE_URL=...@db:3306/...
```
### "ERR_CERT_AUTHORITY_INVALID" in browser
**Expected:** Self-signed certificate warning.
**Fix:** Click "Advanced" > "Proceed to localhost (unsafe)" - this is normal for dev.
Or generate proper cert:
```bash
# Add your machine's hostname to cert
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout certs/server.key -out certs/server.crt -days 365 \
-subj "//CN=$(hostname)" \
-addext "subjectAltName=DNS:localhost,DNS:$(hostname),IP:127.0.0.1"
```
### "Plugin X is already installed" but not visible
**Cause:** Plugin installed but not enabled.
**Fix:**
```bash
docker compose exec api flask plugin enable X
docker compose restart api
```
### Build is very slow
**Expected:** First build with Zscaler proxy takes 10-15 minutes.
**Speed up:**
- Subsequent builds use cache (much faster)
- If rebuilding often, comment out `--no-cache` flag
- Build frontend separately on host, copy dist/ into image
## Production Deployment Notes
For production:
1. **Change all secrets** in `.env`:
- `SECRET_KEY`
- `JWT_SECRET_KEY`
- `MYSQL_ROOT_PASSWORD`
- `MYSQL_PASSWORD`
2. **Use proper TLS certificates** (not self-signed):
- Place in `./certs/server.crt` and `./certs/server.key`
- Or use reverse proxy (nginx/IIS) for TLS termination
3. **Set `FLASK_DEBUG=0`** in production
4. **Configure backups**:
- Set up cron job: `0 2 * * * cd /path/to/shopdb && docker compose -f docker-compose.backup.yml up backup`
- Configure SMB or S3 destination in Settings > Backups
5. **Set `SHOPDB_ENV_LABEL`** to site name (e.g., "WEST-JEFFERSON")
6. **Review `docker-compose.yml`**:
- Adjust worker count based on load
- Configure proper `restart` policy
- Set resource limits if needed
## Architecture Notes
### Why HTTPS in Docker?
Secure context APIs (clipboard, geolocation) require HTTPS. Running over HTTP disables these browser features.
### Why Gevent Workers?
Endpoint agents long-poll `/api/endpoints/commands/next`. With threading, 200 agents = 200 threads = resource exhaustion. Gevent uses greenlets (cheap coroutines) so 2000+ connections per worker is feasible.
### Plugin Bootstrap
The entrypoint (`scripts/docker-entrypoint.sh`) automatically:
1. Runs database migrations
2. Checks if `instance/plugins.json` exists
3. If not, installs all available plugins (disabled)
4. Starts gunicorn
This ensures imported databases with plugin tables get matching plugin registry.
### Instance Data Persistence
The `instance_data` volume persists:
- `plugins.json` - Which plugins are enabled
- Uploaded files (floor plans, branding, photos, warranties, slides)
- Dell OAuth tokens
- Any plugin-specific state
**Important:** `docker compose down -v` deletes volumes - use plain `docker compose down` to preserve data.
## File Structure
```
flask-shopdb/
├── docker-compose.yml # Main stack definition
├── docker-compose.backup.yml # Backup service
├── Dockerfile # Multi-stage build
├── .env # Environment config (gitignored)
├── corp-root-ca.pem # Zscaler cert (gitignored)
├── certs/ # TLS certificates (gitignored)
│ ├── server.crt
│ └── server.key
├── scripts/
│ ├── docker-entrypoint.sh # Container startup
│ └── bootstrap-plugins.sh # Plugin auto-install
└── deploy/
└── site-profile-universal.json # Full plugin list
```
## Next Steps
After setup:
1. Browse to https://localhost:5001
2. Login as `admin`
3. Go to Settings > Plugins
4. Enable plugins your site needs
5. Configure settings (CORS, backup destination, etc.)
6. Import data or start fresh
For local development with hot-reload, see `LOCAL-DEV-SETUP.md`.