依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
11 KiB
11 KiB
Build & Deploy
建置、本機開發、Docker 打包、部署的實務細節。
1. Makefile(visionA-backend)
.PHONY: build build-api build-proxy build-dev test clean docker-build docker-up docker-down run-dev
GO ?= go
GO_FLAGS ?= -ldflags="-s -w"
OUT_DIR ?= dist
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
build: build-api build-proxy
build-api:
$(GO) build $(GO_FLAGS) -o $(OUT_DIR)/api-server ./cmd/api-server
build-proxy:
$(GO) build $(GO_FLAGS) -o $(OUT_DIR)/remote-proxy ./cmd/remote-proxy
test:
$(GO) test -race -coverprofile=coverage.out ./...
lint:
$(GO) vet ./...
gofmt -l . | grep -v '^$$' && exit 1 || true
clean:
rm -rf $(OUT_DIR) coverage.out
# --- Docker ---
docker-build:
docker build -f docker/Dockerfile.api-server -t visiona/api-server:$(VERSION) .
docker build -f docker/Dockerfile.remote-proxy -t visiona/remote-proxy:$(VERSION) .
docker-up:
docker compose -f docker/docker-compose.yml up --build
docker-down:
docker compose -f docker/docker-compose.yml down
# --- Dev ---
# 本機同時啟動兩個 binary(非雛形交付物,僅開發便利)。
# 交付物定義見 design-doc.md §2.4 Non-Goal。
run-dev:
@trap 'kill 0' EXIT; \
$(GO) run ./cmd/remote-proxy & \
$(GO) run ./cmd/api-server & \
wait
2. Dockerfile.api-server
# --- build stage ---
FROM golang:1.26-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/api-server ./cmd/api-server
# --- runtime stage ---
FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=build /out/api-server /app/api-server
USER nonroot:nonroot
EXPOSE 3001
ENTRYPOINT ["/app/api-server"]
3. Dockerfile.remote-proxy
FROM golang:1.26-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/remote-proxy ./cmd/remote-proxy
FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=build /out/remote-proxy /app/remote-proxy
USER nonroot:nonroot
EXPOSE 3800 3801
ENTRYPOINT ["/app/remote-proxy"]
4. docker/docker-compose.yml
services:
api-server:
build:
context: ../
dockerfile: docker/Dockerfile.api-server
image: visiona/api-server:dev
ports: ["3001:3001"]
environment:
VISIONA_API_PORT: "3001"
VISIONA_SESSION_BACKEND: inmemory
VISIONA_STORAGE_BACKEND: localfs
VISIONA_STORAGE_LOCALFS_ROOT: /data/storage
VISIONA_STORAGE_LOCALFS_BASE_URL: http://localhost:3001/storage
VISIONA_STORAGE_SIGNING_SECRET: dev-secret-do-not-use-in-prod
VISIONA_AUTH_MODE: static
VISIONA_STATIC_USER_ID: demo-user
VISIONA_PAIRING_MODE: static
VISIONA_PAIRING_TOKEN: "${VISIONA_PAIRING_TOKEN}"
VISIONA_CONVERTER_MODE: stub
volumes:
- storage-data:/data
remote-proxy:
build:
context: ../
dockerfile: docker/Dockerfile.remote-proxy
image: visiona/remote-proxy:dev
ports:
- "3800:3800" # tunnel
- "3801:3801" # internal
environment:
VISIONA_TUNNEL_PORT: "3800"
VISIONA_PROXY_INTERNAL_PORT: "3801"
VISIONA_SESSION_BACKEND: inmemory
VISIONA_PAIRING_MODE: static
VISIONA_PAIRING_TOKEN: "${VISIONA_PAIRING_TOKEN}"
# 雛形設計(ADR-006 / Q1):
# - remote-proxy 是唯一持有 yamux.Session 的 process(in-memory)
# - api-server 無狀態,透過 internal HTTP 向 remote-proxy 查詢 session
# - 兩 binary 之間用 VISIONA_PROXY_INTERNAL_URL 連結(下方 api-server 已設 env)
volumes:
storage-data:
另外 api-server service 需要新增 env(指向 remote-proxy 的 internal URL):
api-server:
environment:
# 上方所有 env 保留,新增:
VISIONA_SESSION_BACKEND: proxy-client
VISIONA_PROXY_INTERNAL_URL: http://remote-proxy:3801
4.1 雛形推薦的開發方式
# .env
VISIONA_PAIRING_TOKEN="vAc_$(openssl rand -hex 16)" # 格式見 security.md §1.3
# 方式 1:本機 Makefile 平行跑兩個 binary(開發便利工具)
make run-dev
# 方式 2:Docker Compose(更接近 Production 拓撲)
make docker-up
結論:雛形交付物是雙 binary + docker-compose(兩者皆可用於 demo);make run-dev 僅為本機開發便利工具(非交付物,見 design-doc.md §2.4 Non-Goal)。
5. 前端建置
cd visionA-frontend
pnpm install
pnpm dev # 本機開發 http://localhost:3000
pnpm build # 產出 .next/
pnpm start # 生產模式跑 Next.js server
5.1 Next.js Build 模式
| 模式 | 用途 | 如何設定 |
|---|---|---|
next build + next start |
SSR / ISR 支援 | 適合 Phase 1,需要 Next.js runtime |
output: 'export' |
靜態 export,放 CDN | 若所有頁面都可靜態化,最便宜 |
雛形建議:next start on Node,方便 API rewrites / middleware;Phase 1 視需求切換。
5.2 環境變數
# visionA-frontend/.env.example
NEXT_PUBLIC_API_BASE=http://localhost:3001
NEXT_PUBLIC_WS_BASE=ws://localhost:3001
# 雛形開發用:
NEXT_PUBLIC_DEV_PAIRING_TOKEN=<same as VISIONA_PAIRING_TOKEN>
6. 本機完整開發流程
# Terminal 1 — Backend
cd visionA-backend
export VISIONA_PAIRING_TOKEN=$(openssl rand -hex 32)
export VISIONA_STATIC_USER_ID=demo-user
make run-dev
# api at :3001, tunnel at :3800
# Terminal 2 — local agent (local-tool 現有 + 開雲端模式,或 POC 的 edge-ai-server)
cd /Users/jimchen/Innovedus/edge-ai-platform/edge-ai-platform
./dist/edge-ai-server --relay-url=ws://localhost:3800/tunnel/connect --relay-token=$VISIONA_PAIRING_TOKEN
# Terminal 3 — Frontend
cd visionA-frontend
cp .env.example .env.local
# 編輯 .env.local 填入 PAIRING_TOKEN
pnpm dev
# http://localhost:3000
7. Phase 1 部署草圖
7.1 AWS ECS Fargate + Application Load Balancer
┌──────────────────────────────────────────────────────────────┐
│ Route 53 — api.visiona.cloud / proxy.visiona.cloud │
└─────────────────────┬────────────────────┬───────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌───────────────────┐
│ ALB │ │ NLB │
│ (HTTPS/WSS) │ │ (TCP passthrough)│
│ api.* │ │ proxy.* │
└────────┬────────┘ └─────────┬─────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ ECS Service: │ │ ECS Service: │
│ api-server │ │ remote-proxy │
│ (Fargate, 2+ tasks)│ │ (Fargate, 2+ tasks) │
└──────┬──────────────┘ └──────────┬──────────┘
│ │
└──────┬───────────────┬──────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ ElastiCache │ │ RDS │
│ Redis │ │ PostgreSQL │
└──────────────┘ └─────────────────┘
▲ ▲
│ │
└────────────────┘
│
┌──────────────┐
│ S3 bucket │
└──────────────┘
7.2 Kubernetes 方案
- api-server:
Deployment+HorizontalPodAutoscaler - remote-proxy:
Deployment+ HPA(按 tunnel 數 metric) - Redis / Postgres:managed service 或 StatefulSet
- Ingress:nginx-ingress 或 cloud LB controller
Cloud-agnostic 原則:Helm chart 不綁特定雲,storage / DB 依 env 注入連線資訊。
8. CI/CD(Phase 1 規劃)
8.1 GitHub Actions
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
backend-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: '1.26' }
- run: cd visionA-backend && make test
- run: cd visionA-backend && make lint
frontend-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'pnpm' }
- run: cd visionA-frontend && pnpm install --frozen-lockfile
- run: cd visionA-frontend && pnpm test
- run: cd visionA-frontend && pnpm build
8.2 Release Pipeline(Phase 1)
- PR merged to
main - CI 跑 test + lint + build
- 產出 Docker image → push to registry(ECR / GCR / GitHub Packages)
- Tag image 為
main-<sha> - 手動觸發 deploy(或 GitOps 自動)→ ECS task definition 更新 / K8s rollout
9. 環境變數對照表(摘要)
| Env | 雛形 | Phase 1 |
|---|---|---|
VISIONA_AUTH_MODE |
static |
clerk / oidc |
VISIONA_PAIRING_MODE |
static |
db |
VISIONA_SESSION_BACKEND |
inmemory |
redis |
VISIONA_STORAGE_BACKEND |
localfs |
s3 |
VISIONA_CONVERTER_MODE |
stub |
http |
VISIONA_REDIS_URL |
— | redis://... |
VISIONA_DB_URL |
— | postgres://... |
VISIONA_S3_* |
— | 實際 credentials |
雛形實作重點:
make run-dev(單 binary 兩 listener)- 不需要 Docker;Docker 檔案寫好以備 Phase 1
- 不需要 CI;但 Makefile 有
test+lint方便本機檢查
Phase 1 必做:
- Docker image CI 產出
- K8s / ECS manifest
- Blue-green 或 rolling deploy
- Staging 環境