AWS: ECS Fargate + ELB的使用(CDK)

AWS: ECS Fargate + ELB的使用(CDK)

一、ECS定义

Amazon Elastic Container Service (Amazon ECS)是一种高度可扩展的快速容器管理服务,它又包含两种服务:

  • 使用 Fargate 启动类型,它是基于Container的Serverless服务, 用户无需关心服务器,只需要上传自定义的镜像,剩下的工作交给Fargate 就可以了;注意这里host与pod的比例是1:1,也就是一个虚拟机对应于一个pod。
  • 使用 EC2 启动类型,它是使用EC2作为服务器,用户是需要关心服务器的;注意这里host与pod的比例是1:many,也就是一个虚拟机对应多个pod。

注意:AWS Fargate 与AWS Lambda无服务底层技术是使用Firecracker,不是Kubernetes,参考这里

二、使用场景

下图是Amazon ECS 架构,一个Region下包含多个可用区(AZ),一个ECS集群(Cluster)是跨多个可用区的,一个集群(Cluster)里包含一个或多个服务(Service),一个服务(Service)里包含一个或多个任务(Task),一个任务(Task)里包含一个或多个容器镜像(image)。
在这里插入图片描述
下图是它的常见使用案例,包含的内容如下:

  1. 首先开发人员需要创建自定义的镜像将其推送至Amazon ECR(EC2 Container Registry)
  2. 定义任务(Task),它是ECS调度的最小单元,类似于K8S中的Pod,里面包含一个或多个容器镜像
  3. 创建服务(Service),一个服务通常由多个一组同类的任务(Task)组成。
  4. 创建一个ELB(Elastic Load Balancer),通过它将服务暴露出去
  5. 在ELB(Elastic Load Balancer)的前面,通常会配置一个API Gateway,实现服务治理。

在这里插入图片描述

三、Demo with Fargate

本节将通过AWS CDK创建一个ECS Fargate集群,然后将集群里的服务通过负载均衡器暴露给外网访问。

1. 运行命令,创建一个CDK项目

mkdir ecs-demo && cd ecs-demo
cdk init -l typescript

2. 创建一个Stack

import * as cdk from "@aws-cdk/core"; // 必须引入的包
import * as ecs from "@aws-cdk/aws-ecs"; // 我们使用ECS服务,那就引入此包
import * as ec2 from "@aws-cdk/aws-ec2"; // for VPC
import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2"; // 使用负载均衡器,需要引入此包

export class EcsFargateDemoStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 通常VPC已经默认创建好了,这一步是为了寻找现有的VPC
    const vpc = ec2.Vpc.fromLookup(this, "Vpc", { isDefault: true });

    // 创建一个集群
    const cluster: ecs.Cluster = new ecs.Cluster(this, "FargateCluster", {
      vpc: vpc,
    });

	// 创建一个Task的定义,注意是FARGATE类型,同时也包含了cpu和内存
    const taskDefinition = new ecs.TaskDefinition(this, "cong-task", {
      compatibility: ecs.Compatibility.FARGATE,
      cpu: "512",
      memoryMiB: "1024",
    });
	
    // 向Task的定义里添加一个镜像,暴露80端
    taskDefinition
      .addContainer("cong-demo", {
        image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
        memoryLimitMiB: 512,
      })
      .addPortMappings({ containerPort: 80 });

    // 创建一个service,service里包含task的定义,期望运行的task数量是4
    const fargateService = new ecs.FargateService(this, "FargateService", {
      cluster: cluster,
      taskDefinition: taskDefinition,
      desiredCount: 4,
      serviceName: "Cong-Service",
    });
      
    // service有自动横向扩缩的功用,这里给task的数量定义一个扩缩范围
    fargateService.autoScaleTaskCount({
      maxCapacity: 10,
      minCapacity: 1,
    });

    // 创建一个load balancer,它是由监听器(listener)和目标组(targate group)组成
    const elb = new elbv2.ApplicationLoadBalancer(this, "Elb", {
      vpc: vpc,
      internetFacing: true, // 向外网暴露
    });

    const listener = elb.addListener("Listener", {
      port: 80,
      protocol: elbv2.ApplicationProtocol.HTTP,
    });

    listener.addTargets("service-A", {
      port: 80,
      targets: [fargateService],
    });
    
    // 输出load balancer的DNS name,直接在浏览器中打开它就可以访问到本服务了。
    new cdk.CfnOutput(this, "LB DNS NAME", {
      value: listener.loadBalancer.loadBalancerDnsName,
    });
  }
}

