본문 바로가기
Devops

AWS ECS + Fargate 를 이용해 애플리케이션 배포하기

by heekng 2024. 1. 1.
반응형

현재 저희 백엔드 팀 신규 프로젝트의 인프라는 AWS ECS + fargate 으로 운영되고 있습니다.
저희 팀의 데브옵스 성격의 작업을 대부분 담당하는 입장에서 팀원 모두가 기본적인 인프라 구성을 이해해야 하고, 신규 서버 생성시 배포환경을 구성하는 과정을 정리할 필요성이 있다고 느껴 해당 글을 작성합니다.

왜 ECS + Fargate 인가?

ECS를 선택한 이유는 여러가지 있습니다.

  1. 별도의 인스턴스 설정을 AWS 웹 또는 CLI에서 편리하게 설정할 수 있습니다.
  2. 데브옵스를 담당하는 팀(개발자) 인력이 별도로 존재하지 않을 때, ECS에 대한 이해도만 어느정도 있다면 서비스 운영을 할 수 있습니다.
  3. 기존 운영되고 있던 두개 서비스 모두 EC2 인스턴스 또는 마이그레이션 되지 않은 IDC 서버에서 운영되고 있는데, 각각의 서비스의 WAS설정과 인스턴스 설정 또는 서버의 사양이 달라 생기는 이슈가 종종 생기고는 했습니다.
    이슈 해결 과정에서 잘 운영되는 서버와 문제가 생긴 서버를 비교해가면서 WAS 서버와 WEB 서버의 설정을 맞춰 나가는 상황이 생기는데, AWS를 사용할 수 있는 상황에서는 편리하게 운영되는 서버 모두 동일한 환경을 맞출 수 있습니다.

EC2와 fargate를 선택하는 기준은 특별하게 두지 않았지만
EC2를 선택하였을 때 오토스케일링, 네트워크 설정 등을 직접 유지보수해야한다는 불편함이 있고
Fargate의 경우 사용한 금액에 대해서만 비용을 지불하는 강력한 장점이 있어 Fargate를 선택하게 되었습니다.

개인적인 생각으로 현재 사내 환경에서는 코드로 인프라를 관리할 수 있다는 점과 모든 서버가 동일한 환경을 가지고 있다는 점이 최고의 장점이라 생각합니다.

배포 환경 구성

배포는 Github Action으로 진행합니다.
Jenkins도 사용할 수 있지만, 배포 파이프라인도 action 코드로 관리하기 위해 선택했습니다.

Elastic Container Registry (ECR) 설정

ECR -> Repositories -> 리포지토리 생성

  • Github repository 또는 Docker image 와 같은 개념이라고 보면 됩니다.
  • 이후 ECS에서 사용할 이미지를 저장할 저장소를 생성합니다.
  • Nginx, Application, pinpoint agent 등이 있을 수 있겠네요.

임시로 지정할 이미지 등록

ECR -> 리포지토리

뒤에서 진행할 github action으로 image push 단계까지만 진행해 이미지를 리포지토리에 push하면 해당 단계를 생략할 수 있습니다.

  • 로컬에서 임시로 실행할 이미지를 푸시합니다.
  • awscli 설치 후 aws configure 명령어로 접근 가능한 계정을 등록한 후 진행하면 됩니다.

ECS Task definition 설정

Elastic Container Service -> 테스크 정의 -> 새 태스크 정의 생성

  • Task definition은 Docker compose의 docker-compose.yml과 같이 하나의 컨테이너에서 실행될 작업을 정의합니다.
  • 실제 운영될 태스크 정의는 github action에서 만들어서 등록하며, 해당 단계는 임시로 ecs 서비스를 등록하기 위해 생성합니다.

 

 

ECS 클러스터 생성

Elastic Container Service -> 클러스터 생성

 

ECS 서비스 생성

Elastic Container Service -> 클러스터 선택 -> 서비스 생성


실제로 애플리케이션이 운영될 서비스를 생성합니다.

 

 

 


기다리면 로드밸런서, 대상그룹 등을 생성 한 후 서비스가 실행됩니다.
임시로 올려둔 이미지에서 대상그룹 상태확인 경로를 지원하지 않을 경우 테스크를 반복해서 재실행 할 수 있습니다.
현재 예제에서는 Nginx 이미지만 task에 등록했으며 Nginx에서 /nginx-health-check 경로로 들어오는 요청에 200 으로 응답할 수 있도록 설정했습니다.

