AWS CDK를 사용하여 Amazon ECS 리소스 생성 - Amazon Elastic Container Service

AWS CDK를 사용하여 Amazon ECS 리소스 생성

AWS Cloud Development Kit (AWS CDK)는 코드형 인프라(IAC) 프레임워크로 선택한 프로그래밍 언어를 사용하여 AWS 클라우드 인프라를 정의할 수 있습니다. 자체 클라우드 인프라를 정의하려면 먼저 하나 이상의 스택을 포함하는 앱(CDK 지원 언어 중 하나로)을 작성해야 합니다. 그런 다음 합성하여 AWS CloudFormation 템플릿을 만들고 리소스를 AWS 계정에 배포합니다. 이 주제의 절차를 따라서 Amazon Elastic Container Service(Amazon ECS)와 AWS CDK를 사용하여 컨테이너식 웹 서버를 Fargate에 배포할 수 있습니다.

CDK에 포함된 AWS Construct Library는 AWS 서비스가 제공하는 리소스를 모델링하는 데 사용할 수 있는 모듈을 제공합니다. 많이 사용되는 서비스의 라이브러리는 스마트 기본값과 모범 사례를 통해 큐레이트된 구조를 제공합니다. 특히 이 모듈 중 하나인 aws-ecs-patterns는 컨테이너화된 서비스와 필요한 모든 지원 리소스를 몇 줄의 코드로 정의하는 데 사용할 수 있는 상위 수준의 추상화를 제공합니다.

이 주제에서는 ApplicationLoadBalancedFargateService 구조를 사용합니다. 이 구조는 애플리케이션 로드 밸런서 뒤의 Fargate에 Amazon ECS 서비스를 배포합니다. 또한 aws-ecs-patterns 모듈에는 네트워크 로드 밸런서를 사용하고 Amazon EC2를 실행하는 구조도 포함되어 있습니다.

이 작업을 시작하기 전에 AWS CDK 개발 환경을 구축하고 다음 명령을 실행하여 AWS CDK를 설치합니다. AWS CDK 개발 환경 설정 방법에 대한 지침은 AWS CDK로 시작하기 - 사전 조건을 참조하세요.

npm install -g aws-cdk
참고

이 지침에서는 AWS CDK v2를 사용하고 있다고 가정합니다.

1단계: AWS CDK 프로젝트 설정

새 AWS CDK 앱에 대한 디렉터리를 생성하고 프로젝트를 초기화합니다.

TypeScript
mkdir hello-ecs cd hello-ecs cdk init --language typescript
JavaScript
mkdir hello-ecs cd hello-ecs cdk init --language javascript
Python
mkdir hello-ecs cd hello-ecs cdk init --language python

프로젝트가 시작되면 프로젝트의 가상 환경을 활성화하고 AWS CDK의 기준선 종속성을 설치합니다.

source .venv/bin/activate python -m pip install -r requirements.txt
Java
mkdir hello-ecs cd hello-ecs cdk init --language java

이 Maven 프로젝트를 Java IDE로 가져옵니다. 예를 들어 Eclipse에서 파일 > 가져오기 > Maven > 기존 Maven 프로젝트를 사용합니다.

C#
mkdir hello-ecs cd hello-ecs cdk init --language csharp
Go
mkdir hello-ecs cd hello-ecs cdk init --language go
참고

AWS CDK 애플리케이션 템플릿은 프로젝트 디렉토리의 이름을 사용하여 소스 파일 및 클래스의 이름을 생성합니다. 이 예에서 디렉터리 이름은 hello-ecs입니다. 다른 프로젝트 디렉터리 이름을 사용하는 경우 앱이 이 지침과 일치하지 않습니다.

AWS CDK v2에는 aws-cdk-lib라는 단일 패키지에 모든 AWS 서비스에 대한 안정적인 구성이 포함되어 있습니다. 이 패키지는 프로젝트를 초기화하거나 종속 항목으로 설치됩니다. 특정 프로그래밍 언어로 작업하는 경우 프로젝트를 처음 빌드할 때 패키지가 설치됩니다. 이 주제에서는 Amazon ECS 작업을 위한 높은 수준의 추상화를 제공하는 Amazon ECS 패턴 구문을 사용하는 방법을 설명합니다. 이 모듈은 Amazon ECS 구조 및 다른 구조를 사용하여 Amazon ECS 애플리케이션에 필요한 리소스를 프로비저닝합니다.

