개요

이 가이드는 사전 구축된 Docker 이미지를 사용하여 Amazon ECS(Elastic Container Service) 및 Fargate로 AWS에 CaseBender를 배포하는 과정을 안내합니다.

사전 요구 사항

  1. AWS 계정
  2. AWS CLI 설치 및 구성
  3. Docker 설치

1단계: 초기 설정

AWS CLI 설치 및 구성

# Homebrew 사용
brew install awscli

# AWS CLI 구성

aws configure

# ECR용 Docker 구성

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $(aws sts get-caller-identity --query Account --output text).dkr.ecr.us-east-1.amazonaws.com

2단계: AWS 인프라 설정

스토리지용 S3 버킷 생성

# S3 버킷 생성
aws s3 create-bucket \
  --bucket casebender-storage \
  --region us-east-1

# 버전 관리 활성화 (선택 사항)
aws s3api put-bucket-versioning \
  --bucket casebender-storage \
  --versioning-configuration Status=Enabled

# S3 접근용 IAM 사용자 생성
aws iam create-user --user-name casebender-storage-user

# 정책 생성 및 연결
aws iam create-policy \
  --policy-name casebender-storage-policy \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::casebender-storage",
                "arn:aws:s3:::casebender-storage/*"
            ]
        }
    ]
}'

# 사용자에게 정책 연결
aws iam attach-user-policy \
  --user-name casebender-storage-user \
  --policy-arn arn:aws:iam::YOUR_ACCOUNT_ID:policy/casebender-storage-policy

# 액세스 키 생성
aws iam create-access-key --user-name casebender-storage-user

VPC 생성

# VPC 생성
aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=casebender-vpc}]'

# DNS 호스트 이름 활성화
aws ec2 modify-vpc-attribute \
  --vpc-id <vpc-id> \
  --enable-dns-hostnames

서브넷 생성

# 퍼블릭 서브넷 생성
aws ec2 create-subnet \
  --vpc-id <vpc-id> \
  --cidr-block 10.0.1.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=casebender-public-1a}]'

aws ec2 create-subnet \
  --vpc-id <vpc-id> \
  --cidr-block 10.0.2.0/24 \
  --availability-zone us-east-1b \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=casebender-public-1b}]'

RDS(PostgreSQL) 설정

# DB 서브넷 그룹 생성
aws rds create-db-subnet-group \
  --db-subnet-group-name casebender-db-subnet \
  --db-subnet-group-description "Subnet group for CaseBender RDS" \
  --subnet-ids "<subnet-1-id>" "<subnet-2-id>"

# RDS 인스턴스 생성
aws rds create-db-instance \
  --db-instance-identifier casebender-db \
  --db-instance-class db.t3.medium \
  --engine postgres \
  --master-username superadmin \
  --master-user-password <your-secure-password> \
  --allocated-storage 20 \
  --db-subnet-group-name casebender-db-subnet

ElastiCache(Redis) 설정

# 캐시 서브넷 그룹 생성
aws elasticache create-cache-subnet-group \
  --cache-subnet-group-name casebender-cache-subnet \
  --cache-subnet-group-description "Subnet group for CaseBender Redis" \
  --subnet-ids "<subnet-1-id>" "<subnet-2-id>"

# Redis 클러스터 생성
aws elasticache create-cache-cluster \
  --cache-cluster-id casebender-redis \
  --engine redis \
  --cache-node-type cache.t3.micro \
  --num-cache-nodes 1 \
  --cache-subnet-group-name casebender-cache-subnet

3단계: ECR 리포지토리 생성

# 각 서비스에 대한 리포지토리 생성
aws ecr create-repository --repository-name casebender/app
aws ecr create-repository --repository-name casebender/workflow-processor
aws ecr create-repository --repository-name casebender/misp-processor

# AWS 계정 ID 가져오기
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# CaseBender 이미지 가져오기
docker pull casebender/casebender:latest
docker pull casebender/workflow-processor:latest
docker pull casebender/misp-processor:latest

# ECR용 이미지 태그 지정
docker tag casebender/casebender:latest ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/app:latest
docker tag casebender/workflow-processor:latest ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/workflow-processor:latest
docker tag casebender/misp-processor:latest ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/misp-processor:latest

# ECR에 이미지 푸시
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/app:latest
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/workflow-processor:latest
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/casebender/misp-processor:latest

4단계: ECS 클러스터 생성

# ECS 클러스터 생성
aws ecs create-cluster --cluster-name casebender-cluster

# 태스크 실행 역할 생성
aws iam create-role \
  --role-name ecsTaskExecutionRole \
  --assume-role-policy-document file://task-execution-assume-role.json

# 정책 연결
aws iam attach-role-policy \
  --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

5단계: 태스크 정의 생성

각 서비스에 대한 태스크 정의 JSON 파일 생성:

