Update deployment: Go API backend with Docker Compose
- Update Dockerfile to golang:1.26-alpine (matching go.mod) - Update deploy.sh for Docker Compose workflow - Rewrite DEPLOYMENT.md for new API architecture - Add env vars for face service and Redis configuration
This commit is contained in:
parent
ad0e78c9c9
commit
475fd95a59
183
DEPLOYMENT.md
183
DEPLOYMENT.md
@ -1,10 +1,10 @@
|
|||||||
# Hatch Landing Page — Deployment Guide
|
# Hatch API — Deployment Guide
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The Hatch landing page (`hatch.surf`) is a **static HTML** site deployed via **Docker** on the production server.
|
The Hatch API (`hatch.surf`) is a **Go backend** deployed via **Docker Compose** on the production server.
|
||||||
|
|
||||||
**Key principle**: All changes MUST go through the git repository and automated deployment. Never edit files directly on the server or use `/var/www`.
|
**Key principle**: All changes MUST go through the git repository and automated deployment. Never edit files directly on the server.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -13,17 +13,16 @@ The Hatch landing page (`hatch.surf`) is a **static HTML** site deployed via **D
|
|||||||
**Location**: `/home/nara/apps/web/`
|
**Location**: `/home/nara/apps/web/`
|
||||||
|
|
||||||
**Git Remotes**:
|
**Git Remotes**:
|
||||||
- `github` → `elfoundation/hatch` (source of truth)
|
- `github` → `el-foundation/hatch-surf` (Gitea, source of truth)
|
||||||
- `origin` → `el-foundation/hatch-surf` (Gitea push-mirror, deploys to hatch.surf)
|
- `origin` → `el-foundation/hatch-surf` (Gitea push-mirror)
|
||||||
|
|
||||||
**Structure**:
|
**Structure**:
|
||||||
```
|
```
|
||||||
site/ # Static files served by nginx
|
cmd/hatch/ # Go server entrypoint
|
||||||
index.html # Main landing page
|
internal/ # Go packages (handler, store, face, etc.)
|
||||||
style.css # Styles
|
Dockerfile # Multi-stage Docker build (Go → scratch)
|
||||||
main.js # Anime.js animations
|
docker-compose.yml # Container orchestration (hatch + caddy)
|
||||||
Dockerfile # Docker build configuration (static files only)
|
Caddyfile # Caddy reverse proxy config
|
||||||
docker-compose.yml # Container orchestration
|
|
||||||
deploy.sh # Deployment script
|
deploy.sh # Deployment script
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -31,18 +30,11 @@ deploy.sh # Deployment script
|
|||||||
|
|
||||||
## Deployment Flow
|
## Deployment Flow
|
||||||
|
|
||||||
### Source of Truth
|
|
||||||
|
|
||||||
**GitHub `main`** is the source of truth. Gitea is a push-mirror.
|
|
||||||
|
|
||||||
All development happens via PRs to `elfoundation/hatch`. The deploy script pulls from GitHub and pushes to Gitea.
|
|
||||||
|
|
||||||
### Automatic (Recommended)
|
### Automatic (Recommended)
|
||||||
|
|
||||||
1. Merge PR to GitHub `main`
|
1. Merge PR to `main`
|
||||||
2. Gitea webhook triggers `gitea-webhook.py`
|
2. Gitea webhook triggers `gitea-webhook.py`
|
||||||
3. Script runs `deploy.sh` automatically
|
3. Script runs `deploy.sh` automatically
|
||||||
4. `deploy.sh` pulls from GitHub, pushes to Gitea (push-mirror), rebuilds Docker, restarts
|
|
||||||
|
|
||||||
### Manual
|
### Manual
|
||||||
|
|
||||||
@ -54,56 +46,66 @@ cd /home/nara/apps/web
|
|||||||
**What `deploy.sh` does**:
|
**What `deploy.sh` does**:
|
||||||
1. `git pull github main` — fetches latest from GitHub (source of truth)
|
1. `git pull github main` — fetches latest from GitHub (source of truth)
|
||||||
2. `git push origin main` — syncs to Gitea (push-mirror)
|
2. `git push origin main` — syncs to Gitea (push-mirror)
|
||||||
3. `cd site && docker build -t hatch-web:latest .` — rebuilds image
|
3. `docker compose down` — stops current containers
|
||||||
4. Stops and removes old container
|
4. `docker compose build hatch` — rebuilds the Go binary in a multi-stage build
|
||||||
5. `docker run -d` — starts new container
|
5. `docker compose --profile with-caddy up -d` — starts hatch + caddy
|
||||||
6. Verifies deployment with health check
|
6. Verifies health endpoint responds 200
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Docker Setup
|
## Docker Setup
|
||||||
|
|
||||||
**Container**: `hatch-web`
|
**Services**:
|
||||||
**Port**: `8080` (mapped to host)
|
| Service | Image | Port | Purpose |
|
||||||
**Internal**: nginx serving static files on port 80
|
|----------|----------------|-------|------------------------|
|
||||||
**Network**: `hatch-network`
|
| `hatch` | web-hatch | 8080 | Go API server |
|
||||||
|
| `caddy` | caddy:2-alpine | 80/443| Reverse proxy + TLS |
|
||||||
|
|
||||||
|
**Profiles**:
|
||||||
|
- Default: `hatch` only (API on port 8080)
|
||||||
|
- `with-caddy`: `hatch` + `caddy` (full HTTPS via Let's Encrypt)
|
||||||
|
|
||||||
**Check status**:
|
**Check status**:
|
||||||
```bash
|
```bash
|
||||||
docker ps | grep hatch-web
|
docker compose ps
|
||||||
docker logs hatch-web
|
docker compose logs hatch
|
||||||
|
docker compose logs caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
**Restart without rebuild**:
|
---
|
||||||
```bash
|
|
||||||
docker compose -f /home/nara/apps/web/docker-compose.yml restart
|
## Environment Variables
|
||||||
```
|
|
||||||
|
Configure in `.env` or pass to Docker Compose:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|------------------------|----------------------|--------------------------------------|
|
||||||
|
| `HATCH_HOSTNAME` | `localhost` | Public hostname for TLS cert |
|
||||||
|
| `HATCH_BASE_URL` | `http://localhost` | Base URL for SSE and self-references |
|
||||||
|
| `FACE_SERVICE_ENABLED` | `false` | Enable AWS Rekognition face search |
|
||||||
|
| `AWS_REGION` | `us-east-1` | AWS region for Rekognition |
|
||||||
|
| `REKOGNITION_COLLECTION` | `hatch-faces` | Rekognition collection ID |
|
||||||
|
| `REDIS_URL` | — | Redis URL (optional, for caching) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Making Changes
|
## Making Changes
|
||||||
|
|
||||||
### 1. Edit the static files
|
### 1. Edit Go source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/nara/apps/web
|
cd /home/nara/apps/web
|
||||||
|
vim cmd/hatch/main.go # Server entrypoint
|
||||||
# Edit landing page
|
vim internal/handler/ # HTTP handlers
|
||||||
vim out/index.html
|
vim internal/face/ # Face detection service
|
||||||
|
vim internal/store/ # Database layer
|
||||||
# Edit styles
|
|
||||||
vim out/style.css
|
|
||||||
|
|
||||||
# Edit animations
|
|
||||||
vim out/main.js
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Test locally (optional)
|
### 2. Build and test locally
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Serve files locally
|
go build ./cmd/hatch/
|
||||||
cd out && python3 -m http.server 3000
|
go test ./...
|
||||||
# Visit http://localhost:3000
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Commit and push
|
### 3. Commit and push
|
||||||
@ -124,23 +126,21 @@ Either wait for webhook or run manually:
|
|||||||
### 5. Verify
|
### 5. Verify
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s http://localhost:8080 | head -20
|
curl -s http://localhost:8080/healthz
|
||||||
# Or visit https://hatch.surf
|
curl -s http://localhost:8080/readyz
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Common Mistakes to Avoid
|
## Common Mistakes to Avoid
|
||||||
|
|
||||||
❌ **DON'T**: Edit files in `/var/www/html/`
|
|
||||||
❌ **DON'T**: Edit files directly inside the Docker container
|
❌ **DON'T**: Edit files directly inside the Docker container
|
||||||
❌ **DON'T**: Work on static HTML/CSS files in a workspace without committing
|
❌ **DON'T**: Commit without testing
|
||||||
❌ **DON'T**: Copy files to the server without using git
|
❌ **DON'T**: Use `go run` in production
|
||||||
|
|
||||||
✅ **DO**: Edit files in `/home/nara/apps/web/out/`
|
✅ **DO**: Test with `go test ./...` before committing
|
||||||
✅ **DO**: Commit changes to git
|
✅ **DO**: Use `docker compose build` for production builds
|
||||||
✅ **DO**: Use `deploy.sh` for deployment
|
✅ **DO**: Check health endpoints after deployment
|
||||||
✅ **DO**: Test locally before pushing
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -151,9 +151,6 @@ If a deployment causes issues:
|
|||||||
```bash
|
```bash
|
||||||
cd /home/nara/apps/web
|
cd /home/nara/apps/web
|
||||||
|
|
||||||
# View recent commits
|
|
||||||
git log --oneline -10
|
|
||||||
|
|
||||||
# Revert to previous version
|
# Revert to previous version
|
||||||
git revert HEAD
|
git revert HEAD
|
||||||
git push origin main
|
git push origin main
|
||||||
@ -162,22 +159,21 @@ git push origin main
|
|||||||
./deploy.sh
|
./deploy.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Previous builds are backed up in `out.backup.*` directories.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Cache Headers
|
## API Endpoints
|
||||||
|
|
||||||
**HTML pages**: `no-cache, must-revalidate` — browsers always revalidate, ensuring fresh content after deploys.
|
| Method | Path | Description |
|
||||||
|
|--------|-------------------------------|---------------------------------|
|
||||||
**Static assets** (CSS/JS/images): `public, immutable` with 1-year expiry.
|
| GET | `/healthz` | Liveness probe |
|
||||||
|
| GET | `/readyz` | Readiness probe (checks DB) |
|
||||||
**Nginx config**: Built into the Dockerfile.
|
| GET | `/api/v1/spaces` | List public spaces |
|
||||||
|
| POST | `/api/v1/spaces` | Create a space |
|
||||||
If users report seeing old content after deployment:
|
| GET | `/api/v1/spaces/{slug}` | Get space by slug |
|
||||||
1. Verify the container is running: `docker ps | grep hatch-web`
|
| POST | `/api/v1/spaces/{slug}/photos`| Upload photo to space |
|
||||||
2. Check the HTML has new content: `curl -s http://localhost:8080 | head -5`
|
| POST | `/api/v1/face/search` | Search faces by image |
|
||||||
3. Ask user to hard refresh: Ctrl+Shift+R (Chrome/Firefox) or Cmd+Shift+R (Mac)
|
| GET | `/e/{endpointID}` | Inspect captured requests |
|
||||||
|
| GET | `/e/{endpointID}/events` | SSE stream for live updates |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -185,26 +181,51 @@ If users report seeing old content after deployment:
|
|||||||
|
|
||||||
**Container not starting**:
|
**Container not starting**:
|
||||||
```bash
|
```bash
|
||||||
docker logs hatch-web
|
docker compose logs hatch
|
||||||
docker compose -f /home/nara/apps/web/docker-compose.yml up -d
|
docker compose logs caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
**Changes not showing**:
|
**Health check failing**:
|
||||||
1. Verify commit pushed: `git log --oneline -1`
|
```bash
|
||||||
2. Check container was rebuilt: `docker images | grep hatch-web`
|
curl -v http://localhost:8080/healthz
|
||||||
3. Force rebuild: `cd /home/nara/apps/web && docker build -t hatch-web:latest . && docker compose up -d`
|
docker compose exec hatch /bin/hatch # Check binary exists
|
||||||
|
```
|
||||||
|
|
||||||
|
**Database issues**:
|
||||||
|
```bash
|
||||||
|
# SQLite database is persisted in a Docker volume
|
||||||
|
docker volume ls | grep hatch-data
|
||||||
|
docker run --rm -v hatch-data:/data alpine ls -la /data/
|
||||||
|
```
|
||||||
|
|
||||||
**Port conflict**:
|
**Port conflict**:
|
||||||
```bash
|
```bash
|
||||||
lsof -i :8080
|
sudo lsof -i :8080
|
||||||
# Kill conflicting process or change port in docker-compose.yml
|
# Kill conflicting process or change port in docker-compose.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Integration Tests
|
||||||
|
|
||||||
|
Integration tests require AWS credentials (build tag: `integration`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export AWS_REGION=us-east-1
|
||||||
|
export REKOGNITION_COLLECTION=hatch-faces-test
|
||||||
|
go test -tags=integration ./internal/face/... -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Performance benchmarks (build tag: `performance`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags=performance -bench=. ./internal/face/...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
- **DevOps**: Jing Yang (Sam Lee)
|
|
||||||
- **CTO**: Jordan Patel
|
- **CTO**: Jordan Patel
|
||||||
- **Repository**: `/home/nara/apps/web/`
|
- **Repository**: `/home/nara/apps/web/`
|
||||||
- **Live site**: https://hatch.surf
|
- **API**: http://localhost:8080 (internal) / https://hatch.surf (public)
|
||||||
|
|||||||
27
deploy.sh
27
deploy.sh
@ -1,9 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Deploy hatch.surf - triggered by Gitea webhook or manually
|
# Deploy hatch.surf API — triggered by Gitea webhook or manually
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "=== Deploying hatch.surf ==="
|
echo "=== Deploying hatch API ==="
|
||||||
cd /home/nara/apps/web
|
cd /home/nara/apps/web
|
||||||
|
|
||||||
# Pull latest changes from GitHub (primary source)
|
# Pull latest changes from GitHub (primary source)
|
||||||
@ -12,20 +12,21 @@ git pull github main
|
|||||||
# Push to Gitea (push-mirror)
|
# Push to Gitea (push-mirror)
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
||||||
# Build and deploy from site directory
|
# Stop old containers
|
||||||
cd site
|
docker compose down 2>/dev/null || true
|
||||||
docker build -t hatch-web:latest .
|
|
||||||
docker stop hatch-web 2>/dev/null || true
|
# Rebuild and start
|
||||||
docker rm hatch-web 2>/dev/null || true
|
docker compose build hatch
|
||||||
docker run -d --name hatch-web --restart unless-stopped -p 8080:80 hatch-web:latest
|
docker compose --profile with-caddy up -d
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Verify
|
# Verify
|
||||||
sleep 5
|
sleep 3
|
||||||
if curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 | grep -q "200"; then
|
if curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/healthz | grep -q "200"; then
|
||||||
echo "✅ hatch.surf deployed successfully!"
|
echo "✅ hatch API deployed successfully!"
|
||||||
|
echo " Health: http://localhost:8080/healthz"
|
||||||
|
echo " Ready: http://localhost:8080/readyz"
|
||||||
else
|
else
|
||||||
echo "❌ Deployment failed!"
|
echo "❌ Deployment failed!"
|
||||||
docker logs hatch-web
|
docker compose logs --tail=20 hatch
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user