이러한 라이브러리를 CDK 애플리케이션으로 가져오는 데 사용하는 이름은 사용하는 프로그래밍 언어에 따라 약간 다릅니다. 참고로 다음은 지원되는 각 CDK 프로그래밍 언어에서 사용되는 이름입니다.

TypeScript
aws-cdk-lib/aws-ecs aws-cdk-lib/aws-ecs-patterns
JavaScript
aws-cdk-lib/aws-ecs aws-cdk-lib/aws-ecs-patterns
Python
aws_cdk.aws_ecs aws_cdk.aws_ecs_patterns
Java
software.amazon.awscdk.services.ecs software.amazon.awscdk.services.ecs.patterns
C#
Amazon.CDK.AWS.ECS Amazon.CDK.AWS.ECS.Patterns
Go
github.com/aws/aws-cdk-go/awscdk/v2/awsecs github.com/aws/aws-cdk-go/awscdk/v2/awsecspatterns

2단계: AWS CDK를 사용하여 Fargate에 컨테이너화된 웹 서버 정의

DockerHub에서 amazon-ecs-sample 컨테이너 이미지를 사용합니다. 이 이미지에는 Amazon Linux 2에서 실행되는 PHP 웹 앱이 포함되어 있습니다.

생성한 AWS CDK 프로젝트의 경우 스택 정의가 포함된 파일을 다음 예제 중 하나와 비슷하게 편집합니다.

참고

스택은 배포 단위입니다. 모든 리소스는 스택에 있어야 하며 스택의 모든 리소스는 동시에 배포됩니다. 리소스를 배포하지 못하면 이미 배포된 다른 모든 리소스가 롤백됩니다. AWS CDK 앱은 여러 스택을 포함할 수 있으며 한 스택의 리소스는 다른 스택의 리소스를 참조할 수 있습니다.

TypeScript

다음과 유사하도록 lib/hello-ecs-stack.ts을(를) 업데이트합니다.

import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as ecs from 'aws-cdk-lib/aws-ecs'; import * as ecsp from 'aws-cdk-lib/aws-ecs-patterns'; export class HelloEcsStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); new ecsp.ApplicationLoadBalancedFargateService(this, 'MyWebServer', { taskImageOptions: { image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), }, publicLoadBalancer: true }); } }
JavaScript

다음과 유사하도록 lib/hello-ecs-stack.js을(를) 업데이트합니다.

const cdk = require('aws-cdk-lib'); const { Construct } = require('constructs'); const ecs = require('aws-cdk-lib/aws-ecs'); const ecsp = require('aws-cdk-lib/aws-ecs-patterns'); class HelloEcsStack extends cdk.Stack { constructor(scope = Construct, id = string, props = cdk.StackProps) { super(scope, id, props); new ecsp.ApplicationLoadBalancedFargateService(this, 'MyWebServer', { taskImageOptions: { image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), }, publicLoadBalancer: true }); } } module.exports = { HelloEcsStack }
Python

다음과 유사하도록 hello-ecs/hello_ecs_stack.py을(를) 업데이트합니다.

import aws_cdk as cdk from constructs import Construct import aws_cdk.aws_ecs as ecs import aws_cdk.aws_ecs_patterns as ecsp class HelloEcsStack(cdk.Stack): def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) ecsp.ApplicationLoadBalancedFargateService(self, "MyWebServer", task_image_options=ecsp.ApplicationLoadBalancedTaskImageOptions( image=ecs.ContainerImage.from_registry("amazon/amazon-ecs-sample")), public_load_balancer=True )
Java

다음과 유사하도록 src/main/java/com.myorg/HelloEcsStack.java을(를) 업데이트합니다.

package com.myorg; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import software.amazon.awscdk.services.ecs.ContainerImage; import software.amazon.awscdk.services.ecs.patterns.ApplicationLoadBalancedFargateService; import software.amazon.awscdk.services.ecs.patterns.ApplicationLoadBalancedTaskImageOptions; public class HelloEcsStack extends Stack { public HelloEcsStack(final Construct scope, final String id) { this(scope, id, null); } public HelloEcsStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); ApplicationLoadBalancedFargateService.Builder.create(this, "MyWebServer") .taskImageOptions(ApplicationLoadBalancedTaskImageOptions.builder() .image(ContainerImage.fromRegistry("amazon/amazon-ecs-sample")) .build()) .publicLoadBalancer(true) .build(); } }
C#

