CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pulumi--kubernetes

A comprehensive Pulumi resource provider for creating and managing Kubernetes resources and workloads in a running cluster

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

kustomize-integration.mddocs/

Kustomize Integration

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.

Package Import

import { kustomize } from "@pulumi/kubernetes";
import * as k8s from "@pulumi/kubernetes";

// Direct Kustomize imports
import { Directory } from "@pulumi/kubernetes/kustomize/v2";

Directory (kustomize/v2)

Directory deploys resources from a Kustomize directory configuration, applying all transformations and overlays defined in the Kustomize setup.

class Directory extends pulumi.ComponentResource {
    constructor(name: string, args: DirectoryArgs, opts?: pulumi.ComponentResourceOptions)
    
    // Resource access methods (inherited from base collection resource)
    public getResource<T extends pulumi.CustomResource>(
        group: string,
        version: string,
        kind: string,
        name: string,
        namespace?: string
    ): T
    
    public getResource(groupVersionKind: string, name: string, namespace?: string): pulumi.CustomResource
    public getResourceProperty(groupVersionKind: string, name: string, property: string, namespace?: string): pulumi.Output<any>
}

interface DirectoryArgs {
    // Directory specification
    directory: pulumi.Input<string>;                 // Path to kustomization directory
    
    // Namespace options
    namespace?: pulumi.Input<string>;                // Override namespace for all resources
    
    // Resource options
    resourcePrefix?: pulumi.Input<string>;           // Prefix for resource names
    
    // Transformation options
    transformations?: pulumi.ResourceTransformation[]; // Additional Pulumi transformations
    
    // Deployment options
    skipAwait?: pulumi.Input<boolean>;               // Skip waiting for resource readiness
}

Directory Usage Examples

// Deploy from basic kustomize directory
const baseApp = new k8s.kustomize.v2.Directory("base-app", {
    directory: "./kustomize/base",
});

// Deploy from overlay directory
const prodApp = new k8s.kustomize.v2.Directory("prod-app", {
    directory: "./kustomize/overlays/production",
    namespace: "production", // Override kustomize namespace
});

// Deploy with resource prefix
const stagingApp = new k8s.kustomize.v2.Directory("staging-app", {
    directory: "./kustomize/overlays/staging",
    resourcePrefix: "staging-",
});