{
  "family": "casebender-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "executionRoleArn": "arn:aws:iam::<aws-account-id>:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "<aws-account-id>.dkr.ecr.us-east-1.amazonaws.com/casebender/app:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "POSTGRES_PRISMA_URL",
          "value": "postgresql://superadmin:password@casebender-db.xxxxx.region.rds.amazonaws.com:5432/casebender"
        },
        {
          "name": "REDIS_URL",
          "value": "redis://casebender-redis.xxxxx.region.cache.amazonaws.com:6379"
        },
        {
          "name": "AWS_S3_BUCKET",
          "value": "casebender-storage"
        },
        {
          "name": "AWS_S3_REGION",
          "value": "us-east-1"
        }
      ],
      "secrets": [
        {
          "name": "AUTH_SECRET",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:auth-secret"
        },
        {
          "name": "AUTH_SALT",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:auth-salt"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/casebender",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "app"
        }
      }
    }
  ]
}

태스크 정의 등록:

# 태스크 정의 등록
aws ecs register-task-definition --cli-input-json file://app-task-definition.json
aws ecs register-task-definition --cli-input-json file://workflow-processor-task-definition.json
aws ecs register-task-definition --cli-input-json file://misp-processor-task-definition.json

6단계: 애플리케이션 로드 밸런서 생성

# ALB 생성
aws elbv2 create-load-balancer \
  --name casebender-alb \
  --subnets <subnet-1-id> <subnet-2-id> \
  --security-groups <security-group-id>

# 대상 그룹 생성
aws elbv2 create-target-group \
  --name casebender-tg \
  --protocol HTTP \
  --port 3000 \
  --vpc-id <vpc-id> \
  --target-type ip

# 리스너 생성
aws elbv2 create-listener \
  --load-balancer-arn <alb-arn> \
  --protocol HTTPS \
  --port 443 \
  --certificates CertificateArn=<certificate-arn> \
  --default-actions Type=forward,TargetGroupArn=<target-group-arn>

7단계: ECS 서비스 생성

# 메인 앱용 서비스 생성
aws ecs create-service \
  --cluster casebender-cluster \
  --service-name casebender-app \
  --task-definition casebender-app \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<subnet-1-id>,<subnet-2-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=<target-group-arn>,containerName=app,containerPort=3000"

# 프로세서용 서비스 생성
aws ecs create-service \
  --cluster casebender-cluster \
  --service-name workflow-processor \
  --task-definition casebender-workflow-processor \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<subnet-1-id>,<subnet-2-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}"

aws ecs create-service \
  --cluster casebender-cluster \
  --service-name misp-processor \
  --task-definition casebender-misp-processor \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[<subnet-1-id>,<subnet-2-id>],securityGroups=[<security-group-id>],assignPublicIp=ENABLED}"

8단계: Route 53 설정 (선택 사항)

사용자 정의 도메인을 사용하는 경우:

# 호스팅 영역 생성 (존재하지 않는 경우)
aws route53 create-hosted-zone \
  --name yourdomain.com \
  --caller-reference $(date +%s)

# A 레코드 생성
aws route53 change-resource-record-sets \
  --hosted-zone-id <zone-id> \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "yourdomain.com",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "<alb-hosted-zone-id>",
          "DNSName": "<alb-dns-name>",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

모니터링 및 유지 관리

CloudWatch 경보 설정

# CPU 사용률 경보 생성
aws cloudwatch put-metric-alarm \
  --alarm-name casebender-cpu-alarm \
  --alarm-description "CPU 사용률이 80%를 초과했습니다" \
  --metric-name CPUUtilization \
  --namespace AWS/ECS \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --dimensions Name=ClusterName,Value=casebender-cluster \
  --evaluation-periods 2 \
  --alarm-actions <sns-topic-arn>

로그 보기

# 서비스 로그 보기
aws logs get-log-events \
  --log-group-name /ecs/casebender \
  --log-stream-name app/<container-id>

서비스 업데이트

# 새 태스크 정의로 서비스 업데이트
aws ecs update-service \
  --cluster casebender-cluster \
  --service casebender-app \
  --task-definition casebender-app:NEW_REVISION

비용 최적화

  1. 중요하지 않은 워크로드에 Fargate Spot 사용
  2. 메트릭 기반 자동 확장 구현
  3. 적절한 인스턴스 크기 선택
  4. 예측 가능한 워크로드에 예약 인스턴스 사용

보안 모범 사례

  1. 민감한 데이터에 AWS Secrets Manager 사용
  2. WAF 규칙 구현
  3. VPC 흐름 로그 활성화
  4. 정기적인 보안 그룹 감사
  5. AWS GuardDuty 활성화

다음 단계

  • AWS CodePipeline으로 CI/CD 파이프라인 설정
  • 백업 전략 구성
  • 모니터링 및 경고 구현
  • 보안 모범 사례 검토