다음과 유사하도록 src/HelloEcs/HelloEcsStack.cs을(를) 업데이트합니다.

using Amazon.CDK; using Constructs; using Amazon.CDK.AWS.ECS; using Amazon.CDK.AWS.ECS.Patterns; namespace HelloEcs { public class HelloEcsStack : Stack { internal HelloEcsStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { new ApplicationLoadBalancedFargateService(this, "MyWebServer", new ApplicationLoadBalancedFargateServiceProps { TaskImageOptions = new ApplicationLoadBalancedTaskImageOptions { Image = ContainerImage.FromRegistry("amazon/amazon-ecs-sample") }, PublicLoadBalancer = true }); } } }
Go

다음과 유사하도록 hello-ecs.go을(를) 업데이트합니다.

package main import ( "github.com/aws/aws-cdk-go/awscdk/v2" // "github.com/aws/aws-cdk-go/awscdk/v2/awssqs" "github.com/aws/aws-cdk-go/awscdk/v2/awsecs" "github.com/aws/aws-cdk-go/awscdk/v2/awsecspatterns" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type HelloEcsStackProps struct { awscdk.StackProps } func NewHelloEcsStack(scope constructs.Construct, id string, props *HelloEcsStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } stack := awscdk.NewStack(scope, &id, &sprops) // The code that defines your stack goes here // example resource // queue := awssqs.NewQueue(stack, jsii.String("HelloEcsQueue"), &awssqs.QueueProps{ // VisibilityTimeout: awscdk.Duration_Seconds(jsii.Number(300)), // }) res := awsecspatterns.NewApplicationLoadBalancedFargateService(stack, jsii.String("MyWebServer"), &awsecspatterns.ApplicationLoadBalancedFargateServiceProps{ TaskImageOptions: &awsecspatterns.ApplicationLoadBalancedTaskImageOptions{ Image: awsecs.ContainerImage_FromRegistry(jsii.String("amazon/amazon-ecs-sample"), &awsecs.RepositoryImageProps{}), }, }, ) awscdk.NewCfnOutput(stack, jsii.String("LoadBalancerDNS"), &awscdk.CfnOutputProps{Value: res.LoadBalancer().LoadBalancerDnsName()}) return stack } func main() { defer jsii.Close() app := awscdk.NewApp(nil) NewHelloEcsStack(app, "HelloEcsStack", &HelloEcsStackProps{ awscdk.StackProps{ Env: env(), }, }) app.Synth(nil) } // env determines the AWS environment (account+region) in which our stack is to // be deployed. For more information see: https://docs.aws.amazon.com/cdk/latest/guide/environments.html func env() *awscdk.Environment { // If unspecified, this stack will be "environment-agnostic". // Account/Region-dependent features and context lookups will not work, but a // single synthesized template can be deployed anywhere. //--------------------------------------------------------------------------- return nil // Uncomment if you know exactly what account and region you want to deploy // the stack to. This is the recommendation for production stacks. //--------------------------------------------------------------------------- // return &awscdk.Environment{ // Account: jsii.String("123456789012"), // Region: jsii.String("us-east-1"), // } // Uncomment to specialize this stack for the AWS Account and Region that are // implied by the current CLI configuration. This is recommended for dev // stacks. //--------------------------------------------------------------------------- // return &awscdk.Environment{ // Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")), // Region: jsii.String(os.Getenv("CDK_DEFAULT_REGION")), // } }

위의 짧은 스니펫에는 다음이 포함됩니다.

  • 서비스의 논리적 이름: MyWebServer.

  • DockerHub에서 얻은 컨테이너 이미지: amazon/amazon-ecs-sample.

  • 로드 밸런서에 퍼블릭 주소가 있으므로 인터넷에서 액세스할 수 있다는 사실 등 기타 관련 정보.

AWS CDK에서는 다음 리소스를 포함하여 웹 서버를 배포하는 데 필요한 모든 리소스를 만듭니다. 이 예제에서는 이러한 리소스가 생략되었습니다.

  • Amazon ECS 클러스터

  • Amazon VPC 및 Amazon EC2 인스턴스

  • Auto Scaling 그룹

  • Application Load Balancer

  • IAM 역할 및 정책

자동 프로비저닝된 일부 리소스는 스택에 정의된 모든 Amazon ECS 서비스에서 공유됩니다.

소스 파일을 저장한 다음 애플리케이션의 기본 디렉터리에서 cdk synth 명령을 실행합니다. AWS CDK가 앱을 실행하고 앱에서 AWS CloudFormation 템플릿을 합성한 다음 템플릿을 표시합니다. 템플릿은 약 600줄의 YAML 파일입니다. 파일의 시작 부분이 여기에 표시됩니다. 템플릿이 이 예제와 다를 수 있습니다.

Resources: MyWebServerLB3B5FD3AB: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: LoadBalancerAttributes: - Key: deletion_protection.enabled Value: "false" Scheme: internet-facing SecurityGroups: - Fn::GetAtt: - MyWebServerLBSecurityGroup01B285AA - GroupId Subnets: - Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99 - Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A Type: application DependsOn: - EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1DefaultRouteFF4E2178 - EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2DefaultRouteB1375520 Metadata: aws:cdk:path: HelloEcsStack/MyWebServer/LB/Resource MyWebServerLBSecurityGroup01B285AA: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Automatically created Security Group for ELB HelloEcsStackMyWebServerLB06757F57 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 Description: Allow from anyone on port 80 FromPort: 80 IpProtocol: tcp ToPort: 80 VpcId: Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521 Metadata: aws:cdk:path: HelloEcsStack/MyWebServer/LB/SecurityGroup/Resource # and so on for another few hundred lines

AWS 계정의 서비스를 배포하려면 cdk deploy 애플리케이션의 기본 디렉터리에 있는 명령을 실행합니다. AWS CDK가 생성한 IAM 정책을 승인하라는 메시지가 표시됩니다.

배포에는 AWS CDK가 여러 리소스를 생성하면서 몇 분이 소요됩니다. 배포에서 출력되는 마지막 몇 줄에는 로드 밸런서의 퍼블릭 호스트 이름과 새 웹 서버의 URL이 포함됩니다. 내용은 다음과 같습니다.

Outputs: HelloEcsStack.MyWebServerLoadBalancerDNSXXXXXXX = Hello-MyWeb-ZZZZZZZZZZZZZ-ZZZZZZZZZZ.us-west-2.elb.amazonaws.com HelloEcsStack.MyWebServerServiceURLYYYYYYYY = http://Hello-MyWeb-ZZZZZZZZZZZZZ-ZZZZZZZZZZ.us-west-2.elb.amazonaws.com

3단계: 웹 서비스 테스트

배포 출력에서 URL을 복사해 웹 브라우저에 붙여 넣습니다. 웹 서버의 다음 환영 메시지가 표시됩니다.

Amazon ECS 샘플 애플리케이션의 스크린샷 출력에 “애플리케이션이 현재 Amazon ECS에서 실행 중입니다"로 표시됩니다.

4단계: 정리

웹 서버 사용을 마친 후 애플리케이션의 기본 디렉터리에 있는 cdk destroy 명령을 실행하여 CDK를 사용하여 서비스를 종료합니다. 이렇게 하면 향후 의도하지 않은 요금이 발생하는 것을 방지할 수 있습니다.

다음 단계

AWS CDK를 사용하여 AWS 인프라를 개발하는 방법에 대해 자세히 알아보려면 AWS CDK 개발자 안내서를 참조하세요.

선택한 언어로 AWS CDK 앱을 작성하는 방법에 대한 내용은 다음을 참조하세요.

TypeScript

TypeScript에서 AWS CDK 작업

JavaScript

JavaScript에서 AWS CDK 작업

Python

Python에서 AWS CDK 작업

Java

Java에서 AWS CDK 작업

C#

C#에서 AWS CDK 작업

Go

Go에서 AWS CDK 작업

이 주제에서 사용한 AWS 구성 라이브러리에 대한 자세한 내용은 아래 AWS CDK API 참조 개요를 참조하세요.