moai-adk 0.4.4__py3-none-any.whl → 0.4.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/__init__.py +1 -1
- moai_adk/core/project/initializer.py +12 -5
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +829 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +78 -77
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +87 -77
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +76 -66
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +86 -59
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/reference.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +87 -73
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +87 -79
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +87 -71
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +78 -62
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +78 -55
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +78 -64
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +604 -56
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +974 -44
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/reference.md +801 -0
- moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +88 -61
- moai_adk/templates/.claude/skills/moai-claude-code/examples.md +16 -500
- moai_adk/templates/.claude/skills/moai-claude-code/reference.md +15 -420
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +234 -43
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +1633 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +660 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +97 -69
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +97 -72
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +97 -67
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +97 -79
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -71
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +656 -60
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +1107 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +1533 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +87 -78
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +87 -70
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +87 -86
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +77 -62
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +88 -56
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +90 -71
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +78 -58
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +78 -51
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +253 -32
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +1099 -0
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-clojure/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-clojure/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +98 -76
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +86 -61
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-lang-elixir/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-haskell/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-haskell/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +87 -61
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +88 -59
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +32 -0
- moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +86 -61
- moai_adk/templates/.claude/skills/moai-lang-julia/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-julia/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +86 -61
- moai_adk/templates/.claude/skills/moai-lang-lua/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-lua/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +86 -61
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +388 -53
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +624 -0
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +316 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +90 -59
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +34 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/CHECKLIST.md +482 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/EXAMPLES.md +52 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/INTERACTIVE-DISCOVERY.md +524 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/METADATA.md +477 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL-UPDATE-ADVISOR.md +577 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL.md +560 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/STRUCTURE.md +583 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/WEB-RESEARCH.md +526 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/reference.md +69 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/scripts/generate-structure.sh +328 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/scripts/validate-skill.sh +312 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/SKILL_TEMPLATE.md +245 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/examples-template.md +285 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/reference-template.md +278 -0
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/scripts-template.sh +303 -0
- moai_adk/templates/CLAUDE.md +43 -11
- moai_adk-0.4.7.dist-info/METADATA +1162 -0
- moai_adk-0.4.7.dist-info/RECORD +275 -0
- moai_adk-0.4.4.dist-info/METADATA +0 -369
- moai_adk-0.4.4.dist-info/RECORD +0 -152
- {moai_adk-0.4.4.dist-info → moai_adk-0.4.7.dist-info}/WHEEL +0 -0
- {moai_adk-0.4.4.dist-info → moai_adk-0.4.7.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.4.4.dist-info → moai_adk-0.4.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1633 @@
|
|
|
1
|
+
# Backend Architecture Examples
|
|
2
|
+
|
|
3
|
+
## Example 1: Microservices with Kubernetes 1.31 + Istio 1.21
|
|
4
|
+
|
|
5
|
+
### Architecture Overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
9
|
+
│ Istio Ingress Gateway │
|
|
10
|
+
│ (TLS termination, routing) │
|
|
11
|
+
└───────────────────────┬─────────────────────────────────────┘
|
|
12
|
+
│
|
|
13
|
+
┌───────────────┼───────────────┐
|
|
14
|
+
│ │ │
|
|
15
|
+
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
|
|
16
|
+
│ User │ │ Product │ │ Order │
|
|
17
|
+
│ Service │ │ Service │ │ Service │
|
|
18
|
+
│ (Go) │ │ (Python)│ │ (Node) │
|
|
19
|
+
└────┬────┘ └────┬────┘ └────┬────┘
|
|
20
|
+
│ │ │
|
|
21
|
+
└──────────────┼──────────────┘
|
|
22
|
+
│
|
|
23
|
+
┌────────▼────────┐
|
|
24
|
+
│ PostgreSQL │
|
|
25
|
+
│ (Primary + │
|
|
26
|
+
│ Read Replicas)│
|
|
27
|
+
└─────────────────┘
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Service Deployment (User Service - Go)
|
|
31
|
+
|
|
32
|
+
**Dockerfile**:
|
|
33
|
+
```dockerfile
|
|
34
|
+
# Multi-stage build for Go service
|
|
35
|
+
FROM golang:1.21-alpine AS builder
|
|
36
|
+
WORKDIR /app
|
|
37
|
+
COPY go.mod go.sum ./
|
|
38
|
+
RUN go mod download
|
|
39
|
+
COPY . .
|
|
40
|
+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o user-service .
|
|
41
|
+
|
|
42
|
+
FROM alpine:latest
|
|
43
|
+
RUN apk --no-cache add ca-certificates
|
|
44
|
+
WORKDIR /root/
|
|
45
|
+
COPY --from=builder /app/user-service .
|
|
46
|
+
EXPOSE 8080
|
|
47
|
+
CMD ["./user-service"]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Kubernetes Deployment**:
|
|
51
|
+
```yaml
|
|
52
|
+
apiVersion: apps/v1
|
|
53
|
+
kind: Deployment
|
|
54
|
+
metadata:
|
|
55
|
+
name: user-service
|
|
56
|
+
namespace: production
|
|
57
|
+
spec:
|
|
58
|
+
replicas: 3
|
|
59
|
+
selector:
|
|
60
|
+
matchLabels:
|
|
61
|
+
app: user-service
|
|
62
|
+
version: v1
|
|
63
|
+
template:
|
|
64
|
+
metadata:
|
|
65
|
+
labels:
|
|
66
|
+
app: user-service
|
|
67
|
+
version: v1
|
|
68
|
+
annotations:
|
|
69
|
+
prometheus.io/scrape: "true"
|
|
70
|
+
prometheus.io/port: "8080"
|
|
71
|
+
prometheus.io/path: "/metrics"
|
|
72
|
+
spec:
|
|
73
|
+
containers:
|
|
74
|
+
- name: user-service
|
|
75
|
+
image: user-service:v1.2.3
|
|
76
|
+
ports:
|
|
77
|
+
- containerPort: 8080
|
|
78
|
+
name: http
|
|
79
|
+
protocol: TCP
|
|
80
|
+
env:
|
|
81
|
+
- name: DATABASE_URL
|
|
82
|
+
valueFrom:
|
|
83
|
+
secretKeyRef:
|
|
84
|
+
name: database-credentials
|
|
85
|
+
key: url
|
|
86
|
+
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
|
87
|
+
value: "otel-collector:4317"
|
|
88
|
+
resources:
|
|
89
|
+
requests:
|
|
90
|
+
cpu: "100m"
|
|
91
|
+
memory: "128Mi"
|
|
92
|
+
limits:
|
|
93
|
+
cpu: "500m"
|
|
94
|
+
memory: "512Mi"
|
|
95
|
+
livenessProbe:
|
|
96
|
+
httpGet:
|
|
97
|
+
path: /health/live
|
|
98
|
+
port: 8080
|
|
99
|
+
initialDelaySeconds: 30
|
|
100
|
+
periodSeconds: 10
|
|
101
|
+
readinessProbe:
|
|
102
|
+
httpGet:
|
|
103
|
+
path: /health/ready
|
|
104
|
+
port: 8080
|
|
105
|
+
initialDelaySeconds: 5
|
|
106
|
+
periodSeconds: 5
|
|
107
|
+
---
|
|
108
|
+
apiVersion: v1
|
|
109
|
+
kind: Service
|
|
110
|
+
metadata:
|
|
111
|
+
name: user-service
|
|
112
|
+
namespace: production
|
|
113
|
+
spec:
|
|
114
|
+
selector:
|
|
115
|
+
app: user-service
|
|
116
|
+
ports:
|
|
117
|
+
- port: 80
|
|
118
|
+
targetPort: 8080
|
|
119
|
+
protocol: TCP
|
|
120
|
+
name: http
|
|
121
|
+
type: ClusterIP
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Istio Configuration
|
|
125
|
+
|
|
126
|
+
**VirtualService (Traffic Routing)**:
|
|
127
|
+
```yaml
|
|
128
|
+
apiVersion: networking.istio.io/v1beta1
|
|
129
|
+
kind: VirtualService
|
|
130
|
+
metadata:
|
|
131
|
+
name: user-service
|
|
132
|
+
namespace: production
|
|
133
|
+
spec:
|
|
134
|
+
hosts:
|
|
135
|
+
- user-service
|
|
136
|
+
http:
|
|
137
|
+
- match:
|
|
138
|
+
- headers:
|
|
139
|
+
x-version:
|
|
140
|
+
exact: "v2"
|
|
141
|
+
route:
|
|
142
|
+
- destination:
|
|
143
|
+
host: user-service
|
|
144
|
+
subset: v2
|
|
145
|
+
- route:
|
|
146
|
+
- destination:
|
|
147
|
+
host: user-service
|
|
148
|
+
subset: v1
|
|
149
|
+
weight: 90
|
|
150
|
+
- destination:
|
|
151
|
+
host: user-service
|
|
152
|
+
subset: v2
|
|
153
|
+
weight: 10 # Canary: 10% traffic to v2
|
|
154
|
+
timeout: 5s
|
|
155
|
+
retries:
|
|
156
|
+
attempts: 3
|
|
157
|
+
perTryTimeout: 2s
|
|
158
|
+
retryOn: 5xx,reset,connect-failure,refused-stream
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**DestinationRule (Traffic Policy)**:
|
|
162
|
+
```yaml
|
|
163
|
+
apiVersion: networking.istio.io/v1beta1
|
|
164
|
+
kind: DestinationRule
|
|
165
|
+
metadata:
|
|
166
|
+
name: user-service
|
|
167
|
+
namespace: production
|
|
168
|
+
spec:
|
|
169
|
+
host: user-service
|
|
170
|
+
trafficPolicy:
|
|
171
|
+
connectionPool:
|
|
172
|
+
tcp:
|
|
173
|
+
maxConnections: 100
|
|
174
|
+
http:
|
|
175
|
+
http1MaxPendingRequests: 50
|
|
176
|
+
http2MaxRequests: 100
|
|
177
|
+
outlierDetection:
|
|
178
|
+
consecutiveErrors: 5
|
|
179
|
+
interval: 30s
|
|
180
|
+
baseEjectionTime: 30s
|
|
181
|
+
maxEjectionPercent: 50
|
|
182
|
+
loadBalancer:
|
|
183
|
+
simple: LEAST_REQUEST
|
|
184
|
+
subsets:
|
|
185
|
+
- name: v1
|
|
186
|
+
labels:
|
|
187
|
+
version: v1
|
|
188
|
+
- name: v2
|
|
189
|
+
labels:
|
|
190
|
+
version: v2
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**PeerAuthentication (mTLS)**:
|
|
194
|
+
```yaml
|
|
195
|
+
apiVersion: security.istio.io/v1beta1
|
|
196
|
+
kind: PeerAuthentication
|
|
197
|
+
metadata:
|
|
198
|
+
name: default
|
|
199
|
+
namespace: production
|
|
200
|
+
spec:
|
|
201
|
+
mtls:
|
|
202
|
+
mode: STRICT # Enforce mTLS for all services
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**AuthorizationPolicy (RBAC)**:
|
|
206
|
+
```yaml
|
|
207
|
+
apiVersion: security.istio.io/v1beta1
|
|
208
|
+
kind: AuthorizationPolicy
|
|
209
|
+
metadata:
|
|
210
|
+
name: user-service-authz
|
|
211
|
+
namespace: production
|
|
212
|
+
spec:
|
|
213
|
+
selector:
|
|
214
|
+
matchLabels:
|
|
215
|
+
app: user-service
|
|
216
|
+
action: ALLOW
|
|
217
|
+
rules:
|
|
218
|
+
- from:
|
|
219
|
+
- source:
|
|
220
|
+
principals: ["cluster.local/ns/production/sa/order-service"]
|
|
221
|
+
to:
|
|
222
|
+
- operation:
|
|
223
|
+
methods: ["GET", "POST"]
|
|
224
|
+
paths: ["/api/users/*"]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Observability Integration
|
|
228
|
+
|
|
229
|
+
**OpenTelemetry Instrumentation (Go)**:
|
|
230
|
+
```go
|
|
231
|
+
package main
|
|
232
|
+
|
|
233
|
+
import (
|
|
234
|
+
"context"
|
|
235
|
+
"log"
|
|
236
|
+
"net/http"
|
|
237
|
+
|
|
238
|
+
"go.opentelemetry.io/otel"
|
|
239
|
+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
|
240
|
+
"go.opentelemetry.io/otel/sdk/resource"
|
|
241
|
+
"go.opentelemetry.io/otel/sdk/trace"
|
|
242
|
+
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
|
243
|
+
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
func initTracer() (*trace.TracerProvider, error) {
|
|
247
|
+
exporter, err := otlptracegrpc.New(
|
|
248
|
+
context.Background(),
|
|
249
|
+
otlptracegrpc.WithEndpoint("otel-collector:4317"),
|
|
250
|
+
otlptracegrpc.WithInsecure(),
|
|
251
|
+
)
|
|
252
|
+
if err != nil {
|
|
253
|
+
return nil, err
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
tp := trace.NewTracerProvider(
|
|
257
|
+
trace.WithBatcher(exporter),
|
|
258
|
+
trace.WithResource(resource.NewWithAttributes(
|
|
259
|
+
semconv.SchemaURL,
|
|
260
|
+
semconv.ServiceNameKey.String("user-service"),
|
|
261
|
+
semconv.ServiceVersionKey.String("v1.2.3"),
|
|
262
|
+
)),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
otel.SetTracerProvider(tp)
|
|
266
|
+
return tp, nil
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func main() {
|
|
270
|
+
tp, err := initTracer()
|
|
271
|
+
if err != nil {
|
|
272
|
+
log.Fatal(err)
|
|
273
|
+
}
|
|
274
|
+
defer tp.Shutdown(context.Background())
|
|
275
|
+
|
|
276
|
+
// Wrap HTTP handler with OpenTelemetry instrumentation
|
|
277
|
+
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
278
|
+
w.Write([]byte("Hello, World!"))
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
wrappedHandler := otelhttp.NewHandler(handler, "user-service")
|
|
282
|
+
http.Handle("/", wrappedHandler)
|
|
283
|
+
|
|
284
|
+
log.Println("User service listening on :8080")
|
|
285
|
+
log.Fatal(http.ListenAndServe(":8080", nil))
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Prometheus Metrics (Go)**:
|
|
290
|
+
```go
|
|
291
|
+
import (
|
|
292
|
+
"github.com/prometheus/client_golang/prometheus"
|
|
293
|
+
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
var (
|
|
297
|
+
httpRequestsTotal = prometheus.NewCounterVec(
|
|
298
|
+
prometheus.CounterOpts{
|
|
299
|
+
Name: "http_requests_total",
|
|
300
|
+
Help: "Total number of HTTP requests",
|
|
301
|
+
},
|
|
302
|
+
[]string{"method", "endpoint", "status"},
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
httpRequestDuration = prometheus.NewHistogramVec(
|
|
306
|
+
prometheus.HistogramOpts{
|
|
307
|
+
Name: "http_request_duration_seconds",
|
|
308
|
+
Help: "HTTP request latency",
|
|
309
|
+
Buckets: prometheus.DefBuckets,
|
|
310
|
+
},
|
|
311
|
+
[]string{"method", "endpoint"},
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
func init() {
|
|
316
|
+
prometheus.MustRegister(httpRequestsTotal)
|
|
317
|
+
prometheus.MustRegister(httpRequestDuration)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
func main() {
|
|
321
|
+
// ... OpenTelemetry setup ...
|
|
322
|
+
|
|
323
|
+
http.Handle("/metrics", promhttp.Handler())
|
|
324
|
+
http.ListenAndServe(":8080", nil)
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Database Connection Pooling
|
|
329
|
+
|
|
330
|
+
**PostgreSQL with PgBouncer**:
|
|
331
|
+
```yaml
|
|
332
|
+
apiVersion: apps/v1
|
|
333
|
+
kind: Deployment
|
|
334
|
+
metadata:
|
|
335
|
+
name: pgbouncer
|
|
336
|
+
spec:
|
|
337
|
+
replicas: 2
|
|
338
|
+
template:
|
|
339
|
+
spec:
|
|
340
|
+
containers:
|
|
341
|
+
- name: pgbouncer
|
|
342
|
+
image: pgbouncer/pgbouncer:1.21
|
|
343
|
+
ports:
|
|
344
|
+
- containerPort: 6432
|
|
345
|
+
volumeMounts:
|
|
346
|
+
- name: config
|
|
347
|
+
mountPath: /etc/pgbouncer
|
|
348
|
+
volumes:
|
|
349
|
+
- name: config
|
|
350
|
+
configMap:
|
|
351
|
+
name: pgbouncer-config
|
|
352
|
+
---
|
|
353
|
+
apiVersion: v1
|
|
354
|
+
kind: ConfigMap
|
|
355
|
+
metadata:
|
|
356
|
+
name: pgbouncer-config
|
|
357
|
+
data:
|
|
358
|
+
pgbouncer.ini: |
|
|
359
|
+
[databases]
|
|
360
|
+
production = host=postgres-primary port=5432 dbname=production
|
|
361
|
+
|
|
362
|
+
[pgbouncer]
|
|
363
|
+
listen_addr = 0.0.0.0
|
|
364
|
+
listen_port = 6432
|
|
365
|
+
auth_type = md5
|
|
366
|
+
auth_file = /etc/pgbouncer/userlist.txt
|
|
367
|
+
pool_mode = transaction
|
|
368
|
+
max_client_conn = 1000
|
|
369
|
+
default_pool_size = 20
|
|
370
|
+
reserve_pool_size = 5
|
|
371
|
+
server_lifetime = 3600
|
|
372
|
+
server_idle_timeout = 600
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Application Connection String**:
|
|
376
|
+
```
|
|
377
|
+
DATABASE_URL=postgresql://user:pass@pgbouncer:6432/production?pool_size=10
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Example 2: Event-Driven Architecture with Kafka 3.7
|
|
383
|
+
|
|
384
|
+
### Architecture Overview
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
388
|
+
│ Order │───▶│ Kafka │◀───│ Payment │
|
|
389
|
+
│ Service │ │ Cluster │ │ Service │
|
|
390
|
+
└──────────┘ └────┬─────┘ └──────────┘
|
|
391
|
+
│
|
|
392
|
+
┌──────────┼──────────┐
|
|
393
|
+
│ │ │
|
|
394
|
+
┌────▼───┐ ┌───▼────┐ ┌───▼────┐
|
|
395
|
+
│ Email │ │ Invoice│ │Analytics│
|
|
396
|
+
│Service │ │Service │ │Service │
|
|
397
|
+
└────────┘ └────────┘ └─────────┘
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Kafka Deployment (Kubernetes)
|
|
401
|
+
|
|
402
|
+
**Kafka StatefulSet**:
|
|
403
|
+
```yaml
|
|
404
|
+
apiVersion: apps/v1
|
|
405
|
+
kind: StatefulSet
|
|
406
|
+
metadata:
|
|
407
|
+
name: kafka
|
|
408
|
+
spec:
|
|
409
|
+
serviceName: kafka-headless
|
|
410
|
+
replicas: 3
|
|
411
|
+
selector:
|
|
412
|
+
matchLabels:
|
|
413
|
+
app: kafka
|
|
414
|
+
template:
|
|
415
|
+
metadata:
|
|
416
|
+
labels:
|
|
417
|
+
app: kafka
|
|
418
|
+
spec:
|
|
419
|
+
containers:
|
|
420
|
+
- name: kafka
|
|
421
|
+
image: confluentinc/cp-kafka:7.6.0 # Kafka 3.7.x
|
|
422
|
+
ports:
|
|
423
|
+
- containerPort: 9092
|
|
424
|
+
name: plaintext
|
|
425
|
+
- containerPort: 9093
|
|
426
|
+
name: ssl
|
|
427
|
+
env:
|
|
428
|
+
- name: KAFKA_BROKER_ID
|
|
429
|
+
valueFrom:
|
|
430
|
+
fieldRef:
|
|
431
|
+
fieldPath: metadata.name
|
|
432
|
+
- name: KAFKA_ZOOKEEPER_CONNECT
|
|
433
|
+
value: "zookeeper:2181"
|
|
434
|
+
- name: KAFKA_ADVERTISED_LISTENERS
|
|
435
|
+
value: "PLAINTEXT://$(POD_NAME).kafka-headless:9092"
|
|
436
|
+
- name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR
|
|
437
|
+
value: "3"
|
|
438
|
+
- name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR
|
|
439
|
+
value: "3"
|
|
440
|
+
- name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR
|
|
441
|
+
value: "2"
|
|
442
|
+
- name: KAFKA_LOG_RETENTION_HOURS
|
|
443
|
+
value: "168" # 7 days
|
|
444
|
+
resources:
|
|
445
|
+
requests:
|
|
446
|
+
memory: "2Gi"
|
|
447
|
+
cpu: "1000m"
|
|
448
|
+
limits:
|
|
449
|
+
memory: "4Gi"
|
|
450
|
+
cpu: "2000m"
|
|
451
|
+
volumeMounts:
|
|
452
|
+
- name: data
|
|
453
|
+
mountPath: /var/lib/kafka/data
|
|
454
|
+
volumeClaimTemplates:
|
|
455
|
+
- metadata:
|
|
456
|
+
name: data
|
|
457
|
+
spec:
|
|
458
|
+
accessModes: ["ReadWriteOnce"]
|
|
459
|
+
resources:
|
|
460
|
+
requests:
|
|
461
|
+
storage: 100Gi
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Event Sourcing Pattern
|
|
465
|
+
|
|
466
|
+
**Event Definition**:
|
|
467
|
+
```typescript
|
|
468
|
+
// events/order.events.ts
|
|
469
|
+
export enum OrderEventType {
|
|
470
|
+
ORDER_CREATED = 'order.created',
|
|
471
|
+
ORDER_PAID = 'order.paid',
|
|
472
|
+
ORDER_SHIPPED = 'order.shipped',
|
|
473
|
+
ORDER_DELIVERED = 'order.delivered',
|
|
474
|
+
ORDER_CANCELLED = 'order.cancelled',
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export interface OrderEvent {
|
|
478
|
+
eventId: string;
|
|
479
|
+
eventType: OrderEventType;
|
|
480
|
+
orderId: string;
|
|
481
|
+
timestamp: Date;
|
|
482
|
+
userId: string;
|
|
483
|
+
data: unknown;
|
|
484
|
+
metadata: {
|
|
485
|
+
correlationId: string;
|
|
486
|
+
causationId: string;
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export interface OrderCreatedEvent extends OrderEvent {
|
|
491
|
+
eventType: OrderEventType.ORDER_CREATED;
|
|
492
|
+
data: {
|
|
493
|
+
items: Array<{
|
|
494
|
+
productId: string;
|
|
495
|
+
quantity: number;
|
|
496
|
+
price: number;
|
|
497
|
+
}>;
|
|
498
|
+
totalAmount: number;
|
|
499
|
+
shippingAddress: Address;
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Event Producer (Order Service - Node.js)**:
|
|
505
|
+
```typescript
|
|
506
|
+
import { Kafka, Producer } from 'kafkajs';
|
|
507
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
508
|
+
|
|
509
|
+
const kafka = new Kafka({
|
|
510
|
+
clientId: 'order-service',
|
|
511
|
+
brokers: ['kafka-0.kafka-headless:9092', 'kafka-1.kafka-headless:9092'],
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const producer: Producer = kafka.producer({
|
|
515
|
+
idempotent: true, // Prevent duplicate events
|
|
516
|
+
maxInFlightRequests: 5,
|
|
517
|
+
transactionalId: 'order-service-producer',
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
await producer.connect();
|
|
521
|
+
|
|
522
|
+
export async function publishOrderCreatedEvent(order: Order): Promise<void> {
|
|
523
|
+
const event: OrderCreatedEvent = {
|
|
524
|
+
eventId: uuidv4(),
|
|
525
|
+
eventType: OrderEventType.ORDER_CREATED,
|
|
526
|
+
orderId: order.id,
|
|
527
|
+
timestamp: new Date(),
|
|
528
|
+
userId: order.userId,
|
|
529
|
+
data: {
|
|
530
|
+
items: order.items,
|
|
531
|
+
totalAmount: order.totalAmount,
|
|
532
|
+
shippingAddress: order.shippingAddress,
|
|
533
|
+
},
|
|
534
|
+
metadata: {
|
|
535
|
+
correlationId: order.correlationId,
|
|
536
|
+
causationId: uuidv4(),
|
|
537
|
+
},
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
await producer.send({
|
|
541
|
+
topic: 'orders',
|
|
542
|
+
messages: [
|
|
543
|
+
{
|
|
544
|
+
key: order.id, // Partition by order ID
|
|
545
|
+
value: JSON.stringify(event),
|
|
546
|
+
headers: {
|
|
547
|
+
'event-type': event.eventType,
|
|
548
|
+
'correlation-id': event.metadata.correlationId,
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
],
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**Event Consumer (Email Service - Python)**:
|
|
557
|
+
```python
|
|
558
|
+
from kafka import KafkaConsumer
|
|
559
|
+
from kafka.errors import KafkaError
|
|
560
|
+
import json
|
|
561
|
+
import logging
|
|
562
|
+
|
|
563
|
+
logger = logging.getLogger(__name__)
|
|
564
|
+
|
|
565
|
+
consumer = KafkaConsumer(
|
|
566
|
+
'orders',
|
|
567
|
+
bootstrap_servers=['kafka-0.kafka-headless:9092'],
|
|
568
|
+
group_id='email-service',
|
|
569
|
+
auto_offset_reset='earliest',
|
|
570
|
+
enable_auto_commit=False,
|
|
571
|
+
max_poll_records=100,
|
|
572
|
+
value_deserializer=lambda m: json.loads(m.decode('utf-8')),
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
def process_order_created_event(event: dict):
|
|
576
|
+
"""Send order confirmation email."""
|
|
577
|
+
try:
|
|
578
|
+
send_email(
|
|
579
|
+
to=event['userId'],
|
|
580
|
+
subject=f"Order Confirmation - {event['orderId']}",
|
|
581
|
+
body=render_template('order_confirmation.html', event=event),
|
|
582
|
+
)
|
|
583
|
+
logger.info(f"Email sent for order {event['orderId']}")
|
|
584
|
+
except Exception as e:
|
|
585
|
+
logger.error(f"Failed to send email: {e}")
|
|
586
|
+
raise # Trigger retry
|
|
587
|
+
|
|
588
|
+
for message in consumer:
|
|
589
|
+
try:
|
|
590
|
+
event = message.value
|
|
591
|
+
event_type = message.headers.get('event-type')
|
|
592
|
+
|
|
593
|
+
if event_type == 'order.created':
|
|
594
|
+
process_order_created_event(event)
|
|
595
|
+
|
|
596
|
+
# Commit offset only after successful processing
|
|
597
|
+
consumer.commit()
|
|
598
|
+
|
|
599
|
+
except Exception as e:
|
|
600
|
+
logger.error(f"Error processing message: {e}")
|
|
601
|
+
# Do not commit; retry on next poll
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### CQRS Pattern
|
|
605
|
+
|
|
606
|
+
**Command Model (Write Side)**:
|
|
607
|
+
```typescript
|
|
608
|
+
// commands/create-order.command.ts
|
|
609
|
+
export class CreateOrderCommand {
|
|
610
|
+
constructor(
|
|
611
|
+
public readonly userId: string,
|
|
612
|
+
public readonly items: OrderItem[],
|
|
613
|
+
public readonly shippingAddress: Address,
|
|
614
|
+
) {}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export class CreateOrderHandler {
|
|
618
|
+
constructor(
|
|
619
|
+
private readonly orderRepository: OrderRepository,
|
|
620
|
+
private readonly eventBus: EventBus,
|
|
621
|
+
) {}
|
|
622
|
+
|
|
623
|
+
async execute(command: CreateOrderCommand): Promise<string> {
|
|
624
|
+
// Create order aggregate
|
|
625
|
+
const order = Order.create(
|
|
626
|
+
command.userId,
|
|
627
|
+
command.items,
|
|
628
|
+
command.shippingAddress,
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
// Persist to event store
|
|
632
|
+
await this.orderRepository.save(order);
|
|
633
|
+
|
|
634
|
+
// Publish domain events
|
|
635
|
+
for (const event of order.getUncommittedEvents()) {
|
|
636
|
+
await this.eventBus.publish(event);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
order.markEventsAsCommitted();
|
|
640
|
+
return order.id;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**Query Model (Read Side)**:
|
|
646
|
+
```typescript
|
|
647
|
+
// queries/get-order.query.ts
|
|
648
|
+
export class GetOrderQuery {
|
|
649
|
+
constructor(public readonly orderId: string) {}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
export class GetOrderHandler {
|
|
653
|
+
constructor(private readonly orderReadRepository: OrderReadRepository) {}
|
|
654
|
+
|
|
655
|
+
async execute(query: GetOrderQuery): Promise<OrderDTO> {
|
|
656
|
+
// Read from optimized read model (denormalized)
|
|
657
|
+
const order = await this.orderReadRepository.findById(query.orderId);
|
|
658
|
+
if (!order) {
|
|
659
|
+
throw new OrderNotFoundException(query.orderId);
|
|
660
|
+
}
|
|
661
|
+
return OrderDTO.fromReadModel(order);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
**Read Model Projector (Event Handler)**:
|
|
667
|
+
```typescript
|
|
668
|
+
// projections/order-projection.ts
|
|
669
|
+
export class OrderProjection {
|
|
670
|
+
constructor(private readonly db: Database) {}
|
|
671
|
+
|
|
672
|
+
@EventHandler(OrderEventType.ORDER_CREATED)
|
|
673
|
+
async onOrderCreated(event: OrderCreatedEvent): Promise<void> {
|
|
674
|
+
await this.db.insert('order_read_model', {
|
|
675
|
+
order_id: event.orderId,
|
|
676
|
+
user_id: event.userId,
|
|
677
|
+
status: 'CREATED',
|
|
678
|
+
total_amount: event.data.totalAmount,
|
|
679
|
+
created_at: event.timestamp,
|
|
680
|
+
updated_at: event.timestamp,
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// Insert order items into separate table
|
|
684
|
+
for (const item of event.data.items) {
|
|
685
|
+
await this.db.insert('order_item_read_model', {
|
|
686
|
+
order_id: event.orderId,
|
|
687
|
+
product_id: item.productId,
|
|
688
|
+
quantity: item.quantity,
|
|
689
|
+
price: item.price,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
@EventHandler(OrderEventType.ORDER_PAID)
|
|
695
|
+
async onOrderPaid(event: OrderPaidEvent): Promise<void> {
|
|
696
|
+
await this.db.update(
|
|
697
|
+
'order_read_model',
|
|
698
|
+
{ order_id: event.orderId },
|
|
699
|
+
{ status: 'PAID', updated_at: event.timestamp },
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## Example 3: Serverless with AWS Lambda
|
|
708
|
+
|
|
709
|
+
### Architecture Overview
|
|
710
|
+
|
|
711
|
+
```
|
|
712
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
713
|
+
│ API │───▶│ Lambda │───▶│ DynamoDB │
|
|
714
|
+
│ Gateway │ │ Functions│ └──────────┘
|
|
715
|
+
└──────────┘ └────┬─────┘
|
|
716
|
+
│
|
|
717
|
+
┌────▼────┐
|
|
718
|
+
│ SQS │
|
|
719
|
+
└────┬────┘
|
|
720
|
+
│
|
|
721
|
+
┌────▼────┐
|
|
722
|
+
│ Lambda │
|
|
723
|
+
│ Worker │
|
|
724
|
+
└─────────┘
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### API Lambda Function (Node.js)
|
|
728
|
+
|
|
729
|
+
**handler.ts**:
|
|
730
|
+
```typescript
|
|
731
|
+
import { APIGatewayProxyHandler, APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
|
732
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
733
|
+
import { DynamoDBDocumentClient, PutCommand, GetCommand } from '@aws-sdk/lib-dynamodb';
|
|
734
|
+
import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
|
|
735
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
736
|
+
|
|
737
|
+
const client = new DynamoDBClient({});
|
|
738
|
+
const ddb = DynamoDBDocumentClient.from(client);
|
|
739
|
+
const sqs = new SQSClient({});
|
|
740
|
+
|
|
741
|
+
export const createOrder: APIGatewayProxyHandler = async (
|
|
742
|
+
event: APIGatewayProxyEvent
|
|
743
|
+
): Promise<APIGatewayProxyResult> => {
|
|
744
|
+
try {
|
|
745
|
+
const body = JSON.parse(event.body || '{}');
|
|
746
|
+
const orderId = uuidv4();
|
|
747
|
+
|
|
748
|
+
// Write to DynamoDB
|
|
749
|
+
await ddb.send(
|
|
750
|
+
new PutCommand({
|
|
751
|
+
TableName: process.env.ORDERS_TABLE!,
|
|
752
|
+
Item: {
|
|
753
|
+
orderId,
|
|
754
|
+
userId: body.userId,
|
|
755
|
+
items: body.items,
|
|
756
|
+
totalAmount: body.totalAmount,
|
|
757
|
+
status: 'PENDING',
|
|
758
|
+
createdAt: new Date().toISOString(),
|
|
759
|
+
},
|
|
760
|
+
})
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
// Send message to SQS for async processing
|
|
764
|
+
await sqs.send(
|
|
765
|
+
new SendMessageCommand({
|
|
766
|
+
QueueUrl: process.env.ORDER_QUEUE_URL!,
|
|
767
|
+
MessageBody: JSON.stringify({
|
|
768
|
+
orderId,
|
|
769
|
+
userId: body.userId,
|
|
770
|
+
totalAmount: body.totalAmount,
|
|
771
|
+
}),
|
|
772
|
+
MessageAttributes: {
|
|
773
|
+
eventType: {
|
|
774
|
+
DataType: 'String',
|
|
775
|
+
StringValue: 'ORDER_CREATED',
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
})
|
|
779
|
+
);
|
|
780
|
+
|
|
781
|
+
return {
|
|
782
|
+
statusCode: 201,
|
|
783
|
+
headers: {
|
|
784
|
+
'Content-Type': 'application/json',
|
|
785
|
+
'Access-Control-Allow-Origin': '*',
|
|
786
|
+
},
|
|
787
|
+
body: JSON.stringify({ orderId }),
|
|
788
|
+
};
|
|
789
|
+
} catch (error) {
|
|
790
|
+
console.error('Error creating order:', error);
|
|
791
|
+
return {
|
|
792
|
+
statusCode: 500,
|
|
793
|
+
body: JSON.stringify({ error: 'Internal server error' }),
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
export const getOrder: APIGatewayProxyHandler = async (
|
|
799
|
+
event: APIGatewayProxyEvent
|
|
800
|
+
): Promise<APIGatewayProxyResult> => {
|
|
801
|
+
try {
|
|
802
|
+
const orderId = event.pathParameters?.orderId;
|
|
803
|
+
|
|
804
|
+
const result = await ddb.send(
|
|
805
|
+
new GetCommand({
|
|
806
|
+
TableName: process.env.ORDERS_TABLE!,
|
|
807
|
+
Key: { orderId },
|
|
808
|
+
})
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
if (!result.Item) {
|
|
812
|
+
return {
|
|
813
|
+
statusCode: 404,
|
|
814
|
+
body: JSON.stringify({ error: 'Order not found' }),
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return {
|
|
819
|
+
statusCode: 200,
|
|
820
|
+
headers: {
|
|
821
|
+
'Content-Type': 'application/json',
|
|
822
|
+
'Access-Control-Allow-Origin': '*',
|
|
823
|
+
},
|
|
824
|
+
body: JSON.stringify(result.Item),
|
|
825
|
+
};
|
|
826
|
+
} catch (error) {
|
|
827
|
+
console.error('Error fetching order:', error);
|
|
828
|
+
return {
|
|
829
|
+
statusCode: 500,
|
|
830
|
+
body: JSON.stringify({ error: 'Internal server error' }),
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
**Infrastructure as Code (Terraform)**:
|
|
837
|
+
```hcl
|
|
838
|
+
# lambda.tf
|
|
839
|
+
resource "aws_lambda_function" "create_order" {
|
|
840
|
+
filename = "lambda.zip"
|
|
841
|
+
function_name = "create-order"
|
|
842
|
+
role = aws_iam_role.lambda_exec.arn
|
|
843
|
+
handler = "handler.createOrder"
|
|
844
|
+
runtime = "nodejs20.x"
|
|
845
|
+
memory_size = 512
|
|
846
|
+
timeout = 30
|
|
847
|
+
|
|
848
|
+
environment {
|
|
849
|
+
variables = {
|
|
850
|
+
ORDERS_TABLE = aws_dynamodb_table.orders.name
|
|
851
|
+
ORDER_QUEUE_URL = aws_sqs_queue.orders.url
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
tracing_config {
|
|
856
|
+
mode = "Active" # Enable X-Ray tracing
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
resource "aws_lambda_function" "get_order" {
|
|
861
|
+
filename = "lambda.zip"
|
|
862
|
+
function_name = "get-order"
|
|
863
|
+
role = aws_iam_role.lambda_exec.arn
|
|
864
|
+
handler = "handler.getOrder"
|
|
865
|
+
runtime = "nodejs20.x"
|
|
866
|
+
memory_size = 256
|
|
867
|
+
timeout = 10
|
|
868
|
+
|
|
869
|
+
environment {
|
|
870
|
+
variables = {
|
|
871
|
+
ORDERS_TABLE = aws_dynamodb_table.orders.name
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
tracing_config {
|
|
876
|
+
mode = "Active"
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
# API Gateway
|
|
881
|
+
resource "aws_apigatewayv2_api" "main" {
|
|
882
|
+
name = "orders-api"
|
|
883
|
+
protocol_type = "HTTP"
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
resource "aws_apigatewayv2_route" "create_order" {
|
|
887
|
+
api_id = aws_apigatewayv2_api.main.id
|
|
888
|
+
route_key = "POST /orders"
|
|
889
|
+
target = "integrations/${aws_apigatewayv2_integration.create_order.id}"
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
resource "aws_apigatewayv2_route" "get_order" {
|
|
893
|
+
api_id = aws_apigatewayv2_api.main.id
|
|
894
|
+
route_key = "GET /orders/{orderId}"
|
|
895
|
+
target = "integrations/${aws_apigatewayv2_integration.get_order.id}"
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
# DynamoDB
|
|
899
|
+
resource "aws_dynamodb_table" "orders" {
|
|
900
|
+
name = "orders"
|
|
901
|
+
billing_mode = "PAY_PER_REQUEST"
|
|
902
|
+
hash_key = "orderId"
|
|
903
|
+
|
|
904
|
+
attribute {
|
|
905
|
+
name = "orderId"
|
|
906
|
+
type = "S"
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
point_in_time_recovery {
|
|
910
|
+
enabled = true
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
# SQS
|
|
915
|
+
resource "aws_sqs_queue" "orders" {
|
|
916
|
+
name = "orders-queue"
|
|
917
|
+
visibility_timeout_seconds = 300
|
|
918
|
+
message_retention_seconds = 1209600 # 14 days
|
|
919
|
+
receive_wait_time_seconds = 20 # Long polling
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### SQS Worker Lambda (Async Processing)
|
|
924
|
+
|
|
925
|
+
```typescript
|
|
926
|
+
import { SQSHandler, SQSEvent } from 'aws-lambda';
|
|
927
|
+
import { DynamoDBDocumentClient, UpdateCommand } from '@aws-sdk/lib-dynamodb';
|
|
928
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
929
|
+
|
|
930
|
+
const client = new DynamoDBClient({});
|
|
931
|
+
const ddb = DynamoDBDocumentClient.from(client);
|
|
932
|
+
|
|
933
|
+
export const processOrder: SQSHandler = async (event: SQSEvent): Promise<void> => {
|
|
934
|
+
for (const record of event.Records) {
|
|
935
|
+
try {
|
|
936
|
+
const message = JSON.parse(record.body);
|
|
937
|
+
const { orderId, userId, totalAmount } = message;
|
|
938
|
+
|
|
939
|
+
// Simulate payment processing
|
|
940
|
+
const paymentResult = await processPayment(userId, totalAmount);
|
|
941
|
+
|
|
942
|
+
// Update order status
|
|
943
|
+
await ddb.send(
|
|
944
|
+
new UpdateCommand({
|
|
945
|
+
TableName: process.env.ORDERS_TABLE!,
|
|
946
|
+
Key: { orderId },
|
|
947
|
+
UpdateExpression: 'SET #status = :status, paymentId = :paymentId',
|
|
948
|
+
ExpressionAttributeNames: {
|
|
949
|
+
'#status': 'status',
|
|
950
|
+
},
|
|
951
|
+
ExpressionAttributeValues: {
|
|
952
|
+
':status': paymentResult.success ? 'PAID' : 'PAYMENT_FAILED',
|
|
953
|
+
':paymentId': paymentResult.paymentId,
|
|
954
|
+
},
|
|
955
|
+
})
|
|
956
|
+
);
|
|
957
|
+
|
|
958
|
+
console.log(`Order ${orderId} processed successfully`);
|
|
959
|
+
} catch (error) {
|
|
960
|
+
console.error('Error processing order:', error);
|
|
961
|
+
throw error; // Trigger SQS retry
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
async function processPayment(userId: string, amount: number): Promise<{ success: boolean; paymentId: string }> {
|
|
967
|
+
// Simulate external payment API call
|
|
968
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
969
|
+
return { success: true, paymentId: `pay_${Date.now()}` };
|
|
970
|
+
}
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
### Cold Start Optimization
|
|
974
|
+
|
|
975
|
+
**Provisioned Concurrency**:
|
|
976
|
+
```hcl
|
|
977
|
+
resource "aws_lambda_provisioned_concurrency_config" "create_order" {
|
|
978
|
+
function_name = aws_lambda_function.create_order.function_name
|
|
979
|
+
provisioned_concurrent_executions = 5 # Keep 5 warm instances
|
|
980
|
+
qualifier = aws_lambda_function.create_order.version
|
|
981
|
+
}
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
**Lambda Layers (Shared Dependencies)**:
|
|
985
|
+
```hcl
|
|
986
|
+
resource "aws_lambda_layer_version" "dependencies" {
|
|
987
|
+
filename = "layer.zip"
|
|
988
|
+
layer_name = "shared-dependencies"
|
|
989
|
+
compatible_runtimes = ["nodejs20.x"]
|
|
990
|
+
description = "Shared dependencies for Lambda functions"
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
resource "aws_lambda_function" "create_order" {
|
|
994
|
+
# ... other config ...
|
|
995
|
+
layers = [aws_lambda_layer_version.dependencies.arn]
|
|
996
|
+
}
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
1001
|
+
## Example 4: Observability with OpenTelemetry 1.24 + Prometheus 2.48
|
|
1002
|
+
|
|
1003
|
+
### Full Stack Observability Setup
|
|
1004
|
+
|
|
1005
|
+
**OpenTelemetry Collector Deployment**:
|
|
1006
|
+
```yaml
|
|
1007
|
+
apiVersion: apps/v1
|
|
1008
|
+
kind: Deployment
|
|
1009
|
+
metadata:
|
|
1010
|
+
name: otel-collector
|
|
1011
|
+
namespace: observability
|
|
1012
|
+
spec:
|
|
1013
|
+
replicas: 2
|
|
1014
|
+
selector:
|
|
1015
|
+
matchLabels:
|
|
1016
|
+
app: otel-collector
|
|
1017
|
+
template:
|
|
1018
|
+
metadata:
|
|
1019
|
+
labels:
|
|
1020
|
+
app: otel-collector
|
|
1021
|
+
spec:
|
|
1022
|
+
containers:
|
|
1023
|
+
- name: otel-collector
|
|
1024
|
+
image: otel/opentelemetry-collector-contrib:0.92.0
|
|
1025
|
+
ports:
|
|
1026
|
+
- containerPort: 4317 # OTLP gRPC
|
|
1027
|
+
- containerPort: 4318 # OTLP HTTP
|
|
1028
|
+
- containerPort: 8889 # Prometheus metrics
|
|
1029
|
+
volumeMounts:
|
|
1030
|
+
- name: config
|
|
1031
|
+
mountPath: /etc/otelcol
|
|
1032
|
+
resources:
|
|
1033
|
+
requests:
|
|
1034
|
+
cpu: "200m"
|
|
1035
|
+
memory: "256Mi"
|
|
1036
|
+
limits:
|
|
1037
|
+
cpu: "1000m"
|
|
1038
|
+
memory: "1Gi"
|
|
1039
|
+
volumes:
|
|
1040
|
+
- name: config
|
|
1041
|
+
configMap:
|
|
1042
|
+
name: otel-collector-config
|
|
1043
|
+
---
|
|
1044
|
+
apiVersion: v1
|
|
1045
|
+
kind: ConfigMap
|
|
1046
|
+
metadata:
|
|
1047
|
+
name: otel-collector-config
|
|
1048
|
+
namespace: observability
|
|
1049
|
+
data:
|
|
1050
|
+
config.yaml: |
|
|
1051
|
+
receivers:
|
|
1052
|
+
otlp:
|
|
1053
|
+
protocols:
|
|
1054
|
+
grpc:
|
|
1055
|
+
endpoint: 0.0.0.0:4317
|
|
1056
|
+
http:
|
|
1057
|
+
endpoint: 0.0.0.0:4318
|
|
1058
|
+
|
|
1059
|
+
processors:
|
|
1060
|
+
batch:
|
|
1061
|
+
timeout: 10s
|
|
1062
|
+
send_batch_size: 1024
|
|
1063
|
+
memory_limiter:
|
|
1064
|
+
check_interval: 1s
|
|
1065
|
+
limit_mib: 512
|
|
1066
|
+
resource:
|
|
1067
|
+
attributes:
|
|
1068
|
+
- key: environment
|
|
1069
|
+
value: production
|
|
1070
|
+
action: insert
|
|
1071
|
+
|
|
1072
|
+
exporters:
|
|
1073
|
+
prometheus:
|
|
1074
|
+
endpoint: "0.0.0.0:8889"
|
|
1075
|
+
jaeger:
|
|
1076
|
+
endpoint: "jaeger-collector.observability:14250"
|
|
1077
|
+
tls:
|
|
1078
|
+
insecure: true
|
|
1079
|
+
elasticsearch:
|
|
1080
|
+
endpoints: ["http://elasticsearch.observability:9200"]
|
|
1081
|
+
logs_index: "otel-logs"
|
|
1082
|
+
|
|
1083
|
+
service:
|
|
1084
|
+
pipelines:
|
|
1085
|
+
traces:
|
|
1086
|
+
receivers: [otlp]
|
|
1087
|
+
processors: [memory_limiter, batch, resource]
|
|
1088
|
+
exporters: [jaeger]
|
|
1089
|
+
metrics:
|
|
1090
|
+
receivers: [otlp]
|
|
1091
|
+
processors: [memory_limiter, batch, resource]
|
|
1092
|
+
exporters: [prometheus]
|
|
1093
|
+
logs:
|
|
1094
|
+
receivers: [otlp]
|
|
1095
|
+
processors: [memory_limiter, batch, resource]
|
|
1096
|
+
exporters: [elasticsearch]
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
**Application Instrumentation (Python FastAPI)**:
|
|
1100
|
+
```python
|
|
1101
|
+
from fastapi import FastAPI
|
|
1102
|
+
from opentelemetry import trace, metrics
|
|
1103
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
1104
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
1105
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
1106
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
1107
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
1108
|
+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
|
1109
|
+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
1110
|
+
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
|
|
1111
|
+
|
|
1112
|
+
# Setup tracing
|
|
1113
|
+
trace_provider = TracerProvider()
|
|
1114
|
+
trace_exporter = OTLPSpanExporter(endpoint="otel-collector:4317", insecure=True)
|
|
1115
|
+
trace_provider.add_span_processor(BatchSpanProcessor(trace_exporter))
|
|
1116
|
+
trace.set_tracer_provider(trace_provider)
|
|
1117
|
+
|
|
1118
|
+
# Setup metrics
|
|
1119
|
+
metric_reader = PeriodicExportingMetricReader(
|
|
1120
|
+
OTLPMetricExporter(endpoint="otel-collector:4317", insecure=True),
|
|
1121
|
+
export_interval_millis=30000,
|
|
1122
|
+
)
|
|
1123
|
+
metric_provider = MeterProvider(metric_readers=[metric_reader])
|
|
1124
|
+
metrics.set_meter_provider(metric_provider)
|
|
1125
|
+
|
|
1126
|
+
# Create app
|
|
1127
|
+
app = FastAPI()
|
|
1128
|
+
|
|
1129
|
+
# Auto-instrument FastAPI
|
|
1130
|
+
FastAPIInstrumentor.instrument_app(app)
|
|
1131
|
+
|
|
1132
|
+
# Custom metrics
|
|
1133
|
+
meter = metrics.get_meter(__name__)
|
|
1134
|
+
order_counter = meter.create_counter(
|
|
1135
|
+
name="orders_created_total",
|
|
1136
|
+
description="Total number of orders created",
|
|
1137
|
+
unit="1",
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
@app.post("/orders")
|
|
1141
|
+
async def create_order(order: Order):
|
|
1142
|
+
tracer = trace.get_tracer(__name__)
|
|
1143
|
+
with tracer.start_as_current_span("create_order") as span:
|
|
1144
|
+
span.set_attribute("order.id", order.id)
|
|
1145
|
+
span.set_attribute("order.amount", order.totalAmount)
|
|
1146
|
+
|
|
1147
|
+
# Business logic
|
|
1148
|
+
result = await process_order(order)
|
|
1149
|
+
|
|
1150
|
+
# Record metric
|
|
1151
|
+
order_counter.add(1, {"status": result.status})
|
|
1152
|
+
|
|
1153
|
+
return result
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
**Grafana Dashboards (Provisioning)**:
|
|
1157
|
+
```yaml
|
|
1158
|
+
# grafana-dashboard-configmap.yaml
|
|
1159
|
+
apiVersion: v1
|
|
1160
|
+
kind: ConfigMap
|
|
1161
|
+
metadata:
|
|
1162
|
+
name: grafana-dashboards
|
|
1163
|
+
namespace: observability
|
|
1164
|
+
data:
|
|
1165
|
+
backend-overview.json: |
|
|
1166
|
+
{
|
|
1167
|
+
"dashboard": {
|
|
1168
|
+
"title": "Backend Overview",
|
|
1169
|
+
"panels": [
|
|
1170
|
+
{
|
|
1171
|
+
"title": "Request Rate",
|
|
1172
|
+
"targets": [
|
|
1173
|
+
{
|
|
1174
|
+
"expr": "rate(http_requests_total[5m])"
|
|
1175
|
+
}
|
|
1176
|
+
]
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
"title": "Error Rate",
|
|
1180
|
+
"targets": [
|
|
1181
|
+
{
|
|
1182
|
+
"expr": "rate(http_requests_total{status=~\"5..\"}[5m]) / rate(http_requests_total[5m])"
|
|
1183
|
+
}
|
|
1184
|
+
]
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
"title": "Latency (p95)",
|
|
1188
|
+
"targets": [
|
|
1189
|
+
{
|
|
1190
|
+
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))"
|
|
1191
|
+
}
|
|
1192
|
+
]
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
"title": "Active Connections",
|
|
1196
|
+
"targets": [
|
|
1197
|
+
{
|
|
1198
|
+
"expr": "sum(rate(http_requests_total[1m]))"
|
|
1199
|
+
}
|
|
1200
|
+
]
|
|
1201
|
+
}
|
|
1202
|
+
]
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
**Alert Rules (PrometheusRule)**:
|
|
1208
|
+
```yaml
|
|
1209
|
+
apiVersion: monitoring.coreos.com/v1
|
|
1210
|
+
kind: PrometheusRule
|
|
1211
|
+
metadata:
|
|
1212
|
+
name: backend-alerts
|
|
1213
|
+
namespace: observability
|
|
1214
|
+
spec:
|
|
1215
|
+
groups:
|
|
1216
|
+
- name: backend
|
|
1217
|
+
interval: 30s
|
|
1218
|
+
rules:
|
|
1219
|
+
- alert: HighErrorRate
|
|
1220
|
+
expr: |
|
|
1221
|
+
(
|
|
1222
|
+
rate(http_requests_total{status=~"5.."}[5m])
|
|
1223
|
+
/
|
|
1224
|
+
rate(http_requests_total[5m])
|
|
1225
|
+
) > 0.05
|
|
1226
|
+
for: 5m
|
|
1227
|
+
labels:
|
|
1228
|
+
severity: critical
|
|
1229
|
+
annotations:
|
|
1230
|
+
summary: "High error rate detected (instance {{ $labels.instance }})"
|
|
1231
|
+
description: "Error rate is {{ $value | humanizePercentage }} for the last 5 minutes"
|
|
1232
|
+
|
|
1233
|
+
- alert: HighLatency
|
|
1234
|
+
expr: |
|
|
1235
|
+
histogram_quantile(0.95,
|
|
1236
|
+
rate(http_request_duration_seconds_bucket[5m])
|
|
1237
|
+
) > 1.0
|
|
1238
|
+
for: 10m
|
|
1239
|
+
labels:
|
|
1240
|
+
severity: warning
|
|
1241
|
+
annotations:
|
|
1242
|
+
summary: "High latency detected (instance {{ $labels.instance }})"
|
|
1243
|
+
description: "P95 latency is {{ $value }}s for the last 10 minutes"
|
|
1244
|
+
|
|
1245
|
+
- alert: ServiceDown
|
|
1246
|
+
expr: up{job="backend"} == 0
|
|
1247
|
+
for: 2m
|
|
1248
|
+
labels:
|
|
1249
|
+
severity: critical
|
|
1250
|
+
annotations:
|
|
1251
|
+
summary: "Service is down (instance {{ $labels.instance }})"
|
|
1252
|
+
description: "Backend service has been down for more than 2 minutes"
|
|
1253
|
+
|
|
1254
|
+
- alert: HighMemoryUsage
|
|
1255
|
+
expr: |
|
|
1256
|
+
(
|
|
1257
|
+
container_memory_usage_bytes{pod=~"backend-.*"}
|
|
1258
|
+
/
|
|
1259
|
+
container_spec_memory_limit_bytes{pod=~"backend-.*"}
|
|
1260
|
+
) > 0.9
|
|
1261
|
+
for: 5m
|
|
1262
|
+
labels:
|
|
1263
|
+
severity: warning
|
|
1264
|
+
annotations:
|
|
1265
|
+
summary: "High memory usage (pod {{ $labels.pod }})"
|
|
1266
|
+
description: "Memory usage is {{ $value | humanizePercentage }}"
|
|
1267
|
+
|
|
1268
|
+
- alert: PodCrashLooping
|
|
1269
|
+
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
|
|
1270
|
+
for: 5m
|
|
1271
|
+
labels:
|
|
1272
|
+
severity: critical
|
|
1273
|
+
annotations:
|
|
1274
|
+
summary: "Pod is crash looping (pod {{ $labels.pod }})"
|
|
1275
|
+
description: "Pod has restarted {{ $value }} times in the last 15 minutes"
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
### Distributed Tracing (Jaeger UI)
|
|
1279
|
+
|
|
1280
|
+
**Example Trace (User Request → Order → Payment → Email)**:
|
|
1281
|
+
```
|
|
1282
|
+
Trace ID: abc123def456
|
|
1283
|
+
|
|
1284
|
+
Span 1: [user-service] GET /api/users/123 (20ms)
|
|
1285
|
+
└─ Span 2: [order-service] POST /orders (150ms)
|
|
1286
|
+
├─ Span 3: [postgres] SELECT * FROM users (5ms)
|
|
1287
|
+
├─ Span 4: [postgres] INSERT INTO orders (10ms)
|
|
1288
|
+
└─ Span 5: [payment-service] POST /charge (100ms)
|
|
1289
|
+
├─ Span 6: [redis] GET payment_cache (2ms)
|
|
1290
|
+
└─ Span 7: [stripe-api] POST /charges (90ms)
|
|
1291
|
+
└─ Span 8: [kafka] Publish order.created (5ms)
|
|
1292
|
+
|
|
1293
|
+
Span 9: [email-service] Kafka Consumer (500ms)
|
|
1294
|
+
└─ Span 10: [smtp] Send email (480ms)
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
**Benefits**:
|
|
1298
|
+
- Identify bottlenecks (Span 10: SMTP is slowest)
|
|
1299
|
+
- Visualize service dependencies
|
|
1300
|
+
- Root cause analysis for failures
|
|
1301
|
+
- Latency breakdown by service
|
|
1302
|
+
|
|
1303
|
+
---
|
|
1304
|
+
|
|
1305
|
+
---
|
|
1306
|
+
|
|
1307
|
+
## Example 5: Harbor Registry with Image Scanning
|
|
1308
|
+
|
|
1309
|
+
### Harbor 2.10.x Deployment
|
|
1310
|
+
|
|
1311
|
+
**Harbor Helm Installation**:
|
|
1312
|
+
```bash
|
|
1313
|
+
helm repo add harbor https://helm.goharbor.io
|
|
1314
|
+
helm install harbor harbor/harbor \
|
|
1315
|
+
--namespace harbor \
|
|
1316
|
+
--create-namespace \
|
|
1317
|
+
--set expose.type=ingress \
|
|
1318
|
+
--set expose.ingress.hosts.core=harbor.example.com \
|
|
1319
|
+
--set externalURL=https://harbor.example.com \
|
|
1320
|
+
--set persistence.enabled=true \
|
|
1321
|
+
--set persistence.persistentVolumeClaim.registry.size=100Gi \
|
|
1322
|
+
--set harborAdminPassword=admin123 \
|
|
1323
|
+
--set trivy.enabled=true \
|
|
1324
|
+
--set notary.enabled=true
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
**Trivy Scanner Configuration**:
|
|
1328
|
+
```yaml
|
|
1329
|
+
apiVersion: v1
|
|
1330
|
+
kind: ConfigMap
|
|
1331
|
+
metadata:
|
|
1332
|
+
name: harbor-trivy-config
|
|
1333
|
+
namespace: harbor
|
|
1334
|
+
data:
|
|
1335
|
+
trivy.yaml: |
|
|
1336
|
+
severity:
|
|
1337
|
+
- CRITICAL
|
|
1338
|
+
- HIGH
|
|
1339
|
+
- MEDIUM
|
|
1340
|
+
vuln-type:
|
|
1341
|
+
- os
|
|
1342
|
+
- library
|
|
1343
|
+
skip-update: false
|
|
1344
|
+
timeout: 5m
|
|
1345
|
+
```
|
|
1346
|
+
|
|
1347
|
+
**Robot Account for CI/CD**:
|
|
1348
|
+
```bash
|
|
1349
|
+
# Create robot account via Harbor API
|
|
1350
|
+
curl -X POST "https://harbor.example.com/api/v2.0/robots" \
|
|
1351
|
+
-H "Authorization: Basic $(echo -n admin:admin123 | base64)" \
|
|
1352
|
+
-H "Content-Type: application/json" \
|
|
1353
|
+
-d '{
|
|
1354
|
+
"name": "ci-robot",
|
|
1355
|
+
"duration": -1,
|
|
1356
|
+
"description": "CI/CD robot account",
|
|
1357
|
+
"permissions": [
|
|
1358
|
+
{
|
|
1359
|
+
"kind": "project",
|
|
1360
|
+
"namespace": "production",
|
|
1361
|
+
"access": [
|
|
1362
|
+
{"resource": "repository", "action": "push"},
|
|
1363
|
+
{"resource": "repository", "action": "pull"}
|
|
1364
|
+
]
|
|
1365
|
+
}
|
|
1366
|
+
]
|
|
1367
|
+
}'
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
**GitHub Actions Integration**:
|
|
1371
|
+
```yaml
|
|
1372
|
+
# .github/workflows/build.yml
|
|
1373
|
+
name: Build and Push to Harbor
|
|
1374
|
+
on:
|
|
1375
|
+
push:
|
|
1376
|
+
branches: [main]
|
|
1377
|
+
|
|
1378
|
+
jobs:
|
|
1379
|
+
build:
|
|
1380
|
+
runs-on: ubuntu-latest
|
|
1381
|
+
steps:
|
|
1382
|
+
- uses: actions/checkout@v4
|
|
1383
|
+
|
|
1384
|
+
- name: Login to Harbor
|
|
1385
|
+
uses: docker/login-action@v3
|
|
1386
|
+
with:
|
|
1387
|
+
registry: harbor.example.com
|
|
1388
|
+
username: robot$ci-robot
|
|
1389
|
+
password: ${{ secrets.HARBOR_ROBOT_TOKEN }}
|
|
1390
|
+
|
|
1391
|
+
- name: Build and push
|
|
1392
|
+
uses: docker/build-push-action@v5
|
|
1393
|
+
with:
|
|
1394
|
+
context: .
|
|
1395
|
+
push: true
|
|
1396
|
+
tags: harbor.example.com/production/backend:${{ github.sha }}
|
|
1397
|
+
|
|
1398
|
+
- name: Trigger Trivy scan
|
|
1399
|
+
run: |
|
|
1400
|
+
curl -X POST "https://harbor.example.com/api/v2.0/projects/production/repositories/backend/artifacts/${{ github.sha }}/scan" \
|
|
1401
|
+
-H "Authorization: Basic ${{ secrets.HARBOR_BASIC_AUTH }}"
|
|
1402
|
+
|
|
1403
|
+
- name: Check scan result
|
|
1404
|
+
run: |
|
|
1405
|
+
while true; do
|
|
1406
|
+
STATUS=$(curl -s "https://harbor.example.com/api/v2.0/projects/production/repositories/backend/artifacts/${{ github.sha }}" \
|
|
1407
|
+
-H "Authorization: Basic ${{ secrets.HARBOR_BASIC_AUTH }}" | jq -r '.scan_overview."application/vnd.security.vulnerability.report; version=1.1".scan_status')
|
|
1408
|
+
if [ "$STATUS" = "Success" ]; then
|
|
1409
|
+
CRITICAL=$(curl -s "https://harbor.example.com/api/v2.0/projects/production/repositories/backend/artifacts/${{ github.sha }}" \
|
|
1410
|
+
-H "Authorization: Basic ${{ secrets.HARBOR_BASIC_AUTH }}" | jq -r '.scan_overview."application/vnd.security.vulnerability.report; version=1.1".summary.critical')
|
|
1411
|
+
if [ "$CRITICAL" -gt 0 ]; then
|
|
1412
|
+
echo "CRITICAL vulnerabilities found: $CRITICAL"
|
|
1413
|
+
exit 1
|
|
1414
|
+
fi
|
|
1415
|
+
break
|
|
1416
|
+
fi
|
|
1417
|
+
sleep 5
|
|
1418
|
+
done
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
**Webhook for Vulnerability Notifications**:
|
|
1422
|
+
```yaml
|
|
1423
|
+
apiVersion: v1
|
|
1424
|
+
kind: ConfigMap
|
|
1425
|
+
metadata:
|
|
1426
|
+
name: harbor-webhook-config
|
|
1427
|
+
data:
|
|
1428
|
+
webhook.json: |
|
|
1429
|
+
{
|
|
1430
|
+
"name": "slack-vulnerabilities",
|
|
1431
|
+
"description": "Notify Slack on critical vulnerabilities",
|
|
1432
|
+
"events": ["SCANNING_COMPLETED"],
|
|
1433
|
+
"targets": [
|
|
1434
|
+
{
|
|
1435
|
+
"type": "slack",
|
|
1436
|
+
"address": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
|
|
1437
|
+
"skip_cert_verify": false
|
|
1438
|
+
}
|
|
1439
|
+
],
|
|
1440
|
+
"enabled": true
|
|
1441
|
+
}
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
---
|
|
1445
|
+
|
|
1446
|
+
## Example 6: GitOps with ArgoCD 2.10.x
|
|
1447
|
+
|
|
1448
|
+
### ArgoCD Installation and Setup
|
|
1449
|
+
|
|
1450
|
+
**Install ArgoCD**:
|
|
1451
|
+
```bash
|
|
1452
|
+
kubectl create namespace argocd
|
|
1453
|
+
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.10.0/manifests/install.yaml
|
|
1454
|
+
|
|
1455
|
+
# Get admin password
|
|
1456
|
+
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
**Application Manifest (GitOps)**:
|
|
1460
|
+
```yaml
|
|
1461
|
+
# apps/backend-app.yaml
|
|
1462
|
+
apiVersion: argoproj.io/v1alpha1
|
|
1463
|
+
kind: Application
|
|
1464
|
+
metadata:
|
|
1465
|
+
name: backend-production
|
|
1466
|
+
namespace: argocd
|
|
1467
|
+
spec:
|
|
1468
|
+
project: default
|
|
1469
|
+
source:
|
|
1470
|
+
repoURL: https://github.com/example/backend-manifests
|
|
1471
|
+
targetRevision: main
|
|
1472
|
+
path: production
|
|
1473
|
+
helm:
|
|
1474
|
+
values: |
|
|
1475
|
+
image:
|
|
1476
|
+
repository: harbor.example.com/production/backend
|
|
1477
|
+
tag: latest
|
|
1478
|
+
replicaCount: 3
|
|
1479
|
+
resources:
|
|
1480
|
+
requests:
|
|
1481
|
+
cpu: 100m
|
|
1482
|
+
memory: 128Mi
|
|
1483
|
+
limits:
|
|
1484
|
+
cpu: 500m
|
|
1485
|
+
memory: 512Mi
|
|
1486
|
+
destination:
|
|
1487
|
+
server: https://kubernetes.default.svc
|
|
1488
|
+
namespace: production
|
|
1489
|
+
syncPolicy:
|
|
1490
|
+
automated:
|
|
1491
|
+
prune: true
|
|
1492
|
+
selfHeal: true
|
|
1493
|
+
syncOptions:
|
|
1494
|
+
- CreateNamespace=true
|
|
1495
|
+
retry:
|
|
1496
|
+
limit: 5
|
|
1497
|
+
backoff:
|
|
1498
|
+
duration: 5s
|
|
1499
|
+
factor: 2
|
|
1500
|
+
maxDuration: 3m
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1503
|
+
**AppProject for Multi-Tenancy**:
|
|
1504
|
+
```yaml
|
|
1505
|
+
apiVersion: argoproj.io/v1alpha1
|
|
1506
|
+
kind: AppProject
|
|
1507
|
+
metadata:
|
|
1508
|
+
name: production
|
|
1509
|
+
namespace: argocd
|
|
1510
|
+
spec:
|
|
1511
|
+
description: Production environment
|
|
1512
|
+
sourceRepos:
|
|
1513
|
+
- 'https://github.com/example/*'
|
|
1514
|
+
destinations:
|
|
1515
|
+
- namespace: 'production'
|
|
1516
|
+
server: https://kubernetes.default.svc
|
|
1517
|
+
clusterResourceWhitelist:
|
|
1518
|
+
- group: ''
|
|
1519
|
+
kind: Namespace
|
|
1520
|
+
- group: 'apps'
|
|
1521
|
+
kind: Deployment
|
|
1522
|
+
- group: 'v1'
|
|
1523
|
+
kind: Service
|
|
1524
|
+
namespaceResourceBlacklist:
|
|
1525
|
+
- group: ''
|
|
1526
|
+
kind: ResourceQuota
|
|
1527
|
+
- group: ''
|
|
1528
|
+
kind: LimitRange
|
|
1529
|
+
roles:
|
|
1530
|
+
- name: prod-admin
|
|
1531
|
+
description: Admin privileges for production
|
|
1532
|
+
policies:
|
|
1533
|
+
- p, proj:production:prod-admin, applications, *, production/*, allow
|
|
1534
|
+
groups:
|
|
1535
|
+
- production-team
|
|
1536
|
+
```
|
|
1537
|
+
|
|
1538
|
+
**Canary Rollout with Argo Rollouts**:
|
|
1539
|
+
```yaml
|
|
1540
|
+
apiVersion: argoproj.io/v1alpha1
|
|
1541
|
+
kind: Rollout
|
|
1542
|
+
metadata:
|
|
1543
|
+
name: backend
|
|
1544
|
+
namespace: production
|
|
1545
|
+
spec:
|
|
1546
|
+
replicas: 5
|
|
1547
|
+
strategy:
|
|
1548
|
+
canary:
|
|
1549
|
+
steps:
|
|
1550
|
+
- setWeight: 20
|
|
1551
|
+
- pause: {duration: 5m}
|
|
1552
|
+
- setWeight: 40
|
|
1553
|
+
- pause: {duration: 5m}
|
|
1554
|
+
- setWeight: 60
|
|
1555
|
+
- pause: {duration: 5m}
|
|
1556
|
+
- setWeight: 80
|
|
1557
|
+
- pause: {duration: 5m}
|
|
1558
|
+
canaryService: backend-canary
|
|
1559
|
+
stableService: backend-stable
|
|
1560
|
+
trafficRouting:
|
|
1561
|
+
istio:
|
|
1562
|
+
virtualService:
|
|
1563
|
+
name: backend
|
|
1564
|
+
routes:
|
|
1565
|
+
- primary
|
|
1566
|
+
revisionHistoryLimit: 2
|
|
1567
|
+
selector:
|
|
1568
|
+
matchLabels:
|
|
1569
|
+
app: backend
|
|
1570
|
+
template:
|
|
1571
|
+
metadata:
|
|
1572
|
+
labels:
|
|
1573
|
+
app: backend
|
|
1574
|
+
spec:
|
|
1575
|
+
containers:
|
|
1576
|
+
- name: backend
|
|
1577
|
+
image: harbor.example.com/production/backend:latest
|
|
1578
|
+
ports:
|
|
1579
|
+
- containerPort: 8080
|
|
1580
|
+
livenessProbe:
|
|
1581
|
+
httpGet:
|
|
1582
|
+
path: /health/live
|
|
1583
|
+
port: 8080
|
|
1584
|
+
readinessProbe:
|
|
1585
|
+
httpGet:
|
|
1586
|
+
path: /health/ready
|
|
1587
|
+
port: 8080
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
**Analysis Template (Automated Rollback)**:
|
|
1591
|
+
```yaml
|
|
1592
|
+
apiVersion: argoproj.io/v1alpha1
|
|
1593
|
+
kind: AnalysisTemplate
|
|
1594
|
+
metadata:
|
|
1595
|
+
name: backend-success-rate
|
|
1596
|
+
namespace: production
|
|
1597
|
+
spec:
|
|
1598
|
+
metrics:
|
|
1599
|
+
- name: success-rate
|
|
1600
|
+
interval: 1m
|
|
1601
|
+
successCondition: result >= 0.95
|
|
1602
|
+
failureLimit: 3
|
|
1603
|
+
provider:
|
|
1604
|
+
prometheus:
|
|
1605
|
+
address: http://prometheus.observability:9090
|
|
1606
|
+
query: |
|
|
1607
|
+
sum(rate(http_requests_total{app="backend",status!~"5.."}[5m]))
|
|
1608
|
+
/
|
|
1609
|
+
sum(rate(http_requests_total{app="backend"}[5m]))
|
|
1610
|
+
```
|
|
1611
|
+
|
|
1612
|
+
---
|
|
1613
|
+
|
|
1614
|
+
## Summary
|
|
1615
|
+
|
|
1616
|
+
These examples demonstrate production-ready backend architectures with:
|
|
1617
|
+
|
|
1618
|
+
1. **Microservices**: Kubernetes 1.31 + Istio 1.21 for orchestration, traffic management, and security
|
|
1619
|
+
2. **Event-Driven**: Kafka 3.7 for event streaming, CQRS for read/write separation
|
|
1620
|
+
3. **Serverless**: AWS Lambda for auto-scaling, cost-effective event-driven workloads
|
|
1621
|
+
4. **Observability**: OpenTelemetry 1.24 + Prometheus 2.48 + Jaeger 1.51 for full-stack telemetry
|
|
1622
|
+
5. **Container Registry**: Harbor 2.10.x with Trivy scanning for vulnerability management
|
|
1623
|
+
6. **GitOps**: ArgoCD 2.10.x with automated rollouts and canary deployments
|
|
1624
|
+
|
|
1625
|
+
All examples include:
|
|
1626
|
+
- Latest tool versions (2025-10-22)
|
|
1627
|
+
- Production-grade configurations
|
|
1628
|
+
- Security best practices (mTLS, RBAC, OWASP compliance, image scanning)
|
|
1629
|
+
- Observability integration
|
|
1630
|
+
- Performance optimization (connection pooling, caching, rate limiting)
|
|
1631
|
+
- Infrastructure as Code (Kubernetes manifests, Terraform)
|
|
1632
|
+
- CI/CD integration (GitHub Actions, ArgoCD)
|
|
1633
|
+
- Automated deployment strategies (canary, blue-green)
|