Amazon ECS 기반 컨테이너 웹 앱 배포
시작 안내서
모듈 2: 인프라 생성 및 애플리케이션 배포
이 모듈에서는 ECS 클러스터를 생성하고 CDK로 앱을 배포합니다
소개
이 모듈에서는 ECS 클러스터를 설정하는 데 필요한 모든 인프라를 생성하고 샘플 컨테이너를 배포하는 AWS CDK 애플리케이션을 생성합니다.
학습 내용
- 간단한 CDK 애플리케이션 생성
- 용량을 위해 Fargate로 ECS 클러스터 생성
- 애플리케이션 배포를 위한 작업 정의 및 서비스 생성
소요 시간
15분
모듈 선행 요구 사항
- 관리자 수준의 액세스 권한이 있는 AWS 계정**
- 권장 브라우저: 최신 버전의 Chrome 또는 Firefox
[**]생성된 지 24시간이 지나지 않은 계정은 이 자습서를 완료하는 데 필요한 서비스에 액세스할 권한이 아직 없을 수 있습니다.
구현
CDK 앱 생성
먼저 CDK가 설치되어 있는지 확인합니다. 아직 설치하지 않았다면 AWS CDK를 사용하여 시작하기를 따라하면 됩니다.
cdk --version
언어로 TypeScript를 사용해 CDK 애플리케이션 골격을 생성합니다. 터미널에서 다음 명령을 실행합니다.
mkdir cdk-ecs-infra
cd cdk-ecs-infra
cdk init app --language typescript
이는 다음 내용을 출력합니다.
Applying project template app for typescript
# Welcome to your CDK TypeScript project!
This is a blank project for TypeScript development with CDK.
The `cdk.json` file tells the CDK Toolkit how to execute your app.
## Useful commands
* `npm run build` compile typescript to js
* `npm run watch` watch for changes and compile
* `npm run test` perform the jest unit tests
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk synth` emits the synthesized CloudFormation template
Executing npm install...
✅ All done!
리소스 스택에 대한 코드 생성
파일 /lib/cdk-ecs-infra-stack.ts로 이동합니다. 생성하려는 리소스 스택에 대한 코드를 작성할 위치입니다.
리소스 스택은 특정 계정에 프로비저닝 될 클라우드 인프라 리소스의 집합(이 가이드 수행의 경우 모든 AWS 리소스)입니다. 스택에 해당 리소스가 프로비저닝될 계정/리전을 구성할 수 있습니다(AWS CDK로 시작하기 가이드에 수록된 바와 같이).
이 리소스 스택에서는 다음의 리소스를 생성하게 됩니다.
- IAM 역할: 이 역할은 다른 AWS 서비스를 호출할 수 있도록 컨테이너에 할당됩니다.
- ECS 작업 정의: 컨테이너 시작 시 사용하는 특정 파라미터.
- Fargate 로드 밸런스 서비스를 위한 ECS 패턴: 이는 클러스터, 로드 밸런서, 서비스를 생성하는 데 필요한 모든 구성 요소의 복잡성을 추상화하고 모두 함께 작동하도록 구성합니다.
ECS 클러스터 생성
EC2 클러스터 생성을 시작하기 위해 먼저 정확한 모듈을 가져와야 합니다.
npm i @aws-cdk/aws-s3-assets
그런 다음 lib/cdk-eb-infra-stack.ts 파일에서 파일 상단에 종속성을 추가합니다.
npm i @aws-cdk/aws-ec2 @aws-cdk/aws-ecs @aws-cdk/aws-ecs-patterns @aws-cdk/aws-iam
그런 다음 lib/cdk-eb-infra-stack.ts 파일을 편집해 파일 최상단에 종속성을 추가합니다.
import ecs = require('@aws-cdk/aws-ecs'); // Allows working with ECS resources
import ec2 = require('@aws-cdk/aws-ec2'); // Allows working with EC2 and VPC resources
import iam = require('@aws-cdk/aws-iam'); // Allows working with IAM resources
import ecsPatterns = require('@aws-cdk/aws-ecs-patterns') // Helper to create ECS services with loadbalancers, and configure them
해당 모듈은 웹 애플리케이션 배포에 필요한 모든 구성 요소에 대한 액세스를 제공합니다. 첫 단계로는 다음 코드를 추가하여 계정 내에 있는 기존 기본 VPC를 찾습니다.
// Look up the default VPC
const vpc = ec2.Vpc.fromLookup(this, "VPC", {
isDefault: true
});
다음으로 사용할 컨테이너 및 구성 방법을 정의해야 합니다. 이는 컨테이너 포트, 필요한 CPU 및 메모리 양, 사용할 컨테이너 이미지를 제공하는 작업 정의를 생성하여 수행합니다. 이 가이드에서는 SampleApp 폴더에 샘플 애플리케이션과 함께 제공된 컨테이너 이미지를 구축하고 CDK가 컨테이너의 구축, 업로드 및 배포를 관리하도록 합니다. 또한 향후 가이드를 위해 작업 정의에 연결할 비어 있는 IAM 역할을 생성합니다.
작업 정의 및 IAM 역할을 추가하려면 다음 코드를 추가합니다.
const taskIamRole = new iam.Role(this, "AppRole", {
roleName: "AppRole",
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});
const taskDefinition = new ecs.FargateTaskDefinition(this, 'Task', {
taskRole: taskIamRole,
});
taskDefinition.addContainer('MyContainer', {
image: ecs.ContainerImage.fromAsset('../SampleApp'),
portMappings: [{ containerPort: 80 }],
memoryReservationMiB: 256,
cpu : 256,
});
위의 코드에서 FargateTaskDefintion 및 ContainerImage.fromAsset을 사용하여 Fargate에 배포할 작업 정의 유형을 지정했음을 확인할 수 있습니다. CDK는 SampleApp 디렉터리의 Dockerfile을 사용하여 컨테이너 이미지를 구축합니다.
다음으로 ECS 클러스터 생성, 서비스 정의, 로드 밸런서 생성, 서비스에 연결하도록 구성 및 필요한 보안 그룹 규칙을 설정해야 합니다. ECS의 서비스는 원하는 사본 수, 배포 전략 및 기타 구성을 지정하여 작업 정의를 시작하는 데 사용됩니다. 예를 들어, 컨테이너 사본을 1개만 시작할 것입니다. 보안 그룹은 인스턴스에 대한 가상 방화벽과 같이 작동하여 인바운드 및 아웃바운드 트래픽을 제어하지만 사용 중인 ECS 패턴이 이를 수행하므로 구성할 필요는 없습니다.
작업 정의 아래의 프로젝트에 다음의 코드를 추가합니다.
new ecsPatterns.ApplicationLoadBalancedFargateService(this, "MyApp", {
vpc: vpc,
taskDefinition: taskDefinition,
desiredCount: 1,
serviceName: 'MyWebApp',
assignPublicIp: true,
publicLoadBalancer: true,
})
배포할 컨테이너 이미지를 정의하는 작업 정의와 함께 모든 리소스를 생성할 위치를 지정하기 위해 이전에 조회한 VPC 개체를 전달 중입니다. desiredCount는 원하는 사본 수, serviceName은 서비스를 호출하려는 항목을 나타내며 publicLoadBalancer는 인터넷을 통해 액세스할 수 있도록 true로 설정됩니다. 이 예에서는 프라이빗 서브넷이 없는 기본 VPC를 사용하고 있으므로 assignPublicIp을 true로 설정하는 라인이 중요합니다. 모범 사례에서는 프라이빗 서브넷에서 서비스를 시작하는 것을 권장하지만 이 가이드의 범위를 벗어납니다.
이제 웹 애플리케이션을 배포할 준비가 되었지만 배포하려는 계정에 CDK를 설정해야 합니다.
bin/cdk-ecs-infra.ts 파일을 편집하여 14행의 주석을 제거합니다.
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
여기서는 AWS CLI에 구성된 계정 ID와 리전을 사용할 것입니다. CDK를 사용하기 전에 부트스트랩을 해야 합니다. 이렇게 하면 CDK가 계정에서 인프라를 관리하는 데 필요한 인프라가 생성됩니다. CDK를 부트스트랩하려면 cdk bootstrap을 실행합니다. 다음과 유사한 출력이 표시됩니다.
cdk bootstrap
#output
⏳ Bootstrapping environment aws://0123456789012/<region>...
✅ Environment aws://0123456789012/<region> bootstrapped
이는 CDK에 필요한 인프라를 생성하여 계정의 인프라를 관리합니다. CDK 애플리케이션을 시작하는 데 익숙하지 않다면 AWS CDK로 시작하기 가이드를 살펴보는 것이 좋습니다.
부트스트래핑이 완료되면 cdk deploy를 실행하여 필요한 컨테이너, 클러스터 및 모든 기타 인프라를 배포합니다. 다음과 유사한 출력이 표시됩니다.

CDK는 인프라를 배포하기 전에 보안 구성 변경에 대한 메시지를 표시합니다. 인프라 생성 시 IAM 역할 및 보안 그룹을 생성하여 보안 그룹을 변경하기 때문입니다. y를 누른 다음 Enter 키를 눌러 배포합니다. 이제 CDK가 정의된 모든 인프라를 설정합니다. 완료하는 데 몇 분 정도 걸립니다.
실행되는 동안 다음과 같은 업데이트가 표시됩니다.

완료되면 다음과 같은 서비스에 액세스하기 위한 퍼블릭 URL 링크가 있는 출력이 표시됩니다.

전체 코드 샘플
import * as cdk from '@aws-cdk/core';
import ecs = require('@aws-cdk/aws-ecs');
import ec2 = require('@aws-cdk/aws-ec2');
import iam = require('@aws-cdk/aws-iam');
import ecsPatterns = require('@aws-cdk/aws-ecs-patterns')
export class CdkEcsInfraStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Look up the default VPC
const vpc = ec2.Vpc.fromLookup(this, "VPC", {
isDefault: true
});
const taskIamRole = new iam.Role(this, "AppRole", {
roleName: "AppRole",
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});
const taskDefinition = new ecs.FargateTaskDefinition(this, 'Task', {
taskRole: taskIamRole,
});
taskDefinition.addContainer('MyContainer', {
image: ecs.ContainerImage.fromAsset('../SampleApp'),
portMappings: [{ containerPort: 80 }],
memoryReservationMiB: 256,
cpu : 256,
});
new ecsPatterns.ApplicationLoadBalancedFargateService(this, "MyApp", {
vpc: vpc,
taskDefinition: taskDefinition,
desiredCount: 1,
serviceName: 'MyWebApp',
assignPublicIp: true,
publicLoadBalancer: true,
})
}
}