Bài viết thuộc series “Chinh phục CDK”
Giới thiệu
Trong bài này chúng ta sẽ tìm hiểu về khái niệm Construct Tree và Construct Layer.
Construct
Như đã tìm hiểu ở các bài trước, Construct là phần cơ bản nhất của CDK. Một Construct sẽ định danh cho một tài nguyên trên Cloud và chứa mọi thứ cần thiết để tạo được tài nguyên đó.
Nhưng làm thế nào để CDK biết được nên tạo Construct nào trước, Construct nào sau để các tài nguyên của ta hoạt động đúng? Ví dụ ta cần phải tạo VPC trước khi tạo EC2 ở trong VPC đó.
Và tại sao có tài nguyên ta chỉ cần dùng một hàm là có thể tạo được nhưng lại có tài nguyên ta phải viết rất nhiều hàm khác nhau để tạo nó? Ví dụ như ở bài trước EC2 ta chỉ dùng một hàm còn Elasticache ta lại dùng rất nhiều hàm khác nhau.
Khái niệm Construct Tree và Construct Layer sẽ giúp ta trả lời các câu hỏi này.
Construct Tree
Trong các ví dụ ta đã làm thì một ứng dụng CDK đều có 3 thành phần: App, Stack, Construct. Ví dụ:
package main
import (
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awss3"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
)
type S3SimpleStackProps struct {
awscdk.StackProps
}
func NewS3SimpleStack(scope constructs.Construct, id string, props *S3SimpleStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
awss3.NewBucket(stack, jsii.String("L2"), &awss3.BucketProps{
Versioned: jsii.Bool(false),
})
return stack
}
func main() {
defer jsii.Close()
app := awscdk.NewApp(nil)
NewS3SimpleStack(app, "S3SimpleStack", &S3SimpleStackProps{
awscdk.StackProps{
Env: env(),
},
})
app.Synth(nil)
}
func env() *awscdk.Environment {
return nil
}
Tất cả các thành phần trên đều được truyền vào giá trị scope
ngoại trừ App. Bằng cách truyền scope
vào các thành phần thì CDK định nghĩa được hệ thống cấp bậc giữa các thành phần. Hệ thống cấp bậc này được gọi là Construct Tree.
Ở đỉnh của Construct Tree là App. Bên trong App ta có một hay nhiều Stack. Bên trong Stack ta có một hay nhiều Construct. Bên trong Construct có thể chứa L1 Construct hoặc L2 Construct. Và cứ như vậy đi xuống để tạo thành Construct Tree. Mục đích của Construct Tree là để CDK tạo các tài nguyên theo thứ tự cần thiết.
Ngoài tham số thứ nhất được truyền vào Construct là scope
thì còn có tham số thứ hai là ID, giá trị này được dùng để định danh cho Construct trong scope
. AWS CDK sử dụng ID từ đỉnh của Construct Tree cho tới các thành phần Construct con để tạo danh định danh cho một Construct. Ví dụ trong đoạn code ở trên ID định danh cho S3 Construct có thể là S3SimpleStack-L2
.
Construct Layer
Tất cả các Construct đều được định nghĩa trong thư viện CDK và được chia thành ba cấp bậc là: L1 Construct, L2 Construct, L3 Construct.
L1 Construct
Bắt đầu với cấp thấp nhất là L1 Construct, đây là Construct dùng để đại diện cho một tài nguyên cụ thể trong AWS CloudFormation. Tên của các hàm L1 Construct đều được bắt đầu với Cfn
. Khi sử dụng L1 Construct ta phải chỉ định toàn bộ thuộc tính của tài nguyên đó như lúc ta xài CloudFormation.
Ví dụ ở bài trước ta đã tạo Elasticache dùng L1 Construct, hàm để tạo Elasticache có tên là NewCfnCacheCluster
:
func NewQuestionCacheStack(scope constructs.Construct, id string, props *QuestionCacheStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
...
// SG Construct
sg := awsec2.NewSecurityGroup(stack, jsii.String("CacheSG"), &awsec2.SecurityGroupProps{
AllowAllOutbound: jsii.Bool(true),
Vpc: vpc,
})
sg.AddIngressRule(
awsec2.Peer_AnyIpv4(),
awsec2.NewPort(&awsec2.PortProps{
FromPort: jsii.Number(6379),
ToPort: jsii.Number(6379),
StringRepresentation: jsii.String("Redis"),
Protocol: awsec2.Protocol_ALL,
}),
jsii.String("Redis Port"),
jsii.Bool(false),
)
// Elasticache Construct
awselasticache.NewCfnCacheCluster(stack, jsii.String("Cache"), &awselasticache.CfnCacheClusterProps{
Engine: jsii.String("redis"),
CacheNodeType: jsii.String("cache.t2.micro"),
NumCacheNodes: jsii.Number(1),
VpcSecurityGroupIds: &[]*string{sg.SecurityGroupId()},
})
return stack
}
Như bạn thấy là để có thể truy cập được Elasticache ta phải tạo thêm Security Group. Nếu dùng L1 Construct để tạo tài nguyên thì yêu cầu ta phải hiểu rõ thuộc tính của tài nguyên đó trong CloudFormation.
L2 Construct
Tầng tiếp theo là L2 Construct, đây Construct dùng để đại diện cho một tài nguyên cụ thể trên AWS chứ không phải một tài nguyên của CloudFormation. L2 Construct là tập họp của nhiều L1 Construct mà đã được viết sẵn, giúp ta tạo tài nguyên một cách dễ dàng hơn hay vì phải viết một đống code cho L1 Construct. Khi sử dụng L2 Construct ta chỉ cần truyền một vài thuộc tính đơn giản. Ta không cần phải biết rõ chi tiết về các thuộc tính bên dưới của tài nguyên như khi dùng L1 Construct.
Ví dụ tạo S3 bằng L2 Construct:
func NewS3SimpleStack(scope constructs.Construct, id string, props *S3SimpleStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)
awss3.NewBucket(stack, jsii.String("L2"), &awss3.BucketProps{
Versioned: jsii.Bool(false),
})
return stack
}
L3 Construct
Tầng cao nhất là L3 Construct hay còn được gọi là Patterns. L3 Construct được thiết kế để giúp ta tạo được các mẫu hạ tầng quen thuộc mà tập họp rất nhiều các tài nguyên khác nhau trên AWS. Ví dụ Amazon ECS với ALB. Thay vì ta phải viết L2 Construct cho ECS và L2 Construct cho ALB với rất nhiều cấu hình khác nhau để kết nối hai thằng, thì ta chỉ cần dùng L3 Construct:
loadBalancedFargateService := ecsPatterns.NewApplicationLoadBalancedFargateService(this, jsii.String("Service"), &ApplicationLoadBalancedFargateServiceProps{
Cluster: Cluster,
MemoryLimitMiB: jsii.Number(1024),
DesiredCount: jsii.Number(1),
Cpu: jsii.Number(512),
TaskImageOptions: &ApplicationLoadBalancedTaskImageOptions{
Image: ecs.ContainerImage_FromRegistry(jsii.String("amazon/amazon-ecs-sample")),
},
TaskSubnets: &SubnetSelection{
Subnets: []iSubnet{
ec2.Subnet_FromSubnetId(this, jsii.String("subnet"), jsii.String("VpcISOLATEDSubnet1Subnet80F07FA0")),
},
},
LoadBalancerName: jsii.String("application-lb-name"),
})
Ta tìm hiểu cách sử dụng của từng Construct và nó có Layer nào thông qua API Reference.
Kết luận
Vậy là ta đã tìm hiểu xong khái niệm Construct Tree và Layer. Một lưu ý là ta nên sử dụng L2 Construct để dễ dàng cho việc phát triển ứng dụng bằng CDK, tuy nhiên không phải tài nguyên nào cũng có L2 Construct vì CDK vẫn đang trong quá trình phát triển, khi đó bạn có thể dùng L1 Construct thay thế.
Tác giả Quân Huỳnh
Nếu bài viết có gì sai hoặc cần cập nhật thì liên hệ Admin.
Tham gia nhóm chat của DevOps VN tại Telegram.
Kém tiếng Anh và cần nâng cao trình độ giao tiếp: Tại sao bạn học không hiệu quả?