만약 healthCheck가 원활하지 않아 태스크가 정상적으로 실행되지 않는다면 다음 단계인 codeDeploy 생성이 진행되지 않을 수 있습니다.

또한, 생성된 로드밸런서의 가용영역을 public subnet 으로 변경해주는 작업이 필요합니다.

서비스는 private 서브넷, 로드밸런서는 public 서브넷에 둡니다.
로드밸런서를 통해서만 서비스에 접근 가능하게 하기 위함입니다.
로드밸런서를 미리 private 서브넷에 생성해두고 기존 로드밸런서 사용 옵션을 선택해도 좋습니다.

 

로그 그룹 생성

CloudWatch -> 로그 그룹 생성


ECS 테스크 내에서 사용할 로그 그룹을 생성합니다.

Github Action 정의

상황에 따라 Action 플로우가 달라지기 때문에 도커 이미지 빌드 및 배포 실행까지의 과정을 정리합니다.

  - name: build docker image
	id: build-docker-image
	env:
	  ECR_REGISTRY: # ECR REGISTRY 값
	  ECR_REPOSITORY: # ECR REPOSITORY 값
	  DOCKER_FILE_PATH: # 빌드할 도커 파일 경로
	  IMAGE_TAG: # 도커 이미지의 TAG
	run: |
	  docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f $DOCKER_FILE_PATH .
	  docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
	  echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

  - name: Render ECS Task Definition
	id: render-task-definition
	uses: aws-actions/amazon-ecs-render-task-definition@v1.1.3
	with:
	  task-definition: # 베이스가 될 Task definition json 파일 경로
	  container-name: # 컨테이너명
	  image: ${{ steps.build-docker-image.outputs.image }}

  - name: deploy ECS Task Definition
	uses: aws-actions/amazon-ecs-deploy-task-definition@v1.4.10
	with:
	  task-definition: ${{ steps.render-task-definition.outputs.task-definition }}
	  service: # ECS 서비스명
	  cluster: # ECS 클러스터 명
	  wait-for-service-stability: true
	  codedeploy-appspec: # codedeploy appspec yml 파일 경로
	  codedeploy-application: # codedeploy application 명
	  codedeploy-deployment-group: # codedeploy deployment group 명
  • build-docker-image 스탭
    • 도커 이미지를 빌드합니다.
  • render-task-definition 스탭
    • 현재 배포할 task definition을 작성합니다.
    • 기존에 작성된 task definition을 가져와 원하는 컨테이너의 이미지 값을 수정할 수 있습니다.
    • 완료시 재 작성된 task-definition을 변수로 output합니다.
  • deploy ECS Task Definition 스탭
    • 이전 스탭인 render-task-definition 스탭에서 output된 task-definition 값을 가지고 배포를 실행합니다.
    • wait-for-service-stability 옵션은 배포가 모두 완료될 때 까지 step을 마치지 않고 대기하는 설정입니다.

마침

배포라는 것이 한번 설정해두면 꽤 오랜 시간 지나면 어떤 식으로 구성되었는지 잊게 되는 것 같습니다.
이런 관점에서 보면 코드로 배포가 관리되는 것이 히스토리 확인 및 이슈 발생 시 빠른 대응이 가능하다는 점에서 큰 장점으로 다가옵니다.
ECS 가 만능은 아니지만 환경에 따라 ECS + EC2로 배포환경을 사용할 수 있으니 고려해보는 것이 좋을 것 같습니다.

ECS 사용시 주의&고려해야 할 점

ECS로 컨테이너 오케스트레이션을 사용하면서 가장 불편했던 점은 로그 조회입니다.
로그그룹을 설정해 Cloud Watch에서 컨테이너상에 저장된 로그를 조회할 수 있지만, 생각보다 CloudWatch는 친절하지 않습니다.
분산 환경에서 EC2를 사용하게 된다면 이러한 불편함은 더 크게 다가와 최악의 경우에는 이슈 발생시 각각의 서버를 직접 들어가 수십개의 터미널 창으로 로그를 찾게 됩니다.
분산 환경에서 로그를 수집하는 다양한 방법이 존재하지만 저는 ElasticSearch를 사용해 로그를 수집하고 있습니다. 관련 글은 ELK 스택을 사용해 Springboot 로그 수집하기에서 확인할 수 있습니다.
그리고 Pinpoint도 큰 도움이 되는데요. javaAgent를 이용해 서버에 들어오는 요청과 응답 그리고 코드상에서 이루어지는 진행 과정을 모두 모니터링 할 수 있기에 적극 추천드립니다.

반응형