SiamCafe.net Blog
Technology

Skaffold Dev SaaS Architecture

skaffold dev saas architecture
Skaffold Dev SaaS Architecture | SiamCafe Blog
2026-03-24· อ. บอม — SiamCafe.net· 1,649 คำ

Skaffold Dev SaaS Architecture คืออะไร

Skaffold เป็น open source CLI tool จาก Google ที่ช่วย automate development workflow สำหรับ Kubernetes applications ทำให้ build, push และ deploy เร็วขึ้นด้วย hot-reload SaaS (Software as a Service) Architecture คือการออกแบบระบบ software ที่ให้บริการผ่าน cloud แบบ multi-tenant subscription-based การรวมสองแนวคิดนี้ช่วยให้ทีม development สร้าง SaaS applications บน Kubernetes ได้เร็วขึ้น ด้วย inner development loop ที่ smooth และ architecture ที่ scalable

Skaffold Fundamentals

# skaffold_basics.py — Skaffold fundamentals
import json

class SkaffoldBasics:
    FEATURES = {
        "build": {
            "name": "Build",
            "description": "สร้าง container images อัตโนมัติ — Docker, Buildpacks, Jib, Ko",
            "options": ["Docker", "Google Cloud Build", "Buildpacks", "Jib (Java)", "Ko (Go)"],
        },
        "deploy": {
            "name": "Deploy",
            "description": "Deploy ไป Kubernetes อัตโนมัติ — kubectl, Helm, Kustomize",
            "options": ["kubectl", "Helm", "Kustomize", "Cloud Run"],
        },
        "dev": {
            "name": "Dev Mode (skaffold dev)",
            "description": "Watch files → auto build → auto deploy → stream logs",
            "benefit": "Inner development loop เร็วมาก — เปลี่ยน code → เห็นผลใน K8s ทันที",
        },
        "debug": {
            "name": "Debug",
            "description": "Remote debugging สำหรับ containers ที่รันใน K8s",
            "support": "Go, Java, Node.js, Python",
        },
        "profiles": {
            "name": "Profiles",
            "description": "Configuration สำหรับ environments ต่างๆ — dev, staging, production",
        },
    }

    SKAFFOLD_YAML = """
# skaffold.yaml — Basic configuration
apiVersion: skaffold/v4beta6
kind: Config
metadata:
  name: my-saas-app

build:
  artifacts:
    - image: my-saas-api
      context: services/api
      docker:
        dockerfile: Dockerfile
    - image: my-saas-worker
      context: services/worker
    - image: my-saas-web
      context: services/web
  local:
    push: false
    useBuildkit: true

deploy:
  helm:
    releases:
      - name: my-saas
        chartPath: charts/my-saas
        valuesFiles:
          - charts/my-saas/values-dev.yaml
        setValueTemplates:
          api.image: "{{.IMAGE_FULLY_QUALIFIED_my_saas_api}}"
          worker.image: "{{.IMAGE_FULLY_QUALIFIED_my_saas_worker}}"
          web.image: "{{.IMAGE_FULLY_QUALIFIED_my_saas_web}}"

profiles:
  - name: dev
    activation:
      - command: dev
    patches:
      - op: replace
        path: /deploy/helm/releases/0/valuesFiles/0
        value: charts/my-saas/values-dev.yaml
  
  - name: staging
    build:
      artifacts:
        - image: gcr.io/my-project/my-saas-api
          context: services/api
    deploy:
      helm:
        releases:
          - name: my-saas
            chartPath: charts/my-saas
            valuesFiles: [charts/my-saas/values-staging.yaml]

portForward:
  - resourceType: service
    resourceName: my-saas-api
    port: 8080
    localPort: 8080
  - resourceType: service
    resourceName: my-saas-web
    port: 3000
    localPort: 3000
"""

    def show_features(self):
        print("=== Skaffold Features ===\n")
        for key, f in self.FEATURES.items():
            print(f"[{f['name']}]")
            print(f"  {f['description']}")
            print()

    def show_yaml(self):
        print("=== skaffold.yaml ===")
        print(self.SKAFFOLD_YAML[:500])

basics = SkaffoldBasics()
basics.show_features()
basics.show_yaml()

SaaS Architecture Design

