@crossdelta/infrastructure 0.6.3 → 0.6.4

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.
Files changed (2) hide show
  1. package/README.md +59 -599
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -3,670 +3,130 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@crossdelta/infrastructure.svg)](https://www.npmjs.com/package/@crossdelta/infrastructure)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Infrastructure-as-Code toolkit for deploying microservices to Kubernetes with Pulumi.
7
-
8
- **Current Support:**
9
- - ✅ **DigitalOcean Kubernetes (DOKS)** - Production-ready, fully tested
10
- - ✅ **Local Kubernetes (k3d)** - Development environment
11
-
12
- **Planned Support:**
13
- - 🚧 **AWS EKS** - Planned (contributions welcome)
14
- - 🚧 **Azure AKS** - Planned (contributions welcome)
15
- - 🚧 **Google GKE** - Planned (contributions welcome)
16
-
17
- The architecture is **provider-agnostic** - runtime components (NATS, Ingress, Cert-Manager) use provider-specific implementations, but service configs remain portable.
18
-
19
- ## Architecture Overview
20
-
21
- ```
22
- ┌─────────────────────────────────────────┐
23
- │ DigitalOcean Kubernetes (DOKS) │
24
- │ │
25
- ┌───────────────┤ Runtime Components: │
26
- │ │ • NATS + JetStream (messaging) │
27
- Internet │ • NGINX Ingress (routing) │
28
- │ │ • Cert-Manager (TLS certs) │
29
- │ └─────────────────────────────────────────┘
30
- ▼ │
31
- ┌──────────┐ ▼
32
- │ DNS │ ┌──────────────────────────────────────────┐
33
- └────┬─────┘ │ Microservices: │
34
- │ │ │
35
- ▼ │ ┌──────────────┐ ┌──────────────┐ │
36
- ┌──────────┐ │ │ api-gateway │ │ orders │ │
37
- │ Load │◄────────┼──┤ :4000 │ │ :4001 │ │
38
- │ Balancer │ │ └──────┬───────┘ └──────┬───────┘ │
39
- └──────────┘ │ │ │ │
40
- │ ▼ ▼ │
41
- │ ┌──────────────────────────────┐ │
42
- │ │ NATS (Event Stream) │ │
43
- │ └──────────────────────────────┘ │
44
- │ ▲ ▲ │
45
- │ │ │ │
46
- │ ┌──────┴───────┐ ┌──────┴───────┐ │
47
- │ │notifications │ │ storefront │ │
48
- │ │ :4002 │ │ :3000 │ │
49
- │ └──────────────┘ └──────────────┘ │
50
- └──────────────────────────────────────────┘
51
- ```
52
-
53
- ### Why Kubernetes?
54
-
55
- | Feature | Benefit |
56
- |---------|---------|
57
- | **Declarative** | Define desired state, K8s maintains it |
58
- | **Self-healing** | Auto-restart failed pods, reschedule on node failure |
59
- | **Scaling** | Horizontal pod autoscaling based on metrics |
60
- | **Rolling updates** | Zero-downtime deployments |
61
- | **Service discovery** | Built-in DNS for pod-to-pod communication |
62
- | **Persistent volumes** | StatefulSets for databases and message queues |
63
-
64
- ## Features
65
-
66
- ### 🚀 Core Features
67
- - **Provider-Agnostic** - One config for DOKS, k3d, EKS, AKS, GKE
68
- - **Unified Runtime API** - Single `deployRuntime()` for NATS, Ingress, Cert-Manager
69
- - **Service Discovery** - Auto-discover services from `infra/services/*.ts`
70
- - **Fluent Port Configuration** - Modern API for HTTP, gRPC, custom ports
71
- - **Health Checks** - Automatic HTTP health check configuration
72
- - **Type-Safe** - Full TypeScript support with strict typing
73
-
74
- ### 📦 Runtime Components
75
- - **NATS + JetStream** - Event-driven messaging with persistence
76
- - **NGINX Ingress** - HTTP routing and load balancing
77
- - **Cert-Manager** - Automatic TLS certificates via Let's Encrypt
78
- - **OpenTelemetry** - Distributed tracing and metrics (via `@crossdelta/telemetry`)
79
-
80
- ### 🔧 Developer Experience
81
- - **Auto-Generated Types** - Service names from directory structure
82
- - **Environment Generation** - `.env.local` from Pulumi secrets
83
- - **Image Registry Support** - GHCR, DockerHub, DOCR
84
- - **Health Check Integration** - Kubernetes liveness/readiness probes
6
+ Opinionated Pulumi abstractions for deploying microservices to Kubernetes. Turns per-service config objects into Deployments, Services, Ingress, probes, and secrets.
85
7
 
86
8
  ## Installation
87
9
 
88
10
  ```bash
89
- npm install @crossdelta/infrastructure
11
+ npm install @crossdelta/infrastructure @pulumi/pulumi @pulumi/kubernetes
90
12
  ```
91
13
 
92
- ### Peer Dependencies
14
+ ## Usage
93
15
 
94
- This package requires Pulumi packages as peer dependencies. Install them based on your target cloud provider:
95
-
96
- ```bash
97
- # Required - Pulumi core and Kubernetes
98
- npm install @pulumi/pulumi @pulumi/kubernetes
99
-
100
- # Optional - DigitalOcean provider (for DOKS)
101
- npm install @pulumi/digitalocean
102
- ```
103
-
104
- | Package | Required | Description |
105
- |---------|----------|-------------|
106
- | `@pulumi/pulumi` | ✅ Yes | Pulumi core SDK |
107
- | `@pulumi/kubernetes` | ✅ Yes | Kubernetes resources |
108
- | `@pulumi/digitalocean` | Optional | DigitalOcean provider (DOKS, VPC, etc.) |
109
-
110
- ## Quick Start
111
-
112
- ### 1. Create service configs
16
+ ### Service config
113
17
 
114
18
  ```typescript
115
- // infra/services/orders.ts
116
19
  import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
117
20
 
118
21
  const config: K8sServiceConfig = {
119
- name: 'orders',
120
- ports: ports().http(4001).build(),
22
+ name: 'my-service',
23
+ ports: ports().http(4000).public().build(),
121
24
  replicas: 1,
122
- healthCheck: { httpPath: '/health' },
25
+ healthCheck: {
26
+ httpPath: '/health',
27
+ readinessPath: '/health/ready',
28
+ },
29
+ ingress: { path: '/api', host: 'example.com' },
30
+ env: { DATABASE_URL: dbUrl },
31
+ secrets: { API_KEY: apiKey },
123
32
  resources: {
124
33
  requests: { cpu: '50m', memory: '64Mi' },
125
- limits: { cpu: '150m', memory: '128Mi' },
126
- },
127
- }
128
-
129
- export default config
130
- ```
131
-
132
- ```typescript
133
- // infra/services/api-gateway.ts - Public service with ingress
134
- import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
135
-
136
- const config: K8sServiceConfig = {
137
- name: 'api-gateway',
138
- ports: ports().http(4000).public().build(),
139
- ingress: {
140
- path: '/api',
141
- host: 'api.example.com',
34
+ limits: { cpu: '200m', memory: '256Mi' },
142
35
  },
143
- replicas: 2,
144
- healthCheck: { httpPath: '/health' },
145
36
  }
146
37
 
147
38
  export default config
148
39
  ```
149
40
 
150
- ### 2. Create your Pulumi infrastructure
41
+ ### Deploy services
151
42
 
152
43
  ```typescript
153
- // infra/index.ts
154
- import {
155
- createDOKSCluster,
156
- createNamespace,
157
- createVPC,
158
- deployK8sServices,
159
- deployRuntime,
160
- discoverServiceConfigs,
161
- } from '@crossdelta/infrastructure'
44
+ import { deployK8sServices, discoverServiceConfigs } from '@crossdelta/infrastructure'
162
45
 
163
- const region = 'fra1'
164
- const namespace = 'my-platform'
46
+ const serviceConfigs = discoverServiceConfigs('services')
165
47
 
166
- // 1. Create VPC for private networking
167
- const vpc = createVPC({
168
- name: `${namespace}-vpc`,
169
- region,
170
- description: 'VPC for platform internal communication',
48
+ deployK8sServices({
49
+ provider,
50
+ namespace,
51
+ serviceConfigs,
52
+ env: { NATS_URL: natsUrl },
171
53
  })
54
+ ```
172
55
 
173
- // 2. Create Kubernetes cluster
174
- const { cluster, provider, kubeconfig } = createDOKSCluster({
175
- name: `${namespace}-cluster`,
176
- region,
177
- vpcUuid: vpc.id,
178
- nodePool: {
179
- name: 'default',
180
- size: 's-2vcpu-4gb',
181
- nodeCount: 2,
182
- },
183
- })
56
+ ### Deploy runtime components
184
57
 
185
- // 3. Create namespace
186
- createNamespace(provider, namespace)
58
+ ```typescript
59
+ import { deployRuntime } from '@crossdelta/infrastructure'
187
60
 
188
- // 4. Deploy runtime components (NATS, Ingress, Cert-Manager)
189
61
  const runtime = deployRuntime(provider, namespace, {
190
- nats: {
191
- enabled: true,
192
- config: {
193
- replicas: 1,
194
- jetstream: {
195
- enabled: true,
196
- storageSize: '1Gi',
197
- },
198
- },
199
- },
200
- ingress: {
201
- enabled: true,
202
- config: { replicas: 1 },
203
- },
204
- certManager: {
205
- enabled: true,
206
- config: { email: 'admin@example.com' },
207
- },
62
+ nats: { enabled: true, config: { replicas: 1, jetstream: { enabled: true } } },
63
+ ingress: { enabled: true },
64
+ certManager: { enabled: true, config: { email: 'ops@example.com' } },
208
65
  })
209
-
210
- // 5. Auto-discover and deploy services
211
- const serviceConfigs = discoverServiceConfigs('services')
212
- const deployedServices = deployK8sServices({
213
- provider,
214
- namespace,
215
- serviceConfigs,
216
- env: {
217
- NATS_URL: runtime.natsUrl,
218
- },
219
- })
220
-
221
- // Export outputs
222
- export const clusterEndpoint = cluster.endpoint
223
- export const clusterKubeconfig = kubeconfig
224
- export const natsInternalUrl = runtime.natsUrl
225
- export const loadBalancerIP = runtime.loadBalancerIp
226
66
  ```
227
67
 
228
- ## K8sServiceConfig API
229
-
230
- The `K8sServiceConfig` type is the central configuration for Kubernetes services:
68
+ ## K8sServiceConfig
231
69
 
232
70
  ```typescript
233
71
  interface K8sServiceConfig {
234
- /** Service name (used for deployment, service, ingress names) */
235
72
  name: string
236
-
237
- /** Port configuration (use fluent ports() builder) */
238
73
  ports: PortConfig
239
-
240
- /** Number of pod replicas (default: 1) */
241
74
  replicas?: number
242
-
243
- /** Resource requests and limits */
244
75
  resources?: {
245
76
  requests?: { cpu: string; memory: string }
246
77
  limits?: { cpu: string; memory: string }
247
78
  }
248
-
249
- /** Health check configuration */
250
79
  healthCheck?: {
251
- httpPath: string // HTTP endpoint (e.g., '/health')
252
- initialDelaySeconds?: number // Wait before first check (default: 10)
253
- periodSeconds?: number // Check interval (default: 10)
80
+ httpPath: string
81
+ readinessPath?: string // Falls back to httpPath
82
+ initialDelaySeconds?: number // Default: 10
83
+ periodSeconds?: number // Default: 10
84
+ failureThreshold?: number // Default: 3
254
85
  }
255
-
256
- /** Ingress configuration for public services */
257
86
  ingress?: {
258
- path: string // URL path (e.g., '/api')
259
- host?: string // Domain (e.g., 'api.example.com')
87
+ path: string
88
+ host?: string
260
89
  }
261
-
262
- /** Environment variables */
263
90
  env?: Record<string, pulumi.Input<string>>
264
-
265
- /** Kubernetes secrets */
266
91
  secrets?: Record<string, pulumi.Input<string>>
267
-
268
- /** Custom image (defaults to GHCR with auto-generated tag) */
269
92
  image?: string
270
-
271
- /** Skip deployment */
272
93
  skip?: boolean
273
94
  }
274
95
  ```
275
96
 
276
- ### Fluent Ports API
277
-
278
- Build port configurations with a fluent, chainable API:
97
+ ## Fluent Ports API
279
98
 
280
99
  ```typescript
281
100
  import { ports } from '@crossdelta/infrastructure'
282
101
 
283
- // Simple HTTP service
284
- ports().http(4000).build()
285
-
286
- // Public HTTP service (exposed via ingress)
287
- ports().http(4000).public().build()
288
-
289
- // gRPC service
290
- ports().grpc(50051).build()
291
-
292
- // Multiple ports
293
- ports()
294
- .http(4000) // Primary port
295
- .addHttp(8080, 'metrics').public() // Additional public port
296
- .add(9090, 'admin') // Additional internal port
297
- .build()
298
-
299
- // Custom protocol
300
- ports()
301
- .primary(4222, 'client')
302
- .protocol('TCP')
303
- .add(8222, 'monitoring')
304
- .build()
305
- ```
306
-
307
- **Port Builder Methods:**
308
- - `.http(port)` - HTTP primary port
309
- - `.https(port)` - HTTPS primary port
310
- - `.grpc(port)` - gRPC primary port
311
- - `.primary(port, name?)` - Custom primary port
312
- - `.add(port, name?, protocol?)` - Add additional port
313
- - `.addHttp(port, name?)` - Add HTTP additional port
314
- - `.addGrpc(port, name?)` - Add gRPC additional port
315
- - `.public()` - Mark last port as public (exposed via ingress)
316
- - `.protocol(type)` - Set protocol for last port
317
- - `.build()` - Build final config
318
-
319
- ## Service Configuration Examples
320
-
321
- ### Public HTTP Service with Ingress
322
-
323
- ```typescript
324
- import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
325
-
326
- const config: K8sServiceConfig = {
327
- name: 'api-gateway',
328
- ports: ports().http(4000).public().build(),
329
- replicas: 2,
330
- healthCheck: { httpPath: '/health' },
331
- ingress: {
332
- path: '/api',
333
- host: 'api.example.com',
334
- },
335
- resources: {
336
- requests: { cpu: '100m', memory: '128Mi' },
337
- limits: { cpu: '500m', memory: '512Mi' },
338
- },
339
- }
340
-
341
- export default config
342
- ```
343
-
344
- ### Internal Service (Event Consumer)
345
-
346
- ```typescript
347
- import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
348
-
349
- const config: K8sServiceConfig = {
350
- name: 'notifications',
351
- ports: ports().http(4002).build(),
352
- replicas: 1,
353
- healthCheck: { httpPath: '/health' },
354
- resources: {
355
- requests: { cpu: '50m', memory: '64Mi' },
356
- limits: { cpu: '150m', memory: '128Mi' },
357
- },
358
- }
359
-
360
- export default config
361
- ```
362
-
363
- ### Service with Multiple Ports
364
-
365
- ```typescript
366
- import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
367
-
368
- const config: K8sServiceConfig = {
369
- name: 'api-gateway',
370
- ports: ports()
371
- .http(4000) // Primary HTTP port
372
- .addHttp(8080, 'metrics').public() // Metrics endpoint (public)
373
- .add(9090, 'admin') // Admin endpoint (internal)
374
- .build(),
375
- replicas: 2,
376
- healthCheck: { httpPath: '/health' },
377
- }
378
-
379
- export default config
380
- ```
381
-
382
- ### gRPC Service
383
-
384
- ```typescript
385
- import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
386
-
387
- const config: K8sServiceConfig = {
388
- name: 'grpc-service',
389
- ports: ports().grpc(50051).build(),
390
- replicas: 2,
391
- healthCheck: {
392
- httpPath: '/grpc.health.v1.Health/Check',
393
- initialDelaySeconds: 15,
394
- },
395
- }
396
-
397
- export default config
398
- ```
399
-
400
- ## deployRuntime() - Unified Runtime Components
401
-
402
- Deploy all runtime components (NATS, Ingress, Cert-Manager) with a single function:
403
-
404
- ```typescript
405
- import { deployRuntime } from '@crossdelta/infrastructure'
406
-
407
- const runtime = deployRuntime(provider, namespace, {
408
- // NATS with JetStream for event-driven messaging
409
- nats: {
410
- enabled: true,
411
- config: {
412
- replicas: 1,
413
- jetstream: {
414
- enabled: true,
415
- storageSize: '1Gi',
416
- storageClass: 'do-block-storage',
417
- },
418
- resources: {
419
- requests: { cpu: '25m', memory: '64Mi' },
420
- limits: { cpu: '100m', memory: '192Mi' },
421
- },
422
- },
423
- },
424
-
425
- // NGINX Ingress Controller for HTTP routing
426
- ingress: {
427
- enabled: true,
428
- config: {
429
- replicas: 1,
430
- resources: {
431
- requests: { cpu: '50m', memory: '64Mi' },
432
- limits: { cpu: '100m', memory: '128Mi' },
433
- },
434
- },
435
- },
436
-
437
- // Cert-Manager for automatic TLS certificates
438
- certManager: {
439
- enabled: true,
440
- config: {
441
- email: 'admin@example.com',
442
- staging: false, // Use Let's Encrypt production
443
- resources: {
444
- requests: { cpu: '10m', memory: '32Mi' },
445
- limits: { cpu: '100m', memory: '128Mi' },
446
- },
447
- },
448
- },
449
- })
450
-
451
- // Use runtime outputs
452
- console.log('NATS URL:', runtime.natsUrl)
453
- console.log('LoadBalancer IP:', runtime.loadBalancerIp)
454
- console.log('Cert-Manager Ready:', runtime.certManagerReady)
102
+ ports().http(4000).build() // Simple HTTP
103
+ ports().http(4000).public().build() // Public (ingress)
104
+ ports().grpc(50051).build() // gRPC
105
+ ports().http(4000).addHttp(8080, 'metrics').build() // Multiple ports
455
106
  ```
456
107
 
457
- **Returns:**
458
- ```typescript
459
- interface RuntimeDeploymentResult {
460
- natsUrl?: pulumi.Output<string> // NATS connection URL
461
- loadBalancerIp?: pulumi.Output<string> // Ingress LoadBalancer IP
462
- certManagerReady?: boolean // Cert-Manager installed
463
- }
464
- ```
465
-
466
- ## Environment Generation
467
-
468
- Generate `.env.local` for local development from Pulumi config:
469
-
470
- ```bash
471
- # In your workspace root
472
- bun dev # Automatically generates .env.local before starting services
473
- ```
474
-
475
- **Generated `.env.local`:**
476
- ```bash
477
- # Auto-generated from Pulumi config (DO NOT EDIT)
478
- ORDERS_PORT=4001
479
- NOTIFICATIONS_PORT=4002
480
- API_GATEWAY_PORT=4000
481
- STOREFRONT_PORT=3000
482
-
483
- NATS_URL=nats://localhost:4222
484
- DATABASE_URL=postgresql://localhost:5432/mydb
485
- API_KEY=your-secret-key
486
- ```
487
-
488
- **Generated `infra/env.ts`:**
489
- ```typescript
490
- // Auto-generated type-safe service names
491
- export type ServiceName = 'orders' | 'notifications' | 'api-gateway' | 'storefront'
492
-
493
- export function getServicePort(name: ServiceName, fallback?: number): number {
494
- const port = process.env[`${name.toUpperCase().replace(/-/g, '_')}_PORT`]
495
- return port ? Number.parseInt(port, 10) : (fallback ?? 8080)
496
- }
497
- ```
108
+ | Method | Description |
109
+ |--------|-------------|
110
+ | `.http(port)` | HTTP primary port |
111
+ | `.grpc(port)` | gRPC primary port |
112
+ | `.primary(port, name?)` | Custom primary port |
113
+ | `.add(port, name?, protocol?)` | Additional port |
114
+ | `.addHttp(port, name?)` | Additional HTTP port |
115
+ | `.addGrpc(port, name?)` | Additional gRPC port |
116
+ | `.public()` | Expose via ingress |
117
+ | `.protocol(type)` | Set protocol (TCP, UDP, SCTP) |
118
+ | `.build()` | Build config |
498
119
 
499
120
  ## API Reference
500
121
 
501
- ### Core Functions
502
-
503
122
  | Function | Description |
504
123
  |----------|-------------|
505
- | `deployRuntime(provider, namespace, config)` | Deploy runtime components (NATS, Ingress, Cert-Manager) |
506
- | `discoverServiceConfigs(dir, options?)` | Auto-discover K8s service configs from directory |
507
- | `deployK8sServices(options)` | Deploy services to Kubernetes cluster |
508
- | `createDOKSCluster(config)` | Create DigitalOcean Kubernetes cluster |
509
- | `createVPC(config)` | Create VPC for private networking |
510
- | `createNamespace(provider, name, labels?)` | Create Kubernetes namespace |
511
- | `createImagePullSecret(provider, namespace, name, config)` | Create secret for private registries |
512
-
513
- ### Port Configuration
514
-
515
- | Function | Description |
516
- |----------|-------------|
517
- | `ports()` | Start fluent port builder |
518
- | `.http(port)` | Set HTTP primary port |
519
- | `.https(port)` | Set HTTPS primary port |
520
- | `.grpc(port)` | Set gRPC primary port |
521
- | `.primary(port, name?)` | Set custom primary port |
522
- | `.add(port, name?, protocol?)` | Add additional port |
523
- | `.addHttp(port, name?)` | Add HTTP additional port |
524
- | `.addGrpc(port, name?)` | Add gRPC additional port |
525
- | `.public()` | Mark last port as public (ingress) |
526
- | `.protocol(type)` | Set protocol ('TCP', 'UDP', 'SCTP') |
527
- | `.build()` | Build final port configuration |
528
-
529
- ### Service Discovery Options
530
-
531
- ```typescript
532
- discoverServiceConfigs('services', {
533
- filter?: RegExp | string, // Filter by service name pattern
534
- include?: string[], // Whitelist specific services
535
- exclude?: string[], // Blacklist specific services
536
- tags?: string[], // Filter by tags
537
- env?: Record<string, string>, // Inject env vars
538
- validate?: boolean, // Validate configs (default: true)
539
- sort?: boolean, // Sort by name (default: true)
540
- })
541
- ```
542
-
543
- ### Kubernetes Deployment Options
544
-
545
- ```typescript
546
- deployK8sServices({
547
- provider: k8s.Provider, // Kubernetes provider
548
- namespace: string, // Target namespace
549
- serviceConfigs: K8sServiceConfig[], // Service configurations
550
- env?: Record<string, pulumi.Input<string>>, // Global env vars
551
- secrets?: Record<string, pulumi.Input<string>>, // Global secrets
552
- imagePullSecrets?: string[], // Image pull secret names
553
- registry?: string, // Override registry URL
554
- })
555
- ```
556
-
557
- Returns a Map:
558
- ```typescript
559
- Map<string, {
560
- deployment: k8s.apps.v1.Deployment, // Kubernetes Deployment
561
- service: k8s.core.v1.Service, // Kubernetes Service
562
- ingress?: k8s.networking.v1.Ingress, // Ingress (if public)
563
- internalUrl: pulumi.Output<string>, // Internal service URL
564
- }>
565
- ```
566
-
567
- ### Environment Helpers (`@crossdelta/infrastructure/env`)
568
-
569
- ```typescript
570
- import { getServicePort } from '@crossdelta/infrastructure/env'
571
-
572
- // Read SERVICE_NAME_PORT from environment
573
- const port = getServicePort('orders', 4001) // Fallback to 4001
574
- ```
575
-
576
- ## Cost Estimation (DOKS)
577
-
578
- ### Basic Setup
579
- - **DOKS Cluster**: $12/month (control plane)
580
- - **Worker Nodes**: 2× s-2vcpu-4gb = $36/month
581
- - **Load Balancer**: $12/month (managed by DOKS)
582
- - **Block Storage**: 10GB for NATS JetStream = $1/month
583
- - **Total**: ~$61/month
584
-
585
- ### Production Setup
586
- - **DOKS Cluster**: $12/month
587
- - **Worker Nodes**: 3× s-2vcpu-4gb = $54/month (HA)
588
- - **Load Balancer**: $12/month
589
- - **Block Storage**: 50GB = $5/month
590
- - **Total**: ~$83/month
591
-
592
- ### Scaling Considerations
593
-
594
- | Workload | Node Size | Monthly Cost |
595
- |----------|-----------|--------------|
596
- | Development | 1× s-2vcpu-4gb | $12 + $18 = $30/mo |
597
- | Staging | 2× s-2vcpu-4gb | $12 + $36 = $48/mo |
598
- | Production | 3× s-4vcpu-8gb | $12 + $144 = $156/mo |
599
- | High Traffic | 5× s-4vcpu-8gb | $12 + $240 = $252/mo |
600
-
601
- **Benefits of Kubernetes:**
602
- - **Auto-scaling**: HPA automatically adjusts pod count
603
- - **Self-healing**: Failed pods restart automatically
604
- - **Rolling updates**: Zero-downtime deployments
605
- - **Resource efficiency**: Better node utilization vs. dedicated VMs
606
-
607
- ## Adding New Providers
608
-
609
- The library is designed to be **provider-agnostic**. To add support for a new cloud provider (AWS EKS, Azure AKS, GKE):
610
-
611
- ### 1. Implement Provider-Specific Runtime Components
612
-
613
- Create a new directory under `lib/runtimes/`:
614
-
615
- ```
616
- lib/runtimes/
617
- ├── doks/ # DigitalOcean Kubernetes (reference implementation)
618
- │ ├── nats.ts
619
- │ ├── ingress.ts
620
- │ └── cert-manager.ts
621
- └── eks/ # Your new provider
622
- ├── nats.ts
623
- ├── ingress.ts
624
- └── cert-manager.ts
625
- ```
626
-
627
- ### 2. Follow the Interface Pattern
628
-
629
- Each component should export a deployment function:
630
-
631
- ```typescript
632
- // lib/runtimes/eks/nats.ts
633
- export function deployNats(
634
- provider: k8s.Provider,
635
- namespace: string,
636
- config: NatsConfig
637
- ): NatsDeploymentResult {
638
- // Provider-specific NATS deployment
639
- // Use Helm, manifests, or provider-specific resources
640
-
641
- return {
642
- release,
643
- internalUrl: `nats://nats.${namespace}.svc.cluster.local:4222`,
644
- serviceDns: `nats.${namespace}.svc.cluster.local`,
645
- }
646
- }
647
- ```
648
-
649
- ### 3. Update deployRuntime()
650
-
651
- Add your provider to `lib/core/runtime.ts`:
652
-
653
- ```typescript
654
- // Deploy NATS if enabled
655
- if (config.nats?.enabled) {
656
- if (providerName === 'doks') {
657
- const nats = deployNats(provider, namespace, config.nats.config || {})
658
- result.natsUrl = pulumi.output(nats.internalUrl)
659
- } else if (providerName === 'eks') {
660
- const { deployNats } = require('../runtimes/eks/nats')
661
- const nats = deployNats(provider, namespace, config.nats.config || {})
662
- result.natsUrl = pulumi.output(nats.internalUrl)
663
- }
664
- }
665
- ```
666
-
667
- ### 4. Service Configs Remain Portable
668
-
669
- Service configurations (`K8sServiceConfig`) work across all providers without changes. Only runtime components need provider-specific implementations.
124
+ | `deployRuntime(provider, ns, config)` | Deploy runtime components |
125
+ | `discoverServiceConfigs(dir, opts?)` | Auto-discover service configs from filesystem |
126
+ | `deployK8sServices(opts)` | Deploy services to cluster |
127
+ | `createNamespace(provider, name)` | Create k8s namespace |
128
+ | `createImagePullSecret(...)` | Create registry pull secret |
129
+ | `ports()` | Fluent port builder |
670
130
 
671
131
  ## License
672
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/infrastructure",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@crossdelta/cloudevents": "0.6.4"
38
+ "@crossdelta/cloudevents": "0.6.7"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@pulumi/digitalocean": "^4.0.0",