0
# Kustomize Integration
1
2
The Kustomize integration enables you to deploy applications using Kustomize directory configurations while gaining full Pulumi lifecycle management, resource transformations, and infrastructure-as-code capabilities.
3
4
## Package Import
5
6
```typescript { .api }
7
import { kustomize } from "@pulumi/kubernetes";
8
import * as k8s from "@pulumi/kubernetes";
9
10
// Direct Kustomize imports
11
import { Directory } from "@pulumi/kubernetes/kustomize/v2";
12
```
13
14
## Directory (kustomize/v2)
15
16
Directory deploys resources from a Kustomize directory configuration, applying all transformations and overlays defined in the Kustomize setup.
17
18
```typescript { .api }
19
class Directory extends pulumi.ComponentResource {
20
constructor(name: string, args: DirectoryArgs, opts?: pulumi.ComponentResourceOptions)
21
22
// Resource access methods (inherited from base collection resource)
23
public getResource<T extends pulumi.CustomResource>(
24
group: string,
25
version: string,
26
kind: string,
27
name: string,
28
namespace?: string
29
): T
30
31
public getResource(groupVersionKind: string, name: string, namespace?: string): pulumi.CustomResource
32
public getResourceProperty(groupVersionKind: string, name: string, property: string, namespace?: string): pulumi.Output<any>
33
}
34
35
interface DirectoryArgs {
36
// Directory specification
37
directory: pulumi.Input<string>; // Path to kustomization directory
38
39
// Namespace options
40
namespace?: pulumi.Input<string>; // Override namespace for all resources
41
42
// Resource options
43
resourcePrefix?: pulumi.Input<string>; // Prefix for resource names
44
45
// Transformation options
46
transformations?: pulumi.ResourceTransformation[]; // Additional Pulumi transformations
47
48
// Deployment options
49
skipAwait?: pulumi.Input<boolean>; // Skip waiting for resource readiness
50
}
51
```
52
53
### Directory Usage Examples
54
55
```typescript { .api }
56
// Deploy from basic kustomize directory
57
const baseApp = new k8s.kustomize.v2.Directory("base-app", {
58
directory: "./kustomize/base",
59
});
60
61
// Deploy from overlay directory
62
const prodApp = new k8s.kustomize.v2.Directory("prod-app", {
63
directory: "./kustomize/overlays/production",
64
namespace: "production", // Override kustomize namespace
65
});
66
67
// Deploy with resource prefix
68
const stagingApp = new k8s.kustomize.v2.Directory("staging-app", {
69
directory: "./kustomize/overlays/staging",
70
resourcePrefix: "staging-",
71
});
72
73
// Deploy with Pulumi transformations
74
const secureApp = new k8s.kustomize.v2.Directory("secure-app", {
75
directory: "./kustomize/overlays/production",
76
transformations: [
77
// Add security labels
78
(args: any) => {
79
if (args.type.startsWith("kubernetes:")) {
80
args.props = pulumi.output(args.props).apply((props: any) => ({
81
...props,
82
metadata: {
83
...props.metadata,
84
labels: {
85
...props.metadata?.labels,
86
"security.kubernetes.io/policy": "restricted",
87
"app.kubernetes.io/managed-by": "pulumi",
88
},
89
},
90
}));
91
}
92
return { props: args.props, opts: args.opts };
93
},
94
// Enforce resource limits
95
(args: any) => {
96
if (args.type === "kubernetes:apps/v1:Deployment") {
97
args.props = pulumi.output(args.props).apply((props: any) => ({
98
...props,
99
spec: {
100
...props.spec,
101
template: {
102
...props.spec.template,
103
spec: {
104
...props.spec.template.spec,
105
securityContext: {
106
...props.spec.template.spec.securityContext,
107
runAsNonRoot: true,
108
runAsUser: 1000,
109
fsGroup: 2000,
110
},
111
containers: props.spec.template.spec.containers?.map((container: any) => ({
112
...container,
113
securityContext: {
114
...container.securityContext,
115
allowPrivilegeEscalation: false,
116
readOnlyRootFilesystem: true,
117
capabilities: {
118
drop: ["ALL"],
119
},
120
},
121
resources: {
122
requests: {
123
cpu: "100m",
124
memory: "128Mi",
125
...container.resources?.requests,
126
},
127
limits: {
128
cpu: "500m",
129
memory: "512Mi",
130
...container.resources?.limits,
131
},
132
},
133
})),
134
},
135
},
136
},
137
}));
138
}
139
return { props: args.props, opts: args.opts };
140
},
141
],
142
});
143
144
// Deploy with skip await for faster deployment
145
const fastApp = new k8s.kustomize.v2.Directory("fast-app", {
146
directory: "./kustomize/overlays/development",
147
skipAwait: true, // Don't wait for resources to become ready
148
});
149
```
150
151
## Kustomize Directory Structure Examples
152
153
### Basic Application Structure
154
155
```
156
kustomize/
157
├── base/
158
│ ├── kustomization.yaml
159
│ ├── deployment.yaml
160
│ ├── service.yaml
161
│ └── configmap.yaml
162
└── overlays/
163
├── development/
164
│ ├── kustomization.yaml
165
│ ├── deployment-patch.yaml
166
│ └── ingress.yaml
167
├── staging/
168
│ ├── kustomization.yaml
169
│ ├── deployment-patch.yaml
170
│ ├── hpa.yaml
171
│ └── ingress.yaml
172
└── production/
173
├── kustomization.yaml
174
├── deployment-patch.yaml
175
├── hpa.yaml
176
├── pdb.yaml
177
└── ingress.yaml
178
```
179
180
### Base Kustomization Example
181
182
```yaml
183
# kustomize/base/kustomization.yaml
184
apiVersion: kustomize.config.k8s.io/v1beta1
185
kind: Kustomization
186
187
resources:
188
- deployment.yaml
189
- service.yaml
190
- configmap.yaml
191
192
commonLabels:
193
app.kubernetes.io/name: myapp
194
app.kubernetes.io/component: backend
195
196
commonAnnotations:
197
app.kubernetes.io/version: "1.0.0"
198
199
images:
200
- name: myapp
201
newTag: latest
202
203
configMapGenerator:
204
- name: app-config
205
literals:
206
- PORT=8080
207
- LOG_LEVEL=info
208
```
209
210
### Overlay Kustomization Example
211
212
```yaml
213
# kustomize/overlays/production/kustomization.yaml
214
apiVersion: kustomize.config.k8s.io/v1beta1
215
kind: Kustomization
216
217
namespace: production
218
219
bases:
220
- ../../base
221
222
resources:
223
- hpa.yaml
224
- pdb.yaml
225
- ingress.yaml
226
227
patchesStrategicMerge:
228
- deployment-patch.yaml
229
230
images:
231
- name: myapp
232
newTag: v1.2.3
233
234
replicas:
235
- name: myapp-deployment
236
count: 5
237
238
configMapGenerator:
239
- name: app-config
240
behavior: merge
241
literals:
242
- LOG_LEVEL=warn
243
- REPLICAS=5
244
- ENVIRONMENT=production
245
246
secretGenerator:
247
- name: app-secrets
248
literals:
249
- DATABASE_PASSWORD=prod-password
250
- API_KEY=prod-api-key
251
```
252
253
## Complete Application Examples
254
255
### Multi-Environment Deployment
256
257
```typescript { .api }
258
// Development environment
259
const devApp = new k8s.kustomize.v2.Directory("dev-app", {
260
directory: "./kustomize/overlays/development",
261
transformations: [
262
// Development-specific transformations
263
(args: any) => {
264
if (args.type.startsWith("kubernetes:")) {
265
args.props = pulumi.output(args.props).apply((props: any) => ({
266
...props,
267
metadata: {
268
...props.metadata,
269
labels: {
270
...props.metadata?.labels,
271
environment: "development",
272
"cost-center": "engineering",
273
},
274
},
275
}));
276
}
277
return { props: args.props, opts: args.opts };
278
},
279
],
280
});
281
282
// Staging environment
283
const stagingApp = new k8s.kustomize.v2.Directory("staging-app", {
284
directory: "./kustomize/overlays/staging",
285
transformations: [
286
// Staging-specific transformations
287
(args: any) => {
288
if (args.type.startsWith("kubernetes:")) {
289
args.props = pulumi.output(args.props).apply((props: any) => ({
290
...props,
291
metadata: {
292
...props.metadata,
293
labels: {
294
...props.metadata?.labels,
295
environment: "staging",
296
"cost-center": "qa",
297
},
298
annotations: {
299
...props.metadata?.annotations,
300
"deployment.kubernetes.io/revision": "staging",
301
},
302
},
303
}));
304
}
305
return { props: args.props, opts: args.opts };
306
},
307
],
308
});
309
310
// Production environment
311
const prodApp = new k8s.kustomize.v2.Directory("prod-app", {
312
directory: "./kustomize/overlays/production",
313
transformations: [
314
// Production-specific transformations
315
(args: any) => {
316
if (args.type.startsWith("kubernetes:")) {
317
args.props = pulumi.output(args.props).apply((props: any) => ({
318
...props,
319
metadata: {
320
...props.metadata,
321
labels: {
322
...props.metadata?.labels,
323
environment: "production",
324
"cost-center": "production",
325
"backup.kubernetes.io/enabled": "true",
326
},
327
annotations: {
328
...props.metadata?.annotations,
329
"deployment.kubernetes.io/revision": "production",
330
"monitoring.coreos.com/enabled": "true",
331
},
332
},
333
}));
334
}
335
return { props: args.props, opts: args.opts };
336
},
337
// Production security hardening
338
(args: any) => {
339
if (args.type === "kubernetes:apps/v1:Deployment") {
340
args.props = pulumi.output(args.props).apply((props: any) => ({
341
...props,
342
spec: {
343
...props.spec,
344
template: {
345
...props.spec.template,
346
spec: {
347
...props.spec.template.spec,
348
securityContext: {
349
runAsNonRoot: true,
350
runAsUser: 1000,
351
fsGroup: 2000,
352
},
353
containers: props.spec.template.spec.containers?.map((container: any) => ({
354
...container,
355
securityContext: {
356
allowPrivilegeEscalation: false,
357
readOnlyRootFilesystem: true,
358
capabilities: { drop: ["ALL"] },
359
},
360
})),
361
},
362
},
363
},
364
}));
365
}
366
return { props: args.props, opts: args.opts };
367
},
368
],
369
});
370
371
// Access resources from deployments
372
const prodDeployment = prodApp.getResource("apps/v1", "Deployment", "myapp-deployment");
373
const prodService = prodApp.getResource("v1", "Service", "myapp-service");
374
375
// Export important endpoints
376
export const productionEndpoint = prodService.status.loadBalancer.ingress[0].hostname;
377
export const deploymentReplicas = prodDeployment.spec.replicas;
378
```
379
380
### Microservices Application with Kustomize
381
382
```typescript { .api }
383
// Deploy each microservice from its own kustomize overlay
384
const userService = new k8s.kustomize.v2.Directory("user-service", {
385
directory: "./kustomize/services/user-service/overlays/production",
386
namespace: "services",
387
transformations: [
388
// Service-specific transformations
389
(args: any) => {
390
if (args.type.startsWith("kubernetes:")) {
391
args.props = pulumi.output(args.props).apply((props: any) => ({
392
...props,
393
metadata: {
394
...props.metadata,
395
labels: {
396
...props.metadata?.labels,
397
"service.kubernetes.io/name": "user-service",
398
"service.kubernetes.io/version": "v2.1.0",
399
},
400
},
401
}));
402
}
403
return { props: args.props, opts: args.opts };
404
},
405
],
406
});
407
408
const orderService = new k8s.kustomize.v2.Directory("order-service", {
409
directory: "./kustomize/services/order-service/overlays/production",
410
namespace: "services",
411
transformations: [
412
(args: any) => {
413
if (args.type.startsWith("kubernetes:")) {
414
args.props = pulumi.output(args.props).apply((props: any) => ({
415
...props,
416
metadata: {
417
...props.metadata,
418
labels: {
419
...props.metadata?.labels,
420
"service.kubernetes.io/name": "order-service",
421
"service.kubernetes.io/version": "v1.5.2",
422
},
423
},
424
}));
425
}
426
return { props: args.props, opts: args.opts };
427
},
428
],
429
}, {
430
dependsOn: [userService], // Order service depends on user service
431
});
432
433
const paymentService = new k8s.kustomize.v2.Directory("payment-service", {
434
directory: "./kustomize/services/payment-service/overlays/production",
435
namespace: "services",
436
transformations: [
437
(args: any) => {
438
if (args.type.startsWith("kubernetes:")) {
439
args.props = pulumi.output(args.props).apply((props: any) => ({
440
...props,
441
metadata: {
442
...props.metadata,
443
labels: {
444
...props.metadata?.labels,
445
"service.kubernetes.io/name": "payment-service",
446
"service.kubernetes.io/version": "v3.0.1",
447
},
448
},
449
}));
450
}
451
return { props: args.props, opts: args.opts };
452
},
453
],
454
}, {
455
dependsOn: [userService, orderService],
456
});
457
458
// Deploy shared infrastructure
459
const sharedInfra = new k8s.kustomize.v2.Directory("shared-infrastructure", {
460
directory: "./kustomize/infrastructure/production",
461
namespace: "infrastructure",
462
});
463
464
// Deploy API gateway that depends on all services
465
const apiGateway = new k8s.kustomize.v2.Directory("api-gateway", {
466
directory: "./kustomize/gateway/overlays/production",
467
namespace: "gateway",
468
}, {
469
dependsOn: [userService, orderService, paymentService],
470
});
471
```
472
473
### GitOps Integration
474
475
```typescript { .api }
476
// Function to deploy from different Git branches/tags
477
const deployFromGit = (environment: string, gitRef: string) => {
478
return new k8s.kustomize.v2.Directory(`app-${environment}`, {
479
directory: `./kustomize/overlays/${environment}`,
480
transformations: [
481
// Add Git metadata
482
(args: any) => {
483
if (args.type.startsWith("kubernetes:")) {
484
args.props = pulumi.output(args.props).apply((props: any) => ({
485
...props,
486
metadata: {
487
...props.metadata,
488
annotations: {
489
...props.metadata?.annotations,
490
"gitops.kubernetes.io/revision": gitRef,
491
"gitops.kubernetes.io/environment": environment,
492
"deployment.kubernetes.io/timestamp": new Date().toISOString(),
493
},
494
},
495
}));
496
}
497
return { props: args.props, opts: args.opts };
498
},
499
],
500
});
501
};
502
503
// Deploy based on Git context
504
const gitBranch = process.env.GIT_BRANCH || "main";
505
const gitCommit = process.env.GIT_COMMIT || "unknown";
506
const environment = pulumi.getStack();
507
508
const gitOpsDeployment = deployFromGit(environment, `${gitBranch}@${gitCommit}`);
509
510
// Export deployment metadata
511
export const deploymentInfo = {
512
environment: environment,
513
gitBranch: gitBranch,
514
gitCommit: gitCommit,
515
deploymentTime: new Date().toISOString(),
516
};
517
```
518
519
### Advanced Kustomize Patterns
520
521
```typescript { .api }
522
// Multi-cluster deployment with region-specific configurations
523
const deployToRegion = (region: string, clusterEndpoint: string) => {
524
return new k8s.kustomize.v2.Directory(`app-${region}`, {
525
directory: "./kustomize/overlays/multi-region",
526
transformations: [
527
// Region-specific labels and configurations
528
(args: any) => {
529
if (args.type.startsWith("kubernetes:")) {
530
args.props = pulumi.output(args.props).apply((props: any) => ({
531
...props,
532
metadata: {
533
...props.metadata,
534
labels: {
535
...props.metadata?.labels,
536
"topology.kubernetes.io/region": region,
537
"deployment.kubernetes.io/cluster": clusterEndpoint,
538
},
539
},
540
}));
541
}
542
return { props: args.props, opts: args.opts };
543
},
544
// Regional service configurations
545
(args: any) => {
546
if (args.type === "v1/Service" && args.props.spec?.type === "LoadBalancer") {
547
args.props = pulumi.output(args.props).apply((props: any) => ({
548
...props,
549
metadata: {
550
...props.metadata,
551
annotations: {
552
...props.metadata?.annotations,
553
"service.beta.kubernetes.io/aws-load-balancer-type":
554
region.startsWith("us-") ? "nlb" : "classic",
555
"service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true",
556
},
557
},
558
}));
559
}
560
return { props: args.props, opts: args.opts };
561
},
562
],
563
});
564
};
565
566
// Deploy to multiple regions
567
const regions = [
568
{ name: "us-east-1", endpoint: "cluster-us-east-1.example.com" },
569
{ name: "us-west-2", endpoint: "cluster-us-west-2.example.com" },
570
{ name: "eu-west-1", endpoint: "cluster-eu-west-1.example.com" },
571
];
572
573
const regionalDeployments = regions.map(region =>
574
deployToRegion(region.name, region.endpoint)
575
);
576
```
577
578
### Resource Access and Integration
579
580
```typescript { .api }
581
// Deploy application with kustomize
582
const app = new k8s.kustomize.v2.Directory("main-app", {
583
directory: "./kustomize/overlays/production",
584
});
585
586
// Access specific resources from the kustomize deployment
587
const deployment = app.getResource("apps/v1", "Deployment", "myapp");
588
const service = app.getResource("v1", "Service", "myapp-service");
589
const ingress = app.getResource("networking.k8s.io/v1", "Ingress", "myapp-ingress");
590
const hpa = app.getResource("autoscaling/v2", "HorizontalPodAutoscaler", "myapp-hpa");
591
592
// Get resource properties for integration with other services
593
const serviceClusterIP = app.getResourceProperty("v1/Service", "myapp-service", "spec.clusterIP");
594
const ingressStatus = app.getResourceProperty("networking.k8s.io/v1/Ingress", "myapp-ingress", "status");
595
596
// Create additional resources that depend on kustomize deployment
597
const monitoring = new k8s.core.v1.Service("monitoring-service", {
598
metadata: {
599
name: "app-monitoring",
600
namespace: "monitoring",
601
},
602
spec: {
603
selector: {
604
app: "myapp", // Matches labels from kustomize deployment
605
},
606
ports: [{
607
port: 9090,
608
targetPort: 9090,
609
name: "metrics",
610
}],
611
},
612
}, {
613
dependsOn: [app],
614
});
615
616
// Export important values
617
export const applicationEndpoint = ingress.status.loadBalancer.ingress[0].hostname;
618
export const internalServiceIP = serviceClusterIP;
619
export const currentReplicas = deployment.status.readyReplicas;
620
export const hpaStatus = hpa.status;
621
```
622
623
## Best Practices
624
625
### Kustomize Structure Best Practices
626
627
1. **Base and Overlays**: Use clear separation between base configurations and environment-specific overlays
628
2. **Common Resources**: Keep shared resources in the base and environment-specific resources in overlays
629
3. **Naming Conventions**: Use consistent naming conventions across all kustomize files
630
4. **Documentation**: Document the purpose and structure of each kustomization
631
5. **Testing**: Test kustomize configurations locally before deploying
632
633
### Integration Best Practices
634
635
1. **Resource Access**: Use the resource access methods for inter-resource dependencies
636
2. **Transformations**: Use Pulumi transformations for concerns not handled by Kustomize
637
3. **Dependencies**: Use Pulumi dependencies to ensure proper deployment order
638
4. **Environment Management**: Use separate overlays for different environments
639
5. **Version Control**: Keep kustomize configurations in version control alongside Pulumi code
640
641
### Security and Operations Best Practices
642
643
1. **Secret Management**: Use Pulumi secrets for sensitive data rather than Kustomize secretGenerator
644
2. **Resource Limits**: Ensure all containers have resource requests and limits
645
3. **Security Context**: Apply appropriate security contexts and policies
646
4. **Monitoring**: Include monitoring and observability configurations
647
5. **Backup Strategy**: Implement backup strategies for persistent data
648
649
The Kustomize integration provides a powerful way to manage complex Kubernetes applications while leveraging existing Kustomize configurations and gaining the benefits of Pulumi's infrastructure-as-code capabilities and resource management features.