/ecs-template

Build a server with ecs service with terraform and measure the cost with infracost.

Primary LanguageHCLMIT LicenseMIT

Download

  1. git clone https://github.com/ghdwlsgur/ecs-infra.git

  2. 리포지토리 CircleCI 등록

Terraform 변수 등록

  • _terraform/terraform.tfvars를 생성하고 아래 변수를 등록해주세요.

예시

ECS_AMI             = "ami-0b41652f00b442576"
INSTANCE_TYPE       = "t2.medium"
AWS_REGION          = "eu-central-1"
AWS_RESOURCE_PREFIX = "borough-market"
EMAIL               = "redmax45@naver.com"
  • [required] ECS_AMI

컨테이너를 생성할 인스턴의 AMI를 등록합니다.

  • [required] INSTANCE_TYPE

컨테이너를 생성할 인스턴스의 타입을 지정합니다.

  • [required] AWS_REGION

리소스를 생성할 리전을 등록합니다.

  • [required] AWS_RESOURCE_PREFIX

리소스명을 등록합니다.

  • [required] EMAIL

ECS 알람을 전송받을 이메일을 등록합니다.

CircleCI 환경변수 등록

CircleCI - Project Settings - Environment Variables

AWS 계정 (aws profile은 default로 진행합니다.)

! 환경변수명을 동일하게 등록해주세요.

  • [required] AWS_ACCESS_KEY

aws_access_key_id를 등록합니다.

  • [required] AWS_ACCESS_SECRET

aws_secret_access_key를 등록합니다.

  • [required] AWS_ACCOUNT_ID

12자리 ACCOUNT ID를 등록합니다.

  • [required] AWS_REGION

테라폼 리소스가 설치될 리전을 등록합니다. 테라폼에 등록할 변수인 AWS_REGION과 동일해야 합니다.

  • [required] AWS_USERNAME

CI/CD 워크로드 중 aws credentials 유효성을 확인하는데 사용됩니다.

  • [required] AWS_RESOURCE_NAME_PREFIX

AWS 리소스명을 지칭합니다. 테라폼에 등록할 환경변수인 AWS_RESOURCE_PREFIX와 동일해야 합니다.

Docker HUB 계정

  • [required] DOCKER_PASSWORD
  • [required] DOCKER_USERNAME

MongoDB URI

  • [required] mongoURI

테라폼 리소스 생성

cd _terraform 
terraform init 
terraform plan
terraform apply --auto-approve

파일구조 수정

📦 borough-market
└── 🗂 ephemeral
     └── 📂 .circleci
          └── 📄 config.yml 
📦 borough-market
└── 🗂 .circleci
     └── 📄 config.yml 

파일 수정 중 CircleCI 동작을 방지하기 위해 임시로 담아둔 폴더에서 밖으로 빼줍니다. 리소스를 모두 생성한 후 github 코드를 푸시하여 CircleCI를 동작합니다.

자세한 설명은 README.md에 작성하겠습니다.

Docker Compose Stack

nginx 웹서버의 설정 파일에서 다운스트림의 express 서버의 포트 3000번을 80번으로 매핑, 리버스 프록시를 구성하여 http로 들어오는 클라이언트의 요청에 응답하도록 하고 nginx와 express는 동일 네트워크에 속하도록 하여 해당 네트워크에 접근 가능하게 하며 몽고 디비와 express 서버를 같은 네트워크 브릿지로 묶어 외부에서 몽고 디비 컨테이너의 접근을 차단하도록 구성하였습니다.

CircleCI

workflow는 총 4단계로 구성하였고 aws 크레덴셜 유효성을 체크하는 job을 분리하여 유효성 실패시 조기에 실패하도록 구성하였으며 도커 허브 저장소는 현재 퍼블릭으로 유지중인 redmax45 계정의 저장소를 사용하였습니다.

build-and-deploy 진행과정은 다음과 같습니다.

  • node 이미지 빌드, nginx 이미지 빌드 (몽고DB 이미지는 도커 컴포즈 빌드 시에 빌드됩니다.)
  • express 서버 reponse 200 응답 확인
  • 첫 단계에서 빌드한 노드 이미지nginx 이미지, redmax45/mongo:0.1 이미지를 로컬에서 도커 컴포즈로 말아올린 후 ecr 저장소에 푸시합니다.
  • ecs 컨테이너에 새 서비스를 업데이트한 후 어플리케이션 로드 밸런서 DNS 200 응답 확인
  • CircleCI 상세 과정

처음 테라폼 리소스 구성시에 ecs 컨테이너에 nginx 이미지만 띄워져 있어 nginx 페이지만 로드되지만 CI/CD를 진행한 후에 도커 컴포즈를 업데이트하여 서비스를 구성하도록 하였고 위 그림과 같이 도커 허브와 ECR 저장소에 등록되는 이미지는 동일해야 했으며 이를 위해 이미지 태그를 매번 실행시마다 변경되는 고유한 값인 CIRCLE_SHA1로 지정하여 워크로드 상 빌드 및 테스트를 완료한 후 최종적으로 ECR 저장소에 푸시 및 서비스 업데이트를 진행하도록 하였습니다.

AWS Architecture

VPC

image

대략적인 VPC 아키텍처는 다음과 같습니다. 퍼블릭 서브넷에 존재하는 라우팅 테이블은 인터넷 게이트웨이와 연결되어 퍼블릭 서브넷에 위치한 인스턴스에 공용 아이피를 할당할 경우 인터넷 게이트웨이를 통해 외부와 통신이 가능해집니다. 반면에 프라이빗 서브넷의 라우팅 테이블에는 인터넷 게이트웨이와 연결되어 있지 않아 외부와 통신이 불가능합니다. 본 아키텍처에서는 NAT Gateway나 NAT Device를 사용하지 않으므로 앞으로 생성할 EC2 인스턴스도 퍼블릭 서브넷안에서만 자동 확장하여 생성할 수 있게 설정하였습니다.

Service

image

생성되는 전체 서비스는 다음과 같습니다. 앞서 설명드린 것처럼 Auto Scaling이 위치한 가용영역은 eu-central-1a, eu-central-1b, eu-central-1c가 되지만 퍼블릭 서브넷으로 범위를 한정하였습니다. 서비스 업데이트 시 ECR 저장소에 이미지를 푸시하고 해당 이미지를 도커 컴포즈로 말아올려 EC2 인스턴스의 세 개의 컨테이너를 올립니다.

Monitoring

image

모니터링 구조는 다음과 같습니다. CI/CD 워크로드를 진행하기 전 최초 리소스 생성시 Nginx 컨테이너 하나만 생성되는데 이 컨테이너의 로그를 CloudWatch 로그로 추적합니다. CloudWatch Alarms는 테라폼 모듈로 구축한 리소스로 CPU, 메모리 사용률을 추적하여 기준치 알람을 전송하고 EC2 인스턴스의 오토스케일링을 진행합니다.