반응형
250x250
Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

가끔 보자, 하늘.

CDK로 AWS 인프라 구축하기 - #4 ECS Cluster 구성 본문

개발 이야기/인프라 구축 및 운영

CDK로 AWS 인프라 구축하기 - #4 ECS Cluster 구성

가온아 2022. 6. 17. 14:05

이번에는 ECS를 사용해 간단한 웹 서비스를 구성하는 방법을 살펴 보겠습니다.

웹 서비스는 사내에서 docker로 빌드되어 ECR로 업데이트 되어 있다고 가정하고 아래와 같은 절차를 통해 웹 서비스가 된다고 가정합니다.

  • ECS Cluster의 Task들은 private subnet에서 실행되므로 ECR 접근을 위해 VPC Endpoint를 사용합니다.(참고 문서 링크)
  • 유저들은 public subnet에 설치된 ALB(80 Port)로 접속을 시도 (실 서비스에서는 앞 단에 Route53을 설정하겠지만 여기서는 ALB로 바로 연결한다고 가정합니다.)
  • ALB는 private subnet의 ECS Cluster(3000 Port)로 포워딩합니다.

이를 간단히 도식화하면 아래와 같습니다.

아래 예제 코드 중 VPC와 Security Group는 이전 문서(VPC , SG)를 참고하세요. ECS 관련 공식 문서(ECS, ECR)에서 상세한 정보를 확인할 수 있습니다.

우선 ecs를 빌드할 함수를 정의합니다. 인자는 이전에 생성해 둔 vpc와 sg 리스트를 전달하겠습니다.

.
.
//	interval 지정을 위해 Duration 추가
import { Duration, Stack, StackProps } from 'aws-cdk-lib';
//	ecr, ecs libs
import * as ecr from "aws-cdk-lib/aws-ecr";
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
.
.

build_ecs(vpc:ec2.Vpc, listSG: {[name:string] : ec2.SecurityGroup}){
    // vpc에서 private subnet을 리스트로 가져옵니다. 
    const subnetIds: string[] = [];
    vpc.privateSubnets.forEach((subnet, index) => {
      subnetIds.push(subnet.subnetId);
    });
    .
    .
    .
}

ECS에서 task를 생성하려면 ECR로부터 등록된 docker 이미지를 가져와야 합니다. 이를 위해서는 private subnet에서 ECR로 접근할 수 있도록 ecr.api , ecr.dkr, logs 서비스를 사용할 수 있는 VPC endpoint를 설정해야 합니다.

.
.
//  ECR 접근을 위한 VPC endpoint 설정
    const cfnVPCEndpoint_api = new ec2.CfnVPCEndpoint(this, 'ecs2ecr-api',{
      serviceName: 'com.amazonaws.ap-northeast-2.ecr.api',
      vpcId: vpc.vpcId,
      privateDnsEnabled: true,
      subnetIds : subnetIds,
      securityGroupIds: [listSG.web_3000_for_private.securityGroupId],
      vpcEndpointType: "Interface"
    });
    const cfnVPCEndpoint_dkr = new ec2.CfnVPCEndpoint(this, 'ecs2ecr-dkr',{
      serviceName: 'com.amazonaws.ap-northeast-2.ecr.dkr',
      vpcId: vpc.vpcId,
      privateDnsEnabled: true,
      subnetIds : subnetIds,
      securityGroupIds: [listSG.web_3000_for_private.securityGroupId],
      vpcEndpointType: "Interface"
    });
    const cfnVPCEndpoint_logs = new ec2.CfnVPCEndpoint(this, 'ecs2logs',{
      serviceName: 'com.amazonaws.ap-northeast-2.logs',
      vpcId: vpc.vpcId,
      privateDnsEnabled: true,
      subnetIds : subnetIds,
      securityGroupIds: [listSG.web_3000_for_private.securityGroupId],
      vpcEndpointType: "Interface"
    });    
    .
    .

그리고 ECR의 저장소를 지정합니다. 

    .
    //  가져올 이미지의 저장소 위치
    const repository = ecr.Repository.fromRepositoryName(
      this,
      "your_aws_id",
      "repository_name"
    );
    .

ECS Cluster를 생성하고 task를 정의합니다.

    .
    // ECS Cluster를 생성합니다.
    const cluster_web = new ecs.Cluster(this, 'hello-web', {
      clusterName: 'hello-web',
      containerInsights: false,
      enableFargateCapacityProviders: false,
      vpc : vpc,
    });
    
    //  cluster의 capacity 설정
    cluster_web.addCapacity('hello-web', {
      instanceType: new ec2.InstanceType("t2.small"),
      desiredCapacity: 1, // 초기 instance 생성 개수
      maxCapacity: 2,
      minCapacity: 1,
      // vpcSubnets : default > all private subnets.
    });

    //  task를 정의
    const taskDefinition = new ecs.FargateTaskDefinition(this, "hello-web-task", {
      memoryLimitMiB: 512,
      cpu: 256,
    });
    
    const container = taskDefinition.addContainer('hello-web-container', {
      // docker image 지정. 위에서 정의한 repository입니다.
      image : ecs.ContainerImage.fromEcrRepository(repository), 
      memoryLimitMiB: 512,
      portMappings: [{ containerPort: 3000 }], // 생성된 웹 서비스는 3000 포트를 사용한다고 가정합니다.
    });
    
    //  Service를 정의
    const service = new ecs.FargateService(this, 'hello-web-service', {
      cluster: cluster_web, 
      taskDefinition, 
      desiredCount:1,
      circuitBreaker: {rollback: true}, // // 비정상 서비스가 배포된 경우 자동으로 롤백되도록 설정
      securityGroups : [listSG.web_3000_for_private],
      serviceName : 'heelo-web-ecs-service'
    });
    .

이제 private subnet에 배포된 서비스에 접근할 수 있도록 ALB를 연결합니다. port 80번으로 접근할 수 있도록 설정합니다.

    .
    // 외부에서의 접근을 위한 alb 설정
    const lb = new elbv2.ApplicationLoadBalancer(this, 'hello-web-alb', {vpc, internetFacing:true});
    const listener = lb.addListener('hello-web-listener', {port:80});
    listener.addTargets('hello-web-target', {
      port: 80,
      targets: [service],
      healthCheck: {
        path : '/index.html',
        interval: Duration.minutes(1)
      }
    });

 

지금까지 CDK를 이용해 웹 서비스를 배포하는 인프라를 구축하는 방법을 알아보았습니다. 다음은 마지막으로 지금까지의 샘플 코드를 통합하고  Autoscaling 테스트 및 검증 과정을 진행 후 CDK 관련 정리를 마치겠습니다. :)

반응형