最近討論了 EC2、Spot 與 Fargate 有沒有想過我們是不是可以來一個混搭的系統呢?
混搭系統可以讓我們的系統可以更有彈性的被控制,像是今天可能有顯卡運算資源機器的需求我們就把它掛在 EC2 上面,一般服務型態的服務可以開 EC2 與 EC2 Spot 各半,排成服務就跑在 Fargate 上,流量高的時段就開 EC2 Spot 的機器,這樣聽起來是不是很完美呢 XD ~
所以今天就來說明怎麼建構一個如此有彈性的系統吧!
在開始說明之前先整理一下我們的目標,我們需要擁有三種不同的機器分別是
模擬服務可以在不同的機器上面運作,所以需要開三種不同的 Task 分別是
首先處理 ECS Cluster 並且設定 On-Demand Instance 的 ASG 與 Spot Instance 的 ASG,分別對應到機器等級
Fargate 不用自己控制機器所以這邊不用處理
const cluster = new ecs.Cluster(this, "EcsCluster", { vpc });
const autoScalingGroup = cluster.addCapacity(
"DefaultAutoScalingGroupCapacity",
{
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO
),
desiredCapacity: 1,
machineImage: ecs.EcsOptimizedImage.amazonLinux2(),
}
);
autoScalingGroup.scaleOnCpuUtilization("KeepCpuHalfwayLoaded", {
targetUtilizationPercent: 50,
});
const spotAutoScalingGroup = cluster.addCapacity(
"SpotAutoScalingGroupCapacity",
{
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.SMALL
),
desiredCapacity: 1,
machineImage: ecs.EcsOptimizedImage.amazonLinux2(),
spotPrice: "0.0104",
spotInstanceDraining: true,
}
);
spotAutoScalingGroup.scaleOnCpuUtilization("KeepCpuHalfwayLoaded", {
targetUtilizationPercent: 50,
});
建立完後可以在 ECS Instances 看到有兩種不同大小的機器註冊
因為要實現不同類型的機器跑不同的服務這邊介紹一個新的參數 Constraints
它可以用來對 Task 做一些約束像是機器等級系統種類是否有掛 EFS 等等或是一些自訂參數,但是目前 AWS CDK 還不支援設定設定 Attributes 所以自動參數的部分這次沒有辦法做給大家看有興趣可以參考 AWS 文件直接在 console 或是使用 command 實作
除了 Constraints
這次範例還使用了一個新的 function TaskDefinition
它有一個很好用的屬性 compatibility 使用 EC2_AND_FARGATE
就可以建立一個不管是 EC2 或是 Fargate 都可以一起共用的 Task
另外還有使用一個特別的 Network Mode AWS VPC 它可以把 ENI 網卡直接掛在 Container 上面,往常我們如果直接使用 EC2 部署沒有使用 Load Balance 是沒有辦法兩個 Container 使用同一個 port 的,畢竟在系統上只有一個 process 可以 bind 一個 port,而使用了 AWS VPC 就不會發生這個問題了,因為大家都可以直接使用自己的獨立網卡拉!
上面有提到要使用 Constraints
約束機器可是可以約束的條件要從哪裡找呢?我們可以到 AWS Console ECS 找到 EC2 Instances,在裡面選擇一台機器按下 Actions -> View/Edit Attributes 就可以看到了,如下兩張圖片是我們開設的機器 Attributes
如圖可以看到機器等級是
t3.micro
如圖可以看到機器等級是
t3.small
Fargate 雖然不受 Cluster 管理不過我們一樣要設定 Task
const fargateTaskDefinition = new ecs.FargateTaskDefinition(
this,
"FargateTaskDef",
{
cpu: 256,
memoryLimitMiB: 512,
}
);
可以在 Task 看到 Network Mode 設定為 awsvpc
可以看到 Constraint 裡面是空的
在這邊使用了上面說的 TaskDefinition 所以需要設定 compatibility 為 EC2,不然就要如下使用 Ec2TaskDefinition 不過其實都可以
在前面我們設定 On-Demand Instance 等級為 t3.micro,所以 placementConstraints
設定機器等級限制 t3.micro
const taskDefinition = new ecs.TaskDefinition(this, "TaskDef", {
cpu: "256",
memoryMiB: "512",
compatibility: ecs.Compatibility.EC2,
networkMode: ecs.NetworkMode.AWS_VPC,
placementConstraints: [
ecs.PlacementConstraint.memberOf(
"attribute:ecs.instance-type == t3.micro"
),
],
});
可以在 Task 看到 Network Mode 設定為 awsvpc
可以在 Constraint 看到限制 type 等級 t3.micro
而 Spot Instance 等級為 t3.small,所以在 placementConstraints
設定機器等級限制 t3.small
const spotTaskDefinition = new ecs.Ec2TaskDefinition(this, "SpotTaskDef", {
networkMode: ecs.NetworkMode.AWS_VPC,
placementConstraints: [
ecs.PlacementConstraint.memberOf(
"attribute:ecs.instance-type == t3.small"
),
],
});
可以在 Task 看到 Network Mode 設定為 awsvpc
可以在 Constraint 看到限制 type 等級 t3.small
可以在 ECS Tasks 看到上面設定的所有 Task
這次範例使用一樣的 image 來建立範例,不過我們分別使用了不同的 Task Definition 來限制不同的機器種類
const fargateContainer = fargateTaskDefinition.addContainer(
"WebContainer",
{
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 512,
}
);
fargateContainer.addPortMappings({
containerPort: 80,
});
const container = taskDefinition.addContainer("WebContainer", {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 512,
});
container.addPortMappings({
containerPort: 80,
});
const spotContainer = spotTaskDefinition.addContainer("WebContainer", {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 512,
});
spotContainer.addPortMappings({
containerPort: 80,
});
建立三種不同的 Service 分別管理不同機器等級的 Task
const fatgetService = new ecs.FargateService(this, "FargateService", {
cluster,
taskDefinition: fargateTaskDefinition,
});
const ecsService = new ecs.Ec2Service(this, "Ec2Service", {
cluster,
taskDefinition,
});
const spotEcsService = new ecs.Ec2Service(this, "SpotEc2Service", {
cluster,
taskDefinition: spotTaskDefinition,
});
為了方便測試來把三種 Service 加入一樣的 target 吧!如果混搭的目標是用不同的機器跑不同的 Service 就可以分別投入對應的 Load Balancer 拉!
const lb = new elbv2.ApplicationLoadBalancer(this, "LB", {
vpc,
internetFacing: true,
});
const listener = lb.addListener("Listener", { port: 80 });
listener.addTargets("ECS2", {
port: 80,
targets: [fatgetService, ecsService, spotEcsService],
});
這次也是測試網頁而已就不多提了 XD
今天是一個機器混搭的測試,如此設定就可以在 ECS 部署上有更多的彈性
文章內容主要是網路或是程式開發類型的文章
本文同步刊載於 Clarence 部落格:Day 23 - CDK 建置 Amazon Elastic Container Service(ECS) Service - Fargate 與 EC2 混搭