EC2
The EC2 Construct provides a high-level abstraction for deploying EC2 instances with Docker containers on AWS. It handles instance configuration, security groups, VPC networking, volume management, and automatic Docker container deployment from Dockerfiles.
Features
- EC2 instance deployment with Docker
- Automatic Docker installation and container deployment
- VPC and subnet configuration
- Security group management
- EBS volume management with flexible configuration
- IAM role configuration
- User data script generation
- CloudWatch logging support
- ECR integration for pulling Docker images
Basic Usage
const instance = new Ec2Construct({
instanceName: 'my-app',
vpcName: 'main-vpc',
container: {
dockerFilePath: './Dockerfile',
containerPort: 3000,
environment: {
NODE_ENV: 'production'
}
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MICRO)
}
});
Configuration Options
Container Configuration
interface IContainerConfig {
// Path to your Dockerfile
dockerFilePath: string;
// Port your container listens on
containerPort: number;
// Environment variables to pass to the container
environment?: { [key: string]: string };
// Docker command to run (optional)
command?: string[];
// Docker container name (optional)
containerName?: string;
// Additional docker run options (optional)
dockerRunOptions?: string[];
}
Volume Configuration
The volume configuration supports three levels of customization:
Simplest (size only):
volumes: [{
sizeGiB: 20 // Auto-generates device name and defaults to GP3 with encryption
}]
With device name:
volumes: [{
deviceName: '/dev/sdf',
sizeGiB: 20
}]
Full configuration:
volumes: [{
deviceName: '/dev/sdf',
volume: BlockDeviceVolume.ebs(20, {
volumeType: EbsDeviceVolumeType.GP3,
encrypted: true,
iops: 3000 // For IO1/IO2 volumes
})
}]
Security Group Configuration
interface ISecurityGroupConfig {
// Use existing security group by ID
securityGroupId?: string;
// Or create new security group with custom properties
props?: {
description?: string;
allowAllOutbound?: boolean;
};
}
Subnet Configuration
interface ISubnetConfig {
// Use existing subnet by ID
subnetId?: string;
// Or specify subnet type
subnetType?: SubnetType;
}
// Or use simplified configuration
subnetConfig?: {
subnetType: SubnetType;
}
Instance Role Configuration
interface IInstanceRoleConfig {
// Custom role name
roleName?: string;
// Managed policy ARNs
managedPolicyArns?: string[];
// Managed policies
managedPolicies?: ManagedPolicy[];
// Inline policy statements
inlinePolicies?: PolicyStatement[];
}
Examples
Basic EC2 Instance with Docker Container
const instance = new Ec2Construct({
instanceName: 'api-server',
vpcName: 'main-vpc',
container: {
dockerFilePath: './src/api/Dockerfile',
containerPort: 3000,
environment: {
NODE_ENV: 'production',
PORT: '3000'
}
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MEDIUM)
},
volumes: [{
sizeGiB: 20 // Auto-generates /dev/sdf with GP3 encryption
}]
});
EC2 Instance with Multiple Volumes
const instance = new Ec2Construct({
instanceName: 'data-server',
vpcName: 'main-vpc',
container: {
dockerFilePath: './src/app/Dockerfile',
containerPort: 8080,
environment: {
DATA_DIR: '/data',
CACHE_DIR: '/cache'
}
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE)
},
volumes: [
{ sizeGiB: 50 }, // Auto-generates /dev/sdf
{ sizeGiB: 100 }, // Auto-generates /dev/sdg
{ // Custom device name
deviceName: '/dev/sdh',
sizeGiB: 200
}
]
});
EC2 Instance with Custom Security Group and Subnet
const instance = new Ec2Construct({
instanceName: 'worker',
vpcName: 'main-vpc',
subnet: {
subnetId: 'subnet-12345678'
},
securityGroup: {
securityGroupId: 'sg-existing'
},
container: {
dockerFilePath: './src/worker/Dockerfile',
containerPort: 9000,
environment: {
QUEUE_URL: 'https://sqs.us-east-1.amazonaws.com/123456789/queue'
},
command: ['npm', 'start']
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.SMALL)
}
});
EC2 Instance with High-Performance Volumes
import { BlockDeviceVolume, EbsDeviceVolumeType } from 'aws-cdk-lib/aws-ec2';
const instance = new Ec2Construct({
instanceName: 'database-server',
vpcName: 'main-vpc',
container: {
dockerFilePath: './src/db/Dockerfile',
containerPort: 5432
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.XLARGE)
},
volumes: [{
deviceName: '/dev/sdf',
volume: BlockDeviceVolume.ebs(500, {
volumeType: EbsDeviceVolumeType.IO1,
iops: 4000,
encrypted: true,
deleteOnTermination: false
})
}]
});
EC2 Instance with Custom IAM Role
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
const instance = new Ec2Construct({
instanceName: 's3-processor',
vpcName: 'main-vpc',
container: {
dockerFilePath: './src/processor/Dockerfile',
containerPort: 8080
},
instanceRole: {
roleName: 's3-processor-role',
managedPolicyArns: [
'arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess'
],
inlinePolicies: [
new PolicyStatement({
actions: ['s3:GetObject', 's3:PutObject'],
resources: ['arn:aws:s3:::my-bucket/*']
})
]
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MEDIUM)
}
});
EC2 Instance in Private Subnet
import { SubnetType } from 'aws-cdk-lib/aws-ec2';
const instance = new Ec2Construct({
instanceName: 'internal-service',
vpcName: 'main-vpc',
subnetConfig: {
subnetType: SubnetType.PRIVATE_WITH_EGRESS
},
container: {
dockerFilePath: './src/internal/Dockerfile',
containerPort: 9000
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.SMALL)
}
});
EC2 Instance with Existing VPC
import { Vpc } from 'aws-cdk-lib/aws-ec2';
const existingVpc = Vpc.fromLookup(stack, 'ExistingVPC', {
vpcName: 'production-vpc'
});
const instance = new Ec2Construct({
instanceName: 'production-app',
vpc: existingVpc,
container: {
dockerFilePath: './src/app/Dockerfile',
containerPort: 3000
},
instanceProps: {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE)
}
});
Volume Configuration Details
Auto-Generated Device Names
When deviceName is not specified, the construct automatically generates sequential device names starting from /dev/sdf:
- First volume:
/dev/sdf - Second volume:
/dev/sdg - Third volume:
/dev/sdh - And so on...
Default Volume Settings
When only sizeGiB is specified (without full volume configuration), the construct uses these defaults:
- Volume Type: GP3 (General Purpose SSD)
- Encryption: Enabled
- Delete on Termination: Default AWS behavior
Supported Volume Types
- GP3: General Purpose SSD (default when using
sizeGiB) - GP2: General Purpose SSD (legacy)
- IO1: Provisioned IOPS SSD
- IO2: Provisioned IOPS SSD (next generation)
- ST1: Throughput Optimized HDD
- SC1: Cold HDD
Security Groups
The construct creates and configures security groups based on your instance's needs:
-
Default Security Group (when creating new):
- Allows inbound traffic on container port from within VPC CIDR
- Allows SSH access (port 22) from within VPC CIDR
- Allows all outbound traffic
-
Existing Security Group (when using
securityGroupId):- Uses the specified security group as-is
- Ensure it has appropriate rules for your container port
Docker Container Deployment
The construct automatically:
- Installs Docker on the EC2 instance via user data
- Configures AWS CLI for ECR access
- Logs into ECR using the instance role
- Pulls the Docker image built from your Dockerfile
- Runs the container with your specified configuration
Container Runtime Behavior
- Container runs with
--restart unless-stoppedpolicy - Container port is mapped to the same port on the host
- Environment variables are passed to the container
- Custom commands can be specified
- Container name defaults to instance name (customizable)
IAM Permissions
The instance role automatically includes permissions for:
- ECR access (to pull Docker images)
- CloudWatch Logs (for logging)
- Additional permissions can be added via
instanceRoleconfiguration
Best Practices
-
Instance Configuration
- Choose appropriate instance type for your workload
- Use private subnets for internal services
- Enable detailed monitoring for production instances
- Use spot instances for non-critical workloads (via
instanceProps)
-
Volume Management
- Use GP3 volumes for most workloads (default)
- Use IO1/IO2 for high-performance databases
- Enable encryption for sensitive data
- Set
deleteOnTermination: falsefor persistent data
-
Security
- Use security groups to restrict access
- Place instances in private subnets
- Use IAM roles instead of access keys
- Enable VPC Flow Logs for network monitoring
- Regularly update AMIs and Docker images
-
Container Configuration
- Use environment variables for configuration
- Keep Docker images small and optimized
- Implement health checks in your application
- Use container logs for debugging
-
Networking
- Use existing VPCs when possible
- Place instances in appropriate subnets
- Configure security groups properly
- Use VPC endpoints for AWS services
-
Monitoring
- Enable CloudWatch monitoring
- Set up CloudWatch alarms
- Monitor instance metrics and container logs
- Track volume performance metrics
Troubleshooting
Common issues and solutions:
-
Container Not Starting
- Check instance logs:
sudo journalctl -u docker - Verify Docker installation in user data
- Check ECR login success
- Verify container port configuration
- Check environment variables
- Check instance logs:
-
Volume Issues
- Verify device names are not conflicting
- Check volume attachment in AWS console
- Ensure volumes are formatted if needed
- Verify volume size and type compatibility
-
Security Group Issues
- Verify security group rules allow container port
- Check VPC CIDR blocks
- Ensure security group is attached to instance
- Verify subnet security group associations
-
ECR Access Issues
- Verify instance role has ECR permissions
- Check ECR repository exists and image is pushed
- Verify ECR login in user data script
- Check VPC endpoints for ECR (if using private subnets)
-
Networking Issues
- Verify VPC configuration
- Check route tables
- Ensure internet gateway/NAT gateway for outbound traffic
- Verify DNS resolution
-
User Data Script Issues
- Check CloudWatch Logs for user data output
- Verify script syntax
- Check file permissions
- Review script execution logs
-
Instance Launch Failures
- Check IAM role permissions
- Verify security group rules
- Check subnet availability
- Review instance launch logs
Outputs
The construct automatically provides these outputs:
- Instance ID:
${instanceName}InstanceId - Instance Private IP:
${instanceName}InstancePrivateIp - Instance Public IP:
${instanceName}InstancePublicIp(if in public subnet) - Instance Object: Available via construct output with key
${instanceName}
Limitations
- Maximum of 11 auto-generated device names (sdf through sdp)
- Docker installation and container startup happens via user data (may take a few minutes)
- Instance must have internet access (direct or via NAT) for ECR pull during startup
- Custom user data will be appended to Docker setup script