Amazon ECS 구축 시 삽질기

백엔드·2024년 5월 23일

들어가며

최근 회사에서 dev, prod API Server를 AWS ECS로 구축하는 작업을 진행하였습니다.
자세한 이야기는 다음 기회에 다루기로 하고, 이번 글에서는 AWS ECS 구축하면서 삽질했던 부분에 대해 이야기하겠습니다.

Github Action CICD 배포 시, 용량 문제로 배포 안되는 이슈

CICD 플로우에 대해 간략하게 정리하자면,
Github Action CD Script를 사용해서 도커 파일을 빌드하고 AWS ECR에 도커 이미지를 푸시한 다음,
푸시된 이미지를 가지고 task definition을 update한 뒤,
update된 task definition을 가지고 service를 update하여 API Server container를 배포하는 방식입니다.

EC2를 가지고 ECS Cluster 생성하였고,
EC2는 t3.medium을 사용했습니다. (2cpu, 4gb memory)

또한, 저희는 redis container를 직접 띄어서 사용하고 있었기 때문에
ECS Cluster에서 2개의 service를 만들어야했습니다.

  1. api server용 service
  2. redis용 service

redis task definition에는 0.5 cpu, 0.5 memory 갖도록 설정하였고,
api server task definition에는 1cpu, 2memory를 갖도록 설정하였습니다.

배포 방식은 롤링 배포 방식으로 설정하였습니다.
이러한 상황에서, Github Action을 통해 CD가 돌아가면
EC2 인스턴스에 배포가 완료되는 동안 3개의 container가 띄어져있어야하는 시간이 있습니다.

때문에, EC2 자원이 부족하여 새로운 container를 띄우지 못하고
배포가 계속 지연이되는 문제가 발생하였습니다.

EC2 인스턴스의 하드웨어 자원을 늘리지 않는 방식으로 문제를 해결하고 싶었습니다.
배포 시, 기존 컨테이너를 바로 다운 시킬 수 없을까에 대해 고민해보았고
service 설정 시, 최소 실행 작업으로 조정할 수 있다는 것을 알게되었습니다.
따라서 최소 실행 작업을 0%로 조정하였습니다.

배포가 시작되면, 기존 container를 바로 다운시켜 EC2 자원을 확보할 수 있게하였습니다.
최소 실행 작업 0%로 조정했을 때, 발생할 수 있는 문제도 있습니다.

만약, 기존의 서버가 request 요청을 처리하고 있는 중이였다면, 해당 요청이 완료될 때까지
기다리지 않고 바로 down이 되면서 예상치 못한 문제가 발생할 수 있습니다.

따라서 prod server였다면, 해당 방식이 부적절할 수 있지만
dev server였기 때문에 해당 방식으로 발생하던 문제를 해결하였습니다.

외부 API 호출 시, TimeoutError: Request timed out 에러 발생

저희 서버에서는 opensearch api를 호출하여 유튜브 데이터를 가져오는 로직이 존재하였습니다.
ECS Cluster를 EC2로 구성하고 네트워크 모드를 awsvpc로 설정했을 때, 서버에서 외부로 호출하는 API가 모두 TimeoutError: Request timed out 에러가 발생하였습니다.

왜 그럴까?
찾아보니 다음과 같았습니다.

Amazon EC2 Linux 인스턴스에서 awsvpc 네트워크 모드를 사용하는 태스크를 호스팅할 때 작업 ENI에 퍼블릭 IP 주소가 부여되지 않습니다. 인터넷에 액세스하려면 NAT 게이트웨이를 사용하도록 구성된 프라이빗 서브넷에서 시작되어야 합니다. 자세한 정보는 Amazon VPC 사용 설명서의 NAT 게이트웨이 섹션을 참조하세요. 인바운드 네트워크 액세스는 프라이빗 IP 주소를 사용하는 VPC에서 시도하거나 VPC 내에서 로드 밸런서를 통해 라우팅되어야 합니다. 퍼블릭 서브넷 내에서 시작된 태스크는 인터넷에 액세스할 수 없습니다.

흠, ECS Cluster를 public subnet에 위치시켜도 외부에서 통신이 안되는 듯?
ECS Cluster를 private subnet에 위치 시키고 public subnet에 NAT 게이트웨이 두어 외부와 통신할 수 있도록 구성해야하는 것 같았습니다..

저는 Fargate를 사용해서 해당 문제를 해결하였습니다..

profile
백엔드 개발자

0개의 댓글