3. 部署

运行如下命令可将其直接部署到AWS云上,部署的方式实际上是通过CloudFormation进行部署的。

cdk deploy
# 运行它会在cdk.out文件夹下生成一个CloudFormation的模板
# cdk synth

四、Demo with EC2

本节将通过AWS CDK创建一个ECS EC2集群,然后将集群里的服务通过负载均衡器暴露给外网访问。

1. 创建一个新的stack

在原有项目上创建一个新的stack(ecs-demo-stack.ts), 如下代码上面的fargate的内容基本相同,注意如下注释部分是稍有区别的地方

import * as cdk from "@aws-cdk/core";
import * as ecs from "@aws-cdk/aws-ecs";
import * as ec2 from "@aws-cdk/aws-ec2";
import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2";


export class EcsDemoStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = ec2.Vpc.fromLookup(this, "Vpc", { isDefault: true });



    const cluster: ecs.Cluster = new ecs.Cluster(this, "FargateCluster", {
      vpc: vpc,
    });
	
    // 1. 通过addCapacity方法可将EC2实例添加到集群之中,instanceType是必填项;spotPrice是选填的,如果填了它,那么它就是一个竞价型的实例,价格更便宜,这个数值可以在当前region下的ec2 console页面上找到。
    // 2. addCapacity方法返回的对象是AutoScalingGroup, 由此组管理了一组ecs实例
    // 3. 一个集群中是可以创建多个AutoScalingGroup的,比如:可以由8台标准实例和2台spot实例组成。
    cluster.addCapacity("DefaultAutoScalingGroupCapacity", {
      instanceType: new ec2.InstanceType("t3.micro"),
      desiredCapacity: 2,
      spotPrice: "0.1168",
      minCapacity: 1,
      maxCapacity: 10
    });

    // 注意这里compatibility的值是ec2
    const taskDefinition = new ecs.TaskDefinition(this, "ec2-task", {
      compatibility: ecs.Compatibility.EC2,
      cpu: "256",
      memoryMiB: "512",
    });

    taskDefinition
      .addContainer("nginx", {
        image: ecs.ContainerImage.fromRegistry("nginx"),
        memoryLimitMiB: 512,
      })
      .addPortMappings({ containerPort: 80 });

    const ec2Service = new ecs.Ec2Service(this, "Ec2Service", {
      cluster: cluster,
      taskDefinition: taskDefinition,
      desiredCount: 2,
    });

    // ec2Service.autoScaleTaskCount({
    //   maxCapacity: 11,
    //   minCapacity: 1
    // });

    const elb = new elbv2.ApplicationLoadBalancer(this, "Elb", {
      vpc: vpc,
      internetFacing: true,
    });

    const listener = elb.addListener("Listener", {
      port: 80,
      protocol: elbv2.ApplicationProtocol.HTTP,
    });

    const lbTargetGroup = new elbv2.ApplicationTargetGroup(
      this,
      "TargetGroup",
      { vpc: vpc, port: 80, protocol: elbv2.ApplicationProtocol.HTTP }
    );
    lbTargetGroup.addTarget(ec2Service);

    listener.addTargetGroups("TargetGroup1", {
      targetGroups: [lbTargetGroup],
    });

    new cdk.CfnOutput(this, "LB DNS NAME", {
      value: listener.loadBalancer.loadBalancerDnsName,
    });
  }
}