# saas_arch.py — SaaS architecture design
import json

class SaaSArchitecture:
    COMPONENTS = {
        "api_gateway": {
            "name": "API Gateway",
            "description": "Entry point — routing, rate limiting, authentication",
            "options": "Kong, Traefik, NGINX Ingress, AWS API Gateway",
        },
        "auth": {
            "name": "Authentication & Authorization",
            "description": "User management, JWT, OAuth2, multi-tenant isolation",
            "options": "Auth0, Keycloak, Supabase Auth, custom JWT",
        },
        "api_services": {
            "name": "API Services (Microservices)",
            "description": "Business logic แยกตาม domain — users, billing, core features",
            "options": "FastAPI (Python), Express (Node), Go services",
        },
        "database": {
            "name": "Database (Multi-tenant)",
            "description": "เก็บข้อมูล tenant — shared DB, schema-per-tenant, or DB-per-tenant",
            "options": "PostgreSQL, MongoDB, CockroachDB",
        },
        "message_queue": {
            "name": "Message Queue",
            "description": "Async processing — background jobs, events, notifications",
            "options": "Redis (BullMQ), RabbitMQ, Kafka",
        },
        "billing": {
            "name": "Billing & Subscription",
            "description": "จัดการ plans, payments, invoices",
            "options": "Stripe, Paddle, custom billing service",
        },
    }

    MULTI_TENANT = {
        "shared_db": {
            "name": "Shared Database (tenant_id column)",
            "pros": "ง่าย, ประหยัด, scale ง่าย",
            "cons": "Isolation ต่ำ, noisy neighbor risk",
            "use": "SaaS ขนาดเล็ก-กลาง, tenants ไม่ต้องการ isolation สูง",
        },
        "schema_per_tenant": {
            "name": "Schema per Tenant",
            "pros": "Isolation ดีกว่า, migration ง่ายกว่า DB-per-tenant",
            "cons": "Schema จำนวนมาก, connection pooling ซับซ้อน",
            "use": "SaaS กลาง-ใหญ่, ต้องการ data isolation ระดับหนึ่ง",
        },
        "db_per_tenant": {
            "name": "Database per Tenant",
            "pros": "Isolation สูงสุด, backup/restore per tenant",
            "cons": "แพง, ดูแลยาก, ไม่ scale ถ้า tenants เยอะ",
            "use": "Enterprise SaaS, compliance requirements (PDPA, HIPAA)",
        },
    }

    def show_components(self):
        print("=== SaaS Components ===\n")
        for key, comp in self.COMPONENTS.items():
            print(f"[{comp['name']}]")
            print(f"  {comp['description']}")
            print()

    def show_multi_tenant(self):
        print("=== Multi-tenant Strategies ===")
        for key, mt in self.MULTI_TENANT.items():
            print(f"\n[{mt['name']}]")
            print(f"  Pros: {mt['pros']}")
            print(f"  Cons: {mt['cons']}")

arch = SaaSArchitecture()
arch.show_components()
arch.show_multi_tenant()

Development Workflow

# dev_workflow.py — Skaffold development workflow
import json

