or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

alb-controller.mdauth.mdcluster.mdfargate.mdindex.mdkubectl-provider.mdkubernetes-resources.mdnode-groups.mdservice-accounts.mdutilities.md
tile.json

utilities.mddocs/

Utilities

Helper functions and constants for EKS node configuration, instance type classification, and user data generation.

Capabilities

User Data Functions

Generate bootstrap scripts for EKS worker nodes with different AMI types.

/**
 * Generate Amazon Linux 2 user data for EKS nodes
 * @param cluster - EKS cluster
 * @param autoScalingGroup - Auto Scaling Group for the nodes
 * @param options - Bootstrap configuration options
 * @returns Array of shell commands for user data
 */
function renderAmazonLinuxUserData(
  cluster: ICluster,
  autoScalingGroup: autoscaling.AutoScalingGroup,
  options?: BootstrapOptions
): string[];

/**
 * Generate Bottlerocket user data for EKS nodes
 * @param cluster - EKS cluster
 * @returns Array of TOML configuration lines
 */
function renderBottlerocketUserData(cluster: ICluster): string[];

Usage Examples:

import * as eks from "@aws-cdk/aws-eks";
import * as autoscaling from "@aws-cdk/aws-autoscaling";
import * as ec2 from "@aws-cdk/aws-ec2";

// Create Auto Scaling Group with Amazon Linux 2
const asg = new autoscaling.AutoScalingGroup(this, "WorkerNodes", {
  vpc: cluster.vpc,
  instanceType: new ec2.InstanceType("t3.medium"),
  machineImage: new eks.EksOptimizedImage({
    kubernetesVersion: "1.21",
    nodeType: eks.NodeType.STANDARD,
  }),
});

// Generate user data for Amazon Linux 2 nodes
const userData = eks.renderAmazonLinuxUserData(cluster, asg, {
  useMaxPods: true,
  enableDockerBridge: false,
  kubeletExtraArgs: "--max-pods=110",
  awsApiRetryAttempts: 3,
});

asg.addUserData(...userData);

// Generate user data for Bottlerocket nodes
const bottlerocketAsg = new autoscaling.AutoScalingGroup(this, "BottlerocketNodes", {
  vpc: cluster.vpc,
  instanceType: new ec2.InstanceType("t3.medium"),
  machineImage: new eks.EksOptimizedImage({
    kubernetesVersion: "1.21",
    nodeType: eks.NodeType.STANDARD,
    cpuArch: eks.CpuArch.X86_64,
  }),
});

const bottlerocketUserData = eks.renderBottlerocketUserData(cluster);
bottlerocketAsg.addUserData(...bottlerocketUserData);

Bootstrap Options

Configuration options for EKS node bootstrap process.

/**
 * Bootstrap configuration options for EKS nodes
 */
interface BootstrapOptions {
  /** Additional kubelet arguments */
  readonly kubeletExtraArgs?: string;
  /** Use max pods configuration */
  readonly useMaxPods?: boolean;
  /** Enable Docker bridge network */
  readonly enableDockerBridge?: boolean;
  /** Docker daemon configuration JSON */
  readonly dockerConfigJson?: string;
  /** DNS cluster IP address */
  readonly dnsClusterIp?: string;
  /** Additional bootstrap script arguments */
  readonly additionalArgs?: string;
  /** AWS API retry attempts */
  readonly awsApiRetryAttempts?: number;
  /** Container runtime configuration */
  readonly containerRuntime?: ContainerRuntime;
}

/**
 * Container runtime options
 */
enum ContainerRuntime {
  /** Docker container runtime */
  DOCKER = "docker",
  /** containerd container runtime */
  CONTAINERD = "containerd"
}

Lifecycle Labels

Node lifecycle labels for scheduling and taints.

/**
 * Node lifecycle labels for Kubernetes scheduling
 */
enum LifecycleLabel {
  /** On-Demand instances */
  ON_DEMAND = "OnDemand",
  /** Spot instances */
  SPOT = "Ec2Spot"
}

Usage in Node Labels:

// Automatic lifecycle labeling based on instance type
const spotAsg = new autoscaling.AutoScalingGroup(this, "SpotNodes", {
  vpc: cluster.vpc,
  instanceType: new ec2.InstanceType("t3.medium"),
  spotPrice: "0.05", // Enables spot instances
  machineImage: new eks.EksOptimizedImage(),
});

// User data will automatically include:
// --node-labels lifecycle=Ec2Spot
// --register-with-taints=spotInstance=true:PreferNoSchedule

// On-demand instances get:
// --node-labels lifecycle=OnDemand

Instance Type Classification

Constants for categorizing EC2 instance types by capabilities.

/**
 * Instance type classification constants
 */
const INSTANCE_TYPES: {
  /** GPU-enabled instance families */
  readonly gpu: string[];
  /** AWS Inferentia instance families */
  readonly inferentia: string[];
  /** ARM-based Graviton instance families */
  readonly graviton: string[];
  /** ARM-based Graviton2 instance families */
  readonly graviton2: string[];
};

Instance Type Categories:

// GPU instances for machine learning workloads
INSTANCE_TYPES.gpu = ['p2', 'p3', 'g2', 'g3', 'g4'];

// Inferentia instances for ML inference
INSTANCE_TYPES.inferentia = ['inf1'];

// First-generation Graviton (ARM) instances
INSTANCE_TYPES.graviton = ['a1'];

// Second-generation Graviton2 (ARM) instances
INSTANCE_TYPES.graviton2 = ['c6g', 'm6g', 'r6g', 't4g'];

Usage Examples:

import * as eks from "@aws-cdk/aws-eks";
import * as ec2 from "@aws-cdk/aws-ec2";

// Check if instance type requires GPU-optimized AMI
function requiresGpuAmi(instanceType: ec2.InstanceType): boolean {
  const family = instanceType.toString().split('.')[0];
  return eks.INSTANCE_TYPES.gpu.includes(family);
}

// Select appropriate AMI based on instance type
function selectOptimizedImage(instanceType: ec2.InstanceType): eks.EksOptimizedImage {
  const family = instanceType.toString().split('.')[0];
  
  if (eks.INSTANCE_TYPES.gpu.includes(family)) {
    return new eks.EksOptimizedImage({
      nodeType: eks.NodeType.GPU,
      cpuArch: eks.CpuArch.X86_64,
    });
  }
  
  if (eks.INSTANCE_TYPES.graviton2.includes(family)) {
    return new eks.EksOptimizedImage({
      nodeType: eks.NodeType.STANDARD,
      cpuArch: eks.CpuArch.ARM_64,
    });
  }
  
  if (eks.INSTANCE_TYPES.inferentia.includes(family)) {
    return new eks.EksOptimizedImage({
      nodeType: eks.NodeType.INFERENTIA,
      cpuArch: eks.CpuArch.X86_64,
    });
  }
  
  // Default to standard x86_64
  return new eks.EksOptimizedImage({
    nodeType: eks.NodeType.STANDARD,
    cpuArch: eks.CpuArch.X86_64,
  });
}

// Create instance-type-appropriate node group
const gpuNodeGroup = cluster.addNodegroupCapacity("GpuNodes", {
  instanceTypes: [new ec2.InstanceType("p3.2xlarge")],
  amiType: eks.NodegroupAmiType.AL2_X86_64_GPU,
});

const armNodeGroup = cluster.addNodegroupCapacity("ArmNodes", {
  instanceTypes: [new ec2.InstanceType("t4g.medium")],
  amiType: eks.NodegroupAmiType.AL2_ARM_64,
});

Advanced User Data Configuration

// Advanced bootstrap configuration for production workloads
const productionBootstrapOptions: eks.BootstrapOptions = {
  // Optimize for high pod density
  useMaxPods: true,
  kubeletExtraArgs: [
    "--max-pods=110",
    "--kube-reserved=cpu=250m,memory=1Gi,ephemeral-storage=1Gi",
    "--system-reserved=cpu=250m,memory=1Gi,ephemeral-storage=1Gi",
    "--eviction-hard=memory.available<500Mi,nodefs.available<10%",
  ].join(" "),
  
  // Configure container runtime
  enableDockerBridge: false,
  containerRuntime: eks.ContainerRuntime.CONTAINERD,
  
  // Network and API configuration
  awsApiRetryAttempts: 5,
  dnsClusterIp: "172.20.0.10",
  
  // Custom Docker configuration for private registries
  dockerConfigJson: JSON.stringify({
    "auths": {
      "my-registry.com": {
        "auth": "base64-encoded-credentials"
      }
    }
  }),
  
  // Additional bootstrap arguments
  additionalArgs: "--container-runtime containerd --cni-version v0.8.7",
};

const userData = eks.renderAmazonLinuxUserData(cluster, asg, productionBootstrapOptions);

Best Practices

  1. Resource Reservations: Always configure kube-reserved and system-reserved
  2. Container Runtime: Use containerd for better performance and resource efficiency
  3. Pod Density: Set appropriate max-pods based on instance type and networking requirements
  4. Spot Instances: Leverage automatic spot tainting for cost optimization
  5. Architecture-Specific: Use ARM instances (Graviton2) for cost-effective workloads
// Best practice: Architecture-aware node group creation
function createOptimizedNodeGroup(
  cluster: eks.ICluster,
  instanceType: ec2.InstanceType,
  options: eks.NodegroupOptions = {}
): eks.Nodegroup {
  const family = instanceType.toString().split('.')[0];
  
  // Determine appropriate AMI type
  let amiType: eks.NodegroupAmiType;
  if (eks.INSTANCE_TYPES.gpu.includes(family)) {
    amiType = eks.NodegroupAmiType.AL2_X86_64_GPU;
  } else if (eks.INSTANCE_TYPES.graviton2.includes(family)) {
    amiType = eks.NodegroupAmiType.AL2_ARM_64;
  } else {
    amiType = eks.NodegroupAmiType.AL2_X86_64;
  }
  
  return cluster.addNodegroupCapacity(`${family}-nodes`, {
    instanceTypes: [instanceType],
    amiType,
    ...options,
  });
}