2. 填充bin/ecs-demo.ts

在bin/ecs-demo.ts文件中将此stack new出来。bin/ecs-demo.ts的文件内容如下:

#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "@aws-cdk/core";
import { EcsDemoStack } from "../lib/ecs-demo-stack";
import { EcsFargateDemoStack } from "../lib/ecs-fargate-demo-stack";

const app = new cdk.App();
const env = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
};
new EcsDemoStack(app, "EcsDemoStack", { env });
new EcsFargateDemoStack(app, "EcsFargateDemoStack", { env });

3. 配置实例的扩缩(Instance Auto-Scaling)

如果你的应用是运行在AWS ECS Fargate上,那AWS会自动帮你管理好底层真实的虚拟机及以容器的调度,但如果你使用AWS ECS EC2的话,你预先创建的虚拟机实例数是固定的,它有可能随着task的增加被填满。

为了避免这个放置问题(placement error),我们需要配置Auto-Scaling以便实例能够按需扩缩,代码如下:

const autoScalingGroup = cluster.addCapacity('DefaultAutoScalingGroup', {
  instanceType: new ec2.InstanceType("t2.xlarge"),
  minCapacity: 3,
  maxCapacity: 30,
  desiredCapacity: 3,

  // Give instances 5 minutes to drain running tasks when an instance is
  // terminated. This is the default, turn this off by specifying 0 or
  // change the timeout up to 900 seconds.
  taskDrainTime: Duration.seconds(300)
});

autoScalingGroup.scaleOnCpuUtilization('KeepCpuHalfwayLoaded', {
  targetUtilizationPercent: 50
});

See the @aws-cdk/aws-autoscaling library for more autoscaling options you can configure on your instances.

五、参考链接

  1. ECS官网
  2. cdk ecs
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AWS::S3::S3Client是AWS提供的用于与S3服务进行交互的客户端库。它具有上传文件到S3存储桶的功能。 在使用AWS::S3::S3Client上传文件时,可以通过使用S3的`TransferManager`类来获取上传进度。`TransferManager`类提供了一个`Upload`方法,该方法接受一个`PutObjectRequest`对象作为参数,该对象包含了要上传的文件信息。 要获取上传进度,可以在`PutObjectRequest`对象中设置一个`ProgressListener`。`ProgressListener`是一个接口,它定义了一些回调方法,当上传进度发生变化时,这些方法会被触发。 通过实现`ProgressListener`接口,并将其实例作为参数传递给`PutObjectRequest`对象的`setGeneralProgressListener`方法,就可以获取上传进度。在`ProgressListener`的回调方法中,可以获取当前上传的字节数、总字节数以及上传百分比等信息。 例如,可以创建一个实现`ProgressListener`接口的类`MyProgressListener`,并将其传递给`PutObjectRequest`对象的`setGeneralProgressListener`方法: ```java public class MyProgressListener implements ProgressListener { @Override public void progressChanged(ProgressEvent progressEvent) { long bytesTransferred = progressEvent.getBytesTransfered(); long totalBytes = progressEvent.getBytes(); int percent = (int) (bytesTransferred * 100 / totalBytes); System.out.println("上传进度:" + percent + "%"); } } ``` 然后,在上传文件之前,创建`MyProgressListener`的实例,并将其设置为`PutObjectRequest`对象的`ProgressListener`: ```java MyProgressListener progressListener = new MyProgressListener(); PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, new File(filePath)); putObjectRequest.setGeneralProgressListener(progressListener); TransferManager transferManager = new TransferManager(client); Upload upload = transferManager.upload(putObjectRequest); upload.waitForCompletion(); ``` 这样,当文件上传时,`MyProgressListener`中的`progressChanged`方法会根据上传进度的变化被触发,并打印出上传的百分比。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值