개발 이야기/인프라 구축 및 운영
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 관련 정리를 마치겠습니다. :)
반응형