AWS CDK Construct คืออะไรและใช้งานอย่างไร
AWS CDK (Cloud Development Kit) เป็น Infrastructure as Code (IaC) framework ที่ให้เขียน cloud infrastructure ด้วยภาษา programming จริงเช่น TypeScript, Python, Java, Go แทนที่จะเขียน YAML/JSON แบบ CloudFormation CDK compile โค้ดเป็น CloudFormation templates แล้ว deploy
Construct เป็น building block หลักของ CDK แบ่งเป็น 3 levels คือ L1 (CFN Resources) ที่เป็น direct mapping กับ CloudFormation resources L2 (Curated Constructs) ที่เป็น higher-level abstractions พร้อม sensible defaults และ L3 (Patterns) ที่เป็น complete solutions ที่รวมหลาย resources เข้าด้วยกัน
CDK เหมาะสำหรับ IT professionals ที่ต้องการพัฒนา career ด้าน Cloud Infrastructure เพราะ demand สูง ใช้ programming skills ที่มีอยู่ ทำ code review, testing และ CI/CD ได้เหมือน application code และเป็น skill ที่ต้องการมากในตลาดงาน
ข้อดีของ CDK เมื่อเทียบกับ Terraform คือใช้ภาษา programming จริงที่มี loops, conditionals, type checking IDE support ครบ (autocomplete, error checking) สร้าง reusable constructs ได้ง่าย testing ด้วย standard test frameworks และ integrate กับ AWS services ได้ลึกกว่า
ติดตั้ง CDK และสร้างโปรเจกต์แรก
ขั้นตอนการเริ่มต้นใช้งาน CDK
# ติดตั้ง AWS CDK CLI
npm install -g aws-cdk
# ตรวจสอบ version
cdk --version
# สร้างโปรเจกต์ใหม่
mkdir my-infra && cd my-infra
cdk init app --language typescript
# โครงสร้างโปรเจกต์
# my-infra/
# ├── bin/
# │ └── my-infra.ts # App entry point
# ├── lib/
# │ └── my-infra-stack.ts # Main stack
# ├── test/
# │ └── my-infra.test.ts # Tests
# ├── cdk.json # CDK config
# ├── package.json
# └── tsconfig.json
# Bootstrap CDK (ครั้งแรกเท่านั้น)
cdk bootstrap aws://ACCOUNT_ID/ap-southeast-1
# Synthesize CloudFormation template
cdk synth
# Deploy
cdk deploy
# Diff (ดูการเปลี่ยนแปลง)
cdk diff
# Destroy
cdk destroy
# === bin/my-infra.ts ===
# import * as cdk from 'aws-cdk-lib';
# import { MyInfraStack } from '../lib/my-infra-stack';
#
# const app = new cdk.App();
#
# new MyInfraStack(app, 'MyInfraStack', {
# env: {
# account: process.env.CDK_DEFAULT_ACCOUNT,
# region: 'ap-southeast-1',
# },
# tags: {
# Environment: 'production',
# Team: 'platform',
# ManagedBy: 'cdk',
# },
# });
# === lib/my-infra-stack.ts ===
# import * as cdk from 'aws-cdk-lib';
# import * as ec2 from 'aws-cdk-lib/aws-ec2';
# import * as ecs from 'aws-cdk-lib/aws-ecs';
# import * as rds from 'aws-cdk-lib/aws-rds';
# import { Construct } from 'constructs';
#
# export class MyInfraStack extends cdk.Stack {
# constructor(scope: Construct, id: string, props?: cdk.StackProps) {
# super(scope, id, props);
#
# // VPC
# const vpc = new ec2.Vpc(this, 'MainVpc', {
# maxAzs: 3,
# natGateways: 1,
# });
#
# // ECS Cluster
# const cluster = new ecs.Cluster(this, 'AppCluster', { vpc });
#
# // RDS
# const db = new rds.DatabaseInstance(this, 'AppDb', {
# engine: rds.DatabaseInstanceEngine.postgres({ version: rds.PostgresEngineVersion.VER_15 }),
# vpc,
# instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
# });
# }
# }
สร้าง Custom Constructs สำหรับ Production
สร้าง reusable constructs สำหรับใช้ข้าม projects
// lib/constructs/secure-api.ts — Custom L3 Construct
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
export interface SecureApiProps {
readonly apiName: string;
readonly stageName?: string;
readonly throttleRateLimit?: number;
readonly throttleBurstLimit?: number;
readonly enableWaf?: boolean;
readonly enableAccessLogs?: boolean;
readonly corsOrigins?: string[];
}
export class SecureApi extends Construct {
public readonly api: apigateway.RestApi;
public readonly logGroup: logs.LogGroup;
constructor(scope: Construct, id: string, props: SecureApiProps) {
super(scope, id);
// Access Logs
this.logGroup = new logs.LogGroup(this, 'ApiLogs', {
retention: logs.RetentionDays.THIRTY_DAYS,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// API Gateway
this.api = new apigateway.RestApi(this, 'Api', {
restApiName: props.apiName,
deployOptions: {
stageName: props.stageName || 'prod',
throttlingRateLimit: props.throttleRateLimit || 1000,
throttlingBurstLimit: props.throttleBurstLimit || 500,
accessLogDestination: new apigateway.LogGroupLogDestination(this.logGroup),
accessLogFormat: apigateway.AccessLogFormat.jsonWithStandardFields(),
tracingEnabled: true,
metricsEnabled: true,
},
defaultCorsPreflightOptions: props.corsOrigins ? {
allowOrigins: props.corsOrigins,
allowMethods: apigateway.Cors.ALL_METHODS,
allowHeaders: ['Content-Type', 'Authorization'],
} : undefined,
});
// WAF
if (props.enableWaf !== false) {
const webAcl = new wafv2.CfnWebACL(this, 'WebAcl', {
scope: 'REGIONAL',
defaultAction: { allow: {} },
rules: [
{
name: 'RateLimitRule',
priority: 1,
action: { block: {} },
statement: {
rateBasedStatement: {
limit: 2000,
aggregateKeyType: 'IP',
},
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'RateLimitRule',
sampledRequestsEnabled: true,
},
},
{
name: 'AWSManagedCommonRules',
priority: 2,
overrideAction: { none: {} },
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesCommonRuleSet',
},
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'CommonRules',
sampledRequestsEnabled: true,
},
},
],
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: props.apiName,
sampledRequestsEnabled: true,
},
});
new wafv2.CfnWebACLAssociation(this, 'WafAssoc', {
resourceArn: this.api.deploymentStage.stageArn,
webAclArn: webAcl.attrArn,
});
}
}
addLambdaEndpoint(path: string, method: string, handler: lambda.Function) {
const resource = this.api.root.resourceForPath(path);
resource.addMethod(method, new apigateway.LambdaIntegration(handler), {
authorizationType: apigateway.AuthorizationType.IAM,
});
}
}
// Usage:
// const api = new SecureApi(this, 'MyApi', {
// apiName: 'user-service',
// enableWaf: true,
// corsOrigins: ['https://myapp.com'],
// });
// api.addLambdaEndpoint('/users', 'GET', getUsersLambda);
CDK Patterns สำหรับ Microservices Architecture
Patterns ที่ใช้บ่อยในการสร้าง microservices
// lib/constructs/microservice.ts — Microservice Pattern
import * as cdk from 'aws-cdk-lib';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';
export interface MicroserviceProps {
readonly serviceName: string;
readonly vpc: ec2.IVpc;
readonly cluster: ecs.ICluster;
readonly dockerImage: string;
readonly cpu?: number;
readonly memoryMiB?: number;
readonly desiredCount?: number;
readonly environment?: Record<string, string>;
readonly enableQueue?: boolean;
readonly enableTable?: boolean;
}
export class Microservice extends Construct {
public readonly service: ecsPatterns.ApplicationLoadBalancedFargateService;
public readonly queue?: sqs.Queue;
public readonly deadLetterQueue?: sqs.Queue;
public readonly table?: dynamodb.Table;
public readonly topic: sns.Topic;
constructor(scope: Construct, id: string, props: MicroserviceProps) {
super(scope, id);
// SNS Topic for events
this.topic = new sns.Topic(this, 'Events', {
topicName: `-events`,
});
// SQS Queue (optional)
if (props.enableQueue) {
this.deadLetterQueue = new sqs.Queue(this, 'DLQ', {
queueName: `-dlq`,
retentionPeriod: cdk.Duration.days(14),
});
this.queue = new sqs.Queue(this, 'Queue', {
queueName: `-queue`,
visibilityTimeout: cdk.Duration.seconds(300),
deadLetterQueue: {
queue: this.deadLetterQueue,
maxReceiveCount: 3,
},
});
}
// DynamoDB Table (optional)
if (props.enableTable) {
this.table = new dynamodb.Table(this, 'Table', {
tableName: props.serviceName,
partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: true,
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
}
// ECS Fargate Service
const env: Record<string, string> = {
SERVICE_NAME: props.serviceName,
SNS_TOPIC_ARN: this.topic.topicArn,
...(this.queue ? { SQS_QUEUE_URL: this.queue.queueUrl } : {}),
...(this.table ? { DYNAMODB_TABLE: this.table.tableName } : {}),
...(props.environment || {}),
};
this.service = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'Service', {
cluster: props.cluster,
serviceName: props.serviceName,
cpu: props.cpu || 256,
memoryLimitMiB: props.memoryMiB || 512,
desiredCount: props.desiredCount || 2,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry(props.dockerImage),
environment: env,
containerPort: 8080,
},
circuitBreaker: { rollback: true },
healthCheckGracePeriod: cdk.Duration.seconds(60),
});
// Auto Scaling
const scaling = this.service.service.autoScaleTaskCount({
minCapacity: props.desiredCount || 2,
maxCapacity: (props.desiredCount || 2) * 5,
});
scaling.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});
// Grant permissions
this.topic.grantPublish(this.service.taskDefinition.taskRole);
if (this.queue) {
this.queue.grantConsumeMessages(this.service.taskDefinition.taskRole);
}
if (this.table) {
this.table.grantReadWriteData(this.service.taskDefinition.taskRole);
}
}
}
// Usage in Stack:
// const userService = new Microservice(this, 'UserService', {
// serviceName: 'user-service',
// vpc, cluster,
// dockerImage: '123456.dkr.ecr.region.amazonaws.com/user-service:latest',
// enableQueue: true,
// enableTable: true,
// });
Testing CDK Constructs ด้วย Jest
เขียน unit tests สำหรับ CDK infrastructure
// test/secure-api.test.ts — CDK Construct Tests
import * as cdk from 'aws-cdk-lib';
import { Template, Match, Capture } from 'aws-cdk-lib/assertions';
import { SecureApi } from '../lib/constructs/secure-api';
import { Microservice } from '../lib/constructs/microservice';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
describe('SecureApi Construct', () => {
let app: cdk.App;
let stack: cdk.Stack;
let template: Template;
beforeEach(() => {
app = new cdk.App();
stack = new cdk.Stack(app, 'TestStack');
new SecureApi(stack, 'TestApi', {
apiName: 'test-api',
enableWaf: true,
throttleRateLimit: 500,
corsOrigins: ['https://example.com'],
});
template = Template.fromStack(stack);
});
test('creates API Gateway REST API', () => {
template.hasResourceProperties('AWS::ApiGateway::RestApi', {
Name: 'test-api',
});
});
test('enables throttling', () => {
template.hasResourceProperties('AWS::ApiGateway::Stage', {
MethodSettings: Match.arrayWith([
Match.objectLike({
ThrottlingRateLimit: 500,
}),
]),
});
});
test('creates WAF WebACL', () => {
template.hasResourceProperties('AWS::WAFv2::WebACL', {
Scope: 'REGIONAL',
DefaultAction: { Allow: {} },
});
});
test('creates WAF association', () => {
template.resourceCountIs('AWS::WAFv2::WebACLAssociation', 1);
});
test('creates CloudWatch log group', () => {
template.hasResourceProperties('AWS::Logs::LogGroup', {
RetentionInDays: 30,
});
});
test('enables X-Ray tracing', () => {
template.hasResourceProperties('AWS::ApiGateway::Stage', {
TracingEnabled: true,
});
});
});
describe('Microservice Construct', () => {
let template: Template;
beforeEach(() => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'TestStack');
const vpc = new ec2.Vpc(stack, 'Vpc');
const cluster = new ecs.Cluster(stack, 'Cluster', { vpc });
new Microservice(stack, 'TestService', {
serviceName: 'test-svc',
vpc,
cluster,
dockerImage: 'nginx:latest',
enableQueue: true,
enableTable: true,
desiredCount: 2,
});
template = Template.fromStack(stack);
});
test('creates Fargate service', () => {
template.hasResourceProperties('AWS::ECS::Service', {
ServiceName: 'test-svc',
DesiredCount: 2,
LaunchType: 'FARGATE',
});
});
test('creates SQS queue with DLQ', () => {
template.hasResourceProperties('AWS::SQS::Queue', {
QueueName: 'test-svc-queue',
});
template.hasResourceProperties('AWS::SQS::Queue', {
QueueName: 'test-svc-dlq',
});
});
test('creates DynamoDB table', () => {
template.hasResourceProperties('AWS::DynamoDB::Table', {
TableName: 'test-svc',
BillingMode: 'PAY_PER_REQUEST',
PointInTimeRecoverySpecification: { PointInTimeRecoveryEnabled: true },
});
});
test('creates SNS topic', () => {
template.hasResourceProperties('AWS::SNS::Topic', {
TopicName: 'test-svc-events',
});
});
test('creates auto scaling', () => {
template.hasResourceProperties('AWS::ApplicationAutoScaling::ScalableTarget', {
MinCapacity: 2,
MaxCapacity: 10,
});
});
test('snapshot test', () => {
expect(template.toJSON()).toMatchSnapshot();
});
});
// Run: npx jest --coverage
Career Path สำหรับ Cloud Infrastructure Engineer
แผนพัฒนา career ด้าน Cloud Infrastructure
# === Career Development Roadmap for Cloud/CDK Engineers ===
# Level 1: Junior Cloud Engineer (0-2 years)
# Skills:
# - Basic AWS services (EC2, S3, VPC, IAM, RDS)
# - Linux system administration
# - Git version control
# - Basic networking (TCP/IP, DNS, HTTP)
# - Scripting (Bash, Python)
# - Basic CDK: L1/L2 constructs, simple stacks
#
# Certifications:
# - AWS Cloud Practitioner
# - AWS Solutions Architect Associate
#
# Projects:
# - Deploy static website with CDK (S3 + CloudFront)
# - Create VPC with CDK
# - Deploy simple web app (ECS Fargate + ALB)
# Level 2: Cloud Infrastructure Engineer (2-4 years)
# Skills:
# - Advanced AWS services (ECS, Lambda, DynamoDB, SQS, SNS)
# - CDK L3 constructs and custom constructs
# - CI/CD pipelines (GitHub Actions, CodePipeline)
# - Docker and container orchestration
# - Monitoring (CloudWatch, Datadog)
# - Security best practices (IAM, WAF, encryption)
# - Terraform (multi-cloud awareness)
#
# Certifications:
# - AWS Solutions Architect Professional
# - AWS DevOps Engineer Professional
#
# Projects:
# - Build microservices platform with CDK
# - Create reusable CDK construct library
# - Implement CI/CD for infrastructure
# Level 3: Senior Cloud/Platform Engineer (4-7 years)
# Skills:
# - Multi-account AWS strategy (AWS Organizations)
# - Kubernetes (EKS) management
# - Service mesh (App Mesh, Istio)
# - Cost optimization
# - Disaster recovery planning
# - Team mentoring
# - Architecture design
#
# Certifications:
# - AWS Security Specialty
# - Kubernetes CKA/CKAD
#
# Projects:
# - Design multi-account landing zone
# - Build internal developer platform
# - Implement FinOps practices
# Level 4: Principal/Staff Engineer (7+ years)
# Skills:
# - Enterprise architecture
# - Multi-cloud strategy
# - Technical leadership
# - Open source contribution
# - Conference speaking
# - Strategic planning
# === Salary Ranges (Thailand, 2024) ===
# Junior: 30,000 - 50,000 THB/month
# Mid: 50,000 - 80,000 THB/month
# Senior: 80,000 - 150,000 THB/month
# Principal: 150,000 - 300,000+ THB/month
# Remote (US companies): $80,000 - $200,000+/year
# === Key Resources ===
# - AWS CDK Workshop: https://cdkworkshop.com
# - CDK Patterns: https://cdkpatterns.com
# - Construct Hub: https://constructs.dev
# - AWS Well-Architected Framework
# - AWS Skill Builder (free courses)
FAQ คำถามที่พบบ่อย
Q: CDK กับ Terraform เลือกอันไหน?
A: CDK เหมาะสำหรับทีมที่ใช้ AWS เป็นหลักและมี developers ที่ถนัด TypeScript/Python ข้อดีคือ type safety, IDE support, testing frameworks Terraform เหมาะสำหรับ multi-cloud environments มี HCL ที่เรียนรู้ง่ายกว่า community ใหญ่กว่า หลายทีมใช้ทั้งสองโดย CDK สำหรับ application infrastructure และ Terraform สำหรับ foundational infrastructure
Q: CDK Construct Library ควรจัดการอย่างไร?
A: สร้างเป็น separate npm package หรือ PyPI package ใช้ semantic versioning publish ไปยัง private registry (CodeArtifact, npm private) เขียน tests ครบทุก construct มี documentation และ examples สร้าง CI/CD สำหรับ publish อัตโนมัติ ใช้ projen สำหรับ project management
Q: CDK เรียนรู้ยากไหม?
A: ถ้ามีพื้นฐาน TypeScript หรือ Python แล้วเรียนรู้ CDK ได้เร็ว 1-2 สัปดาห์สำหรับ basics แต่ต้องเข้าใจ AWS services ด้วยซึ่งใช้เวลามากกว่า เริ่มจาก CDK Workshop ที่เป็น hands-on tutorial แล้วลองสร้าง constructs จากงานจริง อ่าน source code ของ L2 constructs เพื่อเข้าใจ patterns
Q: CDK v2 กับ v1 ต่างกันอย่างไร?
A: CDK v2 รวมทุก AWS service libraries เข้า aws-cdk-lib package เดียว ไม่ต้อง install แยกทุก service เหมือน v1 มี assertions library ใหม่สำหรับ testing ดีกว่า v1 (assert module) ทุกโปรเจกต์ใหม่ควรใช้ v2 เท่านั้น v1 หมด maintenance แล้ว migration จาก v1 ไป v2 ทำได้ตาม migration guide อย่างเป็นระบบ