// Deploy with Pulumi transformations
const secureApp = new k8s.kustomize.v2.Directory("secure-app", {
    directory: "./kustomize/overlays/production",
    transformations: [
        // Add security labels
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            "security.kubernetes.io/policy": "restricted",
                            "app.kubernetes.io/managed-by": "pulumi",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
        // Enforce resource limits
        (args: any) => {
            if (args.type === "kubernetes:apps/v1:Deployment") {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    spec: {
                        ...props.spec,
                        template: {
                            ...props.spec.template,
                            spec: {
                                ...props.spec.template.spec,
                                securityContext: {
                                    ...props.spec.template.spec.securityContext,
                                    runAsNonRoot: true,
                                    runAsUser: 1000,
                                    fsGroup: 2000,
                                },
                                containers: props.spec.template.spec.containers?.map((container: any) => ({
                                    ...container,
                                    securityContext: {
                                        ...container.securityContext,
                                        allowPrivilegeEscalation: false,
                                        readOnlyRootFilesystem: true,
                                        capabilities: {
                                            drop: ["ALL"],
                                        },
                                    },
                                    resources: {
                                        requests: {
                                            cpu: "100m",
                                            memory: "128Mi",
                                            ...container.resources?.requests,
                                        },
                                        limits: {
                                            cpu: "500m",
                                            memory: "512Mi",
                                            ...container.resources?.limits,
                                        },
                                    },
                                })),
                            },
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
});

// Deploy with skip await for faster deployment
const fastApp = new k8s.kustomize.v2.Directory("fast-app", {
    directory: "./kustomize/overlays/development",
    skipAwait: true, // Don't wait for resources to become ready
});

Kustomize Directory Structure Examples

Basic Application Structure

kustomize/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── configmap.yaml
└── overlays/
    ├── development/
    │   ├── kustomization.yaml
    │   ├── deployment-patch.yaml
    │   └── ingress.yaml
    ├── staging/
    │   ├── kustomization.yaml
    │   ├── deployment-patch.yaml
    │   ├── hpa.yaml
    │   └── ingress.yaml
    └── production/
        ├── kustomization.yaml
        ├── deployment-patch.yaml
        ├── hpa.yaml
        ├── pdb.yaml
        └── ingress.yaml

Base Kustomization Example

# kustomize/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml
  - configmap.yaml

commonLabels:
  app.kubernetes.io/name: myapp
  app.kubernetes.io/component: backend

commonAnnotations:
  app.kubernetes.io/version: "1.0.0"

images:
  - name: myapp
    newTag: latest

configMapGenerator:
  - name: app-config
    literals:
      - PORT=8080
      - LOG_LEVEL=info

Overlay Kustomization Example

# kustomize/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: production

bases:
  - ../../base

resources:
  - hpa.yaml
  - pdb.yaml
  - ingress.yaml

patchesStrategicMerge:
  - deployment-patch.yaml

images:
  - name: myapp
    newTag: v1.2.3

replicas:
  - name: myapp-deployment
    count: 5

configMapGenerator:
  - name: app-config
    behavior: merge
    literals:
      - LOG_LEVEL=warn
      - REPLICAS=5
      - ENVIRONMENT=production

secretGenerator:
  - name: app-secrets
    literals:
      - DATABASE_PASSWORD=prod-password
      - API_KEY=prod-api-key

Complete Application Examples

Multi-Environment Deployment

// Development environment
const devApp = new k8s.kustomize.v2.Directory("dev-app", {
    directory: "./kustomize/overlays/development",
    transformations: [
        // Development-specific transformations
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            environment: "development",
                            "cost-center": "engineering",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
});

// Staging environment
const stagingApp = new k8s.kustomize.v2.Directory("staging-app", {
    directory: "./kustomize/overlays/staging",
    transformations: [
        // Staging-specific transformations
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            environment: "staging",
                            "cost-center": "qa",
                        },
                        annotations: {
                            ...props.metadata?.annotations,
                            "deployment.kubernetes.io/revision": "staging",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
});

// Production environment
const prodApp = new k8s.kustomize.v2.Directory("prod-app", {
    directory: "./kustomize/overlays/production",
    transformations: [
        // Production-specific transformations
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            environment: "production",
                            "cost-center": "production",
                            "backup.kubernetes.io/enabled": "true",
                        },
                        annotations: {
                            ...props.metadata?.annotations,
                            "deployment.kubernetes.io/revision": "production",
                            "monitoring.coreos.com/enabled": "true",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
        // Production security hardening
        (args: any) => {
            if (args.type === "kubernetes:apps/v1:Deployment") {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    spec: {
                        ...props.spec,
                        template: {
                            ...props.spec.template,
                            spec: {
                                ...props.spec.template.spec,
                                securityContext: {
                                    runAsNonRoot: true,
                                    runAsUser: 1000,
                                    fsGroup: 2000,
                                },
                                containers: props.spec.template.spec.containers?.map((container: any) => ({
                                    ...container,
                                    securityContext: {
                                        allowPrivilegeEscalation: false,
                                        readOnlyRootFilesystem: true,
                                        capabilities: { drop: ["ALL"] },
                                    },
                                })),
                            },
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
});

// Access resources from deployments
const prodDeployment = prodApp.getResource("apps/v1", "Deployment", "myapp-deployment");
const prodService = prodApp.getResource("v1", "Service", "myapp-service");

// Export important endpoints
export const productionEndpoint = prodService.status.loadBalancer.ingress[0].hostname;
export const deploymentReplicas = prodDeployment.spec.replicas;

Microservices Application with Kustomize

// Deploy each microservice from its own kustomize overlay
const userService = new k8s.kustomize.v2.Directory("user-service", {
    directory: "./kustomize/services/user-service/overlays/production",
    namespace: "services",
    transformations: [
        // Service-specific transformations
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            "service.kubernetes.io/name": "user-service",
                            "service.kubernetes.io/version": "v2.1.0",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
});

const orderService = new k8s.kustomize.v2.Directory("order-service", {
    directory: "./kustomize/services/order-service/overlays/production",
    namespace: "services",
    transformations: [
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            "service.kubernetes.io/name": "order-service",
                            "service.kubernetes.io/version": "v1.5.2",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
}, {
    dependsOn: [userService], // Order service depends on user service
});

const paymentService = new k8s.kustomize.v2.Directory("payment-service", {
    directory: "./kustomize/services/payment-service/overlays/production",
    namespace: "services",
    transformations: [
        (args: any) => {
            if (args.type.startsWith("kubernetes:")) {
                args.props = pulumi.output(args.props).apply((props: any) => ({
                    ...props,
                    metadata: {
                        ...props.metadata,
                        labels: {
                            ...props.metadata?.labels,
                            "service.kubernetes.io/name": "payment-service",
                            "service.kubernetes.io/version": "v3.0.1",
                        },
                    },
                }));
            }
            return { props: args.props, opts: args.opts };
        },
    ],
}, {
    dependsOn: [userService, orderService],
});

// Deploy shared infrastructure
const sharedInfra = new k8s.kustomize.v2.Directory("shared-infrastructure", {
    directory: "./kustomize/infrastructure/production",
    namespace: "infrastructure",
});

// Deploy API gateway that depends on all services
const apiGateway = new k8s.kustomize.v2.Directory("api-gateway", {
    directory: "./kustomize/gateway/overlays/production",
    namespace: "gateway",
}, {
    dependsOn: [userService, orderService, paymentService],
});

GitOps Integration

// Function to deploy from different Git branches/tags
const deployFromGit = (environment: string, gitRef: string) => {
    return new k8s.kustomize.v2.Directory(`app-${environment}`, {
        directory: `./kustomize/overlays/${environment}`,
        transformations: [
            // Add Git metadata
            (args: any) => {
                if (args.type.startsWith("kubernetes:")) {
                    args.props = pulumi.output(args.props).apply((props: any) => ({
                        ...props,
                        metadata: {
                            ...props.metadata,
                            annotations: {
                                ...props.metadata?.annotations,
                                "gitops.kubernetes.io/revision": gitRef,
                                "gitops.kubernetes.io/environment": environment,
                                "deployment.kubernetes.io/timestamp": new Date().toISOString(),
                            },
                        },
                    }));
                }
                return { props: args.props, opts: args.opts };
            },
        ],
    });
};

// Deploy based on Git context
const gitBranch = process.env.GIT_BRANCH || "main";
const gitCommit = process.env.GIT_COMMIT || "unknown";
const environment = pulumi.getStack();

const gitOpsDeployment = deployFromGit(environment, `${gitBranch}@${gitCommit}`);

// Export deployment metadata
export const deploymentInfo = {
    environment: environment,
    gitBranch: gitBranch,
    gitCommit: gitCommit,
    deploymentTime: new Date().toISOString(),
};

Advanced Kustomize Patterns

// Multi-cluster deployment with region-specific configurations
const deployToRegion = (region: string, clusterEndpoint: string) => {
    return new k8s.kustomize.v2.Directory(`app-${region}`, {
        directory: "./kustomize/overlays/multi-region",
        transformations: [
            // Region-specific labels and configurations
            (args: any) => {
                if (args.type.startsWith("kubernetes:")) {
                    args.props = pulumi.output(args.props).apply((props: any) => ({
                        ...props,
                        metadata: {
                            ...props.metadata,
                            labels: {
                                ...props.metadata?.labels,
                                "topology.kubernetes.io/region": region,
                                "deployment.kubernetes.io/cluster": clusterEndpoint,
                            },
                        },
                    }));
                }
                return { props: args.props, opts: args.opts };
            },
            // Regional service configurations
            (args: any) => {
                if (args.type === "v1/Service" && args.props.spec?.type === "LoadBalancer") {
                    args.props = pulumi.output(args.props).apply((props: any) => ({
                        ...props,
                        metadata: {
                            ...props.metadata,
                            annotations: {
                                ...props.metadata?.annotations,
                                "service.beta.kubernetes.io/aws-load-balancer-type": 
                                    region.startsWith("us-") ? "nlb" : "classic",
                                "service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true",
                            },
                        },
                    }));
                }
                return { props: args.props, opts: args.opts };
            },
        ],
    });
};

// Deploy to multiple regions
const regions = [
    { name: "us-east-1", endpoint: "cluster-us-east-1.example.com" },
    { name: "us-west-2", endpoint: "cluster-us-west-2.example.com" },
    { name: "eu-west-1", endpoint: "cluster-eu-west-1.example.com" },
];

const regionalDeployments = regions.map(region => 
    deployToRegion(region.name, region.endpoint)
);

Resource Access and Integration

// Deploy application with kustomize
const app = new k8s.kustomize.v2.Directory("main-app", {
    directory: "./kustomize/overlays/production",
});

// Access specific resources from the kustomize deployment
const deployment = app.getResource("apps/v1", "Deployment", "myapp");
const service = app.getResource("v1", "Service", "myapp-service");
const ingress = app.getResource("networking.k8s.io/v1", "Ingress", "myapp-ingress");
const hpa = app.getResource("autoscaling/v2", "HorizontalPodAutoscaler", "myapp-hpa");

// Get resource properties for integration with other services
const serviceClusterIP = app.getResourceProperty("v1/Service", "myapp-service", "spec.clusterIP");
const ingressStatus = app.getResourceProperty("networking.k8s.io/v1/Ingress", "myapp-ingress", "status");

// Create additional resources that depend on kustomize deployment
const monitoring = new k8s.core.v1.Service("monitoring-service", {
    metadata: {
        name: "app-monitoring",
        namespace: "monitoring",
    },
    spec: {
        selector: {
            app: "myapp", // Matches labels from kustomize deployment
        },
        ports: [{
            port: 9090,
            targetPort: 9090,
            name: "metrics",
        }],
    },
}, {
    dependsOn: [app],
});

// Export important values
export const applicationEndpoint = ingress.status.loadBalancer.ingress[0].hostname;
export const internalServiceIP = serviceClusterIP;
export const currentReplicas = deployment.status.readyReplicas;
export const hpaStatus = hpa.status;

Best Practices

Kustomize Structure Best Practices

  1. Base and Overlays: Use clear separation between base configurations and environment-specific overlays
  2. Common Resources: Keep shared resources in the base and environment-specific resources in overlays
  3. Naming Conventions: Use consistent naming conventions across all kustomize files
  4. Documentation: Document the purpose and structure of each kustomization
  5. Testing: Test kustomize configurations locally before deploying

Integration Best Practices

  1. Resource Access: Use the resource access methods for inter-resource dependencies
  2. Transformations: Use Pulumi transformations for concerns not handled by Kustomize
  3. Dependencies: Use Pulumi dependencies to ensure proper deployment order
  4. Environment Management: Use separate overlays for different environments
  5. Version Control: Keep kustomize configurations in version control alongside Pulumi code

Security and Operations Best Practices

  1. Secret Management: Use Pulumi secrets for sensitive data rather than Kustomize secretGenerator
  2. Resource Limits: Ensure all containers have resource requests and limits
  3. Security Context: Apply appropriate security contexts and policies
  4. Monitoring: Include monitoring and observability configurations
  5. Backup Strategy: Implement backup strategies for persistent data

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.

Install with Tessl CLI

npx tessl i tessl/npm-pulumi--kubernetes

docs

core-resources.md

helm-integration.md

index.md

kustomize-integration.md

networking-resources.md

provider-configuration.md

rbac-resources.md

storage-resources.md

workload-resources.md

yaml-deployment.md

tile.json