class DevWorkflow:
    COMMANDS = """
# Skaffold CLI commands
skaffold dev                    # Watch + build + deploy + logs (main command)
skaffold dev --port-forward     # + auto port forward
skaffold dev --trigger=polling  # Poll for changes (ไม่ใช้ file watcher)
skaffold run                    # One-time build + deploy
skaffold run -p staging         # Deploy with staging profile
skaffold debug                  # Dev mode + remote debugger attached
skaffold render                 # Output rendered K8s manifests (dry-run)
skaffold build                  # Build images only
skaffold delete                 # Clean up deployed resources
skaffold diagnose               # Check config for issues
"""

    PROJECT_STRUCTURE = """
# SaaS project structure with Skaffold
my-saas/
├── skaffold.yaml               # Skaffold config
├── services/
│   ├── api/
│   │   ├── Dockerfile
│   │   ├── src/
│   │   │   ├── main.py         # FastAPI app
│   │   │   ├── routers/
│   │   │   ├── models/
│   │   │   └── middleware/
│   │   │       └── tenant.py   # Multi-tenant middleware
│   │   └── requirements.txt
│   ├── worker/
│   │   ├── Dockerfile
│   │   └── src/
│   │       └── tasks.py        # Background workers
│   └── web/
│       ├── Dockerfile
│       └── src/                # Next.js frontend
├── charts/
│   └── my-saas/
│       ├── Chart.yaml
│       ├── values.yaml
│       ├── values-dev.yaml
│       ├── values-staging.yaml
│       └── templates/
│           ├── api-deployment.yaml
│           ├── worker-deployment.yaml
│           └── web-deployment.yaml
├── migrations/
│   └── 001_initial.sql
└── scripts/
    └── seed.py
"""

    FAST_DEV = """
# fast_dev.py — Optimize Skaffold dev loop
# skaffold.yaml additions for faster dev:

build:
  artifacts:
    - image: my-saas-api
      context: services/api
      sync:
        manual:
          - src: "src/**/*.py"
            dest: /app/src
      docker:
        dockerfile: Dockerfile
        cacheFrom: ["my-saas-api:latest"]

# With file sync, Python changes sync directly to container
# without rebuilding Docker image → < 2 second feedback loop
# Combine with uvicorn --reload for instant hot-reload
"""

    def show_commands(self):
        print("=== Skaffold Commands ===")
        print(self.COMMANDS[:500])

    def show_structure(self):
        print(f"\n=== Project Structure ===")
        print(self.PROJECT_STRUCTURE[:500])

    def show_fast_dev(self):
        print(f"\n=== Fast Dev Loop ===")
        print(self.FAST_DEV[:400])

wf = DevWorkflow()
wf.show_commands()
wf.show_structure()

SaaS API Implementation

# saas_api.py — Multi-tenant SaaS API with FastAPI
import json

class SaaSAPI:
    CODE = """
# main.py — Multi-tenant FastAPI SaaS application
from fastapi import FastAPI, Depends, HTTPException, Header
from pydantic import BaseModel
from typing import Optional
import jwt

app = FastAPI(title="My SaaS API")

# Tenant middleware
async def get_current_tenant(authorization: str = Header(None)):
    if not authorization:
        raise HTTPException(401, "Missing authorization")
    
    token = authorization.replace("Bearer ", "")
    try:
        payload = jwt.decode(token, "secret", algorithms=["HS256"])
        return {
            "tenant_id": payload["tenant_id"],
            "user_id": payload["user_id"],
            "plan": payload.get("plan", "free"),
        }
    except jwt.InvalidTokenError:
        raise HTTPException(401, "Invalid token")

# Rate limiting by plan
PLAN_LIMITS = {"free": 100, "pro": 1000, "enterprise": 10000}

async def check_rate_limit(tenant=Depends(get_current_tenant)):
    limit = PLAN_LIMITS.get(tenant["plan"], 100)
    # Check against Redis counter
    # if current_count > limit: raise HTTPException(429, "Rate limit exceeded")
    return tenant

# API routes
class Project(BaseModel):
    name: str
    description: Optional[str] = None

@app.post("/api/projects")
async def create_project(project: Project, tenant=Depends(check_rate_limit)):
    # All queries filtered by tenant_id
    result = await db.execute(
        "INSERT INTO projects (name, description, tenant_id) VALUES ($1, $2, $3) RETURNING id",
        project.name, project.description, tenant["tenant_id"],
    )
    return {"id": result, "tenant_id": tenant["tenant_id"]}

@app.get("/api/projects")
async def list_projects(tenant=Depends(get_current_tenant)):
    projects = await db.fetch_all(
        "SELECT * FROM projects WHERE tenant_id = $1",
        tenant["tenant_id"],
    )
    return projects

# Billing webhook
@app.post("/webhooks/stripe")
async def stripe_webhook(event: dict):
    if event["type"] == "customer.subscription.updated":
        subscription = event["data"]["object"]
        tenant_id = subscription["metadata"]["tenant_id"]
        plan = subscription["items"]["data"][0]["price"]["lookup_key"]
        await db.execute(
            "UPDATE tenants SET plan = $1 WHERE id = $2",
            plan, tenant_id,
        )
    return {"received": True}
"""

    def show_code(self):
        print("=== SaaS API (FastAPI) ===")
        print(self.CODE[:600])

