docs: add implementation plan
This commit is contained in:
@@ -0,0 +1,256 @@
|
|||||||
|
# Docker Watcher — Implementation Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A self-hosted tool that automates Docker container deployment with Nginx Proxy Manager integration. Detects new images from Gitea/GitHub registries, deploys containers, and configures reverse proxy routing — all from a web dashboard.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Gitea CI → pushes image → Registry
|
||||||
|
↓
|
||||||
|
Docker Watcher (Go)
|
||||||
|
├── Webhook listener (instant)
|
||||||
|
└── Registry poller (fallback)
|
||||||
|
↓
|
||||||
|
Match tag → project + stage
|
||||||
|
↓
|
||||||
|
┌── auto_deploy? ──┐
|
||||||
|
↓ ↓
|
||||||
|
Deploy now Notify, wait for
|
||||||
|
manual trigger (UI)
|
||||||
|
↓ ↓
|
||||||
|
Pull image
|
||||||
|
Stop old container
|
||||||
|
Start new container on shared network
|
||||||
|
↓
|
||||||
|
NPM API: create/update proxy host
|
||||||
|
→ stage-dev-project.example.com
|
||||||
|
↓
|
||||||
|
Health check
|
||||||
|
→ success: done, notify
|
||||||
|
→ failure: rollback, alert
|
||||||
|
```
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
| Decision | Choice | Rationale |
|
||||||
|
|----------|--------|-----------|
|
||||||
|
| Language | Go | Single binary, excellent Docker SDK, low resource usage |
|
||||||
|
| Web UI | SvelteKit (embedded in Go binary) | User's existing stack, lightweight |
|
||||||
|
| Reverse proxy | Nginx Proxy Manager | Already deployed, API available |
|
||||||
|
| Routing | Subdomain-based | No sub-path issues with SPAs |
|
||||||
|
| Image detection | Webhook + polling | Webhook for speed, polling as fallback |
|
||||||
|
| Config | YAML | Version-controlled, easy to edit |
|
||||||
|
| Deployment target | Same TrueNAS host | Docker socket mounted |
|
||||||
|
|
||||||
|
## Subdomain Convention
|
||||||
|
|
||||||
|
| Stage | Pattern | Example |
|
||||||
|
|-------|---------|---------|
|
||||||
|
| Dev | `stage-dev-{project}.{domain}` | `stage-dev-web-app-launcher.dolgolyov-family.by` |
|
||||||
|
| Release | `stage-rel-{project}.{domain}` | `stage-rel-web-app-launcher.dolgolyov-family.by` |
|
||||||
|
| Production | `{custom}.{domain}` | `launcher.dolgolyov-family.by` |
|
||||||
|
|
||||||
|
## Config Format
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
global:
|
||||||
|
domain: dolgolyov-family.by
|
||||||
|
network: staging-net
|
||||||
|
npm:
|
||||||
|
url: http://npm:81
|
||||||
|
email: docker-watcher@dolgolyov-family.by
|
||||||
|
password_env: NPM_PASSWORD
|
||||||
|
subdomain_pattern: "stage-{stage}-{project}"
|
||||||
|
notification_url: https://notify.dolgolyov-family.by/webhook
|
||||||
|
|
||||||
|
registries:
|
||||||
|
gitea:
|
||||||
|
url: https://git.dolgolyov-family.by
|
||||||
|
type: gitea
|
||||||
|
token_env: GITEA_TOKEN
|
||||||
|
# github:
|
||||||
|
# url: https://ghcr.io
|
||||||
|
# type: github
|
||||||
|
# token_env: GITHUB_TOKEN
|
||||||
|
|
||||||
|
projects:
|
||||||
|
web-app-launcher:
|
||||||
|
registry: gitea
|
||||||
|
image: gitea.dolgolyov-family.by/alexei/web-app-launcher
|
||||||
|
port: 3000
|
||||||
|
healthcheck: /api/health
|
||||||
|
env_file: ./envs/web-app-launcher.env
|
||||||
|
volumes: []
|
||||||
|
stages:
|
||||||
|
dev:
|
||||||
|
tag_pattern: "dev-*"
|
||||||
|
auto_deploy: true
|
||||||
|
rel:
|
||||||
|
tag_pattern: "v*"
|
||||||
|
auto_deploy: false
|
||||||
|
prod:
|
||||||
|
tag_pattern: "v*"
|
||||||
|
auto_deploy: false
|
||||||
|
confirm: true
|
||||||
|
promote_from: rel
|
||||||
|
subdomain: launcher # overrides pattern → launcher.dolgolyov-family.by
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-watcher/
|
||||||
|
├── cmd/
|
||||||
|
│ └── server/
|
||||||
|
│ └── main.go # Entry point
|
||||||
|
├── internal/
|
||||||
|
│ ├── config/
|
||||||
|
│ │ ├── config.go # YAML parsing, validation
|
||||||
|
│ │ └── config_test.go
|
||||||
|
│ ├── docker/
|
||||||
|
│ │ ├── client.go # Docker Engine API wrapper
|
||||||
|
│ │ ├── container.go # Create, start, stop, remove
|
||||||
|
│ │ └── client_test.go
|
||||||
|
│ ├── npm/
|
||||||
|
│ │ ├── client.go # NPM API client (auth, CRUD proxy hosts)
|
||||||
|
│ │ └── client_test.go
|
||||||
|
│ ├── registry/
|
||||||
|
│ │ ├── registry.go # Interface
|
||||||
|
│ │ ├── gitea.go # Gitea registry implementation
|
||||||
|
│ │ ├── github.go # GitHub Container Registry (future)
|
||||||
|
│ │ ├── poller.go # Periodic tag polling
|
||||||
|
│ │ └── registry_test.go
|
||||||
|
│ ├── deployer/
|
||||||
|
│ │ ├── deployer.go # Orchestrates full deploy flow
|
||||||
|
│ │ ├── rollback.go # Rollback on failure
|
||||||
|
│ │ └── deployer_test.go
|
||||||
|
│ ├── health/
|
||||||
|
│ │ ├── checker.go # HTTP health checks with retries
|
||||||
|
│ │ └── checker_test.go
|
||||||
|
│ ├── notify/
|
||||||
|
│ │ ├── notifier.go # Webhook notifications
|
||||||
|
│ │ └── notifier_test.go
|
||||||
|
│ ├── webhook/
|
||||||
|
│ │ ├── handler.go # Receive push events from Gitea/GitHub
|
||||||
|
│ │ └── handler_test.go
|
||||||
|
│ ├── api/
|
||||||
|
│ │ ├── router.go # HTTP API for web UI
|
||||||
|
│ │ ├── projects.go # Project endpoints
|
||||||
|
│ │ ├── deploys.go # Deploy endpoints
|
||||||
|
│ │ └── middleware.go # Auth, logging, CORS
|
||||||
|
│ └── store/
|
||||||
|
│ ├── store.go # Deploy history, state (SQLite)
|
||||||
|
│ └── store_test.go
|
||||||
|
├── web/ # SvelteKit frontend
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── routes/
|
||||||
|
│ │ │ ├── +page.svelte # Dashboard
|
||||||
|
│ │ │ ├── projects/
|
||||||
|
│ │ │ │ └── [id]/
|
||||||
|
│ │ │ │ └── +page.svelte # Project detail + deploy controls
|
||||||
|
│ │ │ └── settings/
|
||||||
|
│ │ │ └── +page.svelte # Config view
|
||||||
|
│ │ ├── lib/
|
||||||
|
│ │ │ ├── api.ts # API client
|
||||||
|
│ │ │ └── types.ts # Shared types
|
||||||
|
│ │ └── app.html
|
||||||
|
│ ├── package.json
|
||||||
|
│ ├── svelte.config.js
|
||||||
|
│ └── vite.config.ts
|
||||||
|
├── docker-watcher.example.yaml
|
||||||
|
├── Dockerfile
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── go.mod
|
||||||
|
└── go.sum
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Foundation
|
||||||
|
|
||||||
|
Core infrastructure — config, Docker client, NPM client.
|
||||||
|
|
||||||
|
1. **Go project init** — go.mod, directory structure, dependencies
|
||||||
|
2. **Config loader** — parse YAML, validate, resolve env vars
|
||||||
|
3. **Docker client** — connect to socket, pull image, list/start/stop/remove containers, manage networks
|
||||||
|
4. **NPM client** — authenticate (JWT), create/update/delete proxy hosts, list existing hosts
|
||||||
|
5. **Store** — SQLite for deploy history and state tracking
|
||||||
|
|
||||||
|
### Phase 2: Detection & Deployment
|
||||||
|
|
||||||
|
The core loop — detecting new images and deploying them.
|
||||||
|
|
||||||
|
6. **Registry client** — Gitea registry API: list tags, compare with last known
|
||||||
|
7. **Poller** — periodic check for new tags matching configured patterns
|
||||||
|
8. **Webhook handler** — HTTP endpoint receiving push events from Gitea CI
|
||||||
|
9. **Deployer** — orchestrate: pull → stop old → start new → NPM update → health check
|
||||||
|
10. **Health checker** — HTTP GET with retries and timeout
|
||||||
|
11. **Rollback** — on health check failure: stop new, restart old, revert NPM
|
||||||
|
12. **Notifications** — send webhook on deploy success/failure
|
||||||
|
|
||||||
|
### Phase 3: Web UI
|
||||||
|
|
||||||
|
Dashboard for visibility and manual control.
|
||||||
|
|
||||||
|
13. **API layer** — REST endpoints for projects, stages, deploys, manual trigger
|
||||||
|
14. **SvelteKit app** — dashboard, project detail, deploy log, manual deploy button
|
||||||
|
15. **Embed in Go** — build SvelteKit to static, embed with `go:embed`, serve from Go
|
||||||
|
16. **Real-time updates** — SSE or WebSocket for deploy progress
|
||||||
|
|
||||||
|
### Phase 4: Hardening
|
||||||
|
|
||||||
|
17. **Blue-green deploys** — start new, health check, swap, stop old (zero downtime)
|
||||||
|
18. **Promote flow** — enforce `promote_from` for production deploys
|
||||||
|
19. **Auth on dashboard** — basic auth or token-based
|
||||||
|
20. **Graceful shutdown** — drain in-progress deploys on SIGTERM
|
||||||
|
21. **Structured logging** — JSON logs with deploy context
|
||||||
|
|
||||||
|
## Key Dependencies (Go)
|
||||||
|
|
||||||
|
- `github.com/docker/docker` — Docker Engine API
|
||||||
|
- `github.com/go-chi/chi` or `net/http` — HTTP routing
|
||||||
|
- `gopkg.in/yaml.v3` — YAML config
|
||||||
|
- `github.com/mattn/go-sqlite3` or `modernc.org/sqlite` — SQLite (CGo-free)
|
||||||
|
- `github.com/robfig/cron` — Polling scheduler
|
||||||
|
|
||||||
|
## Docker Compose (self-deployment)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
docker-watcher:
|
||||||
|
image: docker-watcher:latest
|
||||||
|
container_name: docker-watcher
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./docker-watcher.yaml:/app/config.yaml:ro
|
||||||
|
- ./envs:/app/envs:ro
|
||||||
|
- ./data:/app/data # SQLite DB
|
||||||
|
environment:
|
||||||
|
- NPM_PASSWORD=${NPM_PASSWORD}
|
||||||
|
- GITEA_TOKEN=${GITEA_TOKEN}
|
||||||
|
networks:
|
||||||
|
- staging-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
staging-net:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Endpoints (Phase 3)
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/projects — list all projects with current status
|
||||||
|
GET /api/projects/:id — project detail + stage statuses
|
||||||
|
GET /api/projects/:id/stages/:stage — stage detail + deploy history
|
||||||
|
POST /api/projects/:id/stages/:stage/deploy — trigger manual deploy
|
||||||
|
POST /api/projects/:id/stages/:stage/rollback — rollback to previous
|
||||||
|
GET /api/deploys — recent deploys across all projects
|
||||||
|
GET /api/deploys/:id/logs — deploy log stream (SSE)
|
||||||
|
POST /api/webhook/gitea — Gitea push event receiver
|
||||||
|
POST /api/webhook/github — GitHub push event receiver (future)
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user