feat: implement multi-stage Dockerfile for deployment infrastructure
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
38
Dockerfile
Normal file
38
Dockerfile
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Builder stage
|
||||||
|
FROM golang:1.26-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy go.mod and go.sum
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
|
||||||
|
# Download dependencies
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build api and cron binaries
|
||||||
|
RUN go build -o api ./cmd/api
|
||||||
|
RUN go build -o cron ./cmd/cron
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy binaries from builder
|
||||||
|
COPY --from=builder /app/api /app/api
|
||||||
|
COPY --from=builder /app/cron /app/cron
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV DB_DSN="postgres://postgres:postgres@postgres:5432/trip_planner?sslmode=disable"
|
||||||
|
ENV REDIS_ADDR="redis:6379"
|
||||||
|
ENV YANDEX_API_KEY=""
|
||||||
|
ENV TRIP_PLANNER_ADMIN_API_KEY=""
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Set entrypoint for api service (default)
|
||||||
|
ENTRYPOINT ["/app/api"]
|
||||||
68
cmd/cron/main.go
Normal file
68
cmd/cron/main.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
|
||||||
|
"trip-planner/internal/cache"
|
||||||
|
"trip-planner/internal/metrics"
|
||||||
|
"trip-planner/internal/yandex"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
redisClient := initRedis()
|
||||||
|
m := metrics.New()
|
||||||
|
|
||||||
|
apiKey := os.Getenv("YANDEX_API_KEY")
|
||||||
|
if apiKey == "" {
|
||||||
|
log.Fatal("YANDEX_API_KEY environment variable is not set")
|
||||||
|
}
|
||||||
|
yandexClient := yandex.NewClient(apiKey, yandex.WithMetrics(m))
|
||||||
|
|
||||||
|
// Define monitored stations
|
||||||
|
monitoredStations := []string{
|
||||||
|
// Add station IDs here
|
||||||
|
}
|
||||||
|
|
||||||
|
monitors := make([]*StationMonitor, 0, len(monitoredStations))
|
||||||
|
for _, stationID := range monitoredStations {
|
||||||
|
monitors = append(monitors, &StationMonitor{
|
||||||
|
ID: stationID,
|
||||||
|
Yandex: yandexClient,
|
||||||
|
Cache: cache.NewCacheStore(redisClient, m),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Set up signal handling for graceful shutdown
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
log.Println("Cron service starting, checking station statuses...")
|
||||||
|
|
||||||
|
// Run the station status check
|
||||||
|
if err := ProcessAllStations(ctx, monitors); err != nil {
|
||||||
|
log.Printf("ERROR: failed to process stations: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for signal to exit
|
||||||
|
<-sigChan
|
||||||
|
log.Println("Cron service shutting down...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// initRedis initializes a Redis client connection.
|
||||||
|
func initRedis() *redis.Client {
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: os.Getenv("REDIS_ADDR"),
|
||||||
|
Password: "",
|
||||||
|
DB: 0,
|
||||||
|
})
|
||||||
|
return rdb
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cron
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package cron
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
116
docs/plans/2026-08-18-implement-deployment-infrastructure.md
Normal file
116
docs/plans/2026-08-18-implement-deployment-infrastructure.md
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# 2026-08-18-implement-deployment-infrastructure
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- Clear description of the feature/change being implemented: Implement the complete deployment infrastructure for the trip-planner service including Dockerfile, updated docker-compose.yml with cron and watchtower services, and Gitea Actions CI/CD workflow.
|
||||||
|
- Problem it solves and key benefits: The project has the core routing logic, API endpoints, caching, and frontend fully implemented, but lacks the deployment infrastructure specified in `docs/deployment.md`. This plan adds the missing Dockerfile, completes docker-compose.yml with cron and watchtower services, and creates the Gitea Actions CI/CD workflow for automated build and deployment.
|
||||||
|
- How it integrates with existing system: The deployment infrastructure wraps the existing Go backend (API and cron services) in Docker containers and provides automated CI/CD pipeline for building and pushing images to a registry, with watchtower handling automatic updates on the production server.
|
||||||
|
|
||||||
|
## Context (from discovery)
|
||||||
|
|
||||||
|
- Files/components involved:
|
||||||
|
- `Dockerfile` (to be created)
|
||||||
|
- `docker-compose.yml` (to be updated with cron and watchtower services, redis_data volume)
|
||||||
|
- `.gitea/workflows/deploy.yml` (to be created)
|
||||||
|
- Existing: `/cmd/api/main.go`, `/cmd/cron/station_status.go`, `go.mod`, `go.sum`
|
||||||
|
- Related patterns found: Multi-stage Docker build for Go applications, Gitea Actions workflow with checkout, setup Go, lint & test, build binaries, Docker build & push, watchtower for CD.
|
||||||
|
- Dependencies identified: Go 1.22+, PostgreSQL 15-alpine, Redis 7-alpine, nickfedor/watchtower image.
|
||||||
|
|
||||||
|
## Development Approach
|
||||||
|
|
||||||
|
- **Testing approach**: TDD (tests first) - Define test scenarios and verify deployment files work correctly
|
||||||
|
- Complete each task fully before moving to the next
|
||||||
|
- Make small, focused changes
|
||||||
|
- **CRITICAL: every task MUST include verification** for deployment files
|
||||||
|
- verification is not optional - they are a required part of the checklist
|
||||||
|
- verify Dockerfile syntax and multi-stage structure
|
||||||
|
- verify docker-compose.yml syntax and service definitions
|
||||||
|
- verify Gitea Actions workflow YAML syntax
|
||||||
|
- include both success and error scenarios in verification
|
||||||
|
- **CRITICAL: all verifications must pass before starting next task** - no exceptions
|
||||||
|
- **CRITICAL: update this plan file when scope changes during implementation**
|
||||||
|
- Keep plan in sync with actual work done
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
- **Verification tests**: required for every task (see Development Approach above)
|
||||||
|
- **Syntax validation**: verify YAML files with yaml lint, verify Dockerfile with docker build --dry-run
|
||||||
|
- **Configuration validation**: verify docker-compose.yml with `docker-compose config`, verify Gitea Actions workflow syntax
|
||||||
|
|
||||||
|
## Progress Tracking
|
||||||
|
|
||||||
|
- Mark completed items with `[x]` immediately when done
|
||||||
|
- Add newly discovered tasks with ➕ prefix
|
||||||
|
- Document issues/blockers with ⚠️ prefix
|
||||||
|
- Update plan if implementation deviates from original scope
|
||||||
|
- Keep plan in sync with actual work done
|
||||||
|
|
||||||
|
## What Goes Where
|
||||||
|
|
||||||
|
- **Implementation Steps** (`[ ]` checkboxes): tasks achievable within this codebase - creating Dockerfile, updating docker-compose.yml, creating Gitea Actions workflow
|
||||||
|
- **Post-Completion** (no checkboxes): items requiring external action - manual testing on production server, registry configuration, third-party service integrations to verify
|
||||||
|
- **Checkbox placement**: Checkboxes belong only in Task sections (`### Task N:`). Do not put checkboxes in Success criteria, Overview, or Context — they cause extra loop iterations.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### Task 1: Create Multi-stage Dockerfile
|
||||||
|
- [x] create Dockerfile with builder stage (Go 1.26+, build api and cron binaries)
|
||||||
|
- [x] create Dockerfile with runtime stage (alpine, copy binaries, set entrypoint)
|
||||||
|
- [x] verify Dockerfile syntax with `docker build` (successful build)
|
||||||
|
- [x] verify multi-stage structure produces minimal runtime image (27.1MB)
|
||||||
|
|
||||||
|
### Task 2: Update docker-compose.yml
|
||||||
|
- [ ] add `cron` service (Go cron binary, depends_on: postgres, redis)
|
||||||
|
- [ ] add `watchtower` service (nickfedor/watchtower image, volume for docker.sock, command for cleanup)
|
||||||
|
- [ ] add `redis_data` volume to volumes section
|
||||||
|
- [ ] verify docker-compose.yml syntax with `docker-compose config`
|
||||||
|
|
||||||
|
### Task 3: Create Gitea Actions CI/CD Workflow
|
||||||
|
- [ ] create `.gitea/workflows/deploy.yml` file
|
||||||
|
- [ ] add checkout step (actions/checkout@v3)
|
||||||
|
- [ ] add setup Go step (actions/setup-go@v4, go-version: '1.22')
|
||||||
|
- [ ] add Go modules cache step (actions/cache@v3)
|
||||||
|
- [ ] add lint & test step (golangci-lint, go test -v -race ./...)
|
||||||
|
- [ ] add Docker Buildx setup step (docker/setup-buildx-action@v2)
|
||||||
|
- [ ] add Docker login step (docker/login-action@v2)
|
||||||
|
- [ ] add Docker build & push step (docker/build-push-action@v4, tags: latest and {{.CommitID}})
|
||||||
|
- [ ] verify Gitea Actions workflow YAML syntax
|
||||||
|
|
||||||
|
### Task 4: Verify acceptance criteria
|
||||||
|
- [ ] verify Dockerfile matches multi-stage specification
|
||||||
|
- [ ] verify docker-compose.yml has api, cron, postgres, redis, watchtower services
|
||||||
|
- [ ] verify Gitea Actions workflow has all required steps (checkout, setup Go, lint & test, build binaries, Docker build & push)
|
||||||
|
- [ ] verify all YAML files are valid syntax
|
||||||
|
- [ ] verify docker-compose.yml volumes section includes pg_data and redis_data
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- **Dockerfile structure**:
|
||||||
|
- Builder stage: `golang:1.22-alpine` as builder, copy go.mod/go.sum, `go mod download`, copy source, `go build -o api cmd/api/main.go`, `go build -o cron cmd/cron/main.go`
|
||||||
|
- Runtime stage: `alpine:latest` or `scratch`, copy binaries from builder, set environment variables, expose port 8080, set entrypoint
|
||||||
|
|
||||||
|
- **docker-compose.yml services**:
|
||||||
|
- `api`: build from Dockerfile, ports: 8080:8080, environment: DB_DSN, REDIS_ADDR, YANDEX_API_KEY, depends_on: db, redis
|
||||||
|
- `cron`: build from Dockerfile, environment: same as api, depends_on: db, redis
|
||||||
|
- `db` (postgres): postgres:15-alpine, environment: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, volumes: pg_data
|
||||||
|
- `redis`: redis:7-alpine, volumes: redis_data
|
||||||
|
- `watchtower`: nickfedor/watchtower, volumes: /var/run/docker.sock:/var/run/docker.sock, command: --interval 60 --cleanup travel-api
|
||||||
|
|
||||||
|
- **.gitea/workflows/deploy.yml structure**:
|
||||||
|
- on: push to master branch
|
||||||
|
- jobs: build-and-deploy on ubuntu-latest
|
||||||
|
- steps: Checkout Code, Set up Go, Go Modules Cache, Run Tests, Set up Docker Buildx, Login to Docker Registry, Build and Push Docker Image
|
||||||
|
|
||||||
|
## Post-Completion
|
||||||
|
|
||||||
|
*Items requiring manual intervention or external systems - no checkboxes, informational only*
|
||||||
|
|
||||||
|
**Manual verification** (if applicable):
|
||||||
|
- Test docker-compose.yml on local Docker environment
|
||||||
|
- Test Gitea Actions workflow in a test repository
|
||||||
|
- Verify watchtower auto-update behavior on production server
|
||||||
|
|
||||||
|
**External system updates** (if applicable):
|
||||||
|
- Configure Docker Registry credentials in Gitea Secrets (DOCKER_USERNAME, DOCKER_PASSWORD)
|
||||||
|
- Configure YANDEX_API_KEY and TRIP_PLANNER_ADMIN_API_KEY in Docker Compose .env file
|
||||||
|
- Verify PostgreSQL and Redis persistence volumes are properly mounted
|
||||||
Reference in New Issue
Block a user