23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
안녕하세요! 아주 오래 전 2021년에 올린 글('AWS의 IaC Framework CDK 소개')에서 AWS CDK의 기본 개념과 장점에 대해 소개드렸는데요.
오랜시간이 지났지만 ^^;; 글 후반부에 이야기 드렸었던 만큼 실제 프로젝트에서 CDK를 활용한 경험을 공유하고자 합니다.
이 글을 통해 지금은 조금 아웃데이트되었지만 Vision AI모델을 AWS상에서 서빙하는 API 서비스를 AWS CDK로 구축하는데 필요한 사례와 노하우를 공유하려고 합니다.
저희가 구축한 Vision AI API 서비스는 다음과 같은 아키텍처로 구성되어 있습니다:
API Gateway: RESTful API 엔드포인트 제공
Lambda Functions: 이미지 처리 및 AI 모델 실행
S3 Buckets: 이미지 저장 및 모델 파일 관리
DynamoDB: 메타데이터 및 처리 결과 저장
CloudFront: 정적 리소스 배포
Cognito: API 인증 및 권한 관리
처음에는 모든 리소스를 하나의 스택에 넣었다가, 관리의 어려움을 겪고 다음과 같이 분리했습니다:
// lib/stacks/network-stack.ts
export class NetworkStack extends cdk.Stack {
public readonly vpc: ec2.Vpc;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'VisionAIVpc', {
maxAzs: 2,
natGateways: 1,
});
}
}
// lib/stacks/storage-stack.ts
export class StorageStack extends cdk.Stack {
public readonly imageBucket: s3.Bucket;
public readonly modelBucket: s3.Bucket;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.imageBucket = new s3.Bucket(this, 'ImageBucket', {
versioned: true,
lifecycleRules: [{
id: 'delete-old-versions',
noncurrentVersionExpiration: cdk.Duration.days(30),
}],
});
this.modelBucket = new s3.Bucket(this, 'ModelBucket', {
encryption: s3.BucketEncryption.S3_MANAGED,
});
}
}
// lib/stacks/api-stack.ts
export class ApiStack extends cdk.Stack {
constructor(
scope: Construct,
id: string,
props: ApiStackProps
) {
super(scope, id, props);
// API Gateway와 Lambda 함수들 정의
}
}개발, 스테이징, 프로덕션 환경을 효율적으로 관리하기 위해 설정 파일을 분리했습니다:
// config/env-config.ts
export interface EnvConfig {
account: string;
region: string;
vpcCidr: string;
lambdaMemory: number;
apiThrottling: {
rateLimit: number;
burstLimit: number;
};
}
export const envConfigs: Record<string, EnvConfig> = {
dev: {
account: '123456789012',
region: 'ap-northeast-2',
vpcCidr: '10.0.0.0/16',
lambdaMemory: 1024,
apiThrottling: {
rateLimit: 100,
burstLimit: 200,
},
},
prod: {
account: '987654321098',
region: 'ap-northeast-2',
vpcCidr: '10.1.0.0/16',
lambdaMemory: 3008,
apiThrottling: {
rateLimit: 1000,
burstLimit: 2000,
},
},
};Vision AI 모델의 의존성이 크기 때문에 Lambda Layer를 적극 활용했습니다:
const aiModelLayer = new lambda.LayerVersion(this, 'AIModelLayer', {
code: lambda.Code.fromAsset('layers/ai-model'),
compatibleRuntimes: [lambda.Runtime.PYTHON_3_9],
description: 'Vision AI model dependencies',
});
const imageProcessor = new lambda.Function(this, 'ImageProcessor', {
runtime: lambda.Runtime.PYTHON_3_9,
code: lambda.Code.fromAsset('src/lambdas/image-processor'),
handler: 'index.handler',
layers: [aiModelLayer],
memorySize: props.config.lambdaMemory,
timeout: cdk.Duration.minutes(5),
environment: {
MODEL_BUCKET: props.modelBucket.bucketName,
},
});const scalableTarget = imageProcessor.addAlias('live').addAutoScaling({
minCapacity: 1,
maxCapacity: 100,
});
scalableTarget.scaleOnUtilization({
utilizationTarget: 0.75,
});imageBucket.addLifecycleRule({
id: 'move-to-ia',
transitions: [{
storageClass: s3.StorageClass.INFREQUENT_ACCESS,
transitionAfter: cdk.Duration.days(30),
}],
});CloudWatch 대시보드와 알람을 코드로 정의하여 일관성을 유지했습니다:
const dashboard = new cloudwatch.Dashboard(this, 'VisionAIDashboard', {
dashboardName: `vision-ai-${props.envName}`,
});
const apiErrors = new cloudwatch.Alarm(this, 'APIErrors', {
metric: api.metricServerError(),
threshold: 10,
evaluationPeriods: 1,
});
// SNS 토픽으로 알람 전송
apiErrors.addAlarmAction(
new cloudwatchActions.SnsAction(alarmTopic)
);GitHub Actions를 활용한 CI/CD 파이프라인을 구성했습니다:
name: Deploy CDK
on:
push:
branches: [main, develop]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Deploy to AWS
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
ENV_NAME=${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
npx cdk deploy --all --require-approval never -c env=$ENV_NAMEVision AI 모델 로딩으로 인한 콜드 스타트가 심각했습니다. 다음과 같이 해결했습니다:
// Provisioned Concurrency 설정
const provisionedConcurrency = imageProcessor.currentVersion.addAlias('provisioned', {
provisionedConcurrentExecutions: 5,
});
// 워밍업 Lambda 추가
const warmer = new lambda.Function(this, 'Warmer', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromInline(`
exports.handler = async () => {
// API 호출하여 Lambda 워밍업
};
`),
handler: 'index.handler',
});
// EventBridge로 5분마다 실행
new events.Rule(this, 'WarmerRule', {
schedule: events.Schedule.rate(cdk.Duration.minutes(5)),
targets: [new targets.LambdaFunction(warmer)],
});스택 간 리소스 참조 시 순환 의존성 문제가 발생했습니다:
// 잘못된 예
// storage-stack.ts
this.bucket.grantRead(props.apiLambda); // apiLambda는 다른 스택에서 생성
// 올바른 예
// api-stack.ts에서 처리
props.storageBucket.grantRead(this.apiLambda);팀원 간 CDK 버전 불일치로 인한 문제를 해결하기 위해:
// package.json
{
"devDependencies": {
"aws-cdk": "2.100.0",
"aws-cdk-lib": "2.100.0",
"constructs": "^10.0.0"
},
"overrides": {
"aws-cdk-lib": "2.100.0"
}
}배포 시간 단축: 수동 배포 대비 90% 시간 단축
인프라 비용 절감: 환경별 최적화로 30% 비용 절감
장애 대응 속도 향상: 코드 기반 롤백으로 5분 내 복구 가능
작게 시작하기: 처음부터 완벽한 구조를 만들려 하지 말고 점진적으로 개선
테스트 작성의 중요성: CDK 테스트로 배포 전 문제 발견
문서화: 팀원들을 위한 CDK 패턴과 가이드라인 문서 작성 필수
이후에 좀 더 고도화된 시스템을 위해 다음과 같은 개선사항을 생각해볼 수 있습니다:
CDK Pipelines 도입으로 더 정교한 CI/CD 구현
Custom Constructs 개발로 재사용성 향상
Multi-Region 배포 지원
FinOps 도구 통합으로 비용 최적화 자동화
AWS CDK는 단순히 CloudFormation의 대체재가 아닌, 인프라를 소프트웨어처럼 다룰 수 있게 해주는 강력한 도구입니다.
초기 학습 곡선은 있지만, 한번 익숙해지면 인프라 관리의 효율성이 크게 향상됩니다.
다음 포스트에서는 CDK Custom Constructs를 활용한 재사용 가능한 컴포넌트 개발에 대해 다루어보겠습니다.
질문이나 공유하고 싶은 경험이 있으시다면 댓글로 남겨주세요!
참고 자료
태그: #AWS #CDK #IaC #ServerlessArchitecture #VisionAI #CloudNative #DevOps
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.