Kustomize Overlay
Kustomize จัดการ Kubernetes YAML ไม่ใช้ Template Base + Overlay แยก Configuration ตาม Environment มาพร้อม kubectl kustomization.yaml Resources Patches
Hexagonal Architecture Ports and Adapters แยก Business Logic Infrastructure Ports Interface Adapters Implementation เปลี่ยน Infrastructure ไม่กระทบ Domain
Kustomize Structure
# === Kustomize Project Structure ===
# k8s/
# ├── base/ # Base Configuration
# │ ├── kustomization.yaml
# │ ├── namespace.yaml
# │ ├── deployment.yaml
# │ ├── service.yaml
# │ ├── configmap.yaml
# │ └── hpa.yaml
# ├── overlays/
# │ ├── dev/ # Development
# │ │ ├── kustomization.yaml
# │ │ ├── deployment-patch.yaml
# │ │ └── configmap-patch.yaml
# │ ├── staging/ # Staging
# │ │ ├── kustomization.yaml
# │ │ ├── deployment-patch.yaml
# │ │ └── ingress.yaml
# │ └── production/ # Production
# │ ├── kustomization.yaml
# │ ├── deployment-patch.yaml
# │ ├── hpa-patch.yaml
# │ ├── ingress.yaml
# │ └── pdb.yaml
# └── components/ # Reusable Components
# ├── monitoring/
# │ ├── kustomization.yaml
# │ └── service-monitor.yaml
# └── istio/
# ├── kustomization.yaml
# └── virtual-service.yaml
# base/kustomization.yaml
# apiVersion: kustomize.config.k8s.io/v1beta1
# kind: Kustomization
# namespace: my-app
# resources:
# - namespace.yaml
# - deployment.yaml
# - service.yaml
# - configmap.yaml
# - hpa.yaml
# commonLabels:
# app: my-app
# managed-by: kustomize
# base/deployment.yaml
# apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: api-server
# spec:
# replicas: 1
# selector:
# matchLabels:
# app: api-server
# template:
# spec:
# containers:
# - name: api-server
# image: my-app:latest
# ports:
# - containerPort: 8080
# resources:
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 500m
# memory: 512Mi
# overlays/production/kustomization.yaml
# apiVersion: kustomize.config.k8s.io/v1beta1
# kind: Kustomization
# namespace: my-app-prod
# bases:
# - ../../base
# components:
# - ../../components/monitoring
# - ../../components/istio
# patches:
# - path: deployment-patch.yaml
# - path: hpa-patch.yaml
# images:
# - name: my-app
# newName: registry.example.com/my-app
# newTag: v1.2.3
# resources:
# - ingress.yaml
# - pdb.yaml
# overlays/production/deployment-patch.yaml
# apiVersion: apps/v1
# kind: Deployment
# metadata:
# name: api-server
# spec:
# replicas: 3
# template:
# spec:
# containers:
# - name: api-server
# resources:
# requests:
# cpu: 500m
# memory: 512Mi
# limits:
# cpu: 2000m
# memory: 2Gi
kustomize_commands = {
"kubectl kustomize overlays/dev": "Preview dev configuration",
"kubectl apply -k overlays/dev": "Apply dev configuration",
"kubectl apply -k overlays/production": "Apply production",
"kustomize build overlays/staging": "Build staging YAML",
"kubectl diff -k overlays/production": "Diff before apply",
"kustomize edit set image my-app=v1.3.0": "Update image tag",
}
print("Kustomize Commands:")
for cmd, desc in kustomize_commands.items():
print(f" {cmd}")
print(f" -> {desc}")
Hexagonal Architecture Pattern
# hexagonal.py — Hexagonal Architecture (Ports and Adapters)
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import List, Optional
# === Domain Layer (Core Business Logic) ===
@dataclass
class Order:
id: str
customer_id: str
items: List[dict]
total: float
status: str = "pending"
# === Ports (Interfaces) ===
class OrderRepository(ABC):
"""Port: Outbound — Database Access"""
@abstractmethod
def save(self, order: Order) -> bool: ...
@abstractmethod
def find_by_id(self, order_id: str) -> Optional[Order]: ...
@abstractmethod
def find_by_customer(self, customer_id: str) -> List[Order]: ...
class PaymentGateway(ABC):
"""Port: Outbound — Payment Processing"""
@abstractmethod
def charge(self, amount: float, customer_id: str) -> dict: ...
class NotificationService(ABC):
"""Port: Outbound — Notifications"""
@abstractmethod
def send(self, to: str, message: str) -> bool: ...
class OrderUseCase(ABC):
"""Port: Inbound — Application API"""
@abstractmethod
def create_order(self, customer_id: str, items: List[dict]) -> Order: ...
@abstractmethod
def get_order(self, order_id: str) -> Optional[Order]: ...
# === Application Service (Orchestration) ===
class OrderService(OrderUseCase):
"""Application Service — Orchestrates Ports"""
def __init__(self, repo: OrderRepository,
payment: PaymentGateway,
notifier: NotificationService):
self.repo = repo
self.payment = payment
self.notifier = notifier
def create_order(self, customer_id: str, items: List[dict]) -> Order:
total = sum(item["price"] * item["qty"] for item in items)
order = Order(
id=f"ORD-{customer_id[:4]}-001",
customer_id=customer_id,
items=items,
total=total,
)
# Charge payment
result = self.payment.charge(total, customer_id)
if result["status"] == "success":
order.status = "paid"
self.repo.save(order)
self.notifier.send(customer_id, f"Order {order.id} confirmed")
else:
order.status = "failed"
return order
def get_order(self, order_id: str) -> Optional[Order]:
return self.repo.find_by_id(order_id)
# === Adapters (Implementations) ===
class PostgresOrderRepository(OrderRepository):
"""Adapter: PostgreSQL Database"""
def save(self, order): print(f" [PostgreSQL] Saved {order.id}"); return True
def find_by_id(self, order_id): return None
def find_by_customer(self, customer_id): return []
class StripePaymentGateway(PaymentGateway):
"""Adapter: Stripe Payment"""
def charge(self, amount, customer_id):
print(f" [Stripe] Charged {amount} to {customer_id}")
return {"status": "success", "transaction_id": "txn_123"}
class EmailNotificationService(NotificationService):
"""Adapter: Email Notification"""
def send(self, to, message):
print(f" [Email] Sent to {to}: {message}")
return True
# === Dependency Injection ===
repo = PostgresOrderRepository()
payment = StripePaymentGateway()
notifier = EmailNotificationService()
service = OrderService(repo, payment, notifier)
print("Hexagonal Architecture Demo:")
order = service.create_order("CUST-001", [
{"name": "Widget", "price": 29.99, "qty": 2},
{"name": "Gadget", "price": 49.99, "qty": 1},
])
print(f" Order: {order.id} | Status: {order.status} | Total: {order.total}")
# แสดง Architecture Layers
layers = {
"Domain": ["Order (Entity)", "Business Rules"],
"Ports (Inbound)": ["OrderUseCase (API Interface)"],
"Ports (Outbound)": ["OrderRepository", "PaymentGateway", "NotificationService"],
"Adapters (Inbound)": ["REST API Controller", "gRPC Handler", "CLI"],
"Adapters (Outbound)": ["PostgreSQL", "Stripe", "Email/SMS"],
}
print(f"\n\nHexagonal Architecture Layers:")
for layer, items in layers.items():
print(f" [{layer}]")
for item in items:
print(f" - {item}")
Kustomize + Hexagonal Mapping
# kustomize_hexagonal.py — Mapping Hexagonal to K8s
mapping = {
"Inbound Adapter: REST API": {
"k8s_resource": "Deployment + Service + Ingress",
"kustomize": "base/deployment.yaml + overlays/*/ingress.yaml",
"port": "8080 (HTTP)",
},
"Inbound Adapter: gRPC": {
"k8s_resource": "Deployment + Service (ClusterIP)",
"kustomize": "base/grpc-service.yaml",
"port": "50051 (gRPC)",
},
"Outbound Adapter: PostgreSQL": {
"k8s_resource": "StatefulSet + PVC + Secret",
"kustomize": "overlays/*/database-patch.yaml",
"port": "5432",
},
"Outbound Adapter: Redis": {
"k8s_resource": "Deployment + Service + ConfigMap",
"kustomize": "components/redis/",
"port": "6379",
},
"Outbound Adapter: Kafka": {
"k8s_resource": "StatefulSet + Service + ConfigMap",
"kustomize": "components/kafka/",
"port": "9092",
},
"Cross-cutting: Monitoring": {
"k8s_resource": "ServiceMonitor + PrometheusRule",
"kustomize": "components/monitoring/",
"port": "9090 (metrics)",
},
}
print("Hexagonal -> Kubernetes Mapping:")
for adapter, info in mapping.items():
print(f"\n [{adapter}]")
for key, value in info.items():
print(f" {key}: {value}")
# Environment Differences
env_diff = {
"Development": {
"replicas": 1,
"resources": "100m CPU, 128Mi RAM",
"database": "SQLite / Docker PostgreSQL",
"cache": "ไม่มี หรือ In-memory",
"ingress": "localhost:8080",
},
"Staging": {
"replicas": 2,
"resources": "250m CPU, 256Mi RAM",
"database": "PostgreSQL (shared)",
"cache": "Redis (shared)",
"ingress": "staging.example.com",
},
"Production": {
"replicas": "3-10 (HPA)",
"resources": "500m-2000m CPU, 512Mi-2Gi RAM",
"database": "PostgreSQL HA (Patroni)",
"cache": "Redis Cluster",
"ingress": "api.example.com + CDN",
},
}
print(f"\n\nEnvironment Configuration:")
for env, config in env_diff.items():
print(f"\n [{env}]")
for key, value in config.items():
print(f" {key}: {value}")
Best Practices
- Base + Overlay: แยก Base Configuration กลาง Overlay เฉพาะ Environment
- Components: ใช้ Kustomize Components สำหรับ Feature ที่ใช้ซ้ำ
- Ports Interface: กำหนด Port เป็น Abstract Class ใน Domain Layer
- Adapters แยก: แต่ละ Adapter เป็น Package แยก เปลี่ยนได้อิสระ
- DI Container: ใช้ Dependency Injection เชื่อม Port กับ Adapter
- GitOps: ใช้ Kustomize กับ Flux CD หรือ ArgoCD GitOps Workflow
Kustomize คืออะไร
จัดการ Kubernetes YAML ไม่ใช้ Template Base Overlay แยก Environment มาพร้อม kubectl kustomization.yaml Resources Patches Components
Hexagonal Architecture คืออะไร
Ports and Adapters แยก Business Logic Infrastructure Ports Interface Adapters Implementation เปลี่ยน Infrastructure ไม่กระทบ Domain
Kustomize Overlay ทำงานอย่างไร
Base Configuration พื้นฐาน Overlays เฉพาะ Environment Patch ทับ Base เปลี่ยน Replicas Image ConfigMap Resource Limits Strategic Merge Patch JSON Patch
Hexagonal Architecture ใช้กับ Kubernetes อย่างไร
Port map K8s Service Inbound Adapters Ingress Service Outbound Adapters External Services Kustomize แยก Adapter Configuration ตาม Environment
สรุป
Kustomize Base Overlay จัดการ K8s YAML แยก Environment Hexagonal Architecture Ports Adapters แยก Domain Infrastructure Dependency Injection เปลี่ยน Adapter ได้อิสระ GitOps Flux CD ArgoCD