api = SaaSAPI()
api.show_code()

Kubernetes Deployment

# k8s_deploy.py — Kubernetes deployment for SaaS
import json

class K8sDeployment:
    HELM_VALUES = """
# values.yaml — Helm values for SaaS deployment
api:
  replicas: 3
  image: my-saas-api:latest
  resources:
    requests:
      cpu: 250m
      memory: 512Mi
    limits:
      cpu: 1000m
      memory: 1Gi
  env:
    DATABASE_URL: "postgresql://user:pass@postgres:5432/saas"
    REDIS_URL: "redis://redis:6379"
    STRIPE_SECRET_KEY: ""
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 10
    targetCPU: 70

worker:
  replicas: 2
  image: my-saas-worker:latest
  resources:
    requests:
      cpu: 500m
      memory: 1Gi

web:
  replicas: 2
  image: my-saas-web:latest

postgresql:
  enabled: true
  primary:
    persistence:
      size: 50Gi

redis:
  enabled: true
  architecture: standalone

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: api.mysaas.com
      paths: [{path: /, pathType: Prefix, service: api}]
    - host: app.mysaas.com
      paths: [{path: /, pathType: Prefix, service: web}]
  tls:
    - secretName: mysaas-tls
      hosts: [api.mysaas.com, app.mysaas.com]
"""

    def show_values(self):
        print("=== Helm Values ===")
        print(self.HELM_VALUES[:600])

    def deployment_tips(self):
        print(f"\n=== Deployment Tips ===")
        tips = [
            "ใช้ Skaffold profiles: dev (local), staging (shared), production (HA)",
            "ใช้ Sealed Secrets หรือ External Secrets สำหรับ sensitive config",
            "ตั้ง HPA (Horizontal Pod Autoscaler) สำหรับ API pods",
            "ใช้ PDB (Pod Disruption Budget) ป้องกัน downtime ระหว่าง rolling update",
            "Enable Prometheus metrics + Grafana dashboards per tenant",
        ]
        for tip in tips:
            print(f"  • {tip}")

k8s = K8sDeployment()
k8s.show_values()
k8s.deployment_tips()

FAQ - คำถามที่พบบ่อย

Q: Skaffold กับ Tilt อันไหนดี?

A: Skaffold: Google-backed, CLI-focused, CI/CD integration ดี, profiles สำหรับหลาย environments Tilt: UI dashboard ดี, Tiltfile (Starlark), live update เร็ว ใช้ Skaffold: CI/CD pipeline integration, simple config, Google Cloud ใช้ Tilt: development-only, ต้องการ UI dashboard, complex microservices

Q: Multi-tenant แบบไหนดี?

A: Shared DB + tenant_id: เริ่มต้นดีที่สุด — ง่าย, ประหยัด, scale ได้ Schema-per-tenant: เมื่อต้องการ isolation มากขึ้น DB-per-tenant: enterprise/compliance requirements เริ่มจาก shared DB → migrate เมื่อจำเป็น 80% ของ SaaS ใช้ shared DB + row-level security

Q: SaaS architecture ต้องมีอะไรบ้าง?

A: Must-have: Authentication (JWT/OAuth), Multi-tenancy, Billing (Stripe), API rate limiting Important: Background workers, Email/notifications, Audit logging, Admin dashboard Nice-to-have: Feature flags, A/B testing, Analytics, SSO, Custom domains เริ่ม MVP: Auth + Core features + Billing → เพิ่มทีหลัง

Q: Skaffold dev loop เร็วแค่ไหน?

A: Without sync: 10-30 seconds (build Docker image + deploy) With file sync: 1-3 seconds (sync files ไม่ rebuild image) With hot-reload (uvicorn/nodemon): < 1 second (code change → see result) Key: ใช้ file sync + hot-reload framework = fastest inner loop

📖 บทความที่เกี่ยวข้อง

Skaffold Dev Agile Scrum Kanbanอ่านบทความ → Skaffold Dev Multi-cloud Strategyอ่านบทความ → Skaffold Dev Multi-tenant Designอ่านบทความ → Skaffold Dev MLOps Workflowอ่านบทความ → Skaffold Dev AR VR Developmentอ่านบทความ →

📚 ดูบทความทั้งหมด →